From c1b02380ac65c7bc08d8364d09604de2835fc34b Mon Sep 17 00:00:00 2001 From: HFO4 <912394456@qq.com> Date: Fri, 20 Dec 2019 11:59:09 +0800 Subject: [PATCH] Feat: adapt copy method for WebDAV --- pkg/util/path.go | 9 +++++ pkg/webdav/file.go | 90 ++++++-------------------------------------- pkg/webdav/webdav.go | 11 +++++- routers/router.go | 1 + 4 files changed, 31 insertions(+), 80 deletions(-) diff --git a/pkg/util/path.go b/pkg/util/path.go index dcb0587..81f6e46 100644 --- a/pkg/util/path.go +++ b/pkg/util/path.go @@ -15,6 +15,15 @@ func FillSlash(path string) string { return path + "/" } +// RemoveSlash 移除路径最后的`/` +// TODO 测试 +func RemoveSlash(path string) string { + if len(path) > 1 { + return strings.TrimSuffix(path, "/") + } + return path +} + // SplitPath 分割路径为列表 func SplitPath(path string) []string { if len(path) == 0 || path[0] != '/' { diff --git a/pkg/webdav/file.go b/pkg/webdav/file.go index 1a74bf7..4f06802 100644 --- a/pkg/webdav/file.go +++ b/pkg/webdav/file.go @@ -180,95 +180,29 @@ func copyProps(dst, src File) error { // copyFiles copies files and/or directories from src to dst. // // See section 9.8.5 for when various HTTP status codes apply. -func copyFiles(ctx context.Context, fs FileSystem, src, dst string, overwrite bool, depth int, recursion int) (status int, err error) { +func copyFiles(ctx context.Context, fs *filesystem.FileSystem, src FileInfo, dst string, overwrite bool, depth int, recursion int) (status int, err error) { if recursion == 1000 { return http.StatusInternalServerError, errRecursionTooDeep } recursion++ - // TODO: section 9.8.3 says that "Note that an infinite-depth COPY of /A/ - // into /A/B/ could lead to infinite recursion if not handled correctly." - - srcFile, err := fs.OpenFile(ctx, src, os.O_RDONLY, 0) - if err != nil { - if os.IsNotExist(err) { - return http.StatusNotFound, err - } - return http.StatusInternalServerError, err - } - defer srcFile.Close() - srcStat, err := srcFile.Stat() - if err != nil { - if os.IsNotExist(err) { - return http.StatusNotFound, err - } - return http.StatusInternalServerError, err - } - srcPerm := srcStat.Mode() & os.ModePerm - - created := false - if _, err := fs.Stat(ctx, dst); err != nil { - if os.IsNotExist(err) { - created = true - } else { - return http.StatusForbidden, err - } - } else { - if !overwrite { - return http.StatusPreconditionFailed, os.ErrExist - } - if err := fs.RemoveAll(ctx, dst); err != nil && !os.IsNotExist(err) { - return http.StatusForbidden, err - } - } - - if srcStat.IsDir() { - if err := fs.Mkdir(ctx, dst, srcPerm); err != nil { - return http.StatusForbidden, err - } - if depth == infiniteDepth { - children, err := srcFile.Readdir(-1) - if err != nil { - return http.StatusForbidden, err - } - for _, c := range children { - name := c.Name() - s := path.Join(src, name) - d := path.Join(dst, name) - cStatus, cErr := copyFiles(ctx, fs, s, d, overwrite, depth, recursion) - if cErr != nil { - // TODO: MultiStatus. - return cStatus, cErr - } - } + if src.IsDir() { + err := fs.Copy( + ctx, + []uint{src.(*model.Folder).ID}, + []uint{}, src.(*model.Folder).Position, + path.Dir(dst), + ) + if err != nil { + return http.StatusInternalServerError, err } - } else { - dstFile, err := fs.OpenFile(ctx, dst, os.O_RDWR|os.O_CREATE|os.O_TRUNC, srcPerm) + err := fs.Copy(ctx, []uint{src.(*model.File).ID}, []uint{}, src.(*model.File).Position, dst) if err != nil { - if os.IsNotExist(err) { - return http.StatusConflict, err - } - return http.StatusForbidden, err - - } - _, copyErr := io.Copy(dstFile, srcFile) - propsErr := copyProps(dstFile, srcFile) - closeErr := dstFile.Close() - if copyErr != nil { - return http.StatusInternalServerError, copyErr - } - if propsErr != nil { - return http.StatusInternalServerError, propsErr - } - if closeErr != nil { - return http.StatusInternalServerError, closeErr + return http.StatusInternalServerError, err } } - if created { - return http.StatusCreated, nil - } return http.StatusNoContent, nil } diff --git a/pkg/webdav/webdav.go b/pkg/webdav/webdav.go index 6984d61..87ab564 100644 --- a/pkg/webdav/webdav.go +++ b/pkg/webdav/webdav.go @@ -12,6 +12,7 @@ import ( "github.com/HFO4/cloudreve/pkg/filesystem" "github.com/HFO4/cloudreve/pkg/filesystem/fsctx" "github.com/HFO4/cloudreve/pkg/filesystem/local" + "github.com/HFO4/cloudreve/pkg/util" "net/http" "net/url" "path" @@ -38,7 +39,7 @@ func (h *Handler) stripPrefix(p string, uid uint) (string, int, error) { } prefix := h.Prefix + strconv.FormatUint(uint64(uid), 10) if r := strings.TrimPrefix(p, prefix); len(r) < len(p) { - return r, http.StatusOK, nil + return util.RemoveSlash(r), http.StatusOK, nil } return p, http.StatusNotFound, errPrefixMismatch } @@ -406,6 +407,12 @@ func (h *Handler) handleCopyMove(w http.ResponseWriter, r *http.Request, fs *fil ctx := r.Context() + isExist, target := isPathExist(ctx, fs, src) + + if !isExist { + return http.StatusNotFound, nil + } + if r.Method == "COPY" { // Section 7.5.1 says that a COPY only needs to lock the destination, // not both destination and source. Strictly speaking, this is racy, @@ -429,7 +436,7 @@ func (h *Handler) handleCopyMove(w http.ResponseWriter, r *http.Request, fs *fil return http.StatusBadRequest, errInvalidDepth } } - return copyFiles(ctx, h.FileSystem, src, dst, r.Header.Get("Overwrite") != "F", depth, 0) + return copyFiles(ctx, fs, target, dst, r.Header.Get("Overwrite") != "F", depth, 0) } release, status, err := h.confirmLocks(r, src, dst, fs) diff --git a/routers/router.go b/routers/router.go index 7f83b54..5e6908d 100644 --- a/routers/router.go +++ b/routers/router.go @@ -24,6 +24,7 @@ func initWebDAV(group *gin.RouterGroup) { group.Handle("LOCK", ":uid/*path", controllers.ServeWebDAV) group.Handle("UNLOCK", ":uid/*path", controllers.ServeWebDAV) group.Handle("PROPPATCH", ":uid/*path", controllers.ServeWebDAV) + group.Handle("COPY", ":uid/*path", controllers.ServeWebDAV) } }