package filewebdav import ( "log/slog" "net/http" "os" "path/filepath" "sync" "github.com/yourusername/caldav-server/internal/auth" "github.com/yourusername/caldav-server/internal/config" xwebdav "golang.org/x/net/webdav" ) // NewHandler returns an http.Handler that provides standard WebDAV file access, // mounted at the fixed URL /files/ for every user and rooted at // dataDir//files/ on disk. The URL is the same for all users — // which user's directory is served is resolved from the Basic Auth identity // in the request context, not from the URL. // // A dedicated xwebdav.Handler (with its own persistent LockSystem) is created // once per user and cached, so LOCK/UNLOCK state survives across requests // instead of being reset on every call. func NewHandler(cfg *config.Config, dataDir string, logger *slog.Logger) http.Handler { var ( mu sync.Mutex handlers = make(map[string]http.Handler) ) return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { p := auth.FromContext(r.Context()) if p == nil { w.WriteHeader(http.StatusUnauthorized) return } mu.Lock() h, ok := handlers[p.Username] if !ok { username := p.Username userDir := filepath.Join(dataDir, username, "files") if err := os.MkdirAll(userDir, 0o755); err != nil { mu.Unlock() logger.Error("creating user WebDAV dir", "user", username, "error", err) http.Error(w, "internal server error", http.StatusInternalServerError) return } // Each user gets their own isolated WebDAV handler (and lock // system) so paths and locks don't bleed between users, even // though they all share the same "/files/" URL. h = &xwebdav.Handler{ FileSystem: xwebdav.Dir(userDir), LockSystem: xwebdav.NewMemLS(), Logger: func(r *http.Request, err error) { if err != nil { logger.Warn("WebDAV error", "user", username, "method", r.Method, "path", r.URL.Path, "error", err) } }, Prefix: "/files", } handlers[username] = h } mu.Unlock() h.ServeHTTP(w, r) }) }