webdav: add MOVE support to server

This commit is contained in:
Simon Ser 2020-01-22 11:43:36 +01:00
parent 83cb67070c
commit 3268102d5a
No known key found for this signature in database
GPG Key ID: 0FDE7BE0E88F5E48
4 changed files with 87 additions and 0 deletions

View File

@ -276,3 +276,7 @@ func (b *backend) Delete(r *http.Request) error {
func (b *backend) Mkcol(r *http.Request) error { func (b *backend) Mkcol(r *http.Request) error {
return internal.HTTPErrorf(http.StatusForbidden, "carddav: address book creation unsupported") 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")
}

View File

@ -122,4 +122,36 @@ func (fs LocalFileSystem) Mkdir(name string) error {
return os.Mkdir(p, 0755) return os.Mkdir(p, 0755)
} }
func (fs LocalFileSystem) MoveAll(src, dst string, overwrite bool) (created bool, err error) {
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
}
var _ FileSystem = LocalFileSystem("") var _ FileSystem = LocalFileSystem("")

View File

@ -5,6 +5,7 @@ import (
"fmt" "fmt"
"mime" "mime"
"net/http" "net/http"
"net/url"
"strings" "strings"
) )
@ -81,6 +82,7 @@ type Backend interface {
Put(r *http.Request) error Put(r *http.Request) error
Delete(r *http.Request) error Delete(r *http.Request) error
Mkcol(r *http.Request) error Mkcol(r *http.Request) error
Move(r *http.Request, dest *Href, overwrite bool) (created bool, err error)
} }
type Handler struct { type Handler struct {
@ -120,6 +122,8 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if err == nil { if err == nil {
w.WriteHeader(http.StatusCreated) w.WriteHeader(http.StatusCreated)
} }
case "MOVE":
err = h.handleMove(w, r)
default: default:
err = HTTPErrorf(http.StatusMethodNotAllowed, "webdav: unsupported method") err = HTTPErrorf(http.StatusMethodNotAllowed, "webdav: unsupported method")
} }
@ -254,3 +258,41 @@ func (h *Handler) handleProppatch(w http.ResponseWriter, r *http.Request) error
ms := NewMultistatus(*resp) ms := NewMultistatus(*resp)
return ServeMultistatus(w, ms) 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
}

View File

@ -18,6 +18,7 @@ type FileSystem interface {
Create(name string) (io.WriteCloser, error) Create(name string) (io.WriteCloser, error)
RemoveAll(name string) error RemoveAll(name string) error
Mkdir(name string) error Mkdir(name string) error
MoveAll(name, dest string, overwrite bool) (created bool, err error)
} }
// Handler handles WebDAV HTTP requests. It can be used to create a WebDAV // Handler handles WebDAV HTTP requests. It can be used to create a WebDAV
@ -213,3 +214,11 @@ func (b *backend) Mkcol(r *http.Request) error {
} }
return 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
}