sugarsync.go 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320
  1. // Package sugarsync provides an interface to the Sugarsync
  2. // object storage system.
  3. package sugarsync
  4. /* FIXME
  5. DirMove tests fails with: Can not move sync folder.
  6. go test -v -short -run TestIntegration/FsMkdir/FsPutFiles/FsDirMove -verbose -dump-bodies
  7. To work around this we use the remote "TestSugarSync:Test" to test with.
  8. */
  9. import (
  10. "context"
  11. "errors"
  12. "fmt"
  13. "io"
  14. "net/http"
  15. "net/url"
  16. "path"
  17. "regexp"
  18. "strconv"
  19. "strings"
  20. "sync"
  21. "time"
  22. "github.com/rclone/rclone/backend/sugarsync/api"
  23. "github.com/rclone/rclone/fs"
  24. "github.com/rclone/rclone/fs/config"
  25. "github.com/rclone/rclone/fs/config/configmap"
  26. "github.com/rclone/rclone/fs/config/configstruct"
  27. "github.com/rclone/rclone/fs/config/obscure"
  28. "github.com/rclone/rclone/fs/fserrors"
  29. "github.com/rclone/rclone/fs/fshttp"
  30. "github.com/rclone/rclone/fs/hash"
  31. "github.com/rclone/rclone/lib/dircache"
  32. "github.com/rclone/rclone/lib/encoder"
  33. "github.com/rclone/rclone/lib/pacer"
  34. "github.com/rclone/rclone/lib/rest"
  35. )
  36. /*
  37. maxFileLength = 16383
  38. canWriteUnnormalized = true
  39. canReadUnnormalized = true
  40. canReadRenormalized = false
  41. canStream = true
  42. */
  43. const (
  44. appID = "/sc/9068489/215_1736969337"
  45. accessKeyID = "OTA2ODQ4OTE1NzEzNDAwNTI4Njc"
  46. encryptedPrivateAccessKey = "JONdXuRLNSRI5ue2Cr-vn-5m_YxyMNq9yHRKUQevqo8uaZjH502Z-x1axhyqOa8cDyldGq08RfFxozo"
  47. minSleep = 10 * time.Millisecond
  48. maxSleep = 2 * time.Second
  49. decayConstant = 2 // bigger for slower decay, exponential
  50. rootURL = "https://api.sugarsync.com"
  51. listChunks = 500 // chunk size to read directory listings
  52. expiryLeeway = 5 * time.Minute // time before the token expires to renew
  53. )
  54. // withDefault returns value but if value is "" then it returns defaultValue
  55. func withDefault(key, defaultValue string) (value string) {
  56. if value == "" {
  57. value = defaultValue
  58. }
  59. return value
  60. }
  61. // Register with Fs
  62. func init() {
  63. fs.Register(&fs.RegInfo{
  64. Name: "sugarsync",
  65. Description: "Sugarsync",
  66. NewFs: NewFs,
  67. Config: func(ctx context.Context, name string, m configmap.Mapper, config fs.ConfigIn) (*fs.ConfigOut, error) {
  68. opt := new(Options)
  69. err := configstruct.Set(m, opt)
  70. if err != nil {
  71. return nil, fmt.Errorf("failed to read options: %w", err)
  72. }
  73. switch config.State {
  74. case "":
  75. if opt.RefreshToken == "" {
  76. return fs.ConfigGoto("username")
  77. }
  78. return fs.ConfigConfirm("refresh", true, "config_refresh", "Already have a token - refresh?")
  79. case "refresh":
  80. if config.Result == "false" {
  81. return nil, nil
  82. }
  83. return fs.ConfigGoto("username")
  84. case "username":
  85. return fs.ConfigInput("password", "config_username", "username (email address)")
  86. case "password":
  87. m.Set("username", config.Result)
  88. return fs.ConfigPassword("auth", "config_password", "Your Sugarsync password.\n\nOnly required during setup and will not be stored.")
  89. case "auth":
  90. username, _ := m.Get("username")
  91. m.Set("username", "")
  92. password := config.Result
  93. authRequest := api.AppAuthorization{
  94. Username: username,
  95. Password: obscure.MustReveal(password),
  96. Application: withDefault(opt.AppID, appID),
  97. AccessKeyID: withDefault(opt.AccessKeyID, accessKeyID),
  98. PrivateAccessKey: withDefault(opt.PrivateAccessKey, obscure.MustReveal(encryptedPrivateAccessKey)),
  99. }
  100. var resp *http.Response
  101. opts := rest.Opts{
  102. Method: "POST",
  103. Path: "/app-authorization",
  104. }
  105. srv := rest.NewClient(fshttp.NewClient(ctx)).SetRoot(rootURL) // FIXME
  106. // FIXME
  107. //err = f.pacer.Call(func() (bool, error) {
  108. resp, err = srv.CallXML(context.Background(), &opts, &authRequest, nil)
  109. // return shouldRetry(ctx, resp, err)
  110. //})
  111. if err != nil {
  112. return nil, fmt.Errorf("failed to get token: %w", err)
  113. }
  114. opt.RefreshToken = resp.Header.Get("Location")
  115. m.Set("refresh_token", opt.RefreshToken)
  116. return nil, nil
  117. }
  118. return nil, fmt.Errorf("unknown state %q", config.State)
  119. }, Options: []fs.Option{{
  120. Name: "app_id",
  121. Help: "Sugarsync App ID.\n\nLeave blank to use rclone's.",
  122. Sensitive: true,
  123. }, {
  124. Name: "access_key_id",
  125. Help: "Sugarsync Access Key ID.\n\nLeave blank to use rclone's.",
  126. Sensitive: true,
  127. }, {
  128. Name: "private_access_key",
  129. Help: "Sugarsync Private Access Key.\n\nLeave blank to use rclone's.",
  130. Sensitive: true,
  131. }, {
  132. Name: "hard_delete",
  133. Help: "Permanently delete files if true\notherwise put them in the deleted files.",
  134. Default: false,
  135. }, {
  136. Name: "refresh_token",
  137. Help: "Sugarsync refresh token.\n\nLeave blank normally, will be auto configured by rclone.",
  138. Advanced: true,
  139. Sensitive: true,
  140. }, {
  141. Name: "authorization",
  142. Help: "Sugarsync authorization.\n\nLeave blank normally, will be auto configured by rclone.",
  143. Advanced: true,
  144. Sensitive: true,
  145. }, {
  146. Name: "authorization_expiry",
  147. Help: "Sugarsync authorization expiry.\n\nLeave blank normally, will be auto configured by rclone.",
  148. Advanced: true,
  149. }, {
  150. Name: "user",
  151. Help: "Sugarsync user.\n\nLeave blank normally, will be auto configured by rclone.",
  152. Advanced: true,
  153. Sensitive: true,
  154. }, {
  155. Name: "root_id",
  156. Help: "Sugarsync root id.\n\nLeave blank normally, will be auto configured by rclone.",
  157. Advanced: true,
  158. Sensitive: true,
  159. }, {
  160. Name: "deleted_id",
  161. Help: "Sugarsync deleted folder id.\n\nLeave blank normally, will be auto configured by rclone.",
  162. Advanced: true,
  163. Sensitive: true,
  164. }, {
  165. Name: config.ConfigEncoding,
  166. Help: config.ConfigEncodingHelp,
  167. Advanced: true,
  168. Default: (encoder.Base |
  169. encoder.EncodeCtl |
  170. encoder.EncodeInvalidUtf8),
  171. }},
  172. })
  173. }
  174. // Options defines the configuration for this backend
  175. type Options struct {
  176. AppID string `config:"app_id"`
  177. AccessKeyID string `config:"access_key_id"`
  178. PrivateAccessKey string `config:"private_access_key"`
  179. HardDelete bool `config:"hard_delete"`
  180. RefreshToken string `config:"refresh_token"`
  181. Authorization string `config:"authorization"`
  182. AuthorizationExpiry string `config:"authorization_expiry"`
  183. User string `config:"user"`
  184. RootID string `config:"root_id"`
  185. DeletedID string `config:"deleted_id"`
  186. Enc encoder.MultiEncoder `config:"encoding"`
  187. }
  188. // Fs represents a remote sugarsync
  189. type Fs struct {
  190. name string // name of this remote
  191. root string // the path we are working on
  192. opt Options // parsed options
  193. features *fs.Features // optional features
  194. srv *rest.Client // the connection to the server
  195. dirCache *dircache.DirCache // Map of directory path to directory id
  196. pacer *fs.Pacer // pacer for API calls
  197. m configmap.Mapper // config file access
  198. authMu sync.Mutex // used when doing authorization
  199. authExpiry time.Time // time the authorization expires
  200. }
  201. // Object describes a sugarsync object
  202. //
  203. // Will definitely have info but maybe not meta
  204. type Object struct {
  205. fs *Fs // what this object is part of
  206. remote string // The remote path
  207. hasMetaData bool // whether info below has been set
  208. size int64 // size of the object
  209. modTime time.Time // modification time of the object
  210. id string // ID of the object
  211. }
  212. // ------------------------------------------------------------
  213. // Name of the remote (as passed into NewFs)
  214. func (f *Fs) Name() string {
  215. return f.name
  216. }
  217. // Root of the remote (as passed into NewFs)
  218. func (f *Fs) Root() string {
  219. return f.root
  220. }
  221. // String converts this Fs to a string
  222. func (f *Fs) String() string {
  223. return fmt.Sprintf("sugarsync root '%s'", f.root)
  224. }
  225. // Features returns the optional features of this Fs
  226. func (f *Fs) Features() *fs.Features {
  227. return f.features
  228. }
  229. // parsePath parses a sugarsync 'url'
  230. func parsePath(path string) (root string) {
  231. root = strings.Trim(path, "/")
  232. return
  233. }
  234. // retryErrorCodes is a slice of error codes that we will retry
  235. var retryErrorCodes = []int{
  236. 429, // Too Many Requests.
  237. 500, // Internal Server Error
  238. 502, // Bad Gateway
  239. 503, // Service Unavailable
  240. 504, // Gateway Timeout
  241. 509, // Bandwidth Limit Exceeded
  242. }
  243. // shouldRetry returns a boolean as to whether this resp and err
  244. // deserve to be retried. It returns the err as a convenience
  245. func shouldRetry(ctx context.Context, resp *http.Response, err error) (bool, error) {
  246. if fserrors.ContextError(ctx, &err) {
  247. return false, err
  248. }
  249. return fserrors.ShouldRetry(err) || fserrors.ShouldRetryHTTP(resp, retryErrorCodes), err
  250. }
  251. // readMetaDataForPath reads the metadata from the path
  252. func (f *Fs) readMetaDataForPath(ctx context.Context, path string) (info *api.File, err error) {
  253. // defer fs.Trace(f, "path=%q", path)("info=%+v, err=%v", &info, &err)
  254. leaf, directoryID, err := f.dirCache.FindPath(ctx, path, false)
  255. if err != nil {
  256. if err == fs.ErrorDirNotFound {
  257. return nil, fs.ErrorObjectNotFound
  258. }
  259. return nil, err
  260. }
  261. found, err := f.listAll(ctx, directoryID, func(item *api.File) bool {
  262. if strings.EqualFold(item.Name, leaf) {
  263. info = item
  264. return true
  265. }
  266. return false
  267. }, nil)
  268. if err != nil {
  269. return nil, err
  270. }
  271. if !found {
  272. return nil, fs.ErrorObjectNotFound
  273. }
  274. return info, nil
  275. }
  276. // readMetaDataForID reads the metadata for a file from the ID
  277. func (f *Fs) readMetaDataForID(ctx context.Context, ID string) (info *api.File, err error) {
  278. var resp *http.Response
  279. opts := rest.Opts{
  280. Method: "GET",
  281. RootURL: ID,
  282. }
  283. err = f.pacer.Call(func() (bool, error) {
  284. resp, err = f.srv.CallXML(ctx, &opts, nil, &info)
  285. return shouldRetry(ctx, resp, err)
  286. })
  287. if err != nil {
  288. if resp != nil && resp.StatusCode == http.StatusNotFound {
  289. return nil, fs.ErrorObjectNotFound
  290. }
  291. return nil, fmt.Errorf("failed to get authorization: %w", err)
  292. }
  293. return info, nil
  294. }
  295. // getAuthToken gets an Auth token from the refresh token
  296. func (f *Fs) getAuthToken(ctx context.Context) error {
  297. fs.Debugf(f, "Renewing token")
  298. var authRequest = api.TokenAuthRequest{
  299. AccessKeyID: withDefault(f.opt.AccessKeyID, accessKeyID),
  300. PrivateAccessKey: withDefault(f.opt.PrivateAccessKey, obscure.MustReveal(encryptedPrivateAccessKey)),
  301. RefreshToken: f.opt.RefreshToken,
  302. }
  303. if authRequest.RefreshToken == "" {
  304. return errors.New("no refresh token found - run `rclone config reconnect`")
  305. }
  306. var authResponse api.Authorization
  307. var err error
  308. var resp *http.Response
  309. opts := rest.Opts{
  310. Method: "POST",
  311. Path: "/authorization",
  312. ExtraHeaders: map[string]string{
  313. "Authorization": "", // unset Authorization
  314. },
  315. }
  316. err = f.pacer.Call(func() (bool, error) {
  317. resp, err = f.srv.CallXML(ctx, &opts, &authRequest, &authResponse)
  318. return shouldRetry(ctx, resp, err)
  319. })
  320. if err != nil {
  321. return fmt.Errorf("failed to get authorization: %w", err)
  322. }
  323. f.opt.Authorization = resp.Header.Get("Location")
  324. f.authExpiry = authResponse.Expiration
  325. f.opt.User = authResponse.User
  326. // Cache the results
  327. f.m.Set("authorization", f.opt.Authorization)
  328. f.m.Set("authorization_expiry", f.authExpiry.Format(time.RFC3339))
  329. f.m.Set("user", f.opt.User)
  330. return nil
  331. }
  332. // Read the auth from the config file and refresh it if it is expired, setting it in srv
  333. func (f *Fs) getAuth(req *http.Request) (err error) {
  334. f.authMu.Lock()
  335. defer f.authMu.Unlock()
  336. ctx := req.Context()
  337. // if have auth, check it is in date
  338. if f.opt.Authorization == "" || f.opt.User == "" || f.authExpiry.IsZero() || time.Until(f.authExpiry) < expiryLeeway {
  339. // Get the auth token
  340. f.srv.SetSigner(nil) // temporarily remove the signer so we don't infinitely recurse
  341. err = f.getAuthToken(ctx)
  342. f.srv.SetSigner(f.getAuth) // replace signer
  343. if err != nil {
  344. return err
  345. }
  346. }
  347. // Set Authorization header
  348. req.Header.Set("Authorization", f.opt.Authorization)
  349. return nil
  350. }
  351. // Read the user info into f
  352. func (f *Fs) getUser(ctx context.Context) (user *api.User, err error) {
  353. var resp *http.Response
  354. opts := rest.Opts{
  355. Method: "GET",
  356. Path: "/user",
  357. }
  358. err = f.pacer.Call(func() (bool, error) {
  359. resp, err = f.srv.CallXML(ctx, &opts, nil, &user)
  360. return shouldRetry(ctx, resp, err)
  361. })
  362. if err != nil {
  363. return nil, fmt.Errorf("failed to get user: %w", err)
  364. }
  365. return user, nil
  366. }
  367. // Read the expiry time from a string
  368. func parseExpiry(expiryString string) time.Time {
  369. if expiryString == "" {
  370. return time.Time{}
  371. }
  372. expiry, err := time.Parse(time.RFC3339, expiryString)
  373. if err != nil {
  374. fs.Debugf("sugarsync", "Invalid expiry time %q read from config", expiryString)
  375. return time.Time{}
  376. }
  377. return expiry
  378. }
  379. // NewFs constructs an Fs from the path, container:path
  380. func NewFs(ctx context.Context, name, root string, m configmap.Mapper) (fs.Fs, error) {
  381. opt := new(Options)
  382. err := configstruct.Set(m, opt)
  383. if err != nil {
  384. return nil, err
  385. }
  386. root = parsePath(root)
  387. client := fshttp.NewClient(ctx)
  388. f := &Fs{
  389. name: name,
  390. root: root,
  391. opt: *opt,
  392. srv: rest.NewClient(client).SetRoot(rootURL),
  393. pacer: fs.NewPacer(ctx, pacer.NewDefault(pacer.MinSleep(minSleep), pacer.MaxSleep(maxSleep), pacer.DecayConstant(decayConstant))),
  394. m: m,
  395. authExpiry: parseExpiry(opt.AuthorizationExpiry),
  396. }
  397. f.features = (&fs.Features{
  398. CaseInsensitive: true,
  399. CanHaveEmptyDirectories: true,
  400. }).Fill(ctx, f)
  401. f.srv.SetSigner(f.getAuth) // use signing hook to get the auth
  402. f.srv.SetErrorHandler(errorHandler)
  403. // Get rootID
  404. if f.opt.RootID == "" {
  405. user, err := f.getUser(ctx)
  406. if err != nil {
  407. return nil, err
  408. }
  409. f.opt.RootID = user.SyncFolders
  410. if strings.HasSuffix(f.opt.RootID, "/contents") {
  411. f.opt.RootID = f.opt.RootID[:len(f.opt.RootID)-9]
  412. } else {
  413. return nil, fmt.Errorf("unexpected rootID %q", f.opt.RootID)
  414. }
  415. // Cache the results
  416. f.m.Set("root_id", f.opt.RootID)
  417. f.opt.DeletedID = user.Deleted
  418. f.m.Set("deleted_id", f.opt.DeletedID)
  419. }
  420. f.dirCache = dircache.New(root, f.opt.RootID, f)
  421. // Find the current root
  422. err = f.dirCache.FindRoot(ctx, false)
  423. if err != nil {
  424. // Assume it is a file
  425. newRoot, remote := dircache.SplitPath(root)
  426. oldDirCache := f.dirCache
  427. f.dirCache = dircache.New(newRoot, f.opt.RootID, f)
  428. f.root = newRoot
  429. resetF := func() {
  430. f.dirCache = oldDirCache
  431. f.root = root
  432. }
  433. // Make new Fs which is the parent
  434. err = f.dirCache.FindRoot(ctx, false)
  435. if err != nil {
  436. // No root so return old f
  437. resetF()
  438. return f, nil
  439. }
  440. _, err := f.newObjectWithInfo(ctx, remote, nil)
  441. if err != nil {
  442. if err == fs.ErrorObjectNotFound {
  443. // File doesn't exist so return old f
  444. resetF()
  445. return f, nil
  446. }
  447. return nil, err
  448. }
  449. // return an error with an fs which points to the parent
  450. return f, fs.ErrorIsFile
  451. }
  452. return f, nil
  453. }
  454. var findError = regexp.MustCompile(`<h3>(.*?)</h3>`)
  455. // errorHandler parses errors from the body
  456. //
  457. // Errors seem to be HTML with <h3> containing the error text
  458. // <h3>Can not move sync folder.</h3>
  459. func errorHandler(resp *http.Response) (err error) {
  460. body, err := rest.ReadBody(resp)
  461. if err != nil {
  462. return fmt.Errorf("error reading error out of body: %w", err)
  463. }
  464. match := findError.FindSubmatch(body)
  465. if match == nil || len(match) < 2 || len(match[1]) == 0 {
  466. return fmt.Errorf("HTTP error %v (%v) returned body: %q", resp.StatusCode, resp.Status, body)
  467. }
  468. return fmt.Errorf("HTTP error %v (%v): %s", resp.StatusCode, resp.Status, match[1])
  469. }
  470. // rootSlash returns root with a slash on if it is empty, otherwise empty string
  471. func (f *Fs) rootSlash() string {
  472. if f.root == "" {
  473. return f.root
  474. }
  475. return f.root + "/"
  476. }
  477. // Return an Object from a path
  478. //
  479. // If it can't be found it returns the error fs.ErrorObjectNotFound.
  480. func (f *Fs) newObjectWithInfo(ctx context.Context, remote string, info *api.File) (fs.Object, error) {
  481. o := &Object{
  482. fs: f,
  483. remote: remote,
  484. }
  485. var err error
  486. if info != nil {
  487. // Set info
  488. err = o.setMetaData(info)
  489. } else {
  490. err = o.readMetaData(ctx) // reads info and meta, returning an error
  491. }
  492. if err != nil {
  493. return nil, err
  494. }
  495. return o, nil
  496. }
  497. // NewObject finds the Object at remote. If it can't be found
  498. // it returns the error fs.ErrorObjectNotFound.
  499. func (f *Fs) NewObject(ctx context.Context, remote string) (fs.Object, error) {
  500. return f.newObjectWithInfo(ctx, remote, nil)
  501. }
  502. // FindLeaf finds a directory of name leaf in the folder with ID pathID
  503. func (f *Fs) FindLeaf(ctx context.Context, pathID, leaf string) (pathIDOut string, found bool, err error) {
  504. //fs.Debugf(f, "FindLeaf(%q, %q)", pathID, leaf)
  505. // Find the leaf in pathID
  506. found, err = f.listAll(ctx, pathID, nil, func(item *api.Collection) bool {
  507. if strings.EqualFold(item.Name, leaf) {
  508. pathIDOut = item.Ref
  509. return true
  510. }
  511. return false
  512. })
  513. // fs.Debugf(f, ">FindLeaf %q, %v, %v", pathIDOut, found, err)
  514. return pathIDOut, found, err
  515. }
  516. // CreateDir makes a directory with pathID as parent and name leaf
  517. func (f *Fs) CreateDir(ctx context.Context, pathID, leaf string) (newID string, err error) {
  518. // fs.Debugf(f, "CreateDir(%q, %q)\n", pathID, leaf)
  519. var resp *http.Response
  520. opts := rest.Opts{
  521. Method: "POST",
  522. RootURL: pathID,
  523. NoResponse: true,
  524. }
  525. var mkdir interface{}
  526. if pathID == f.opt.RootID {
  527. // folders at the root are syncFolders
  528. mkdir = &api.CreateSyncFolder{
  529. Name: f.opt.Enc.FromStandardName(leaf),
  530. }
  531. opts.ExtraHeaders = map[string]string{
  532. "*X-SugarSync-API-Version": "1.5", // non canonical header
  533. }
  534. } else {
  535. mkdir = &api.CreateFolder{
  536. Name: f.opt.Enc.FromStandardName(leaf),
  537. }
  538. }
  539. err = f.pacer.Call(func() (bool, error) {
  540. resp, err = f.srv.CallXML(ctx, &opts, mkdir, nil)
  541. return shouldRetry(ctx, resp, err)
  542. })
  543. if err != nil {
  544. return "", err
  545. }
  546. newID = resp.Header.Get("Location")
  547. if newID == "" {
  548. // look up ID if not returned (e.g. for syncFolder)
  549. var found bool
  550. newID, found, err = f.FindLeaf(ctx, pathID, leaf)
  551. if err != nil {
  552. return "", err
  553. }
  554. if !found {
  555. return "", fmt.Errorf("couldn't find ID for newly created directory %q", leaf)
  556. }
  557. }
  558. return newID, nil
  559. }
  560. // list the objects into the function supplied
  561. //
  562. // Should return true to finish processing
  563. type listAllFileFn func(*api.File) bool
  564. // list the folders into the function supplied
  565. //
  566. // Should return true to finish processing
  567. type listAllFolderFn func(*api.Collection) bool
  568. // Lists the directory required calling the user function on each item found
  569. //
  570. // If the user fn ever returns true then it early exits with found = true
  571. func (f *Fs) listAll(ctx context.Context, dirID string, fileFn listAllFileFn, folderFn listAllFolderFn) (found bool, err error) {
  572. opts := rest.Opts{
  573. Method: "GET",
  574. RootURL: dirID,
  575. Path: "/contents",
  576. Parameters: url.Values{},
  577. }
  578. opts.Parameters.Set("max", strconv.Itoa(listChunks))
  579. start := 0
  580. OUTER:
  581. for {
  582. opts.Parameters.Set("start", strconv.Itoa(start))
  583. var result api.CollectionContents
  584. var resp *http.Response
  585. err = f.pacer.Call(func() (bool, error) {
  586. resp, err = f.srv.CallXML(ctx, &opts, nil, &result)
  587. return shouldRetry(ctx, resp, err)
  588. })
  589. if err != nil {
  590. return found, fmt.Errorf("couldn't list files: %w", err)
  591. }
  592. if fileFn != nil {
  593. for i := range result.Files {
  594. item := &result.Files[i]
  595. item.Name = f.opt.Enc.ToStandardName(item.Name)
  596. if fileFn(item) {
  597. found = true
  598. break OUTER
  599. }
  600. }
  601. }
  602. if folderFn != nil {
  603. for i := range result.Collections {
  604. item := &result.Collections[i]
  605. item.Name = f.opt.Enc.ToStandardName(item.Name)
  606. if folderFn(item) {
  607. found = true
  608. break OUTER
  609. }
  610. }
  611. }
  612. if !result.HasMore {
  613. break
  614. }
  615. start = result.End + 1
  616. }
  617. return
  618. }
  619. // List the objects and directories in dir into entries. The
  620. // entries can be returned in any order but should be for a
  621. // complete directory.
  622. //
  623. // dir should be "" to list the root, and should not have
  624. // trailing slashes.
  625. //
  626. // This should return ErrDirNotFound if the directory isn't
  627. // found.
  628. func (f *Fs) List(ctx context.Context, dir string) (entries fs.DirEntries, err error) {
  629. directoryID, err := f.dirCache.FindDir(ctx, dir, false)
  630. if err != nil {
  631. return nil, err
  632. }
  633. var iErr error
  634. _, err = f.listAll(ctx, directoryID,
  635. func(info *api.File) bool {
  636. remote := path.Join(dir, info.Name)
  637. o, err := f.newObjectWithInfo(ctx, remote, info)
  638. if err != nil {
  639. iErr = err
  640. return true
  641. }
  642. entries = append(entries, o)
  643. return false
  644. },
  645. func(info *api.Collection) bool {
  646. remote := path.Join(dir, info.Name)
  647. id := info.Ref
  648. // cache the directory ID for later lookups
  649. f.dirCache.Put(remote, id)
  650. d := fs.NewDir(remote, info.TimeCreated).SetID(id)
  651. entries = append(entries, d)
  652. return false
  653. })
  654. if err != nil {
  655. return nil, err
  656. }
  657. if iErr != nil {
  658. return nil, iErr
  659. }
  660. return entries, nil
  661. }
  662. // Creates from the parameters passed in a half finished Object which
  663. // must have setMetaData called on it
  664. //
  665. // Returns the object, leaf, directoryID and error.
  666. //
  667. // Used to create new objects
  668. func (f *Fs) createObject(ctx context.Context, remote string, modTime time.Time, size int64) (o *Object, leaf string, directoryID string, err error) {
  669. // Create the directory for the object if it doesn't exist
  670. leaf, directoryID, err = f.dirCache.FindPath(ctx, remote, true)
  671. if err != nil {
  672. return
  673. }
  674. // Temporary Object under construction
  675. o = &Object{
  676. fs: f,
  677. remote: remote,
  678. }
  679. return o, leaf, directoryID, nil
  680. }
  681. // Put the object
  682. //
  683. // Copy the reader in to the new object which is returned.
  684. //
  685. // The new object may have been created if an error is returned
  686. func (f *Fs) Put(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) (fs.Object, error) {
  687. existingObj, err := f.newObjectWithInfo(ctx, src.Remote(), nil)
  688. switch err {
  689. case nil:
  690. return existingObj, existingObj.Update(ctx, in, src, options...)
  691. case fs.ErrorObjectNotFound:
  692. // Not found so create it
  693. return f.PutUnchecked(ctx, in, src, options...)
  694. default:
  695. return nil, err
  696. }
  697. }
  698. // PutStream uploads to the remote path with the modTime given of indeterminate size
  699. func (f *Fs) PutStream(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) (fs.Object, error) {
  700. return f.Put(ctx, in, src, options...)
  701. }
  702. // PutUnchecked the object into the container
  703. //
  704. // This will produce an error if the object already exists.
  705. //
  706. // Copy the reader in to the new object which is returned.
  707. //
  708. // The new object may have been created if an error is returned
  709. func (f *Fs) PutUnchecked(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) (fs.Object, error) {
  710. remote := src.Remote()
  711. size := src.Size()
  712. modTime := src.ModTime(ctx)
  713. o, _, _, err := f.createObject(ctx, remote, modTime, size)
  714. if err != nil {
  715. return nil, err
  716. }
  717. return o, o.Update(ctx, in, src, options...)
  718. }
  719. // Mkdir creates the container if it doesn't exist
  720. func (f *Fs) Mkdir(ctx context.Context, dir string) error {
  721. _, err := f.dirCache.FindDir(ctx, dir, true)
  722. return err
  723. }
  724. // delete removes an object or directory by ID either putting it
  725. // in the Deleted files or deleting it permanently
  726. func (f *Fs) delete(ctx context.Context, isFile bool, id string, remote string, hardDelete bool) (err error) {
  727. if hardDelete {
  728. opts := rest.Opts{
  729. Method: "DELETE",
  730. RootURL: id,
  731. NoResponse: true,
  732. }
  733. return f.pacer.Call(func() (bool, error) {
  734. resp, err := f.srv.Call(ctx, &opts)
  735. return shouldRetry(ctx, resp, err)
  736. })
  737. }
  738. // Move file/dir to deleted files if not hard delete
  739. leaf := path.Base(remote)
  740. if isFile {
  741. _, err = f.moveFile(ctx, id, leaf, f.opt.DeletedID)
  742. } else {
  743. err = f.moveDir(ctx, id, leaf, f.opt.DeletedID)
  744. }
  745. return err
  746. }
  747. // purgeCheck removes the root directory, if check is set then it
  748. // refuses to do so if it has anything in
  749. func (f *Fs) purgeCheck(ctx context.Context, dir string, check bool) error {
  750. root := path.Join(f.root, dir)
  751. if root == "" {
  752. return errors.New("can't purge root directory")
  753. }
  754. dc := f.dirCache
  755. directoryID, err := dc.FindDir(ctx, dir, false)
  756. if err != nil {
  757. return err
  758. }
  759. if check {
  760. found, err := f.listAll(ctx, directoryID, func(item *api.File) bool {
  761. return true
  762. }, func(item *api.Collection) bool {
  763. return true
  764. })
  765. if err != nil {
  766. return err
  767. }
  768. if found {
  769. return fs.ErrorDirectoryNotEmpty
  770. }
  771. }
  772. err = f.delete(ctx, false, directoryID, root, f.opt.HardDelete || check)
  773. if err != nil {
  774. return err
  775. }
  776. f.dirCache.FlushDir(dir)
  777. return nil
  778. }
  779. // Rmdir deletes the root folder
  780. //
  781. // Returns an error if it isn't empty
  782. func (f *Fs) Rmdir(ctx context.Context, dir string) error {
  783. return f.purgeCheck(ctx, dir, true)
  784. }
  785. // Precision return the precision of this Fs
  786. func (f *Fs) Precision() time.Duration {
  787. return fs.ModTimeNotSupported
  788. }
  789. // Copy src to this remote using server-side copy operations.
  790. //
  791. // This is stored with the remote path given.
  792. //
  793. // It returns the destination Object and a possible error.
  794. //
  795. // Will only be called if src.Fs().Name() == f.Name()
  796. //
  797. // If it isn't possible then return fs.ErrorCantCopy
  798. func (f *Fs) Copy(ctx context.Context, src fs.Object, remote string) (fs.Object, error) {
  799. srcObj, ok := src.(*Object)
  800. if !ok {
  801. fs.Debugf(src, "Can't copy - not same remote type")
  802. return nil, fs.ErrorCantCopy
  803. }
  804. err := srcObj.readMetaData(ctx)
  805. if err != nil {
  806. return nil, err
  807. }
  808. srcPath := srcObj.fs.rootSlash() + srcObj.remote
  809. dstPath := f.rootSlash() + remote
  810. if strings.EqualFold(srcPath, dstPath) {
  811. return nil, fmt.Errorf("can't copy %q -> %q as are same name when lowercase", srcPath, dstPath)
  812. }
  813. // Create temporary object
  814. dstObj, leaf, directoryID, err := f.createObject(ctx, remote, srcObj.modTime, srcObj.size)
  815. if err != nil {
  816. return nil, err
  817. }
  818. // Copy the object
  819. opts := rest.Opts{
  820. Method: "POST",
  821. RootURL: directoryID,
  822. NoResponse: true,
  823. }
  824. copyFile := api.CopyFile{
  825. Name: f.opt.Enc.FromStandardName(leaf),
  826. Source: srcObj.id,
  827. }
  828. var resp *http.Response
  829. err = f.pacer.Call(func() (bool, error) {
  830. resp, err = f.srv.CallXML(ctx, &opts, &copyFile, nil)
  831. return shouldRetry(ctx, resp, err)
  832. })
  833. if err != nil {
  834. return nil, err
  835. }
  836. dstObj.id = resp.Header.Get("Location")
  837. err = dstObj.readMetaData(ctx)
  838. if err != nil {
  839. return nil, err
  840. }
  841. return dstObj, nil
  842. }
  843. // Purge deletes all the files in the directory
  844. //
  845. // Optional interface: Only implement this if you have a way of
  846. // deleting all the files quicker than just running Remove() on the
  847. // result of List()
  848. func (f *Fs) Purge(ctx context.Context, dir string) error {
  849. // Caution: Deleting a folder may orphan objects. It's important
  850. // to remove the contents of the folder before you delete the
  851. // folder. That's because removing a folder using DELETE does not
  852. // remove the objects contained within the folder. If you delete
  853. // a folder without first deleting its contents, the contents may
  854. // be rendered inaccessible.
  855. //
  856. // An alternative to permanently deleting a folder is moving it to the
  857. // Deleted Files folder. A folder (and all its contents) in the
  858. // Deleted Files folder can be recovered. Your app can retrieve the
  859. // link to the user's Deleted Files folder from the <deleted> element
  860. // in the user resource representation. Your application can then move
  861. // a folder to the Deleted Files folder by issuing an HTTP PUT request
  862. // to the URL that represents the file resource and provide as input,
  863. // XML that specifies in the <parent> element the link to the Deleted
  864. // Files folder.
  865. if f.opt.HardDelete {
  866. return fs.ErrorCantPurge
  867. }
  868. return f.purgeCheck(ctx, dir, false)
  869. }
  870. // moveFile moves a file server-side
  871. func (f *Fs) moveFile(ctx context.Context, id, leaf, directoryID string) (info *api.File, err error) {
  872. opts := rest.Opts{
  873. Method: "PUT",
  874. RootURL: id,
  875. }
  876. move := api.MoveFile{
  877. Name: f.opt.Enc.FromStandardName(leaf),
  878. Parent: directoryID,
  879. }
  880. var resp *http.Response
  881. err = f.pacer.Call(func() (bool, error) {
  882. resp, err = f.srv.CallXML(ctx, &opts, &move, &info)
  883. return shouldRetry(ctx, resp, err)
  884. })
  885. if err != nil {
  886. return nil, err
  887. }
  888. // The docs say that there is nothing returned but apparently
  889. // there is... however it doesn't have Ref
  890. //
  891. // If ref not set, assume it hasn't changed
  892. if info.Ref == "" {
  893. info.Ref = id
  894. }
  895. return info, nil
  896. }
  897. // moveDir moves a folder server-side
  898. func (f *Fs) moveDir(ctx context.Context, id, leaf, directoryID string) (err error) {
  899. // Move the object
  900. opts := rest.Opts{
  901. Method: "PUT",
  902. RootURL: id,
  903. NoResponse: true,
  904. }
  905. move := api.MoveFolder{
  906. Name: f.opt.Enc.FromStandardName(leaf),
  907. Parent: directoryID,
  908. }
  909. var resp *http.Response
  910. return f.pacer.Call(func() (bool, error) {
  911. resp, err = f.srv.CallXML(ctx, &opts, &move, nil)
  912. return shouldRetry(ctx, resp, err)
  913. })
  914. }
  915. // Move src to this remote using server-side move operations.
  916. //
  917. // This is stored with the remote path given.
  918. //
  919. // It returns the destination Object and a possible error.
  920. //
  921. // Will only be called if src.Fs().Name() == f.Name()
  922. //
  923. // If it isn't possible then return fs.ErrorCantMove
  924. func (f *Fs) Move(ctx context.Context, src fs.Object, remote string) (fs.Object, error) {
  925. srcObj, ok := src.(*Object)
  926. if !ok {
  927. fs.Debugf(src, "Can't move - not same remote type")
  928. return nil, fs.ErrorCantMove
  929. }
  930. // Create temporary object
  931. dstObj, leaf, directoryID, err := f.createObject(ctx, remote, srcObj.modTime, srcObj.size)
  932. if err != nil {
  933. return nil, err
  934. }
  935. // Do the move
  936. info, err := f.moveFile(ctx, srcObj.id, leaf, directoryID)
  937. if err != nil {
  938. return nil, err
  939. }
  940. err = dstObj.setMetaData(info)
  941. if err != nil {
  942. return nil, err
  943. }
  944. return dstObj, nil
  945. }
  946. // DirMove moves src, srcRemote to this remote at dstRemote
  947. // using server-side move operations.
  948. //
  949. // Will only be called if src.Fs().Name() == f.Name()
  950. //
  951. // If it isn't possible then return fs.ErrorCantDirMove
  952. //
  953. // If destination exists then return fs.ErrorDirExists
  954. func (f *Fs) DirMove(ctx context.Context, src fs.Fs, srcRemote, dstRemote string) error {
  955. srcFs, ok := src.(*Fs)
  956. if !ok {
  957. fs.Debugf(srcFs, "Can't move directory - not same remote type")
  958. return fs.ErrorCantDirMove
  959. }
  960. srcID, _, _, dstDirectoryID, dstLeaf, err := f.dirCache.DirMove(ctx, srcFs.dirCache, srcFs.root, srcRemote, f.root, dstRemote)
  961. if err != nil {
  962. return err
  963. }
  964. // Do the move
  965. err = f.moveDir(ctx, srcID, dstLeaf, dstDirectoryID)
  966. if err != nil {
  967. return err
  968. }
  969. srcFs.dirCache.FlushDir(srcRemote)
  970. return nil
  971. }
  972. // PublicLink adds a "readable by anyone with link" permission on the given file or folder.
  973. func (f *Fs) PublicLink(ctx context.Context, remote string, expire fs.Duration, unlink bool) (string, error) {
  974. obj, err := f.NewObject(ctx, remote)
  975. if err != nil {
  976. return "", err
  977. }
  978. o, ok := obj.(*Object)
  979. if !ok {
  980. return "", errors.New("internal error: not an Object")
  981. }
  982. opts := rest.Opts{
  983. Method: "PUT",
  984. RootURL: o.id,
  985. }
  986. linkFile := api.SetPublicLink{
  987. PublicLink: api.PublicLink{Enabled: true},
  988. }
  989. var resp *http.Response
  990. var info *api.File
  991. err = f.pacer.Call(func() (bool, error) {
  992. resp, err = f.srv.CallXML(ctx, &opts, &linkFile, &info)
  993. return shouldRetry(ctx, resp, err)
  994. })
  995. if err != nil {
  996. return "", err
  997. }
  998. return info.PublicLink.URL, err
  999. }
  1000. // DirCacheFlush resets the directory cache - used in testing as an
  1001. // optional interface
  1002. func (f *Fs) DirCacheFlush() {
  1003. f.dirCache.ResetRoot()
  1004. }
  1005. // Hashes returns the supported hash sets.
  1006. func (f *Fs) Hashes() hash.Set {
  1007. return hash.Set(hash.None)
  1008. }
  1009. // ------------------------------------------------------------
  1010. // Fs returns the parent Fs
  1011. func (o *Object) Fs() fs.Info {
  1012. return o.fs
  1013. }
  1014. // Return a string version
  1015. func (o *Object) String() string {
  1016. if o == nil {
  1017. return "<nil>"
  1018. }
  1019. return o.remote
  1020. }
  1021. // Remote returns the remote path
  1022. func (o *Object) Remote() string {
  1023. return o.remote
  1024. }
  1025. // Hash returns the SHA-1 of an object returning a lowercase hex string
  1026. func (o *Object) Hash(ctx context.Context, t hash.Type) (string, error) {
  1027. return "", hash.ErrUnsupported
  1028. }
  1029. // Size returns the size of an object in bytes
  1030. func (o *Object) Size() int64 {
  1031. err := o.readMetaData(context.TODO())
  1032. if err != nil {
  1033. fs.Logf(o, "Failed to read metadata: %v", err)
  1034. return 0
  1035. }
  1036. return o.size
  1037. }
  1038. // setMetaData sets the metadata from info
  1039. func (o *Object) setMetaData(info *api.File) (err error) {
  1040. o.hasMetaData = true
  1041. o.size = info.Size
  1042. o.modTime = info.LastModified
  1043. if info.Ref != "" {
  1044. o.id = info.Ref
  1045. } else if o.id == "" {
  1046. return errors.New("no ID found in response")
  1047. }
  1048. return nil
  1049. }
  1050. // readMetaData gets the metadata if it hasn't already been fetched
  1051. //
  1052. // it also sets the info
  1053. func (o *Object) readMetaData(ctx context.Context) (err error) {
  1054. if o.hasMetaData {
  1055. return nil
  1056. }
  1057. var info *api.File
  1058. if o.id != "" {
  1059. info, err = o.fs.readMetaDataForID(ctx, o.id)
  1060. } else {
  1061. info, err = o.fs.readMetaDataForPath(ctx, o.remote)
  1062. }
  1063. if err != nil {
  1064. return err
  1065. }
  1066. return o.setMetaData(info)
  1067. }
  1068. // ModTime returns the modification time of the object
  1069. //
  1070. // It attempts to read the objects mtime and if that isn't present the
  1071. // LastModified returned in the http headers
  1072. func (o *Object) ModTime(ctx context.Context) time.Time {
  1073. err := o.readMetaData(ctx)
  1074. if err != nil {
  1075. fs.Logf(o, "Failed to read metadata: %v", err)
  1076. return time.Now()
  1077. }
  1078. return o.modTime
  1079. }
  1080. // SetModTime sets the modification time of the local fs object
  1081. func (o *Object) SetModTime(ctx context.Context, modTime time.Time) error {
  1082. // Sugarsync doesn't support setting the mod time.
  1083. //
  1084. // In theory (but not in the docs) you could patch the object,
  1085. // however it doesn't work.
  1086. return fs.ErrorCantSetModTime
  1087. }
  1088. // Storable returns a boolean showing whether this object storable
  1089. func (o *Object) Storable() bool {
  1090. return true
  1091. }
  1092. // Open an object for read
  1093. func (o *Object) Open(ctx context.Context, options ...fs.OpenOption) (in io.ReadCloser, err error) {
  1094. if o.id == "" {
  1095. return nil, errors.New("can't download - no id")
  1096. }
  1097. fs.FixRangeOption(options, o.size)
  1098. var resp *http.Response
  1099. opts := rest.Opts{
  1100. Method: "GET",
  1101. RootURL: o.id,
  1102. Path: "/data",
  1103. Options: options,
  1104. }
  1105. err = o.fs.pacer.Call(func() (bool, error) {
  1106. resp, err = o.fs.srv.Call(ctx, &opts)
  1107. return shouldRetry(ctx, resp, err)
  1108. })
  1109. if err != nil {
  1110. return nil, err
  1111. }
  1112. return resp.Body, err
  1113. }
  1114. // createFile makes an (empty) file with pathID as parent and name leaf and returns the ID
  1115. func (f *Fs) createFile(ctx context.Context, pathID, leaf, mimeType string) (newID string, err error) {
  1116. var resp *http.Response
  1117. opts := rest.Opts{
  1118. Method: "POST",
  1119. RootURL: pathID,
  1120. NoResponse: true,
  1121. }
  1122. mkdir := api.CreateFile{
  1123. Name: f.opt.Enc.FromStandardName(leaf),
  1124. MediaType: mimeType,
  1125. }
  1126. err = f.pacer.Call(func() (bool, error) {
  1127. resp, err = f.srv.CallXML(ctx, &opts, &mkdir, nil)
  1128. return shouldRetry(ctx, resp, err)
  1129. })
  1130. if err != nil {
  1131. return "", err
  1132. }
  1133. return resp.Header.Get("Location"), nil
  1134. }
  1135. // Update the object with the contents of the io.Reader, modTime and size
  1136. //
  1137. // If existing is set then it updates the object rather than creating a new one.
  1138. //
  1139. // The new object may have been created if an error is returned
  1140. func (o *Object) Update(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) (err error) {
  1141. size := src.Size()
  1142. // modTime := src.ModTime(ctx)
  1143. remote := o.Remote()
  1144. // Create the directory for the object if it doesn't exist
  1145. leaf, directoryID, err := o.fs.dirCache.FindPath(ctx, remote, true)
  1146. if err != nil {
  1147. return err
  1148. }
  1149. // if file doesn't exist, create it
  1150. if o.id == "" {
  1151. o.id, err = o.fs.createFile(ctx, directoryID, leaf, fs.MimeType(ctx, src))
  1152. if err != nil {
  1153. return fmt.Errorf("failed to create file: %w", err)
  1154. }
  1155. if o.id == "" {
  1156. return errors.New("failed to create file: no ID")
  1157. }
  1158. // if created the file and returning an error then delete the file
  1159. defer func() {
  1160. if err != nil {
  1161. delErr := o.fs.delete(ctx, true, o.id, remote, o.fs.opt.HardDelete)
  1162. if delErr != nil {
  1163. fs.Errorf(o, "failed to remove failed upload: %v", delErr)
  1164. }
  1165. }
  1166. }()
  1167. }
  1168. var resp *http.Response
  1169. opts := rest.Opts{
  1170. Method: "PUT",
  1171. RootURL: o.id,
  1172. Path: "/data",
  1173. NoResponse: true,
  1174. Options: options,
  1175. Body: in,
  1176. }
  1177. if size >= 0 {
  1178. opts.ContentLength = &size
  1179. }
  1180. err = o.fs.pacer.CallNoRetry(func() (bool, error) {
  1181. resp, err = o.fs.srv.Call(ctx, &opts)
  1182. return shouldRetry(ctx, resp, err)
  1183. })
  1184. if err != nil {
  1185. return fmt.Errorf("failed to upload file: %w", err)
  1186. }
  1187. o.hasMetaData = false
  1188. return o.readMetaData(ctx)
  1189. }
  1190. // Remove an object
  1191. func (o *Object) Remove(ctx context.Context) error {
  1192. return o.fs.delete(ctx, true, o.id, o.remote, o.fs.opt.HardDelete)
  1193. }
  1194. // ID returns the ID of the Object if known, or "" if not
  1195. func (o *Object) ID() string {
  1196. return o.id
  1197. }
  1198. // Check the interfaces are satisfied
  1199. var (
  1200. _ fs.Fs = (*Fs)(nil)
  1201. _ fs.Purger = (*Fs)(nil)
  1202. _ fs.PutStreamer = (*Fs)(nil)
  1203. _ fs.Copier = (*Fs)(nil)
  1204. _ fs.Mover = (*Fs)(nil)
  1205. _ fs.DirMover = (*Fs)(nil)
  1206. _ fs.DirCacheFlusher = (*Fs)(nil)
  1207. _ fs.PublicLinker = (*Fs)(nil)
  1208. _ fs.Object = (*Object)(nil)
  1209. _ fs.IDer = (*Object)(nil)
  1210. )