webdavclient.go 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. package webdavclient
  2. import (
  3. "errors"
  4. "io"
  5. "log"
  6. "os"
  7. "path/filepath"
  8. "regexp"
  9. "strings"
  10. "time"
  11. "github.com/studio-b12/gowebdav"
  12. )
  13. /*
  14. WebDAV Client
  15. This script is design as a wrapper of the studio-b12/gowebdav module
  16. that allow access to webdav network drive in ArozOS and allow arozos
  17. cross-mounting each others
  18. */
  19. type WebDAVFileSystem struct {
  20. UUID string
  21. Hierarchy string
  22. root string
  23. user string
  24. tmp string
  25. c *gowebdav.Client
  26. }
  27. func NewWebDAVMount(UUID string, Hierarchy string, root string, user string, password string, tmpBuff string) (*WebDAVFileSystem, error) {
  28. //Connect to webdav server
  29. c := gowebdav.NewClient(root, user, password)
  30. err := c.Connect()
  31. if err != nil {
  32. log.Println("[WebDAV FS] Unable to connect to remote: ", err.Error())
  33. return nil, err
  34. } else {
  35. log.Println("[WebDAV FS] Connected to remote: " + root)
  36. }
  37. //Create tmp buff folder if not exists
  38. os.MkdirAll(tmpBuff, 0775)
  39. return &WebDAVFileSystem{
  40. UUID: UUID,
  41. Hierarchy: Hierarchy,
  42. c: c,
  43. root: root,
  44. user: user,
  45. tmp: tmpBuff,
  46. }, nil
  47. }
  48. func (e WebDAVFileSystem) Chmod(filename string, mode os.FileMode) error {
  49. return errors.New("filesystem type not supported")
  50. }
  51. func (e WebDAVFileSystem) Chown(filename string, uid int, gid int) error {
  52. return errors.New("filesystem type not supported")
  53. }
  54. func (e WebDAVFileSystem) Chtimes(filename string, atime time.Time, mtime time.Time) error {
  55. return errors.New("filesystem type not supported")
  56. }
  57. func (e WebDAVFileSystem) Create(filename string) (*os.File, error) {
  58. return nil, errors.New("filesystem type not supported")
  59. }
  60. func (e WebDAVFileSystem) Mkdir(filename string, mode os.FileMode) error {
  61. filename = filterFilepath(filepath.ToSlash(filepath.Clean(filename)))
  62. return e.c.Mkdir(filename, mode)
  63. }
  64. func (e WebDAVFileSystem) MkdirAll(filename string, mode os.FileMode) error {
  65. filename = filterFilepath(filepath.ToSlash(filepath.Clean(filename)))
  66. return e.c.MkdirAll(filename, mode)
  67. }
  68. func (e WebDAVFileSystem) Name() string {
  69. return ""
  70. }
  71. func (e WebDAVFileSystem) Open(filename string) (*os.File, error) {
  72. return nil, errors.New("filesystem type not supported")
  73. }
  74. func (e WebDAVFileSystem) OpenFile(filename string, flag int, perm os.FileMode) (*os.File, error) {
  75. return nil, errors.New("filesystem type not supported")
  76. }
  77. func (e WebDAVFileSystem) Remove(filename string) error {
  78. filename = filterFilepath(filepath.ToSlash(filepath.Clean(filename)))
  79. return e.c.Remove(filename)
  80. }
  81. func (e WebDAVFileSystem) RemoveAll(filename string) error {
  82. filename = filterFilepath(filepath.ToSlash(filepath.Clean(filename)))
  83. return e.c.RemoveAll(filename)
  84. }
  85. func (e WebDAVFileSystem) Rename(oldname, newname string) error {
  86. oldname = filterFilepath(filepath.ToSlash(filepath.Clean(oldname)))
  87. newname = filterFilepath(filepath.ToSlash(filepath.Clean(newname)))
  88. return e.c.Rename(oldname, newname, true)
  89. }
  90. func (e WebDAVFileSystem) Stat(filename string) (os.FileInfo, error) {
  91. filename = filterFilepath(filepath.ToSlash(filepath.Clean(filename)))
  92. return e.c.Stat(filename)
  93. }
  94. func (e WebDAVFileSystem) VirtualPathToRealPath(subpath string, username string) (string, error) {
  95. if strings.HasPrefix(subpath, e.UUID+":/") {
  96. //This is full virtual path. Trim the uuid and correct the subpath
  97. subpath = subpath[len(e.UUID+":/"):]
  98. }
  99. if e.Hierarchy == "user" {
  100. return filepath.ToSlash(filepath.Join("users", username, subpath)), nil
  101. } else if e.Hierarchy == "public" {
  102. return filepath.ToSlash(subpath), nil
  103. }
  104. return "", errors.New("unsupported filesystem hierarchy")
  105. }
  106. func (e WebDAVFileSystem) RealPathToVirtualPath(rpath string, username string) (string, error) {
  107. return e.UUID + ":" + filepath.ToSlash(rpath), nil
  108. }
  109. func (e WebDAVFileSystem) FileExists(filename string) bool {
  110. filename = filterFilepath(filepath.ToSlash(filepath.Clean(filename)))
  111. _, err := e.c.Stat(filename)
  112. if os.IsNotExist(err) || err != nil {
  113. return false
  114. }
  115. return true
  116. }
  117. func (e WebDAVFileSystem) IsDir(filename string) bool {
  118. filename = filterFilepath(filepath.ToSlash(filepath.Clean(filename)))
  119. s, err := e.c.Stat(filename)
  120. if err != nil {
  121. return false
  122. }
  123. return s.IsDir()
  124. }
  125. //Notes: This is not actual Glob function. This just emulate Glob using ReadDir with max depth 1 layer
  126. func (e WebDAVFileSystem) Glob(wildcard string) ([]string, error) {
  127. fileInfos, err := e.c.ReadDir(filterFilepath(filepath.ToSlash(filepath.Clean(filepath.Dir(wildcard)))))
  128. if err != nil {
  129. return []string{}, err
  130. }
  131. validFiles := []string{}
  132. for _, fileInfo := range fileInfos {
  133. thisFullPath := filepath.ToSlash(filepath.Join(filepath.Dir(wildcard), fileInfo.Name()))
  134. match, _ := regexp.MatchString(wildcard, thisFullPath)
  135. if match {
  136. validFiles = append(validFiles, thisFullPath)
  137. }
  138. }
  139. return validFiles, nil
  140. }
  141. func (e WebDAVFileSystem) GetFileSize(filename string) int64 {
  142. filename = filterFilepath(filepath.ToSlash(filepath.Clean(filename)))
  143. s, err := e.Stat(filename)
  144. if err != nil {
  145. return 0
  146. }
  147. return s.Size()
  148. }
  149. func (e WebDAVFileSystem) GetModTime(filename string) (int64, error) {
  150. filename = filterFilepath(filepath.ToSlash(filepath.Clean(filename)))
  151. s, err := e.Stat(filename)
  152. if err != nil {
  153. return 0, err
  154. }
  155. return s.ModTime().Unix(), nil
  156. }
  157. func (e WebDAVFileSystem) WriteFile(filename string, content []byte, mode os.FileMode) error {
  158. filename = filterFilepath(filepath.ToSlash(filepath.Clean(filename)))
  159. return e.c.Write(filename, content, mode)
  160. }
  161. func (e WebDAVFileSystem) ReadFile(filename string) ([]byte, error) {
  162. filename = filterFilepath(filepath.ToSlash(filepath.Clean(filename)))
  163. bytes, err := e.c.Read(filename)
  164. if err != nil {
  165. return []byte(""), err
  166. }
  167. return bytes, nil
  168. }
  169. func (e WebDAVFileSystem) WriteStream(filename string, stream io.Reader, mode os.FileMode) error {
  170. filename = filterFilepath(filepath.ToSlash(filepath.Clean(filename)))
  171. return e.c.WriteStream(filename, stream, mode)
  172. }
  173. func (e WebDAVFileSystem) ReadStream(filename string) (io.ReadCloser, error) {
  174. filename = filterFilepath(filepath.ToSlash(filepath.Clean(filename)))
  175. return e.c.ReadStream(filename)
  176. }
  177. func (e WebDAVFileSystem) Walk(rootpath string, walkFn filepath.WalkFunc) error {
  178. rootpath = filepath.ToSlash(filepath.Clean(rootpath))
  179. return e.walk(rootpath, walkFn)
  180. }
  181. func (e WebDAVFileSystem) walk(thisPath string, walkFun filepath.WalkFunc) error {
  182. files, err := e.c.ReadDir(thisPath)
  183. if err != nil {
  184. return err
  185. }
  186. for _, file := range files {
  187. thisFileFullPath := filepath.ToSlash(filepath.Join(thisPath, file.Name()))
  188. if file.IsDir() {
  189. err = walkFun(thisFileFullPath, file, nil)
  190. if err != nil {
  191. return err
  192. }
  193. err = e.walk(thisFileFullPath, walkFun)
  194. if err != nil {
  195. return err
  196. }
  197. } else {
  198. err = walkFun(thisFileFullPath, file, nil)
  199. if err != nil {
  200. return err
  201. }
  202. }
  203. }
  204. return nil
  205. }
  206. func filterFilepath(rawpath string) string {
  207. if strings.HasPrefix(rawpath, "./") {
  208. return rawpath[1:]
  209. } else if rawpath == "." {
  210. return "/"
  211. }
  212. return rawpath
  213. }