rclone/cmd/serve/webdav/webdav.go
Nick Craig-Wood 11da2a6c9b Break the fs package up into smaller parts.
The purpose of this is to make it easier to maintain and eventually to
allow the rclone backends to be re-used in other projects without
having to use the rclone configuration system.

The new code layout is documented in CONTRIBUTING.
2018-01-15 17:51:14 +00:00

143 lines
3.9 KiB
Go

package webdav
// FIXME need to fix directory listings reading each file - make an
// override for getcontenttype property?
import (
"net/http"
"os"
"github.com/ncw/rclone/cmd"
"github.com/ncw/rclone/fs"
"github.com/ncw/rclone/fs/log"
"github.com/ncw/rclone/vfs"
"github.com/ncw/rclone/vfs/vfsflags"
"github.com/spf13/cobra"
"golang.org/x/net/context"
"golang.org/x/net/webdav"
)
// Globals
var (
bindAddress = "localhost:8081"
)
func init() {
Command.Flags().StringVarP(&bindAddress, "addr", "", bindAddress, "IPaddress:Port to bind server to.")
vfsflags.AddFlags(Command.Flags())
}
// Command definition for cobra
var Command = &cobra.Command{
Use: "webdav remote:path",
Short: `Serve remote:path over webdav.`,
Long: `
rclone serve webdav implements a basic webdav server to serve the
remote over HTTP via the webdav protocol. This can be viewed with a
webdav client or you can make a remote of type webdav to read and
write it.
NB at the moment each directory listing reads the start of each file
which is undesirable: see https://github.com/golang/go/issues/22577
` + vfs.Help,
Run: func(command *cobra.Command, args []string) {
cmd.CheckArgs(1, 1, command, args)
fsrc := cmd.NewFsSrc(args)
cmd.Run(false, false, command, func() error {
return serveWebDav(fsrc)
})
},
}
// serve the remote
func serveWebDav(f fs.Fs) error {
fs.Logf(f, "WebDav Server started on %v", bindAddress)
webdavFS := &WebDAV{
f: f,
vfs: vfs.New(f, &vfsflags.Opt),
}
handler := &webdav.Handler{
FileSystem: webdavFS,
LockSystem: webdav.NewMemLS(),
Logger: webdavFS.logRequest, // FIXME
}
// FIXME use our HTTP transport
http.Handle("/", handler)
return http.ListenAndServe(bindAddress, nil)
}
// WebDAV is a webdav.FileSystem interface
//
// A FileSystem implements access to a collection of named files. The elements
// in a file path are separated by slash ('/', U+002F) characters, regardless
// of host operating system convention.
//
// Each method has the same semantics as the os package's function of the same
// name.
//
// Note that the os.Rename documentation says that "OS-specific restrictions
// might apply". In particular, whether or not renaming a file or directory
// overwriting another existing file or directory is an error is OS-dependent.
type WebDAV struct {
f fs.Fs
vfs *vfs.VFS
}
// check interface
var _ webdav.FileSystem = (*WebDAV)(nil)
// logRequest is called by the webdav module on every request
func (w *WebDAV) logRequest(r *http.Request, err error) {
fs.Infof(r.URL.Path, "%s from %s", r.Method, r.RemoteAddr)
}
// Mkdir creates a directory
func (w *WebDAV) Mkdir(ctx context.Context, name string, perm os.FileMode) (err error) {
defer log.Trace(name, "perm=%v", perm)("err = %v", &err)
dir, leaf, err := w.vfs.StatParent(name)
if err != nil {
return err
}
_, err = dir.Mkdir(leaf)
return err
}
// OpenFile opens a file or a directory
func (w *WebDAV) OpenFile(ctx context.Context, name string, flags int, perm os.FileMode) (file webdav.File, err error) {
defer log.Trace(name, "flags=%v, perm=%v", flags, perm)("err = %v", &err)
return w.vfs.OpenFile(name, flags, perm)
}
// RemoveAll removes a file or a directory and its contents
func (w *WebDAV) RemoveAll(ctx context.Context, name string) (err error) {
defer log.Trace(name, "")("err = %v", &err)
node, err := w.vfs.Stat(name)
if err != nil {
return err
}
err = node.RemoveAll()
if err != nil {
return err
}
return nil
}
// Rename a file or a directory
func (w *WebDAV) Rename(ctx context.Context, oldName, newName string) (err error) {
defer log.Trace(oldName, "newName=%q", newName)("err = %v", &err)
return w.vfs.Rename(oldName, newName)
}
// Stat returns info about the file or directory
func (w *WebDAV) Stat(ctx context.Context, name string) (fi os.FileInfo, err error) {
defer log.Trace(name, "")("fi=%+v, err = %v", &fi, &err)
return w.vfs.Stat(name)
}
// check interface
var _ os.FileInfo = vfs.Node(nil)