webdav: implement COPY and MOVE.
Also add a -port flag to litmus_test_server.
13 of 13 copymove tests from the litmus suite pass, as does 16 of 16
basic tests.
Change-Id: Idf92cad281e15db7d4d62e28e366ea7bfa89e564
Reviewed-on: https://go-review.googlesource.com/3470
Reviewed-by: Nick Cooper <nmvc@google.com>
Reviewed-by: Robert Stepanek <robert.stepanek@gmail.com>
Reviewed-by: Nigel Tao <nigeltao@golang.org>
diff --git a/webdav/file.go b/webdav/file.go
index eb6abd5..4069f24 100644
--- a/webdav/file.go
+++ b/webdav/file.go
@@ -547,3 +547,88 @@
f.n.modTime = time.Now()
return lenp, nil
}
+
+// copyFiles copies files and/or directories from src to dst.
+//
+// See section 9.8.5 for when various HTTP status codes apply.
+func copyFiles(fs FileSystem, src, 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(src, os.O_RDONLY, 0)
+ if err != nil {
+ return http.StatusNotFound, err
+ }
+ defer srcFile.Close()
+ srcStat, err := srcFile.Stat()
+ if err != nil {
+ return http.StatusNotFound, err
+ }
+ srcPerm := srcStat.Mode() & os.ModePerm
+
+ created := false
+ if _, err := fs.Stat(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(dst); err != nil && !os.IsNotExist(err) {
+ return http.StatusForbidden, err
+ }
+ }
+
+ if srcStat.IsDir() {
+ if err := fs.Mkdir(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(fs, s, d, overwrite, depth, recursion)
+ if cErr != nil {
+ // TODO: MultiStatus.
+ return cStatus, cErr
+ }
+ }
+ }
+
+ } else {
+ dstFile, err := fs.OpenFile(dst, os.O_RDWR|os.O_CREATE|os.O_TRUNC, srcPerm)
+ if err != nil {
+ if os.IsNotExist(err) {
+ return http.StatusConflict, err
+ }
+ return http.StatusForbidden, err
+
+ }
+ _, copyErr := io.Copy(dstFile, srcFile)
+ closeErr := dstFile.Close()
+ if copyErr != nil {
+ return http.StatusForbidden, copyErr
+ }
+ if closeErr != nil {
+ return http.StatusForbidden, closeErr
+ }
+ }
+
+ if created {
+ return http.StatusCreated, nil
+ }
+ return http.StatusNoContent, nil
+}