mirror of
https://github.com/1f349/go-webdav.git
synced 2024-12-22 08:14:15 +00:00
webdav: add COPY support to server
This commit is contained in:
parent
fda38c8f93
commit
6d229f4e8a
@ -277,6 +277,10 @@ 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) Copy(r *http.Request, dest *internal.Href, recursive, overwrite bool) (created bool, err error) {
|
||||||
|
panic("TODO")
|
||||||
|
}
|
||||||
|
|
||||||
func (b *backend) Move(r *http.Request, dest *internal.Href, overwrite bool) (created bool, err error) {
|
func (b *backend) Move(r *http.Request, dest *internal.Href, overwrite bool) (created bool, err error) {
|
||||||
panic("TODO")
|
panic("TODO")
|
||||||
}
|
}
|
||||||
|
90
fs_local.go
90
fs_local.go
@ -21,7 +21,7 @@ func (fs LocalFileSystem) localPath(name string) (string, error) {
|
|||||||
}
|
}
|
||||||
name = path.Clean(name)
|
name = path.Clean(name)
|
||||||
if !path.IsAbs(name) {
|
if !path.IsAbs(name) {
|
||||||
return "", internal.HTTPErrorf(http.StatusBadRequest, "webdav: expected absolute path")
|
return "", internal.HTTPErrorf(http.StatusBadRequest, "webdav: expected absolute path, got %q", name)
|
||||||
}
|
}
|
||||||
return filepath.Join(string(fs), filepath.FromSlash(name)), nil
|
return filepath.Join(string(fs), filepath.FromSlash(name)), nil
|
||||||
}
|
}
|
||||||
@ -129,6 +129,87 @@ func (fs LocalFileSystem) Mkdir(name string) error {
|
|||||||
return os.Mkdir(p, 0755)
|
return os.Mkdir(p, 0755)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func copyRegularFile(src, dst string, perm os.FileMode) error {
|
||||||
|
srcFile, err := os.Open(src)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer srcFile.Close()
|
||||||
|
|
||||||
|
dstFile, err := os.OpenFile(dst, os.O_RDWR|os.O_CREATE|os.O_TRUNC, perm)
|
||||||
|
if err != nil {
|
||||||
|
// TODO: send http.StatusConflict on os.IsNotExist
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer dstFile.Close()
|
||||||
|
|
||||||
|
if _, err := io.Copy(dstFile, srcFile); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return dstFile.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fs LocalFileSystem) Copy(src, dst string, recursive, 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: "Note that an infinite-depth COPY of /A/ into /A/B/ could lead to
|
||||||
|
// infinite recursion if not handled correctly"
|
||||||
|
|
||||||
|
srcInfo, err := os.Stat(srcPath)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
srcPerm := srcInfo.Mode() & os.ModePerm
|
||||||
|
|
||||||
|
if _, err := os.Stat(dstPath); err != nil {
|
||||||
|
if !os.IsNotExist(err) {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
created = true
|
||||||
|
} else {
|
||||||
|
if !overwrite {
|
||||||
|
return false, os.ErrExist
|
||||||
|
}
|
||||||
|
if err := os.RemoveAll(dstPath); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = filepath.Walk(srcPath, func(p string, fi os.FileInfo, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if fi.IsDir() {
|
||||||
|
if err := os.Mkdir(dstPath, srcPerm); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err := copyRegularFile(srcPath, dstPath, srcPerm); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if fi.IsDir() && !recursive {
|
||||||
|
return filepath.SkipDir
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return created, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (fs LocalFileSystem) MoveAll(src, dst string, overwrite bool) (created bool, err error) {
|
func (fs LocalFileSystem) MoveAll(src, dst string, overwrite bool) (created bool, err error) {
|
||||||
srcPath, err := fs.localPath(src)
|
srcPath, err := fs.localPath(src)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -145,13 +226,12 @@ func (fs LocalFileSystem) MoveAll(src, dst string, overwrite bool) (created bool
|
|||||||
}
|
}
|
||||||
created = true
|
created = true
|
||||||
} else {
|
} else {
|
||||||
if overwrite {
|
if !overwrite {
|
||||||
|
return false, os.ErrExist
|
||||||
|
}
|
||||||
if err := os.RemoveAll(dstPath); err != nil {
|
if err := os.RemoveAll(dstPath); err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
return false, os.ErrExist
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := os.Rename(srcPath, dstPath); err != nil {
|
if err := os.Rename(srcPath, dstPath); err != nil {
|
||||||
|
@ -82,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
|
||||||
|
Copy(r *http.Request, dest *Href, recursive, overwrite bool) (created bool, err error)
|
||||||
Move(r *http.Request, dest *Href, overwrite bool) (created bool, err error)
|
Move(r *http.Request, dest *Href, overwrite bool) (created bool, err error)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -122,8 +123,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":
|
case "COPY", "MOVE":
|
||||||
err = h.handleMove(w, r)
|
err = h.handleCopyMove(w, r)
|
||||||
default:
|
default:
|
||||||
err = HTTPErrorf(http.StatusMethodNotAllowed, "webdav: unsupported method")
|
err = HTTPErrorf(http.StatusMethodNotAllowed, "webdav: unsupported method")
|
||||||
}
|
}
|
||||||
@ -271,7 +272,7 @@ func parseDestination(h http.Header) (*Href, error) {
|
|||||||
return (*Href)(dest), nil
|
return (*Href)(dest), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handler) handleMove(w http.ResponseWriter, r *http.Request) error {
|
func (h *Handler) handleCopyMove(w http.ResponseWriter, r *http.Request) error {
|
||||||
dest, err := parseDestination(r.Header)
|
dest, err := parseDestination(r.Header)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -285,10 +286,37 @@ func (h *Handler) handleMove(w http.ResponseWriter, r *http.Request) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
created, err := h.Backend.Move(r, dest, overwrite)
|
depth := DepthInfinity
|
||||||
|
if s := r.Header.Get("Depth"); s != "" {
|
||||||
|
depth, err = ParseDepth(s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var created bool
|
||||||
|
if r.Method == "COPY" {
|
||||||
|
var recursive bool
|
||||||
|
switch depth {
|
||||||
|
case DepthZero:
|
||||||
|
recursive = false
|
||||||
|
case DepthOne:
|
||||||
|
return HTTPErrorf(http.StatusBadRequest, `webdav: "Depth: 1" is not supported in COPY request`)
|
||||||
|
case DepthInfinity:
|
||||||
|
recursive = true
|
||||||
|
}
|
||||||
|
|
||||||
|
created, err = h.Backend.Copy(r, dest, recursive, overwrite)
|
||||||
|
} else {
|
||||||
|
if depth != DepthInfinity {
|
||||||
|
return HTTPErrorf(http.StatusBadRequest, `webdav: only "Depth: infinity" is accepted in MOVE request`)
|
||||||
|
}
|
||||||
|
created, err = h.Backend.Move(r, dest, overwrite)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
if created {
|
if created {
|
||||||
w.WriteHeader(http.StatusCreated)
|
w.WriteHeader(http.StatusCreated)
|
||||||
} else {
|
} else {
|
||||||
|
@ -19,6 +19,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
|
||||||
|
Copy(name, dest string, recursive, overwrite bool) (created bool, err error)
|
||||||
MoveAll(name, dest string, overwrite bool) (created bool, err error)
|
MoveAll(name, dest string, overwrite bool) (created bool, err error)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -223,6 +224,14 @@ func (b *backend) Mkcol(r *http.Request) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *backend) Copy(r *http.Request, dest *internal.Href, recursive, overwrite bool) (created bool, err error) {
|
||||||
|
created, err = b.FileSystem.Copy(r.URL.Path, dest.Path, recursive, overwrite)
|
||||||
|
if os.IsExist(err) {
|
||||||
|
return false, &internal.HTTPError{http.StatusPreconditionFailed, err}
|
||||||
|
}
|
||||||
|
return created, err
|
||||||
|
}
|
||||||
|
|
||||||
func (b *backend) Move(r *http.Request, dest *internal.Href, overwrite bool) (created bool, err error) {
|
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)
|
created, err = b.FileSystem.MoveAll(r.URL.Path, dest.Path, overwrite)
|
||||||
if os.IsExist(err) {
|
if os.IsExist(err) {
|
||||||
|
Loading…
Reference in New Issue
Block a user