github.com/10XDev/rclone@v1.52.3-0.20200626220027-16af9ab76b2a/backend/sugarsync/sugarsync.go (about)

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