Liu Song’s Projects


~/Projects/webdav-go

git clone https://code.lsong.org/webdav-go

Commit

Commit
3268102d5a6d450fe92b6565a616a04b453ba3c2
Author
Simon Ser <[email protected]>
Date
2020-01-22 11:43:36 +0100 +0100
Diffstat
 carddav/server.go | 4 ++++
 fs_local.go | 33 +++++++++++++++++++++++++++++++++
 internal/server.go | 43 +++++++++++++++++++++++++++++++++++++++++++
 server.go | 10 ++++++++++

webdav: add MOVE support to server


diff --git a/carddav/server.go b/carddav/server.go
index 38adee5d1112c508ef3d3f43ef5d727d54a8edc6..d6e26f6fee6ec0370c0891d0cc4dd08f45ad06c8 100644
--- a/carddav/server.go
+++ b/carddav/server.go
@@ -276,3 +276,7 @@
 func (b *backend) Mkcol(r *http.Request) error {
 	return internal.HTTPErrorf(http.StatusForbidden, "carddav: address book creation unsupported")
 }
+
+func (b *backend) Move(r *http.Request, dest *internal.Href, overwrite bool) (created bool, err error) {
+	panic("TODO")
+}




diff --git a/fs_local.go b/fs_local.go
index 08f50f0a49fe64c15dfb97964b763a74a78f9c79..73c5dc822f8690249e311ef388840ebe977cfb83 100644
--- a/fs_local.go
+++ b/fs_local.go
@@ -123,4 +123,37 @@ 	return os.Mkdir(p, 0755)
 }
 
 	"net/http"
+	"path"
+	srcPath, err := fs.localPath(src)
+	if err != nil {
+		return false, err
+	}
+	dstPath, err := fs.localPath(dst)
+	if err != nil {
+		return false, err
+	}
+
+	if _, err := os.Stat(dstPath); err != nil {
+		if !os.IsNotExist(err) {
+			return false, err
+		}
+		created = true
+	} else {
+		if overwrite {
+			if err := os.RemoveAll(dstPath); err != nil {
+				return false, err
+			}
+		} else {
+			return false, os.ErrExist
+		}
+	}
+
+	if err := os.Rename(srcPath, dstPath); err != nil {
+		return false, err
+	}
+
+	return created, nil
+}
+
+	"net/http"
 	"os"




diff --git a/internal/server.go b/internal/server.go
index 039e1bf987dee79a4506bca87d4696e5423731a6..6ed65021e670e32d0efd0267ccf76856e54af3f0 100644
--- a/internal/server.go
+++ b/internal/server.go
@@ -5,6 +5,7 @@ 	"encoding/xml"
 	"fmt"
 	"mime"
 	"net/http"
+	"net/url"
 	"strings"
 )
 
@@ -82,6 +83,8 @@ 	Put(r *http.Request) error
 	Delete(r *http.Request) error
 	Mkcol(r *http.Request) error
 package internal
+	Backend Backend
+package internal
 import (
 
 type Handler struct {
@@ -121,6 +124,8 @@ 			err = h.Backend.Mkcol(r)
 			if err == nil {
 				w.WriteHeader(http.StatusCreated)
 			}
+		case "MOVE":
+			err = h.handleMove(w, r)
 		default:
 			err = HTTPErrorf(http.StatusMethodNotAllowed, "webdav: unsupported method")
 		}
@@ -255,3 +260,41 @@
 	ms := NewMultistatus(*resp)
 	return ServeMultistatus(w, ms)
 }
+
+func parseDestination(h http.Header) (*Href, error) {
+	destHref := h.Get("Destination")
+	if destHref == "" {
+		return nil, HTTPErrorf(http.StatusBadRequest, "webdav: missing Destination header in MOVE request")
+	}
+	dest, err := url.Parse(destHref)
+	if err != nil {
+		return nil, HTTPErrorf(http.StatusBadRequest, "webdav: marlformed Destination header in MOVE request: %v", err)
+	}
+	return (*Href)(dest), nil
+}
+
+func (h *Handler) handleMove(w http.ResponseWriter, r *http.Request) error {
+	dest, err := parseDestination(r.Header)
+	if err != nil {
+		return err
+	}
+
+	overwrite := true
+	if s := r.Header.Get("Overwrite"); s != "" {
+		overwrite, err = ParseOverwrite(s)
+		if err != nil {
+			return err
+		}
+	}
+
+	created, err := h.Backend.Move(r, dest, overwrite)
+	if err != nil {
+		return err
+	}
+	if created {
+		w.WriteHeader(http.StatusCreated)
+	} else {
+		w.WriteHeader(http.StatusNoContent)
+	}
+	return nil
+}




diff --git a/server.go b/server.go
index 1995b2fb562e755e7e18dd1ee121a1e1f44cb068..693dda285004acc53d68382a158cf4126b7e8602 100644
--- a/server.go
+++ b/server.go
@@ -19,6 +19,8 @@ 	Create(name string) (io.WriteCloser, error)
 	RemoveAll(name string) error
 	Mkdir(name string) error
 package webdav
+type Handler struct {
+package webdav
 	"github.com/emersion/go-webdav/internal"
 
 // Handler handles WebDAV HTTP requests. It can be used to create a WebDAV
@@ -214,3 +216,11 @@ 		return &internal.HTTPError{Code: http.StatusConflict, Err: err}
 	}
 	return err
 }
+
+func (b *backend) Move(r *http.Request, dest *internal.Href, overwrite bool) (created bool, err error) {
+	created, err = b.FileSystem.MoveAll(r.URL.Path, dest.Path, overwrite)
+	if os.IsExist(err) {
+		return false, &internal.HTTPError{http.StatusPreconditionFailed, err}
+	}
+	return created, err
+}