11da2a6c9b
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.
142 lines
3.9 KiB
Go
142 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)
|