Feat: adapt copy method for WebDAV

pull/247/head
HFO4 5 years ago
parent 9fdf2fe7ab
commit c1b02380ac

@ -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] != '/' {

@ -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
}

@ -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)

@ -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)
}
}

Loading…
Cancel
Save