Feat: adapt copy method for WebDAV
This commit is contained in:
parent
9fdf2fe7ab
commit
c1b02380ac
4 changed files with 32 additions and 81 deletions
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
dstFile, err := fs.OpenFile(ctx, dst, os.O_RDWR|os.O_CREATE|os.O_TRUNC, srcPerm)
|
||||
if src.IsDir() {
|
||||
err := fs.Copy(
|
||||
ctx,
|
||||
[]uint{src.(*model.Folder).ID},
|
||||
[]uint{}, src.(*model.Folder).Position,
|
||||
path.Dir(dst),
|
||||
)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return http.StatusConflict, err
|
||||
}
|
||||
return http.StatusForbidden, err
|
||||
|
||||
return http.StatusInternalServerError, 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
|
||||
} else {
|
||||
err := fs.Copy(ctx, []uint{src.(*model.File).ID}, []uint{}, src.(*model.File).Position, dst)
|
||||
if err != nil {
|
||||
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…
Add table
Reference in a new issue