github.com/ncw/rclone@v1.48.1-0.20190724201158-a35aa1360e3e/backend/opendrive/opendrive.go (about)

     1  package opendrive
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"io"
     7  	"net/http"
     8  	"net/url"
     9  	"path"
    10  	"strconv"
    11  	"strings"
    12  	"time"
    13  
    14  	"github.com/ncw/rclone/fs"
    15  	"github.com/ncw/rclone/fs/config/configmap"
    16  	"github.com/ncw/rclone/fs/config/configstruct"
    17  	"github.com/ncw/rclone/fs/config/obscure"
    18  	"github.com/ncw/rclone/fs/fserrors"
    19  	"github.com/ncw/rclone/fs/fshttp"
    20  	"github.com/ncw/rclone/fs/hash"
    21  	"github.com/ncw/rclone/lib/dircache"
    22  	"github.com/ncw/rclone/lib/pacer"
    23  	"github.com/ncw/rclone/lib/readers"
    24  	"github.com/ncw/rclone/lib/rest"
    25  	"github.com/pkg/errors"
    26  )
    27  
    28  const (
    29  	defaultEndpoint = "https://dev.opendrive.com/api/v1"
    30  	minSleep        = 10 * time.Millisecond
    31  	maxSleep        = 5 * time.Minute
    32  	decayConstant   = 1 // bigger for slower decay, exponential
    33  )
    34  
    35  // Register with Fs
    36  func init() {
    37  	fs.Register(&fs.RegInfo{
    38  		Name:        "opendrive",
    39  		Description: "OpenDrive",
    40  		NewFs:       NewFs,
    41  		Options: []fs.Option{{
    42  			Name:     "username",
    43  			Help:     "Username",
    44  			Required: true,
    45  		}, {
    46  			Name:       "password",
    47  			Help:       "Password.",
    48  			IsPassword: true,
    49  			Required:   true,
    50  		}},
    51  	})
    52  }
    53  
    54  // Options defines the configuration for this backend
    55  type Options struct {
    56  	UserName string `config:"username"`
    57  	Password string `config:"password"`
    58  }
    59  
    60  // Fs represents a remote server
    61  type Fs struct {
    62  	name     string             // name of this remote
    63  	root     string             // the path we are working on
    64  	opt      Options            // parsed options
    65  	features *fs.Features       // optional features
    66  	srv      *rest.Client       // the connection to the server
    67  	pacer    *fs.Pacer          // To pace and retry the API calls
    68  	session  UserSessionInfo    // contains the session data
    69  	dirCache *dircache.DirCache // Map of directory path to directory id
    70  }
    71  
    72  // Object describes an object
    73  type Object struct {
    74  	fs      *Fs       // what this object is part of
    75  	remote  string    // The remote path
    76  	id      string    // ID of the file
    77  	modTime time.Time // The modified time of the object if known
    78  	md5     string    // MD5 hash if known
    79  	size    int64     // Size of the object
    80  }
    81  
    82  // parsePath parses an incoming 'url'
    83  func parsePath(path string) (root string) {
    84  	root = strings.Trim(path, "/")
    85  	return
    86  }
    87  
    88  // ------------------------------------------------------------
    89  
    90  // Name of the remote (as passed into NewFs)
    91  func (f *Fs) Name() string {
    92  	return f.name
    93  }
    94  
    95  // Root of the remote (as passed into NewFs)
    96  func (f *Fs) Root() string {
    97  	return f.root
    98  }
    99  
   100  // String converts this Fs to a string
   101  func (f *Fs) String() string {
   102  	return fmt.Sprintf("OpenDrive root '%s'", f.root)
   103  }
   104  
   105  // Features returns the optional features of this Fs
   106  func (f *Fs) Features() *fs.Features {
   107  	return f.features
   108  }
   109  
   110  // Hashes returns the supported hash sets.
   111  func (f *Fs) Hashes() hash.Set {
   112  	return hash.Set(hash.MD5)
   113  }
   114  
   115  // DirCacheFlush resets the directory cache - used in testing as an
   116  // optional interface
   117  func (f *Fs) DirCacheFlush() {
   118  	f.dirCache.ResetRoot()
   119  }
   120  
   121  // NewFs constructs an Fs from the path, bucket:path
   122  func NewFs(name, root string, m configmap.Mapper) (fs.Fs, error) {
   123  	ctx := context.Background()
   124  	// Parse config into Options struct
   125  	opt := new(Options)
   126  	err := configstruct.Set(m, opt)
   127  	if err != nil {
   128  		return nil, err
   129  	}
   130  	root = parsePath(root)
   131  	if opt.UserName == "" {
   132  		return nil, errors.New("username not found")
   133  	}
   134  	opt.Password, err = obscure.Reveal(opt.Password)
   135  	if err != nil {
   136  		return nil, errors.New("password could not revealed")
   137  	}
   138  	if opt.Password == "" {
   139  		return nil, errors.New("password not found")
   140  	}
   141  
   142  	f := &Fs{
   143  		name:  name,
   144  		root:  root,
   145  		opt:   *opt,
   146  		srv:   rest.NewClient(fshttp.NewClient(fs.Config)).SetErrorHandler(errorHandler),
   147  		pacer: fs.NewPacer(pacer.NewDefault(pacer.MinSleep(minSleep), pacer.MaxSleep(maxSleep), pacer.DecayConstant(decayConstant))),
   148  	}
   149  
   150  	f.dirCache = dircache.New(root, "0", f)
   151  
   152  	// set the rootURL for the REST client
   153  	f.srv.SetRoot(defaultEndpoint)
   154  
   155  	// get sessionID
   156  	var resp *http.Response
   157  	err = f.pacer.Call(func() (bool, error) {
   158  		account := Account{Username: opt.UserName, Password: opt.Password}
   159  
   160  		opts := rest.Opts{
   161  			Method: "POST",
   162  			Path:   "/session/login.json",
   163  		}
   164  		resp, err = f.srv.CallJSON(&opts, &account, &f.session)
   165  		return f.shouldRetry(resp, err)
   166  	})
   167  	if err != nil {
   168  		return nil, errors.Wrap(err, "failed to create session")
   169  	}
   170  	fs.Debugf(nil, "Starting OpenDrive session with ID: %s", f.session.SessionID)
   171  
   172  	f.features = (&fs.Features{
   173  		CaseInsensitive:         true,
   174  		CanHaveEmptyDirectories: true,
   175  	}).Fill(f)
   176  
   177  	// Find the current root
   178  	err = f.dirCache.FindRoot(ctx, false)
   179  	if err != nil {
   180  		// Assume it is a file
   181  		newRoot, remote := dircache.SplitPath(root)
   182  		tempF := *f
   183  		tempF.dirCache = dircache.New(newRoot, "0", &tempF)
   184  		tempF.root = newRoot
   185  
   186  		// Make new Fs which is the parent
   187  		err = tempF.dirCache.FindRoot(ctx, false)
   188  		if err != nil {
   189  			// No root so return old f
   190  			return f, nil
   191  		}
   192  		_, err := tempF.newObjectWithInfo(ctx, remote, nil)
   193  		if err != nil {
   194  			if err == fs.ErrorObjectNotFound {
   195  				// File doesn't exist so return old f
   196  				return f, nil
   197  			}
   198  			return nil, err
   199  		}
   200  		// XXX: update the old f here instead of returning tempF, since
   201  		// `features` were already filled with functions having *f as a receiver.
   202  		// See https://github.com/ncw/rclone/issues/2182
   203  		f.dirCache = tempF.dirCache
   204  		f.root = tempF.root
   205  		// return an error with an fs which points to the parent
   206  		return f, fs.ErrorIsFile
   207  	}
   208  	return f, nil
   209  }
   210  
   211  // rootSlash returns root with a slash on if it is empty, otherwise empty string
   212  func (f *Fs) rootSlash() string {
   213  	if f.root == "" {
   214  		return f.root
   215  	}
   216  	return f.root + "/"
   217  }
   218  
   219  // errorHandler parses a non 2xx error response into an error
   220  func errorHandler(resp *http.Response) error {
   221  	errResponse := new(Error)
   222  	err := rest.DecodeJSON(resp, &errResponse)
   223  	if err != nil {
   224  		fs.Debugf(nil, "Couldn't decode error response: %v", err)
   225  	}
   226  	if errResponse.Info.Code == 0 {
   227  		errResponse.Info.Code = resp.StatusCode
   228  	}
   229  	if errResponse.Info.Message == "" {
   230  		errResponse.Info.Message = "Unknown " + resp.Status
   231  	}
   232  	return errResponse
   233  }
   234  
   235  // Mkdir creates the folder if it doesn't exist
   236  func (f *Fs) Mkdir(ctx context.Context, dir string) error {
   237  	// fs.Debugf(nil, "Mkdir(\"%s\")", dir)
   238  	err := f.dirCache.FindRoot(ctx, true)
   239  	if err != nil {
   240  		return err
   241  	}
   242  	if dir != "" {
   243  		_, err = f.dirCache.FindDir(ctx, dir, true)
   244  	}
   245  	return err
   246  }
   247  
   248  // deleteObject removes an object by ID
   249  func (f *Fs) deleteObject(id string) error {
   250  	return f.pacer.Call(func() (bool, error) {
   251  		removeDirData := removeFolder{SessionID: f.session.SessionID, FolderID: id}
   252  		opts := rest.Opts{
   253  			Method:     "POST",
   254  			NoResponse: true,
   255  			Path:       "/folder/remove.json",
   256  		}
   257  		resp, err := f.srv.CallJSON(&opts, &removeDirData, nil)
   258  		return f.shouldRetry(resp, err)
   259  	})
   260  }
   261  
   262  // purgeCheck remotes the root directory, if check is set then it
   263  // refuses to do so if it has anything in
   264  func (f *Fs) purgeCheck(ctx context.Context, dir string, check bool) error {
   265  	root := path.Join(f.root, dir)
   266  	if root == "" {
   267  		return errors.New("can't purge root directory")
   268  	}
   269  	dc := f.dirCache
   270  	err := dc.FindRoot(ctx, false)
   271  	if err != nil {
   272  		return err
   273  	}
   274  	rootID, err := dc.FindDir(ctx, dir, false)
   275  	if err != nil {
   276  		return err
   277  	}
   278  	item, err := f.readMetaDataForFolderID(rootID)
   279  	if err != nil {
   280  		return err
   281  	}
   282  	if check && len(item.Files) != 0 {
   283  		return errors.New("folder not empty")
   284  	}
   285  	err = f.deleteObject(rootID)
   286  	if err != nil {
   287  		return err
   288  	}
   289  	f.dirCache.FlushDir(dir)
   290  	return nil
   291  }
   292  
   293  // Rmdir deletes the root folder
   294  //
   295  // Returns an error if it isn't empty
   296  func (f *Fs) Rmdir(ctx context.Context, dir string) error {
   297  	// fs.Debugf(nil, "Rmdir(\"%s\")", path.Join(f.root, dir))
   298  	return f.purgeCheck(ctx, dir, true)
   299  }
   300  
   301  // Precision of the remote
   302  func (f *Fs) Precision() time.Duration {
   303  	return time.Second
   304  }
   305  
   306  // Copy src to this remote using server side copy operations.
   307  //
   308  // This is stored with the remote path given
   309  //
   310  // It returns the destination Object and a possible error
   311  //
   312  // Will only be called if src.Fs().Name() == f.Name()
   313  //
   314  // If it isn't possible then return fs.ErrorCantCopy
   315  func (f *Fs) Copy(ctx context.Context, src fs.Object, remote string) (fs.Object, error) {
   316  	// fs.Debugf(nil, "Copy(%v)", remote)
   317  	srcObj, ok := src.(*Object)
   318  	if !ok {
   319  		fs.Debugf(src, "Can't copy - not same remote type")
   320  		return nil, fs.ErrorCantCopy
   321  	}
   322  	err := srcObj.readMetaData(ctx)
   323  	if err != nil {
   324  		return nil, err
   325  	}
   326  
   327  	srcPath := srcObj.fs.rootSlash() + srcObj.remote
   328  	dstPath := f.rootSlash() + remote
   329  	if strings.ToLower(srcPath) == strings.ToLower(dstPath) {
   330  		return nil, errors.Errorf("Can't copy %q -> %q as are same name when lowercase", srcPath, dstPath)
   331  	}
   332  
   333  	// Create temporary object
   334  	dstObj, leaf, directoryID, err := f.createObject(ctx, remote, srcObj.modTime, srcObj.size)
   335  	if err != nil {
   336  		return nil, err
   337  	}
   338  	// fs.Debugf(nil, "...%#v\n...%#v", remote, directoryID)
   339  
   340  	// Copy the object
   341  	var resp *http.Response
   342  	response := moveCopyFileResponse{}
   343  	err = f.pacer.Call(func() (bool, error) {
   344  		copyFileData := moveCopyFile{
   345  			SessionID:         f.session.SessionID,
   346  			SrcFileID:         srcObj.id,
   347  			DstFolderID:       directoryID,
   348  			Move:              "false",
   349  			OverwriteIfExists: "true",
   350  			NewFileName:       leaf,
   351  		}
   352  		opts := rest.Opts{
   353  			Method: "POST",
   354  			Path:   "/file/move_copy.json",
   355  		}
   356  		resp, err = f.srv.CallJSON(&opts, &copyFileData, &response)
   357  		return f.shouldRetry(resp, err)
   358  	})
   359  	if err != nil {
   360  		return nil, err
   361  	}
   362  
   363  	size, _ := strconv.ParseInt(response.Size, 10, 64)
   364  	dstObj.id = response.FileID
   365  	dstObj.size = size
   366  
   367  	return dstObj, nil
   368  }
   369  
   370  // Move src to this remote using server side move operations.
   371  //
   372  // This is stored with the remote path given
   373  //
   374  // It returns the destination Object and a possible error
   375  //
   376  // Will only be called if src.Fs().Name() == f.Name()
   377  //
   378  // If it isn't possible then return fs.ErrorCantMove
   379  func (f *Fs) Move(ctx context.Context, src fs.Object, remote string) (fs.Object, error) {
   380  	// fs.Debugf(nil, "Move(%v)", remote)
   381  	srcObj, ok := src.(*Object)
   382  	if !ok {
   383  		fs.Debugf(src, "Can't move - not same remote type")
   384  		return nil, fs.ErrorCantCopy
   385  	}
   386  	err := srcObj.readMetaData(ctx)
   387  	if err != nil {
   388  		return nil, err
   389  	}
   390  
   391  	// Create temporary object
   392  	dstObj, leaf, directoryID, err := f.createObject(ctx, remote, srcObj.modTime, srcObj.size)
   393  	if err != nil {
   394  		return nil, err
   395  	}
   396  
   397  	// Copy the object
   398  	var resp *http.Response
   399  	response := moveCopyFileResponse{}
   400  	err = f.pacer.Call(func() (bool, error) {
   401  		copyFileData := moveCopyFile{
   402  			SessionID:         f.session.SessionID,
   403  			SrcFileID:         srcObj.id,
   404  			DstFolderID:       directoryID,
   405  			Move:              "true",
   406  			OverwriteIfExists: "true",
   407  			NewFileName:       leaf,
   408  		}
   409  		opts := rest.Opts{
   410  			Method: "POST",
   411  			Path:   "/file/move_copy.json",
   412  		}
   413  		resp, err = f.srv.CallJSON(&opts, &copyFileData, &response)
   414  		return f.shouldRetry(resp, err)
   415  	})
   416  	if err != nil {
   417  		return nil, err
   418  	}
   419  
   420  	size, _ := strconv.ParseInt(response.Size, 10, 64)
   421  	dstObj.id = response.FileID
   422  	dstObj.size = size
   423  
   424  	return dstObj, nil
   425  }
   426  
   427  // DirMove moves src, srcRemote to this remote at dstRemote
   428  // using server side move operations.
   429  //
   430  // Will only be called if src.Fs().Name() == f.Name()
   431  //
   432  // If it isn't possible then return fs.ErrorCantDirMove
   433  //
   434  // If destination exists then return fs.ErrorDirExists
   435  func (f *Fs) DirMove(ctx context.Context, src fs.Fs, srcRemote, dstRemote string) (err error) {
   436  	srcFs, ok := src.(*Fs)
   437  	if !ok {
   438  		fs.Debugf(srcFs, "Can't move directory - not same remote type")
   439  		return fs.ErrorCantDirMove
   440  	}
   441  	srcPath := path.Join(srcFs.root, srcRemote)
   442  	dstPath := path.Join(f.root, dstRemote)
   443  
   444  	// Refuse to move to or from the root
   445  	if srcPath == "" || dstPath == "" {
   446  		fs.Debugf(src, "DirMove error: Can't move root")
   447  		return errors.New("can't move root directory")
   448  	}
   449  
   450  	// find the root src directory
   451  	err = srcFs.dirCache.FindRoot(ctx, false)
   452  	if err != nil {
   453  		return err
   454  	}
   455  
   456  	// find the root dst directory
   457  	if dstRemote != "" {
   458  		err = f.dirCache.FindRoot(ctx, true)
   459  		if err != nil {
   460  			return err
   461  		}
   462  	} else {
   463  		if f.dirCache.FoundRoot() {
   464  			return fs.ErrorDirExists
   465  		}
   466  	}
   467  
   468  	// Find ID of dst parent, creating subdirs if necessary
   469  	var leaf, directoryID string
   470  	findPath := dstRemote
   471  	if dstRemote == "" {
   472  		findPath = f.root
   473  	}
   474  	leaf, directoryID, err = f.dirCache.FindPath(ctx, findPath, true)
   475  	if err != nil {
   476  		return err
   477  	}
   478  
   479  	// Check destination does not exist
   480  	if dstRemote != "" {
   481  		_, err = f.dirCache.FindDir(ctx, dstRemote, false)
   482  		if err == fs.ErrorDirNotFound {
   483  			// OK
   484  		} else if err != nil {
   485  			return err
   486  		} else {
   487  			return fs.ErrorDirExists
   488  		}
   489  	}
   490  
   491  	// Find ID of src
   492  	srcID, err := srcFs.dirCache.FindDir(ctx, srcRemote, false)
   493  	if err != nil {
   494  		return err
   495  	}
   496  
   497  	// Do the move
   498  	var resp *http.Response
   499  	response := moveCopyFolderResponse{}
   500  	err = f.pacer.Call(func() (bool, error) {
   501  		moveFolderData := moveCopyFolder{
   502  			SessionID:     f.session.SessionID,
   503  			FolderID:      srcID,
   504  			DstFolderID:   directoryID,
   505  			Move:          "true",
   506  			NewFolderName: leaf,
   507  		}
   508  		opts := rest.Opts{
   509  			Method: "POST",
   510  			Path:   "/folder/move_copy.json",
   511  		}
   512  		resp, err = f.srv.CallJSON(&opts, &moveFolderData, &response)
   513  		return f.shouldRetry(resp, err)
   514  	})
   515  	if err != nil {
   516  		fs.Debugf(src, "DirMove error %v", err)
   517  		return err
   518  	}
   519  
   520  	srcFs.dirCache.FlushDir(srcRemote)
   521  	return nil
   522  }
   523  
   524  // Purge deletes all the files and the container
   525  //
   526  // Optional interface: Only implement this if you have a way of
   527  // deleting all the files quicker than just running Remove() on the
   528  // result of List()
   529  func (f *Fs) Purge(ctx context.Context) error {
   530  	return f.purgeCheck(ctx, "", false)
   531  }
   532  
   533  // Return an Object from a path
   534  //
   535  // If it can't be found it returns the error fs.ErrorObjectNotFound.
   536  func (f *Fs) newObjectWithInfo(ctx context.Context, remote string, file *File) (fs.Object, error) {
   537  	// fs.Debugf(nil, "newObjectWithInfo(%s, %v)", remote, file)
   538  
   539  	var o *Object
   540  	if nil != file {
   541  		o = &Object{
   542  			fs:      f,
   543  			remote:  remote,
   544  			id:      file.FileID,
   545  			modTime: time.Unix(file.DateModified, 0),
   546  			size:    file.Size,
   547  			md5:     file.FileHash,
   548  		}
   549  	} else {
   550  		o = &Object{
   551  			fs:     f,
   552  			remote: remote,
   553  		}
   554  
   555  		err := o.readMetaData(ctx)
   556  		if err != nil {
   557  			return nil, err
   558  		}
   559  	}
   560  	return o, nil
   561  }
   562  
   563  // NewObject finds the Object at remote.  If it can't be found
   564  // it returns the error fs.ErrorObjectNotFound.
   565  func (f *Fs) NewObject(ctx context.Context, remote string) (fs.Object, error) {
   566  	// fs.Debugf(nil, "NewObject(\"%s\")", remote)
   567  	return f.newObjectWithInfo(ctx, remote, nil)
   568  }
   569  
   570  // Creates from the parameters passed in a half finished Object which
   571  // must have setMetaData called on it
   572  //
   573  // Returns the object, leaf, directoryID and error
   574  //
   575  // Used to create new objects
   576  func (f *Fs) createObject(ctx context.Context, remote string, modTime time.Time, size int64) (o *Object, leaf string, directoryID string, err error) {
   577  	// Create the directory for the object if it doesn't exist
   578  	leaf, directoryID, err = f.dirCache.FindRootAndPath(ctx, remote, true)
   579  	if err != nil {
   580  		return nil, leaf, directoryID, err
   581  	}
   582  	// fs.Debugf(nil, "\n...leaf %#v\n...id %#v", leaf, directoryID)
   583  	// Temporary Object under construction
   584  	o = &Object{
   585  		fs:     f,
   586  		remote: remote,
   587  	}
   588  	return o, leaf, directoryID, nil
   589  }
   590  
   591  // readMetaDataForPath reads the metadata from the path
   592  func (f *Fs) readMetaDataForFolderID(id string) (info *FolderList, err error) {
   593  	var resp *http.Response
   594  	opts := rest.Opts{
   595  		Method: "GET",
   596  		Path:   "/folder/list.json/" + f.session.SessionID + "/" + id,
   597  	}
   598  	err = f.pacer.Call(func() (bool, error) {
   599  		resp, err = f.srv.CallJSON(&opts, nil, &info)
   600  		return f.shouldRetry(resp, err)
   601  	})
   602  	if err != nil {
   603  		return nil, err
   604  	}
   605  	if resp != nil {
   606  	}
   607  
   608  	return info, err
   609  }
   610  
   611  // Put the object into the bucket
   612  //
   613  // Copy the reader in to the new object which is returned
   614  //
   615  // The new object may have been created if an error is returned
   616  func (f *Fs) Put(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) (fs.Object, error) {
   617  	remote := src.Remote()
   618  	size := src.Size()
   619  	modTime := src.ModTime(ctx)
   620  
   621  	// fs.Debugf(nil, "Put(%s)", remote)
   622  
   623  	o, leaf, directoryID, err := f.createObject(ctx, remote, modTime, size)
   624  	if err != nil {
   625  		return nil, err
   626  	}
   627  
   628  	if "" == o.id {
   629  		// Attempt to read ID, ignore error
   630  		// FIXME is this correct?
   631  		_ = o.readMetaData(ctx)
   632  	}
   633  
   634  	if "" == o.id {
   635  		// We need to create a ID for this file
   636  		var resp *http.Response
   637  		response := createFileResponse{}
   638  		err := o.fs.pacer.Call(func() (bool, error) {
   639  			createFileData := createFile{SessionID: o.fs.session.SessionID, FolderID: directoryID, Name: replaceReservedChars(leaf)}
   640  			opts := rest.Opts{
   641  				Method: "POST",
   642  				Path:   "/upload/create_file.json",
   643  			}
   644  			resp, err = o.fs.srv.CallJSON(&opts, &createFileData, &response)
   645  			return o.fs.shouldRetry(resp, err)
   646  		})
   647  		if err != nil {
   648  			return nil, errors.Wrap(err, "failed to create file")
   649  		}
   650  
   651  		o.id = response.FileID
   652  	}
   653  
   654  	return o, o.Update(ctx, in, src, options...)
   655  }
   656  
   657  // retryErrorCodes is a slice of error codes that we will retry
   658  var retryErrorCodes = []int{
   659  	400, // Bad request (seen in "Next token is expired")
   660  	401, // Unauthorized (seen in "Token has expired")
   661  	408, // Request Timeout
   662  	423, // Locked - get this on folders sometimes
   663  	429, // Rate exceeded.
   664  	500, // Get occasional 500 Internal Server Error
   665  	502, // Bad Gateway when doing big listings
   666  	503, // Service Unavailable
   667  	504, // Gateway Time-out
   668  }
   669  
   670  // shouldRetry returns a boolean as to whether this resp and err
   671  // deserve to be retried.  It returns the err as a convenience
   672  func (f *Fs) shouldRetry(resp *http.Response, err error) (bool, error) {
   673  	return fserrors.ShouldRetry(err) || fserrors.ShouldRetryHTTP(resp, retryErrorCodes), err
   674  }
   675  
   676  // DirCacher methods
   677  
   678  // CreateDir makes a directory with pathID as parent and name leaf
   679  func (f *Fs) CreateDir(ctx context.Context, pathID, leaf string) (newID string, err error) {
   680  	// fs.Debugf(f, "CreateDir(%q, %q)\n", pathID, replaceReservedChars(leaf))
   681  	var resp *http.Response
   682  	response := createFolderResponse{}
   683  	err = f.pacer.Call(func() (bool, error) {
   684  		createDirData := createFolder{
   685  			SessionID:           f.session.SessionID,
   686  			FolderName:          replaceReservedChars(leaf),
   687  			FolderSubParent:     pathID,
   688  			FolderIsPublic:      0,
   689  			FolderPublicUpl:     0,
   690  			FolderPublicDisplay: 0,
   691  			FolderPublicDnl:     0,
   692  		}
   693  		opts := rest.Opts{
   694  			Method: "POST",
   695  			Path:   "/folder.json",
   696  		}
   697  		resp, err = f.srv.CallJSON(&opts, &createDirData, &response)
   698  		return f.shouldRetry(resp, err)
   699  	})
   700  	if err != nil {
   701  		return "", err
   702  	}
   703  
   704  	return response.FolderID, nil
   705  }
   706  
   707  // FindLeaf finds a directory of name leaf in the folder with ID pathID
   708  func (f *Fs) FindLeaf(ctx context.Context, pathID, leaf string) (pathIDOut string, found bool, err error) {
   709  	// fs.Debugf(nil, "FindLeaf(\"%s\", \"%s\")", pathID, leaf)
   710  
   711  	if pathID == "0" && leaf == "" {
   712  		// fs.Debugf(nil, "Found OpenDrive root")
   713  		// that's the root directory
   714  		return pathID, true, nil
   715  	}
   716  
   717  	// get the folderIDs
   718  	var resp *http.Response
   719  	folderList := FolderList{}
   720  	err = f.pacer.Call(func() (bool, error) {
   721  		opts := rest.Opts{
   722  			Method: "GET",
   723  			Path:   "/folder/list.json/" + f.session.SessionID + "/" + pathID,
   724  		}
   725  		resp, err = f.srv.CallJSON(&opts, nil, &folderList)
   726  		return f.shouldRetry(resp, err)
   727  	})
   728  	if err != nil {
   729  		return "", false, errors.Wrap(err, "failed to get folder list")
   730  	}
   731  
   732  	for _, folder := range folderList.Folders {
   733  		folder.Name = restoreReservedChars(folder.Name)
   734  		// fs.Debugf(nil, "Folder: %s (%s)", folder.Name, folder.FolderID)
   735  
   736  		if leaf == folder.Name {
   737  			// found
   738  			return folder.FolderID, true, nil
   739  		}
   740  	}
   741  
   742  	return "", false, nil
   743  }
   744  
   745  // List the objects and directories in dir into entries.  The
   746  // entries can be returned in any order but should be for a
   747  // complete directory.
   748  //
   749  // dir should be "" to list the root, and should not have
   750  // trailing slashes.
   751  //
   752  // This should return ErrDirNotFound if the directory isn't
   753  // found.
   754  func (f *Fs) List(ctx context.Context, dir string) (entries fs.DirEntries, err error) {
   755  	// fs.Debugf(nil, "List(%v)", dir)
   756  	err = f.dirCache.FindRoot(ctx, false)
   757  	if err != nil {
   758  		return nil, err
   759  	}
   760  	directoryID, err := f.dirCache.FindDir(ctx, dir, false)
   761  	if err != nil {
   762  		return nil, err
   763  	}
   764  
   765  	var resp *http.Response
   766  	opts := rest.Opts{
   767  		Method: "GET",
   768  		Path:   "/folder/list.json/" + f.session.SessionID + "/" + directoryID,
   769  	}
   770  	folderList := FolderList{}
   771  	err = f.pacer.Call(func() (bool, error) {
   772  		resp, err = f.srv.CallJSON(&opts, nil, &folderList)
   773  		return f.shouldRetry(resp, err)
   774  	})
   775  	if err != nil {
   776  		return nil, errors.Wrap(err, "failed to get folder list")
   777  	}
   778  
   779  	for _, folder := range folderList.Folders {
   780  		folder.Name = restoreReservedChars(folder.Name)
   781  		// fs.Debugf(nil, "Folder: %s (%s)", folder.Name, folder.FolderID)
   782  		remote := path.Join(dir, folder.Name)
   783  		// cache the directory ID for later lookups
   784  		f.dirCache.Put(remote, folder.FolderID)
   785  		d := fs.NewDir(remote, time.Unix(folder.DateModified, 0)).SetID(folder.FolderID)
   786  		d.SetItems(int64(folder.ChildFolders))
   787  		entries = append(entries, d)
   788  	}
   789  
   790  	for _, file := range folderList.Files {
   791  		file.Name = restoreReservedChars(file.Name)
   792  		// fs.Debugf(nil, "File: %s (%s)", file.Name, file.FileID)
   793  		remote := path.Join(dir, file.Name)
   794  		o, err := f.newObjectWithInfo(ctx, remote, &file)
   795  		if err != nil {
   796  			return nil, err
   797  		}
   798  		entries = append(entries, o)
   799  	}
   800  
   801  	return entries, nil
   802  }
   803  
   804  // ------------------------------------------------------------
   805  
   806  // Fs returns the parent Fs
   807  func (o *Object) Fs() fs.Info {
   808  	return o.fs
   809  }
   810  
   811  // Return a string version
   812  func (o *Object) String() string {
   813  	if o == nil {
   814  		return "<nil>"
   815  	}
   816  	return o.remote
   817  }
   818  
   819  // Remote returns the remote path
   820  func (o *Object) Remote() string {
   821  	return o.remote
   822  }
   823  
   824  // Hash returns the Md5sum of an object returning a lowercase hex string
   825  func (o *Object) Hash(ctx context.Context, t hash.Type) (string, error) {
   826  	if t != hash.MD5 {
   827  		return "", hash.ErrUnsupported
   828  	}
   829  	return o.md5, nil
   830  }
   831  
   832  // Size returns the size of an object in bytes
   833  func (o *Object) Size() int64 {
   834  	return o.size // Object is likely PENDING
   835  }
   836  
   837  // ModTime returns the modification time of the object
   838  //
   839  //
   840  // It attempts to read the objects mtime and if that isn't present the
   841  // LastModified returned in the http headers
   842  func (o *Object) ModTime(ctx context.Context) time.Time {
   843  	return o.modTime
   844  }
   845  
   846  // SetModTime sets the modification time of the local fs object
   847  func (o *Object) SetModTime(ctx context.Context, modTime time.Time) error {
   848  	// fs.Debugf(nil, "SetModTime(%v)", modTime.String())
   849  	opts := rest.Opts{
   850  		Method:     "PUT",
   851  		NoResponse: true,
   852  		Path:       "/file/filesettings.json",
   853  	}
   854  	update := modTimeFile{SessionID: o.fs.session.SessionID, FileID: o.id, FileModificationTime: strconv.FormatInt(modTime.Unix(), 10)}
   855  	err := o.fs.pacer.Call(func() (bool, error) {
   856  		resp, err := o.fs.srv.CallJSON(&opts, &update, nil)
   857  		return o.fs.shouldRetry(resp, err)
   858  	})
   859  
   860  	o.modTime = modTime
   861  
   862  	return err
   863  }
   864  
   865  // Open an object for read
   866  func (o *Object) Open(ctx context.Context, options ...fs.OpenOption) (in io.ReadCloser, err error) {
   867  	// fs.Debugf(nil, "Open(\"%v\")", o.remote)
   868  	fs.FixRangeOption(options, o.size)
   869  	opts := rest.Opts{
   870  		Method:  "GET",
   871  		Path:    "/download/file.json/" + o.id + "?session_id=" + o.fs.session.SessionID,
   872  		Options: options,
   873  	}
   874  	var resp *http.Response
   875  	err = o.fs.pacer.Call(func() (bool, error) {
   876  		resp, err = o.fs.srv.Call(&opts)
   877  		return o.fs.shouldRetry(resp, err)
   878  	})
   879  	if err != nil {
   880  		return nil, errors.Wrap(err, "failed to open file)")
   881  	}
   882  
   883  	return resp.Body, nil
   884  }
   885  
   886  // Remove an object
   887  func (o *Object) Remove(ctx context.Context) error {
   888  	// fs.Debugf(nil, "Remove(\"%s\")", o.id)
   889  	return o.fs.pacer.Call(func() (bool, error) {
   890  		opts := rest.Opts{
   891  			Method:     "DELETE",
   892  			NoResponse: true,
   893  			Path:       "/file.json/" + o.fs.session.SessionID + "/" + o.id,
   894  		}
   895  		resp, err := o.fs.srv.Call(&opts)
   896  		return o.fs.shouldRetry(resp, err)
   897  	})
   898  }
   899  
   900  // Storable returns a boolean showing whether this object storable
   901  func (o *Object) Storable() bool {
   902  	return true
   903  }
   904  
   905  // Update the object with the contents of the io.Reader, modTime and size
   906  //
   907  // The new object may have been created if an error is returned
   908  func (o *Object) Update(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) error {
   909  	size := src.Size()
   910  	modTime := src.ModTime(ctx)
   911  	// fs.Debugf(nil, "Update(\"%s\", \"%s\")", o.id, o.remote)
   912  
   913  	// Open file for upload
   914  	var resp *http.Response
   915  	openResponse := openUploadResponse{}
   916  	err := o.fs.pacer.Call(func() (bool, error) {
   917  		openUploadData := openUpload{SessionID: o.fs.session.SessionID, FileID: o.id, Size: size}
   918  		// fs.Debugf(nil, "PreOpen: %#v", openUploadData)
   919  		opts := rest.Opts{
   920  			Method: "POST",
   921  			Path:   "/upload/open_file_upload.json",
   922  		}
   923  		resp, err := o.fs.srv.CallJSON(&opts, &openUploadData, &openResponse)
   924  		return o.fs.shouldRetry(resp, err)
   925  	})
   926  	if err != nil {
   927  		return errors.Wrap(err, "failed to create file")
   928  	}
   929  	// resp.Body.Close()
   930  	// fs.Debugf(nil, "PostOpen: %#v", openResponse)
   931  
   932  	// 10 MB chunks size
   933  	chunkSize := int64(1024 * 1024 * 10)
   934  	buf := make([]byte, int(chunkSize))
   935  	chunkOffset := int64(0)
   936  	remainingBytes := size
   937  	chunkCounter := 0
   938  
   939  	for remainingBytes > 0 {
   940  		currentChunkSize := chunkSize
   941  		if currentChunkSize > remainingBytes {
   942  			currentChunkSize = remainingBytes
   943  		}
   944  		remainingBytes -= currentChunkSize
   945  		fs.Debugf(o, "Uploading chunk %d, size=%d, remain=%d", chunkCounter, currentChunkSize, remainingBytes)
   946  
   947  		chunk := readers.NewRepeatableLimitReaderBuffer(in, buf, currentChunkSize)
   948  		var reply uploadFileChunkReply
   949  		err = o.fs.pacer.Call(func() (bool, error) {
   950  			// seek to the start in case this is a retry
   951  			if _, err = chunk.Seek(0, io.SeekStart); err != nil {
   952  				return false, err
   953  			}
   954  			opts := rest.Opts{
   955  				Method: "POST",
   956  				Path:   "/upload/upload_file_chunk.json",
   957  				Body:   chunk,
   958  				MultipartParams: url.Values{
   959  					"session_id":    []string{o.fs.session.SessionID},
   960  					"file_id":       []string{o.id},
   961  					"temp_location": []string{openResponse.TempLocation},
   962  					"chunk_offset":  []string{strconv.FormatInt(chunkOffset, 10)},
   963  					"chunk_size":    []string{strconv.FormatInt(currentChunkSize, 10)},
   964  				},
   965  				MultipartContentName: "file_data", // ..name of the parameter which is the attached file
   966  				MultipartFileName:    o.remote,    // ..name of the file for the attached file
   967  
   968  			}
   969  			resp, err = o.fs.srv.CallJSON(&opts, nil, &reply)
   970  			return o.fs.shouldRetry(resp, err)
   971  		})
   972  		if err != nil {
   973  			return errors.Wrap(err, "failed to create file")
   974  		}
   975  		if reply.TotalWritten != currentChunkSize {
   976  			return errors.Errorf("failed to create file: incomplete write of %d/%d bytes", reply.TotalWritten, currentChunkSize)
   977  		}
   978  
   979  		chunkCounter++
   980  		chunkOffset += currentChunkSize
   981  	}
   982  
   983  	// Close file for upload
   984  	closeResponse := closeUploadResponse{}
   985  	err = o.fs.pacer.Call(func() (bool, error) {
   986  		closeUploadData := closeUpload{SessionID: o.fs.session.SessionID, FileID: o.id, Size: size, TempLocation: openResponse.TempLocation}
   987  		// fs.Debugf(nil, "PreClose: %#v", closeUploadData)
   988  		opts := rest.Opts{
   989  			Method: "POST",
   990  			Path:   "/upload/close_file_upload.json",
   991  		}
   992  		resp, err = o.fs.srv.CallJSON(&opts, &closeUploadData, &closeResponse)
   993  		return o.fs.shouldRetry(resp, err)
   994  	})
   995  	if err != nil {
   996  		return errors.Wrap(err, "failed to create file")
   997  	}
   998  	// fs.Debugf(nil, "PostClose: %#v", closeResponse)
   999  
  1000  	o.id = closeResponse.FileID
  1001  	o.size = closeResponse.Size
  1002  
  1003  	// Set the mod time now
  1004  	err = o.SetModTime(ctx, modTime)
  1005  	if err != nil {
  1006  		return err
  1007  	}
  1008  
  1009  	// Set permissions
  1010  	err = o.fs.pacer.Call(func() (bool, error) {
  1011  		update := permissions{SessionID: o.fs.session.SessionID, FileID: o.id, FileIsPublic: 0}
  1012  		// fs.Debugf(nil, "Permissions : %#v", update)
  1013  		opts := rest.Opts{
  1014  			Method:     "POST",
  1015  			NoResponse: true,
  1016  			Path:       "/file/access.json",
  1017  		}
  1018  		resp, err = o.fs.srv.CallJSON(&opts, &update, nil)
  1019  		return o.fs.shouldRetry(resp, err)
  1020  	})
  1021  	if err != nil {
  1022  		return err
  1023  	}
  1024  
  1025  	return o.readMetaData(ctx)
  1026  }
  1027  
  1028  func (o *Object) readMetaData(ctx context.Context) (err error) {
  1029  	leaf, directoryID, err := o.fs.dirCache.FindRootAndPath(ctx, o.remote, false)
  1030  	if err != nil {
  1031  		if err == fs.ErrorDirNotFound {
  1032  			return fs.ErrorObjectNotFound
  1033  		}
  1034  		return err
  1035  	}
  1036  	var resp *http.Response
  1037  	folderList := FolderList{}
  1038  	err = o.fs.pacer.Call(func() (bool, error) {
  1039  		opts := rest.Opts{
  1040  			Method: "GET",
  1041  			Path:   "/folder/itembyname.json/" + o.fs.session.SessionID + "/" + directoryID + "?name=" + url.QueryEscape(replaceReservedChars(leaf)),
  1042  		}
  1043  		resp, err = o.fs.srv.CallJSON(&opts, nil, &folderList)
  1044  		return o.fs.shouldRetry(resp, err)
  1045  	})
  1046  	if err != nil {
  1047  		return errors.Wrap(err, "failed to get folder list")
  1048  	}
  1049  
  1050  	if len(folderList.Files) == 0 {
  1051  		return fs.ErrorObjectNotFound
  1052  	}
  1053  
  1054  	leafFile := folderList.Files[0]
  1055  	o.id = leafFile.FileID
  1056  	o.modTime = time.Unix(leafFile.DateModified, 0)
  1057  	o.md5 = leafFile.FileHash
  1058  	o.size = leafFile.Size
  1059  
  1060  	return nil
  1061  }
  1062  
  1063  // ID returns the ID of the Object if known, or "" if not
  1064  func (o *Object) ID() string {
  1065  	return o.id
  1066  }
  1067  
  1068  // Check the interfaces are satisfied
  1069  var (
  1070  	_ fs.Fs              = (*Fs)(nil)
  1071  	_ fs.Purger          = (*Fs)(nil)
  1072  	_ fs.Copier          = (*Fs)(nil)
  1073  	_ fs.Mover           = (*Fs)(nil)
  1074  	_ fs.DirMover        = (*Fs)(nil)
  1075  	_ fs.DirCacheFlusher = (*Fs)(nil)
  1076  	_ fs.Object          = (*Object)(nil)
  1077  	_ fs.IDer            = (*Object)(nil)
  1078  )