From 061dc1271344c5a5decb835da148fd8b141a96d3 Mon Sep 17 00:00:00 2001 From: Adphi Date: Tue, 16 Jul 2019 06:29:38 +0000 Subject: [PATCH] Resolve "Add Walk to WebDav interface" --- auth.go | 3 +- client.go | 4 +-- types/webdav.go | 5 +++ webdav.go | 93 +++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 101 insertions(+), 4 deletions(-) create mode 100644 webdav.go diff --git a/auth.go b/auth.go index cf08f01..cb074dc 100644 --- a/auth.go +++ b/auth.go @@ -4,7 +4,6 @@ import ( "fmt" req "github.com/levigross/grequests" - "github.com/studio-b12/gowebdav" "gitlab.bertha.cloud/partitio/Nextcloud-Partitio/gonextcloud/types" ) @@ -37,7 +36,7 @@ func (c *Client) Login(username string, password string) error { return &e } // Create webdav client - c.webdav = gowebdav.NewClient(c.baseURL.String()+"/remote.php/webdav", c.username, c.password) + c.webdav = newWebDav(c.baseURL.String()+"/remote.php/webdav", c.username, c.password) return nil } diff --git a/client.go b/client.go index 1006e70..35facc1 100644 --- a/client.go +++ b/client.go @@ -26,7 +26,7 @@ type Client struct { shares *Shares users *Users groups *Groups - webdav *gowebdav.Client + webdav *webDav } // NewClient create a new Client from the Nextcloud Instance URL @@ -56,7 +56,7 @@ func NewClient(hostname string) (*Client, error) { c.groups = &Groups{c} // Create empty webdav client // It will be replaced after login - c.webdav = &gowebdav.Client{} + c.webdav = &webDav{Client: &gowebdav.Client{}} return c, nil } diff --git a/types/webdav.go b/types/webdav.go index 60e44bc..4f17573 100644 --- a/types/webdav.go +++ b/types/webdav.go @@ -3,6 +3,7 @@ package types import ( "io" "os" + "path/filepath" ) // WebDav available methods @@ -31,4 +32,8 @@ type WebDav interface { Write(path string, data []byte, _ os.FileMode) error // WriteStream writes a stream WriteStream(path string, stream io.Reader, _ os.FileMode) error + + // Walk walks the file tree rooted at root, calling walkFn for each file or + // directory in the tree, including root. + Walk(path string, walkFunc filepath.WalkFunc) error } diff --git a/webdav.go b/webdav.go new file mode 100644 index 0000000..8c7564c --- /dev/null +++ b/webdav.go @@ -0,0 +1,93 @@ +package gonextcloud + +import ( + "os" + "path/filepath" + "sort" + + "github.com/studio-b12/gowebdav" +) + +type webDav struct { + *gowebdav.Client +} + +func newWebDav(url string, user string, password string) *webDav { + wb := gowebdav.NewClient(url, user, password) + return &webDav{Client: wb} +} + +// Implementation adapted from filepath.Walk + +// Walk walks the file tree rooted at root, calling walkFn for each file or +// directory in the tree, including root. All errors that arise visiting files +// and directories are filtered by walkFn. The files are walked in lexical +// order, which makes the output deterministic but means that for very +// large directories Walk can be inefficient. +// Walk does not follow symbolic links. +func (wd *webDav) Walk(root string, walkFn filepath.WalkFunc) error { + info, err := wd.Stat(root) + if err != nil { + err = walkFn(root, nil, err) + } else { + err = wd.walk(root, info, walkFn) + } + if err == filepath.SkipDir { + return nil + } + return err +} + +// walk recursively descends path, calling walkFn. +func (wd *webDav) walk(path string, info os.FileInfo, walkFn filepath.WalkFunc) error { + if !info.IsDir() { + return walkFn(path, info, nil) + } + names, err := wd.readDirNames(path) + err1 := walkFn(path, info, err) + // If err != nil, walk can't walk into this directory. + // err1 != nil means walkFn want walk to skip this directory or stop walking. + // Therefore, if one of err and err1 isn't nil, walk will return. + if err != nil || err1 != nil { + // The caller's behavior is controlled by the return value, which is decided + // by walkFn. walkFn may ignore err and return nil. + // If walkFn returns SkipDir, it will be handled by the caller. + // So walk should return whatever walkFn returns. + return err1 + } + + for _, name := range names { + filename := filepath.Join(path, name) + fileInfo, err := wd.Stat(filename) + if err != nil { + if err := walkFn(filename, fileInfo, err); err != nil && err != filepath.SkipDir { + return err + } + } else { + err = wd.walk(filename, fileInfo, walkFn) + if err != nil { + if !fileInfo.IsDir() || err != filepath.SkipDir { + return err + } + } + } + } + return nil +} + +// readDirNames reads the directory named by dirname and returns +// a sorted list of directory entries. +func (wd *webDav) readDirNames(dirname string) ([]string, error) { + fs, err := wd.ReadDir(dirname) + if err != nil { + return nil, err + } + var names []string + for _, i := range fs { + if i.IsDir() { + names = append(names, i.Name()) + } + } + sort.Strings(names) + return names, nil +}