package ctrl import ( "net/http" "path/filepath" "strings" . "github.com/mickael-kerjean/filestash/server/common" "github.com/mickael-kerjean/filestash/server/model" "github.com/mickael-kerjean/net/webdav" ) func WebdavHandler(ctx *App, res http.ResponseWriter, req *http.Request) { if ctx.Share.Id == "" { http.NotFound(res, req) return } // https://github.com/golang/net/blob/master/webdav/webdav.go#L49-L68 canRead := model.CanRead(ctx) canWrite := model.CanEdit(ctx) canUpload := model.CanUpload(ctx) switch req.Method { case "OPTIONS", "HEAD", "GET": if canRead == false { SendErrorResult(res, ErrPermissionDenied) return } case "MKCOL", "DELETE", "COPY", "MOVE", "PROPPATCH": if canWrite == false { SendErrorResult(res, ErrPermissionDenied) return } case "PROPFIND": if canRead == false { SendErrorResult(res, ErrPermissionDenied) return } case "PUT": if canWrite == false || canUpload == false { SendErrorResult(res, ErrPermissionDenied) return } case "LOCK", "UNLOCK": if canWrite == false || canUpload == false { SendErrorResult(res, ErrPermissionDenied) return } default: SendErrorResult(res, ErrNotImplemented) return } h := &webdav.Handler{ Prefix: "/s/" + ctx.Share.Id, FileSystem: model.NewWebdavFs(ctx.Backend, ctx.Share.Backend, ctx.Share.Path, req), LockSystem: model.NewWebdavLock(), } h.ServeHTTP(res, req) } /* * OSX ask for a lot of crap while mounting as a network drive. To avoid wasting resources with such * an imbecile and considering we can't even see the source code they are running, the best approach we * could go on is: "crap in, crap out" where useless request coming in are identified and answer appropriatly */ func WebdavBlacklist(fn HandlerFunc) HandlerFunc { return HandlerFunc(func(ctx *App, res http.ResponseWriter, req *http.Request) { base := filepath.Base(req.URL.String()) if req.Method == "PUT" || req.Method == "MKCOL" { if strings.HasPrefix(base, "._") { res.WriteHeader(http.StatusMethodNotAllowed) res.Write([]byte("")) return } else if base == ".DS_Store" { res.WriteHeader(http.StatusMethodNotAllowed) res.Write([]byte("")) return } else if base == ".localized" { res.WriteHeader(http.StatusMethodNotAllowed) res.Write([]byte("")) return } } else if req.Method == "PROPFIND" { if strings.HasPrefix(base, "._") { res.WriteHeader(http.StatusForbidden) return } else if base == ".DS_Store" { res.WriteHeader(http.StatusForbidden) res.Write([]byte("")) return } else if base == ".localized" { res.WriteHeader(http.StatusForbidden) return } else if base == ".ql_disablethumbnails" { res.WriteHeader(http.StatusForbidden) res.Write([]byte("")) return } else if base == ".ql_disablecache" { res.WriteHeader(http.StatusForbidden) return } else if base == ".hidden" { res.WriteHeader(http.StatusForbidden) return } else if base == ".Spotlight-V100" { res.WriteHeader(http.StatusForbidden) return } else if base == ".metadata_never_index" { res.WriteHeader(http.StatusForbidden) return } else if base == "Contents" { res.WriteHeader(http.StatusForbidden) return } else if base == ".metadata_never_index_unless_rootfs" { res.WriteHeader(http.StatusForbidden) return } } else if req.Method == "GET" { if base == ".DS_Store" { res.WriteHeader(http.StatusForbidden) res.Write([]byte("")) return } } else if req.Method == "DELETE" { if base == ".DS_Store" { res.WriteHeader(http.StatusForbidden) res.Write([]byte("")) return } } else if req.Method == "LOCK" || req.Method == "UNLOCK" { if base == ".DS_Store" { res.WriteHeader(http.StatusMethodNotAllowed) res.Write([]byte("")) return } } fn(ctx, res, req) }) }