seafile.go 39 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363
  1. // Package seafile provides an interface to the Seafile storage system.
  2. package seafile
  3. import (
  4. "context"
  5. "errors"
  6. "fmt"
  7. "io"
  8. "net/http"
  9. "net/url"
  10. "path"
  11. "strconv"
  12. "strings"
  13. "sync"
  14. "time"
  15. "github.com/coreos/go-semver/semver"
  16. "github.com/rclone/rclone/backend/seafile/api"
  17. "github.com/rclone/rclone/fs"
  18. "github.com/rclone/rclone/fs/config"
  19. "github.com/rclone/rclone/fs/config/configmap"
  20. "github.com/rclone/rclone/fs/config/configstruct"
  21. "github.com/rclone/rclone/fs/config/obscure"
  22. "github.com/rclone/rclone/fs/fserrors"
  23. "github.com/rclone/rclone/fs/fshttp"
  24. "github.com/rclone/rclone/fs/hash"
  25. "github.com/rclone/rclone/lib/bucket"
  26. "github.com/rclone/rclone/lib/cache"
  27. "github.com/rclone/rclone/lib/encoder"
  28. "github.com/rclone/rclone/lib/pacer"
  29. "github.com/rclone/rclone/lib/random"
  30. "github.com/rclone/rclone/lib/rest"
  31. )
  32. const (
  33. librariesCacheKey = "all"
  34. retryAfterHeader = "Retry-After"
  35. configURL = "url"
  36. configUser = "user"
  37. configPassword = "pass"
  38. config2FA = "2fa"
  39. configLibrary = "library"
  40. configLibraryKey = "library_key"
  41. configCreateLibrary = "create_library"
  42. configAuthToken = "auth_token"
  43. )
  44. // This is global to all instances of fs
  45. // (copying from a seafile remote to another remote would create 2 fs)
  46. var (
  47. rangeDownloadNotice sync.Once // Display the notice only once
  48. createLibraryMutex sync.Mutex // Mutex to protect library creation
  49. )
  50. // Register with Fs
  51. func init() {
  52. fs.Register(&fs.RegInfo{
  53. Name: "seafile",
  54. Description: "seafile",
  55. NewFs: NewFs,
  56. Config: Config,
  57. Options: []fs.Option{{
  58. Name: configURL,
  59. Help: "URL of seafile host to connect to.",
  60. Required: true,
  61. Examples: []fs.OptionExample{{
  62. Value: "https://cloud.seafile.com/",
  63. Help: "Connect to cloud.seafile.com.",
  64. }},
  65. Sensitive: true,
  66. }, {
  67. Name: configUser,
  68. Help: "User name (usually email address).",
  69. Required: true,
  70. Sensitive: true,
  71. }, {
  72. // Password is not required, it will be left blank for 2FA
  73. Name: configPassword,
  74. Help: "Password.",
  75. IsPassword: true,
  76. Sensitive: true,
  77. }, {
  78. Name: config2FA,
  79. Help: "Two-factor authentication ('true' if the account has 2FA enabled).",
  80. Default: false,
  81. }, {
  82. Name: configLibrary,
  83. Help: "Name of the library.\n\nLeave blank to access all non-encrypted libraries.",
  84. }, {
  85. Name: configLibraryKey,
  86. Help: "Library password (for encrypted libraries only).\n\nLeave blank if you pass it through the command line.",
  87. IsPassword: true,
  88. Sensitive: true,
  89. }, {
  90. Name: configCreateLibrary,
  91. Help: "Should rclone create a library if it doesn't exist.",
  92. Advanced: true,
  93. Default: false,
  94. }, {
  95. // Keep the authentication token after entering the 2FA code
  96. Name: configAuthToken,
  97. Help: "Authentication token.",
  98. Hide: fs.OptionHideBoth,
  99. Sensitive: true,
  100. }, {
  101. Name: config.ConfigEncoding,
  102. Help: config.ConfigEncodingHelp,
  103. Advanced: true,
  104. Default: (encoder.EncodeZero |
  105. encoder.EncodeCtl |
  106. encoder.EncodeSlash |
  107. encoder.EncodeBackSlash |
  108. encoder.EncodeDoubleQuote |
  109. encoder.EncodeInvalidUtf8),
  110. }},
  111. })
  112. }
  113. // Options defines the configuration for this backend
  114. type Options struct {
  115. URL string `config:"url"`
  116. User string `config:"user"`
  117. Password string `config:"pass"`
  118. Is2FA bool `config:"2fa"`
  119. AuthToken string `config:"auth_token"`
  120. LibraryName string `config:"library"`
  121. LibraryKey string `config:"library_key"`
  122. CreateLibrary bool `config:"create_library"`
  123. Enc encoder.MultiEncoder `config:"encoding"`
  124. }
  125. // Fs represents a remote seafile
  126. type Fs struct {
  127. name string // name of this remote
  128. root string // the path we are working on
  129. libraryName string // current library
  130. encrypted bool // Is this an encrypted library
  131. rootDirectory string // directory part of root (if any)
  132. opt Options // parsed options
  133. libraries *cache.Cache // Keep a cache of libraries
  134. librariesMutex sync.Mutex // Mutex to protect getLibraryID
  135. features *fs.Features // optional features
  136. endpoint *url.URL // URL of the host
  137. endpointURL string // endpoint as a string
  138. srv *rest.Client // the connection to the server
  139. pacer *fs.Pacer // pacer for API calls
  140. authMu sync.Mutex // Mutex to protect library decryption
  141. createDirMutex sync.Mutex // Protect creation of directories
  142. useOldDirectoryAPI bool // Use the old API v2 if seafile < 7
  143. moveDirNotAvailable bool // Version < 7.0 don't have an API to move a directory
  144. renew *Renew // Renew an encrypted library token
  145. }
  146. // ------------------------------------------------------------
  147. // NewFs constructs an Fs from the path, container:path
  148. func NewFs(ctx context.Context, name, root string, m configmap.Mapper) (fs.Fs, error) {
  149. // Parse config into Options struct
  150. opt := new(Options)
  151. err := configstruct.Set(m, opt)
  152. if err != nil {
  153. return nil, err
  154. }
  155. root = strings.Trim(root, "/")
  156. isLibraryRooted := opt.LibraryName != ""
  157. var libraryName, rootDirectory string
  158. if isLibraryRooted {
  159. libraryName = opt.LibraryName
  160. rootDirectory = root
  161. } else {
  162. libraryName, rootDirectory = bucket.Split(root)
  163. }
  164. if !strings.HasSuffix(opt.URL, "/") {
  165. opt.URL += "/"
  166. }
  167. if opt.Password != "" {
  168. var err error
  169. opt.Password, err = obscure.Reveal(opt.Password)
  170. if err != nil {
  171. return nil, fmt.Errorf("couldn't decrypt user password: %w", err)
  172. }
  173. }
  174. if opt.LibraryKey != "" {
  175. var err error
  176. opt.LibraryKey, err = obscure.Reveal(opt.LibraryKey)
  177. if err != nil {
  178. return nil, fmt.Errorf("couldn't decrypt library password: %w", err)
  179. }
  180. }
  181. // Parse the endpoint
  182. u, err := url.Parse(opt.URL)
  183. if err != nil {
  184. return nil, err
  185. }
  186. f := &Fs{
  187. name: name,
  188. root: root,
  189. libraryName: libraryName,
  190. rootDirectory: rootDirectory,
  191. libraries: cache.New(),
  192. opt: *opt,
  193. endpoint: u,
  194. endpointURL: u.String(),
  195. srv: rest.NewClient(fshttp.NewClient(ctx)).SetRoot(u.String()),
  196. pacer: getPacer(ctx, opt.URL),
  197. }
  198. f.features = (&fs.Features{
  199. CanHaveEmptyDirectories: true,
  200. BucketBased: opt.LibraryName == "",
  201. }).Fill(ctx, f)
  202. serverInfo, err := f.getServerInfo(ctx)
  203. if err != nil {
  204. return nil, err
  205. }
  206. fs.Debugf(nil, "Seafile server version %s", serverInfo.Version)
  207. // We don't support lower than seafile v6.0 (version 6.0 is already more than 3 years old)
  208. serverVersion := semver.New(serverInfo.Version)
  209. if serverVersion.Major < 6 {
  210. return nil, errors.New("unsupported Seafile server (version < 6.0)")
  211. }
  212. if serverVersion.Major < 7 {
  213. // Seafile 6 does not support recursive listing
  214. f.useOldDirectoryAPI = true
  215. f.features.ListR = nil
  216. // It also does no support moving directories
  217. f.moveDirNotAvailable = true
  218. }
  219. // Take the authentication token from the configuration first
  220. token := f.opt.AuthToken
  221. if token == "" {
  222. // If not available, send the user/password instead
  223. token, err = f.authorizeAccount(ctx)
  224. if err != nil {
  225. return nil, err
  226. }
  227. }
  228. f.setAuthorizationToken(token)
  229. if f.libraryName != "" {
  230. // Check if the library exists
  231. exists, err := f.libraryExists(ctx, f.libraryName)
  232. if err != nil {
  233. return f, err
  234. }
  235. if !exists {
  236. if f.opt.CreateLibrary {
  237. err := f.mkLibrary(ctx, f.libraryName, "")
  238. if err != nil {
  239. return f, err
  240. }
  241. } else {
  242. return f, fmt.Errorf("library '%s' was not found, and the option to create it is not activated (advanced option)", f.libraryName)
  243. }
  244. }
  245. libraryID, err := f.getLibraryID(ctx, f.libraryName)
  246. if err != nil {
  247. return f, err
  248. }
  249. f.encrypted, err = f.isEncrypted(ctx, libraryID)
  250. if err != nil {
  251. return f, err
  252. }
  253. if f.encrypted {
  254. // If we're inside an encrypted library, let's decrypt it now
  255. err = f.authorizeLibrary(ctx, libraryID)
  256. if err != nil {
  257. return f, err
  258. }
  259. // And remove the public link feature
  260. f.features.PublicLink = nil
  261. // renew the library password every 45 minutes
  262. f.renew = NewRenew(45*time.Minute, func() error {
  263. return f.authorizeLibrary(context.Background(), libraryID)
  264. })
  265. }
  266. } else {
  267. // Deactivate the cleaner feature since there's no library selected
  268. f.features.CleanUp = nil
  269. }
  270. if f.rootDirectory != "" {
  271. // Check to see if the root is an existing file
  272. remote := path.Base(rootDirectory)
  273. f.rootDirectory = path.Dir(rootDirectory)
  274. if f.rootDirectory == "." {
  275. f.rootDirectory = ""
  276. }
  277. _, err := f.NewObject(ctx, remote)
  278. if err != nil {
  279. if errors.Is(err, fs.ErrorObjectNotFound) || errors.Is(err, fs.ErrorNotAFile) {
  280. // File doesn't exist so return the original f
  281. f.rootDirectory = rootDirectory
  282. return f, nil
  283. }
  284. return f, err
  285. }
  286. // Correct root if definitely pointing to a file
  287. f.root = path.Dir(f.root)
  288. if f.root == "." || f.root == "/" {
  289. f.root = ""
  290. }
  291. // return an error with an fs which points to the parent
  292. return f, fs.ErrorIsFile
  293. }
  294. return f, nil
  295. }
  296. // Config callback for 2FA
  297. func Config(ctx context.Context, name string, m configmap.Mapper, config fs.ConfigIn) (*fs.ConfigOut, error) {
  298. serverURL, ok := m.Get(configURL)
  299. if !ok || serverURL == "" {
  300. // If there's no server URL, it means we're trying an operation at the backend level, like a "rclone authorize seafile"
  301. return nil, errors.New("operation not supported on this remote. If you need a 2FA code on your account, use the command: rclone config reconnect <remote name>: ")
  302. }
  303. u, err := url.Parse(serverURL)
  304. if err != nil {
  305. return nil, fmt.Errorf("invalid server URL %s", serverURL)
  306. }
  307. is2faEnabled, _ := m.Get(config2FA)
  308. if is2faEnabled != "true" {
  309. // no need to do anything here
  310. return nil, nil
  311. }
  312. username, _ := m.Get(configUser)
  313. if username == "" {
  314. return nil, errors.New("a username is required")
  315. }
  316. password, _ := m.Get(configPassword)
  317. if password != "" {
  318. password, _ = obscure.Reveal(password)
  319. }
  320. switch config.State {
  321. case "":
  322. // Empty state means it's the first call to the Config function
  323. if password == "" {
  324. return fs.ConfigPassword("password", "config_password", "Two-factor authentication: please enter your password (it won't be saved in the configuration)")
  325. }
  326. // password was successfully loaded from the config
  327. return fs.ConfigGoto("2fa")
  328. case "password":
  329. // password should be coming from the previous state (entered by the user)
  330. password = config.Result
  331. if password == "" {
  332. return fs.ConfigError("", "Password can't be blank")
  333. }
  334. // save it into the configuration file and keep going
  335. m.Set(configPassword, obscure.MustObscure(password))
  336. return fs.ConfigGoto("2fa")
  337. case "2fa":
  338. return fs.ConfigInput("2fa_do", "config_2fa", "Two-factor authentication: please enter your 2FA code")
  339. case "2fa_do":
  340. code := config.Result
  341. if code == "" {
  342. return fs.ConfigError("2fa", "2FA codes can't be blank")
  343. }
  344. // Create rest client for getAuthorizationToken
  345. url := u.String()
  346. if !strings.HasPrefix(url, "/") {
  347. url += "/"
  348. }
  349. srv := rest.NewClient(fshttp.NewClient(ctx)).SetRoot(url)
  350. // We loop asking for a 2FA code
  351. ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
  352. defer cancel()
  353. token, err := getAuthorizationToken(ctx, srv, username, password, code)
  354. if err != nil {
  355. return fs.ConfigConfirm("2fa_error", true, "config_retry", fmt.Sprintf("Authentication failed: %v\n\nTry Again?", err))
  356. }
  357. if token == "" {
  358. return fs.ConfigConfirm("2fa_error", true, "config_retry", "Authentication failed - no token returned.\n\nTry Again?")
  359. }
  360. // Let's save the token into the configuration
  361. m.Set(configAuthToken, token)
  362. // And delete any previous entry for password
  363. m.Set(configPassword, "")
  364. // And we're done here
  365. return nil, nil
  366. case "2fa_error":
  367. if config.Result == "true" {
  368. return fs.ConfigGoto("2fa")
  369. }
  370. return nil, errors.New("2fa authentication failed")
  371. }
  372. return nil, fmt.Errorf("unknown state %q", config.State)
  373. }
  374. // Shutdown the Fs
  375. func (f *Fs) Shutdown(ctx context.Context) error {
  376. if f.renew == nil {
  377. return nil
  378. }
  379. f.renew.Shutdown()
  380. return nil
  381. }
  382. // sets the AuthorizationToken up
  383. func (f *Fs) setAuthorizationToken(token string) {
  384. f.srv.SetHeader("Authorization", "Token "+token)
  385. }
  386. // authorizeAccount gets the auth token.
  387. func (f *Fs) authorizeAccount(ctx context.Context) (string, error) {
  388. f.authMu.Lock()
  389. defer f.authMu.Unlock()
  390. token, err := f.getAuthorizationToken(ctx)
  391. if err != nil {
  392. return "", err
  393. }
  394. return token, nil
  395. }
  396. // retryErrorCodes is a slice of error codes that we will retry
  397. var retryErrorCodes = []int{
  398. 408, // Request Timeout
  399. 429, // Rate exceeded.
  400. 500, // Get occasional 500 Internal Server Error
  401. 503, // Service Unavailable
  402. 504, // Gateway Time-out
  403. 520, // Operation failed (We get them sometimes when running tests in parallel)
  404. }
  405. // shouldRetry returns a boolean as to whether this resp and err
  406. // deserve to be retried. It returns the err as a convenience
  407. func (f *Fs) shouldRetry(ctx context.Context, resp *http.Response, err error) (bool, error) {
  408. if fserrors.ContextError(ctx, &err) {
  409. return false, err
  410. }
  411. // For 429 errors look at the Retry-After: header and
  412. // set the retry appropriately, starting with a minimum of 1
  413. // second if it isn't set.
  414. if resp != nil && (resp.StatusCode == 429) {
  415. var retryAfter = 1
  416. retryAfterString := resp.Header.Get(retryAfterHeader)
  417. if retryAfterString != "" {
  418. var err error
  419. retryAfter, err = strconv.Atoi(retryAfterString)
  420. if err != nil {
  421. fs.Errorf(f, "Malformed %s header %q: %v", retryAfterHeader, retryAfterString, err)
  422. }
  423. }
  424. return true, pacer.RetryAfterError(err, time.Duration(retryAfter)*time.Second)
  425. }
  426. return fserrors.ShouldRetry(err) || fserrors.ShouldRetryHTTP(resp, retryErrorCodes), err
  427. }
  428. func (f *Fs) shouldRetryUpload(ctx context.Context, resp *http.Response, err error) (bool, error) {
  429. if err != nil || (resp != nil && resp.StatusCode > 400) {
  430. return true, err
  431. }
  432. return false, nil
  433. }
  434. // Name of the remote (as passed into NewFs)
  435. func (f *Fs) Name() string {
  436. return f.name
  437. }
  438. // Root of the remote (as passed into NewFs)
  439. func (f *Fs) Root() string {
  440. return f.root
  441. }
  442. // String converts this Fs to a string
  443. func (f *Fs) String() string {
  444. if f.libraryName == "" {
  445. return "seafile root"
  446. }
  447. library := "library"
  448. if f.encrypted {
  449. library = "encrypted " + library
  450. }
  451. if f.rootDirectory == "" {
  452. return fmt.Sprintf("seafile %s '%s'", library, f.libraryName)
  453. }
  454. return fmt.Sprintf("seafile %s '%s' path '%s'", library, f.libraryName, f.rootDirectory)
  455. }
  456. // Precision of the ModTimes in this Fs
  457. func (f *Fs) Precision() time.Duration {
  458. // The API doesn't support setting the modified time
  459. return fs.ModTimeNotSupported
  460. }
  461. // Hashes returns the supported hash sets.
  462. func (f *Fs) Hashes() hash.Set {
  463. return hash.Set(hash.None)
  464. }
  465. // Features returns the optional features of this Fs
  466. func (f *Fs) Features() *fs.Features {
  467. return f.features
  468. }
  469. // List the objects and directories in dir into entries. The
  470. // entries can be returned in any order but should be for a
  471. // complete directory.
  472. //
  473. // dir should be "" to list the root, and should not have
  474. // trailing slashes.
  475. //
  476. // This should return fs.ErrorDirNotFound if the directory isn't
  477. // found.
  478. func (f *Fs) List(ctx context.Context, dir string) (entries fs.DirEntries, err error) {
  479. if dir == "" && f.libraryName == "" {
  480. return f.listLibraries(ctx)
  481. }
  482. return f.listDir(ctx, dir, false)
  483. }
  484. // NewObject finds the Object at remote. If it can't be found
  485. // it returns the error fs.ErrorObjectNotFound.
  486. func (f *Fs) NewObject(ctx context.Context, remote string) (fs.Object, error) {
  487. libraryName, filePath := f.splitPath(remote)
  488. libraryID, err := f.getLibraryID(ctx, libraryName)
  489. if err != nil {
  490. return nil, err
  491. }
  492. err = f.authorizeLibrary(ctx, libraryID)
  493. if err != nil {
  494. return nil, err
  495. }
  496. fileDetails, err := f.getFileDetails(ctx, libraryID, filePath)
  497. if err != nil {
  498. return nil, err
  499. }
  500. modTime, err := time.Parse(time.RFC3339, fileDetails.Modified)
  501. if err != nil {
  502. fs.LogPrintf(fs.LogLevelWarning, fileDetails.Modified, "Cannot parse datetime")
  503. }
  504. o := &Object{
  505. fs: f,
  506. libraryID: libraryID,
  507. id: fileDetails.ID,
  508. remote: remote,
  509. pathInLibrary: filePath,
  510. modTime: modTime,
  511. size: fileDetails.Size,
  512. }
  513. return o, nil
  514. }
  515. // Put in to the remote path with the modTime given of the given size
  516. //
  517. // When called from outside an Fs by rclone, src.Size() will always be >= 0.
  518. // But for unknown-sized objects (indicated by src.Size() == -1), Put should either
  519. // return an error or upload it properly (rather than e.g. calling panic).
  520. //
  521. // May create the object even if it returns an error - if so
  522. // will return the object and the error, otherwise will return
  523. // nil and the error
  524. func (f *Fs) Put(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) (fs.Object, error) {
  525. object := f.newObject(ctx, src.Remote(), src.Size(), src.ModTime(ctx))
  526. // Check if we need to create a new library at that point
  527. if object.libraryID == "" {
  528. library, _ := f.splitPath(object.remote)
  529. err := f.Mkdir(ctx, library)
  530. if err != nil {
  531. return object, err
  532. }
  533. libraryID, err := f.getLibraryID(ctx, library)
  534. if err != nil {
  535. return object, err
  536. }
  537. object.libraryID = libraryID
  538. }
  539. err := object.Update(ctx, in, src, options...)
  540. if err != nil {
  541. return object, err
  542. }
  543. return object, nil
  544. }
  545. // PutStream uploads to the remote path with the modTime given but of indeterminate size
  546. func (f *Fs) PutStream(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) (fs.Object, error) {
  547. return f.Put(ctx, in, src, options...)
  548. }
  549. // Mkdir makes the directory or library
  550. //
  551. // Shouldn't return an error if it already exists
  552. func (f *Fs) Mkdir(ctx context.Context, dir string) error {
  553. libraryName, folder := f.splitPath(dir)
  554. if strings.HasPrefix(dir, libraryName) {
  555. err := f.mkLibrary(ctx, libraryName, "")
  556. if err != nil {
  557. return err
  558. }
  559. if folder == "" {
  560. // No directory to create after the library
  561. return nil
  562. }
  563. }
  564. err := f.mkDir(ctx, dir)
  565. if err != nil {
  566. return err
  567. }
  568. return nil
  569. }
  570. // purgeCheck removes the root directory, if check is set then it
  571. // refuses to do so if it has anything in
  572. func (f *Fs) purgeCheck(ctx context.Context, dir string, check bool) error {
  573. libraryName, dirPath := f.splitPath(dir)
  574. libraryID, err := f.getLibraryID(ctx, libraryName)
  575. if err != nil {
  576. return err
  577. }
  578. if check {
  579. directoryEntries, err := f.getDirectoryEntries(ctx, libraryID, dirPath, false)
  580. if err != nil {
  581. return err
  582. }
  583. if len(directoryEntries) > 0 {
  584. return fs.ErrorDirectoryNotEmpty
  585. }
  586. }
  587. if dirPath == "" || dirPath == "/" {
  588. return f.deleteLibrary(ctx, libraryID)
  589. }
  590. return f.deleteDir(ctx, libraryID, dirPath)
  591. }
  592. // Rmdir removes the directory or library if empty
  593. //
  594. // Return an error if it doesn't exist or isn't empty
  595. func (f *Fs) Rmdir(ctx context.Context, dir string) error {
  596. return f.purgeCheck(ctx, dir, true)
  597. }
  598. // ==================== Optional Interface fs.ListRer ====================
  599. // ListR lists the objects and directories of the Fs starting
  600. // from dir recursively into out.
  601. //
  602. // dir should be "" to start from the root, and should not
  603. // have trailing slashes.
  604. //
  605. // This should return ErrDirNotFound if the directory isn't
  606. // found.
  607. //
  608. // It should call callback for each tranche of entries read.
  609. // These need not be returned in any particular order. If
  610. // callback returns an error then the listing will stop
  611. // immediately.
  612. func (f *Fs) ListR(ctx context.Context, dir string, callback fs.ListRCallback) error {
  613. var err error
  614. if dir == "" && f.libraryName == "" {
  615. libraries, err := f.listLibraries(ctx)
  616. if err != nil {
  617. return err
  618. }
  619. // Send the library list as folders
  620. err = callback(libraries)
  621. if err != nil {
  622. return err
  623. }
  624. // Then list each library
  625. for _, library := range libraries {
  626. err = f.listDirCallback(ctx, library.Remote(), callback)
  627. if err != nil {
  628. return err
  629. }
  630. }
  631. return nil
  632. }
  633. err = f.listDirCallback(ctx, dir, callback)
  634. if err != nil {
  635. return err
  636. }
  637. return nil
  638. }
  639. // ==================== Optional Interface fs.Copier ====================
  640. // Copy src to this remote using server-side copy operations.
  641. //
  642. // This is stored with the remote path given.
  643. //
  644. // It returns the destination Object and a possible error.
  645. //
  646. // If it isn't possible then return fs.ErrorCantCopy
  647. func (f *Fs) Copy(ctx context.Context, src fs.Object, remote string) (fs.Object, error) {
  648. srcObj, ok := src.(*Object)
  649. if !ok {
  650. return nil, fs.ErrorCantCopy
  651. }
  652. srcLibraryName, srcPath := srcObj.fs.splitPath(src.Remote())
  653. srcLibraryID, err := srcObj.fs.getLibraryID(ctx, srcLibraryName)
  654. if err != nil {
  655. return nil, err
  656. }
  657. dstLibraryName, dstPath := f.splitPath(remote)
  658. dstLibraryID, err := f.getLibraryID(ctx, dstLibraryName)
  659. if err != nil {
  660. return nil, err
  661. }
  662. // Seafile does not accept a file name as a destination, only a path.
  663. // The destination filename will be the same as the original, or with (1) added in case it was already existing
  664. dstDir, dstFilename := path.Split(dstPath)
  665. // We have to make sure the destination path exists on the server or it's going to bomb out with an obscure error message
  666. err = f.mkMultiDir(ctx, dstLibraryID, dstDir)
  667. if err != nil {
  668. return nil, err
  669. }
  670. op, err := f.copyFile(ctx, srcLibraryID, srcPath, dstLibraryID, dstDir)
  671. if err != nil {
  672. return nil, err
  673. }
  674. if op.Name != dstFilename {
  675. // Destination was existing, so we need to move the file back into place
  676. err = f.adjustDestination(ctx, dstLibraryID, op.Name, dstPath, dstDir, dstFilename)
  677. if err != nil {
  678. return nil, err
  679. }
  680. }
  681. // Create a new object from the result
  682. return f.NewObject(ctx, remote)
  683. }
  684. // ==================== Optional Interface fs.Mover ====================
  685. // Move src to this remote using server-side move operations.
  686. //
  687. // This is stored with the remote path given.
  688. //
  689. // It returns the destination Object and a possible error.
  690. //
  691. // If it isn't possible then return fs.ErrorCantMove
  692. func (f *Fs) Move(ctx context.Context, src fs.Object, remote string) (fs.Object, error) {
  693. srcObj, ok := src.(*Object)
  694. if !ok {
  695. return nil, fs.ErrorCantMove
  696. }
  697. srcLibraryName, srcPath := srcObj.fs.splitPath(src.Remote())
  698. srcLibraryID, err := srcObj.fs.getLibraryID(ctx, srcLibraryName)
  699. if err != nil {
  700. return nil, err
  701. }
  702. dstLibraryName, dstPath := f.splitPath(remote)
  703. dstLibraryID, err := f.getLibraryID(ctx, dstLibraryName)
  704. if err != nil {
  705. return nil, err
  706. }
  707. // anchor both source and destination paths from the root so we can compare them
  708. srcPath = path.Join("/", srcPath)
  709. dstPath = path.Join("/", dstPath)
  710. srcDir := path.Dir(srcPath)
  711. dstDir, dstFilename := path.Split(dstPath)
  712. if srcLibraryID == dstLibraryID && srcDir == dstDir {
  713. // It's only a simple case of renaming the file
  714. _, err := f.renameFile(ctx, srcLibraryID, srcPath, dstFilename)
  715. if err != nil {
  716. return nil, err
  717. }
  718. return f.NewObject(ctx, remote)
  719. }
  720. // We have to make sure the destination path exists on the server
  721. err = f.mkMultiDir(ctx, dstLibraryID, dstDir)
  722. if err != nil {
  723. return nil, err
  724. }
  725. // Seafile does not accept a file name as a destination, only a path.
  726. // The destination filename will be the same as the original, or with (1) added in case it already exists
  727. op, err := f.moveFile(ctx, srcLibraryID, srcPath, dstLibraryID, dstDir)
  728. if err != nil {
  729. return nil, err
  730. }
  731. if op.Name != dstFilename {
  732. // Destination was existing, so we need to move the file back into place
  733. err = f.adjustDestination(ctx, dstLibraryID, op.Name, dstPath, dstDir, dstFilename)
  734. if err != nil {
  735. return nil, err
  736. }
  737. }
  738. // Create a new object from the result
  739. return f.NewObject(ctx, remote)
  740. }
  741. // adjustDestination rename the file
  742. func (f *Fs) adjustDestination(ctx context.Context, libraryID, srcFilename, dstPath, dstDir, dstFilename string) error {
  743. // Seafile seems to be acting strangely if the renamed file already exists (some cache issue maybe?)
  744. // It's better to delete the destination if it already exists
  745. fileDetail, err := f.getFileDetails(ctx, libraryID, dstPath)
  746. if err != nil && err != fs.ErrorObjectNotFound {
  747. return err
  748. }
  749. if fileDetail != nil {
  750. err = f.deleteFile(ctx, libraryID, dstPath)
  751. if err != nil {
  752. return err
  753. }
  754. }
  755. _, err = f.renameFile(ctx, libraryID, path.Join(dstDir, srcFilename), dstFilename)
  756. if err != nil {
  757. return err
  758. }
  759. return nil
  760. }
  761. // ==================== Optional Interface fs.DirMover ====================
  762. // DirMove moves src, srcRemote to this remote at dstRemote
  763. // using server-side move operations.
  764. //
  765. // Will only be called if src.Fs().Name() == f.Name()
  766. //
  767. // If it isn't possible then return fs.ErrorCantDirMove
  768. //
  769. // If destination exists then return fs.ErrorDirExists
  770. func (f *Fs) DirMove(ctx context.Context, src fs.Fs, srcRemote, dstRemote string) error {
  771. // Cast into a seafile Fs
  772. srcFs, ok := src.(*Fs)
  773. if !ok {
  774. return fs.ErrorCantDirMove
  775. }
  776. srcLibraryName, srcPath := srcFs.splitPath(srcRemote)
  777. srcLibraryID, err := srcFs.getLibraryID(ctx, srcLibraryName)
  778. if err != nil {
  779. return err
  780. }
  781. dstLibraryName, dstPath := f.splitPath(dstRemote)
  782. dstLibraryID, err := f.getLibraryID(ctx, dstLibraryName)
  783. if err != nil {
  784. return err
  785. }
  786. srcDir := path.Dir(srcPath)
  787. dstDir, dstName := path.Split(dstPath)
  788. // anchor both source and destination to the root so we can compare them
  789. srcDir = path.Join("/", srcDir)
  790. dstDir = path.Join("/", dstDir)
  791. // The destination should not exist
  792. entries, err := f.getDirectoryEntries(ctx, dstLibraryID, dstDir, false)
  793. if err != nil && err != fs.ErrorDirNotFound {
  794. return err
  795. }
  796. if err == nil {
  797. for _, entry := range entries {
  798. if entry.Name == dstName {
  799. // Destination exists
  800. return fs.ErrorDirExists
  801. }
  802. }
  803. }
  804. if srcLibraryID == dstLibraryID && srcDir == dstDir {
  805. // It's only renaming
  806. err = srcFs.renameDir(ctx, dstLibraryID, srcPath, dstName)
  807. if err != nil {
  808. return err
  809. }
  810. return nil
  811. }
  812. // Seafile < 7 does not support moving directories
  813. if f.moveDirNotAvailable {
  814. return fs.ErrorCantDirMove
  815. }
  816. // Make sure the destination path exists
  817. err = f.mkMultiDir(ctx, dstLibraryID, dstDir)
  818. if err != nil {
  819. return err
  820. }
  821. // If the destination already exists, seafile will add a " (n)" to the name.
  822. // Sadly this API call will not return the new given name like the move file version does
  823. // So the trick is to rename the directory to something random before moving it
  824. // After the move we rename the random name back to the expected one
  825. // Hopefully there won't be anything with the same name existing at destination ;)
  826. tempName := ".rclone-move-" + random.String(32)
  827. // 1- rename source
  828. err = srcFs.renameDir(ctx, srcLibraryID, srcPath, tempName)
  829. if err != nil {
  830. return fmt.Errorf("cannot rename source directory to a temporary name: %w", err)
  831. }
  832. // 2- move source to destination
  833. err = f.moveDir(ctx, srcLibraryID, srcDir, tempName, dstLibraryID, dstDir)
  834. if err != nil {
  835. // Doh! Let's rename the source back to its original name
  836. _ = srcFs.renameDir(ctx, srcLibraryID, path.Join(srcDir, tempName), path.Base(srcPath))
  837. return err
  838. }
  839. // 3- rename destination back to source name
  840. err = f.renameDir(ctx, dstLibraryID, path.Join(dstDir, tempName), dstName)
  841. if err != nil {
  842. return fmt.Errorf("cannot rename temporary directory to destination name: %w", err)
  843. }
  844. return nil
  845. }
  846. // ==================== Optional Interface fs.Purger ====================
  847. // Purge all files in the directory
  848. //
  849. // Implement this if you have a way of deleting all the files
  850. // quicker than just running Remove() on the result of List()
  851. //
  852. // Return an error if it doesn't exist
  853. func (f *Fs) Purge(ctx context.Context, dir string) error {
  854. return f.purgeCheck(ctx, dir, false)
  855. }
  856. // ==================== Optional Interface fs.CleanUpper ====================
  857. // CleanUp the trash in the Fs
  858. func (f *Fs) CleanUp(ctx context.Context) error {
  859. if f.libraryName == "" {
  860. return errors.New("cannot clean up at the root of the seafile server, please select a library to clean up")
  861. }
  862. libraryID, err := f.getLibraryID(ctx, f.libraryName)
  863. if err != nil {
  864. return err
  865. }
  866. return f.emptyLibraryTrash(ctx, libraryID)
  867. }
  868. // ==================== Optional Interface fs.Abouter ====================
  869. // About gets quota information
  870. func (f *Fs) About(ctx context.Context) (usage *fs.Usage, err error) {
  871. accountInfo, err := f.getUserAccountInfo(ctx)
  872. if err != nil {
  873. return nil, err
  874. }
  875. usage = &fs.Usage{
  876. Used: fs.NewUsageValue(accountInfo.Usage), // bytes in use
  877. }
  878. if accountInfo.Total > 0 {
  879. usage.Total = fs.NewUsageValue(accountInfo.Total) // quota of bytes that can be used
  880. usage.Free = fs.NewUsageValue(accountInfo.Total - accountInfo.Usage) // bytes which can be uploaded before reaching the quota
  881. }
  882. return usage, nil
  883. }
  884. // ==================== Optional Interface fs.UserInfoer ====================
  885. // UserInfo returns info about the connected user
  886. func (f *Fs) UserInfo(ctx context.Context) (map[string]string, error) {
  887. accountInfo, err := f.getUserAccountInfo(ctx)
  888. if err != nil {
  889. return nil, err
  890. }
  891. return map[string]string{
  892. "Name": accountInfo.Name,
  893. "Email": accountInfo.Email,
  894. }, nil
  895. }
  896. // ==================== Optional Interface fs.PublicLinker ====================
  897. // PublicLink generates a public link to the remote path (usually readable by anyone)
  898. func (f *Fs) PublicLink(ctx context.Context, remote string, expire fs.Duration, unlink bool) (string, error) {
  899. libraryName, filePath := f.splitPath(remote)
  900. if libraryName == "" {
  901. // We cannot share the whole seafile server, we need at least a library
  902. return "", errors.New("cannot share the root of the seafile server, please select a library to share")
  903. }
  904. libraryID, err := f.getLibraryID(ctx, libraryName)
  905. if err != nil {
  906. return "", err
  907. }
  908. // List existing links first
  909. shareLinks, err := f.listShareLinks(ctx, libraryID, filePath)
  910. if err != nil {
  911. return "", err
  912. }
  913. if len(shareLinks) > 0 {
  914. for _, shareLink := range shareLinks {
  915. if !shareLink.IsExpired {
  916. return shareLink.Link, nil
  917. }
  918. }
  919. }
  920. // No link was found
  921. shareLink, err := f.createShareLink(ctx, libraryID, filePath)
  922. if err != nil {
  923. return "", err
  924. }
  925. if shareLink.IsExpired {
  926. return "", nil
  927. }
  928. return shareLink.Link, nil
  929. }
  930. func (f *Fs) listLibraries(ctx context.Context) (entries fs.DirEntries, err error) {
  931. libraries, err := f.getCachedLibraries(ctx)
  932. if err != nil {
  933. return nil, errors.New("cannot load libraries")
  934. }
  935. for _, library := range libraries {
  936. d := fs.NewDir(library.Name, time.Unix(library.Modified, 0))
  937. d.SetSize(library.Size)
  938. entries = append(entries, d)
  939. }
  940. return entries, nil
  941. }
  942. func (f *Fs) libraryExists(ctx context.Context, libraryName string) (bool, error) {
  943. libraries, err := f.getCachedLibraries(ctx)
  944. if err != nil {
  945. return false, err
  946. }
  947. for _, library := range libraries {
  948. if library.Name == libraryName {
  949. return true, nil
  950. }
  951. }
  952. return false, nil
  953. }
  954. func (f *Fs) getLibraryID(ctx context.Context, name string) (string, error) {
  955. libraries, err := f.getCachedLibraries(ctx)
  956. if err != nil {
  957. return "", err
  958. }
  959. for _, library := range libraries {
  960. if library.Name == name {
  961. return library.ID, nil
  962. }
  963. }
  964. return "", fmt.Errorf("cannot find library '%s'", name)
  965. }
  966. func (f *Fs) isLibraryInCache(libraryName string) bool {
  967. f.librariesMutex.Lock()
  968. defer f.librariesMutex.Unlock()
  969. if f.libraries == nil {
  970. return false
  971. }
  972. value, found := f.libraries.GetMaybe(librariesCacheKey)
  973. if !found {
  974. return false
  975. }
  976. libraries := value.([]api.Library)
  977. for _, library := range libraries {
  978. if library.Name == libraryName {
  979. return true
  980. }
  981. }
  982. return false
  983. }
  984. func (f *Fs) isEncrypted(ctx context.Context, libraryID string) (bool, error) {
  985. libraries, err := f.getCachedLibraries(ctx)
  986. if err != nil {
  987. return false, err
  988. }
  989. for _, library := range libraries {
  990. if library.ID == libraryID {
  991. return library.Encrypted, nil
  992. }
  993. }
  994. return false, fmt.Errorf("cannot find library ID %s", libraryID)
  995. }
  996. func (f *Fs) authorizeLibrary(ctx context.Context, libraryID string) error {
  997. if libraryID == "" {
  998. return errors.New("a library ID is needed")
  999. }
  1000. if f.opt.LibraryKey == "" {
  1001. // We have no password to send
  1002. return nil
  1003. }
  1004. encrypted, err := f.isEncrypted(ctx, libraryID)
  1005. if err != nil {
  1006. return err
  1007. }
  1008. if encrypted {
  1009. fs.Debugf(nil, "Decrypting library %s", libraryID)
  1010. f.authMu.Lock()
  1011. defer f.authMu.Unlock()
  1012. err := f.decryptLibrary(ctx, libraryID, f.opt.LibraryKey)
  1013. if err != nil {
  1014. return err
  1015. }
  1016. }
  1017. return nil
  1018. }
  1019. func (f *Fs) mkLibrary(ctx context.Context, libraryName, password string) error {
  1020. // lock specific to library creation
  1021. // we cannot reuse the same lock as we will dead-lock ourself if the libraries are not in cache
  1022. createLibraryMutex.Lock()
  1023. defer createLibraryMutex.Unlock()
  1024. if libraryName == "" {
  1025. return errors.New("a library name is needed")
  1026. }
  1027. // It's quite likely that multiple go routines are going to try creating the same library
  1028. // at the start of a sync/copy. After releasing the mutex the calls waiting would try to create
  1029. // the same library again. So we'd better check the library exists first
  1030. if f.isLibraryInCache(libraryName) {
  1031. return nil
  1032. }
  1033. fs.Debugf(nil, "%s: Create library '%s'", f.Name(), libraryName)
  1034. f.librariesMutex.Lock()
  1035. defer f.librariesMutex.Unlock()
  1036. library, err := f.createLibrary(ctx, libraryName, password)
  1037. if err != nil {
  1038. return err
  1039. }
  1040. // Stores the library details into the cache
  1041. value, found := f.libraries.GetMaybe(librariesCacheKey)
  1042. if !found {
  1043. // Don't update the cache at that point
  1044. return nil
  1045. }
  1046. libraries := value.([]api.Library)
  1047. libraries = append(libraries, api.Library{
  1048. ID: library.ID,
  1049. Name: library.Name,
  1050. })
  1051. f.libraries.Put(librariesCacheKey, libraries)
  1052. return nil
  1053. }
  1054. // splitPath returns the library name and the full path inside the library
  1055. func (f *Fs) splitPath(dir string) (library, folder string) {
  1056. library = f.libraryName
  1057. folder = dir
  1058. if library == "" {
  1059. // The first part of the path is the library
  1060. library, folder = bucket.Split(dir)
  1061. } else if f.rootDirectory != "" {
  1062. // Adds the root folder to the path to get a full path
  1063. folder = path.Join(f.rootDirectory, folder)
  1064. }
  1065. return
  1066. }
  1067. func (f *Fs) listDir(ctx context.Context, dir string, recursive bool) (entries fs.DirEntries, err error) {
  1068. libraryName, dirPath := f.splitPath(dir)
  1069. libraryID, err := f.getLibraryID(ctx, libraryName)
  1070. if err != nil {
  1071. return nil, err
  1072. }
  1073. directoryEntries, err := f.getDirectoryEntries(ctx, libraryID, dirPath, recursive)
  1074. if err != nil {
  1075. return nil, err
  1076. }
  1077. return f.buildDirEntries(dir, libraryID, dirPath, directoryEntries, recursive), nil
  1078. }
  1079. // listDirCallback is calling listDir with the recursive option and is sending the result to the callback
  1080. func (f *Fs) listDirCallback(ctx context.Context, dir string, callback fs.ListRCallback) error {
  1081. entries, err := f.listDir(ctx, dir, true)
  1082. if err != nil {
  1083. return err
  1084. }
  1085. err = callback(entries)
  1086. if err != nil {
  1087. return err
  1088. }
  1089. return nil
  1090. }
  1091. func (f *Fs) buildDirEntries(parentPath, libraryID, parentPathInLibrary string, directoryEntries []api.DirEntry, recursive bool) (entries fs.DirEntries) {
  1092. for _, entry := range directoryEntries {
  1093. var filePath, filePathInLibrary string
  1094. if recursive {
  1095. // In recursive mode, paths are built from DirEntry (+ a starting point)
  1096. entryPath := strings.TrimPrefix(entry.Path, "/")
  1097. // If we're listing from some path inside the library (not the root)
  1098. // there's already a path in parameter, which will also be included in the entry path
  1099. entryPath = strings.TrimPrefix(entryPath, parentPathInLibrary)
  1100. entryPath = strings.TrimPrefix(entryPath, "/")
  1101. filePath = path.Join(parentPath, entryPath, entry.Name)
  1102. filePathInLibrary = path.Join(parentPathInLibrary, entryPath, entry.Name)
  1103. } else {
  1104. // In non-recursive mode, paths are build from the parameters
  1105. filePath = path.Join(parentPath, entry.Name)
  1106. filePathInLibrary = path.Join(parentPathInLibrary, entry.Name)
  1107. }
  1108. if entry.Type == api.FileTypeDir {
  1109. d := fs.
  1110. NewDir(filePath, time.Unix(entry.Modified, 0)).
  1111. SetSize(entry.Size).
  1112. SetID(entry.ID)
  1113. entries = append(entries, d)
  1114. } else if entry.Type == api.FileTypeFile {
  1115. object := &Object{
  1116. fs: f,
  1117. id: entry.ID,
  1118. remote: filePath,
  1119. pathInLibrary: filePathInLibrary,
  1120. size: entry.Size,
  1121. modTime: time.Unix(entry.Modified, 0),
  1122. libraryID: libraryID,
  1123. }
  1124. entries = append(entries, object)
  1125. }
  1126. }
  1127. return entries
  1128. }
  1129. func (f *Fs) mkDir(ctx context.Context, dir string) error {
  1130. library, fullPath := f.splitPath(dir)
  1131. libraryID, err := f.getLibraryID(ctx, library)
  1132. if err != nil {
  1133. return err
  1134. }
  1135. return f.mkMultiDir(ctx, libraryID, fullPath)
  1136. }
  1137. func (f *Fs) mkMultiDir(ctx context.Context, libraryID, dir string) error {
  1138. // rebuild the path one by one
  1139. currentPath := ""
  1140. for _, singleDir := range splitPath(dir) {
  1141. currentPath = path.Join(currentPath, singleDir)
  1142. err := f.mkSingleDir(ctx, libraryID, currentPath)
  1143. if err != nil {
  1144. return err
  1145. }
  1146. }
  1147. return nil
  1148. }
  1149. func (f *Fs) mkSingleDir(ctx context.Context, libraryID, dir string) error {
  1150. f.createDirMutex.Lock()
  1151. defer f.createDirMutex.Unlock()
  1152. dirDetails, err := f.getDirectoryDetails(ctx, libraryID, dir)
  1153. if err == nil && dirDetails != nil {
  1154. // Don't fail if the directory exists
  1155. return nil
  1156. }
  1157. if err == fs.ErrorDirNotFound {
  1158. err = f.createDir(ctx, libraryID, dir)
  1159. if err != nil {
  1160. return err
  1161. }
  1162. return nil
  1163. }
  1164. return err
  1165. }
  1166. func (f *Fs) getDirectoryEntries(ctx context.Context, libraryID, folder string, recursive bool) ([]api.DirEntry, error) {
  1167. if f.useOldDirectoryAPI {
  1168. return f.getDirectoryEntriesAPIv2(ctx, libraryID, folder)
  1169. }
  1170. return f.getDirectoryEntriesAPIv21(ctx, libraryID, folder, recursive)
  1171. }
  1172. // splitPath creates a slice of paths
  1173. func splitPath(tree string) (paths []string) {
  1174. tree, leaf := path.Split(path.Clean(tree))
  1175. for leaf != "" && leaf != "." {
  1176. paths = append([]string{leaf}, paths...)
  1177. tree, leaf = path.Split(path.Clean(tree))
  1178. }
  1179. return
  1180. }
  1181. func (f *Fs) getCachedLibraries(ctx context.Context) ([]api.Library, error) {
  1182. f.librariesMutex.Lock()
  1183. defer f.librariesMutex.Unlock()
  1184. libraries, err := f.libraries.Get(librariesCacheKey, func(key string) (value interface{}, ok bool, error error) {
  1185. // Load the libraries if not present in the cache
  1186. libraries, err := f.getLibraries(ctx)
  1187. if err != nil {
  1188. return nil, false, err
  1189. }
  1190. return libraries, true, nil
  1191. })
  1192. if err != nil {
  1193. return nil, err
  1194. }
  1195. // Type assertion
  1196. return libraries.([]api.Library), nil
  1197. }
  1198. func (f *Fs) newObject(ctx context.Context, remote string, size int64, modTime time.Time) *Object {
  1199. libraryName, remotePath := f.splitPath(remote)
  1200. libraryID, _ := f.getLibraryID(ctx, libraryName) // If error it means the library does not exist (yet)
  1201. object := &Object{
  1202. fs: f,
  1203. remote: remote,
  1204. libraryID: libraryID,
  1205. pathInLibrary: remotePath,
  1206. size: size,
  1207. modTime: modTime,
  1208. }
  1209. return object
  1210. }
  1211. // Check the interfaces are satisfied
  1212. var (
  1213. _ fs.Fs = &Fs{}
  1214. _ fs.Abouter = &Fs{}
  1215. _ fs.CleanUpper = &Fs{}
  1216. _ fs.Copier = &Fs{}
  1217. _ fs.Mover = &Fs{}
  1218. _ fs.DirMover = &Fs{}
  1219. _ fs.ListRer = &Fs{}
  1220. _ fs.Purger = &Fs{}
  1221. _ fs.PutStreamer = &Fs{}
  1222. _ fs.PublicLinker = &Fs{}
  1223. _ fs.UserInfoer = &Fs{}
  1224. _ fs.Shutdowner = &Fs{}
  1225. _ fs.Object = &Object{}
  1226. _ fs.IDer = &Object{}
  1227. )