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

     1  // Package premiumizeme provides an interface to the premiumize.me
     2  // object storage system.
     3  package premiumizeme
     4  
     5  /*
     6  Run of rclone info
     7  stringNeedsEscaping = []rune{
     8  	0x00, 0x0A, 0x0D, 0x22, 0x2F, 0x5C, 0xBF, 0xFE
     9  	0x00, 0x0A, 0x0D, '"',  '/',  '\\', 0xBF, 0xFE
    10  }
    11  maxFileLength = 255
    12  canWriteUnnormalized = true
    13  canReadUnnormalized   = true
    14  canReadRenormalized   = false
    15  canStream = false
    16  */
    17  
    18  import (
    19  	"context"
    20  	"encoding/json"
    21  	"fmt"
    22  	"io"
    23  	"log"
    24  	"net"
    25  	"net/http"
    26  	"net/url"
    27  	"path"
    28  	"strings"
    29  	"time"
    30  
    31  	"github.com/pkg/errors"
    32  	"github.com/rclone/rclone/backend/premiumizeme/api"
    33  	"github.com/rclone/rclone/fs"
    34  	"github.com/rclone/rclone/fs/config"
    35  	"github.com/rclone/rclone/fs/config/configmap"
    36  	"github.com/rclone/rclone/fs/config/configstruct"
    37  	"github.com/rclone/rclone/fs/config/obscure"
    38  	"github.com/rclone/rclone/fs/fserrors"
    39  	"github.com/rclone/rclone/fs/fshttp"
    40  	"github.com/rclone/rclone/fs/hash"
    41  	"github.com/rclone/rclone/lib/dircache"
    42  	"github.com/rclone/rclone/lib/encoder"
    43  	"github.com/rclone/rclone/lib/oauthutil"
    44  	"github.com/rclone/rclone/lib/pacer"
    45  	"github.com/rclone/rclone/lib/random"
    46  	"github.com/rclone/rclone/lib/rest"
    47  	"golang.org/x/oauth2"
    48  )
    49  
    50  const (
    51  	rcloneClientID              = "658922194"
    52  	rcloneEncryptedClientSecret = "B5YIvQoRIhcpAYs8HYeyjb9gK-ftmZEbqdh_gNfc4RgO9Q"
    53  	minSleep                    = 10 * time.Millisecond
    54  	maxSleep                    = 2 * time.Second
    55  	decayConstant               = 2   // bigger for slower decay, exponential
    56  	rootID                      = "0" // ID of root folder is always this
    57  	rootURL                     = "https://www.premiumize.me/api"
    58  )
    59  
    60  // Globals
    61  var (
    62  	// Description of how to auth for this app
    63  	oauthConfig = &oauth2.Config{
    64  		Scopes: nil,
    65  		Endpoint: oauth2.Endpoint{
    66  			AuthURL:  "https://www.premiumize.me/authorize",
    67  			TokenURL: "https://www.premiumize.me/token",
    68  		},
    69  		ClientID:     rcloneClientID,
    70  		ClientSecret: obscure.MustReveal(rcloneEncryptedClientSecret),
    71  		RedirectURL:  oauthutil.RedirectURL,
    72  	}
    73  )
    74  
    75  // Register with Fs
    76  func init() {
    77  	fs.Register(&fs.RegInfo{
    78  		Name:        "premiumizeme",
    79  		Description: "premiumize.me",
    80  		NewFs:       NewFs,
    81  		Config: func(name string, m configmap.Mapper) {
    82  			err := oauthutil.Config("premiumizeme", name, m, oauthConfig, nil)
    83  			if err != nil {
    84  				log.Fatalf("Failed to configure token: %v", err)
    85  			}
    86  		},
    87  		Options: []fs.Option{{
    88  			Name: "api_key",
    89  			Help: `API Key.
    90  
    91  This is not normally used - use oauth instead.
    92  `,
    93  			Hide:    fs.OptionHideBoth,
    94  			Default: "",
    95  		}, {
    96  			Name:     config.ConfigEncoding,
    97  			Help:     config.ConfigEncodingHelp,
    98  			Advanced: true,
    99  			// Encode invalid UTF-8 bytes as json doesn't handle them properly.
   100  			Default: (encoder.Display |
   101  				encoder.EncodeBackSlash |
   102  				encoder.EncodeDoubleQuote |
   103  				encoder.EncodeInvalidUtf8),
   104  		}},
   105  	})
   106  }
   107  
   108  // Options defines the configuration for this backend
   109  type Options struct {
   110  	APIKey string               `config:"api_key"`
   111  	Enc    encoder.MultiEncoder `config:"encoding"`
   112  }
   113  
   114  // Fs represents a remote cloud storage system
   115  type Fs struct {
   116  	name         string             // name of this remote
   117  	root         string             // the path we are working on
   118  	opt          Options            // parsed options
   119  	features     *fs.Features       // optional features
   120  	srv          *rest.Client       // the connection to the server
   121  	dirCache     *dircache.DirCache // Map of directory path to directory id
   122  	pacer        *fs.Pacer          // pacer for API calls
   123  	tokenRenewer *oauthutil.Renew   // renew the token on expiry
   124  }
   125  
   126  // Object describes a file
   127  type Object struct {
   128  	fs          *Fs       // what this object is part of
   129  	remote      string    // The remote path
   130  	hasMetaData bool      // metadata is present and correct
   131  	size        int64     // size of the object
   132  	modTime     time.Time // modification time of the object
   133  	id          string    // ID of the object
   134  	parentID    string    // ID of parent directory
   135  	mimeType    string    // Mime type of object
   136  	url         string    // URL to download file
   137  }
   138  
   139  // ------------------------------------------------------------
   140  
   141  // Name of the remote (as passed into NewFs)
   142  func (f *Fs) Name() string {
   143  	return f.name
   144  }
   145  
   146  // Root of the remote (as passed into NewFs)
   147  func (f *Fs) Root() string {
   148  	return f.root
   149  }
   150  
   151  // String converts this Fs to a string
   152  func (f *Fs) String() string {
   153  	return fmt.Sprintf("premiumize.me root '%s'", f.root)
   154  }
   155  
   156  // Features returns the optional features of this Fs
   157  func (f *Fs) Features() *fs.Features {
   158  	return f.features
   159  }
   160  
   161  // parsePath parses a premiumize.me 'url'
   162  func parsePath(path string) (root string) {
   163  	root = strings.Trim(path, "/")
   164  	return
   165  }
   166  
   167  // retryErrorCodes is a slice of error codes that we will retry
   168  var retryErrorCodes = []int{
   169  	429, // Too Many Requests.
   170  	500, // Internal Server Error
   171  	502, // Bad Gateway
   172  	503, // Service Unavailable
   173  	504, // Gateway Timeout
   174  	509, // Bandwidth Limit Exceeded
   175  }
   176  
   177  // shouldRetry returns a boolean as to whether this resp and err
   178  // deserve to be retried.  It returns the err as a convenience
   179  func shouldRetry(resp *http.Response, err error) (bool, error) {
   180  	return fserrors.ShouldRetry(err) || fserrors.ShouldRetryHTTP(resp, retryErrorCodes), err
   181  }
   182  
   183  // readMetaDataForPath reads the metadata from the path
   184  func (f *Fs) readMetaDataForPath(ctx context.Context, path string, directoriesOnly bool, filesOnly bool) (info *api.Item, err error) {
   185  	// defer fs.Trace(f, "path=%q", path)("info=%+v, err=%v", &info, &err)
   186  	leaf, directoryID, err := f.dirCache.FindRootAndPath(ctx, path, false)
   187  	if err != nil {
   188  		if err == fs.ErrorDirNotFound {
   189  			return nil, fs.ErrorObjectNotFound
   190  		}
   191  		return nil, err
   192  	}
   193  
   194  	lcLeaf := strings.ToLower(leaf)
   195  	found, err := f.listAll(ctx, directoryID, directoriesOnly, filesOnly, func(item *api.Item) bool {
   196  		if strings.ToLower(item.Name) == lcLeaf {
   197  			info = item
   198  			return true
   199  		}
   200  		return false
   201  	})
   202  	if err != nil {
   203  		return nil, err
   204  	}
   205  	if !found {
   206  		return nil, fs.ErrorObjectNotFound
   207  	}
   208  	return info, nil
   209  }
   210  
   211  // errorHandler parses a non 2xx error response into an error
   212  func errorHandler(resp *http.Response) error {
   213  	body, err := rest.ReadBody(resp)
   214  	if err != nil {
   215  		body = nil
   216  	}
   217  	var e = api.Response{
   218  		Message: string(body),
   219  		Status:  fmt.Sprintf("%s (%d)", resp.Status, resp.StatusCode),
   220  	}
   221  	if body != nil {
   222  		_ = json.Unmarshal(body, &e)
   223  	}
   224  	return &e
   225  }
   226  
   227  // Return a url.Values with the api key in
   228  func (f *Fs) baseParams() url.Values {
   229  	params := url.Values{}
   230  	if f.opt.APIKey != "" {
   231  		params.Add("apikey", f.opt.APIKey)
   232  	}
   233  	return params
   234  }
   235  
   236  // NewFs constructs an Fs from the path, container:path
   237  func NewFs(name, root string, m configmap.Mapper) (fs.Fs, error) {
   238  	ctx := context.Background()
   239  	// Parse config into Options struct
   240  	opt := new(Options)
   241  	err := configstruct.Set(m, opt)
   242  	if err != nil {
   243  		return nil, err
   244  	}
   245  
   246  	root = parsePath(root)
   247  
   248  	var client *http.Client
   249  	var ts *oauthutil.TokenSource
   250  	if opt.APIKey == "" {
   251  		client, ts, err = oauthutil.NewClient(name, m, oauthConfig)
   252  		if err != nil {
   253  			return nil, errors.Wrap(err, "failed to configure premiumize.me")
   254  		}
   255  	} else {
   256  		client = fshttp.NewClient(fs.Config)
   257  	}
   258  
   259  	f := &Fs{
   260  		name:  name,
   261  		root:  root,
   262  		opt:   *opt,
   263  		srv:   rest.NewClient(client).SetRoot(rootURL),
   264  		pacer: fs.NewPacer(pacer.NewDefault(pacer.MinSleep(minSleep), pacer.MaxSleep(maxSleep), pacer.DecayConstant(decayConstant))),
   265  	}
   266  	f.features = (&fs.Features{
   267  		CaseInsensitive:         true,
   268  		CanHaveEmptyDirectories: true,
   269  		ReadMimeType:            true,
   270  	}).Fill(f)
   271  	f.srv.SetErrorHandler(errorHandler)
   272  
   273  	// Renew the token in the background
   274  	if ts != nil {
   275  		f.tokenRenewer = oauthutil.NewRenew(f.String(), ts, func() error {
   276  			_, err := f.About(ctx)
   277  			return err
   278  		})
   279  	}
   280  
   281  	// Get rootID
   282  	f.dirCache = dircache.New(root, rootID, f)
   283  
   284  	// Find the current root
   285  	err = f.dirCache.FindRoot(ctx, false)
   286  	if err != nil {
   287  		// Assume it is a file
   288  		newRoot, remote := dircache.SplitPath(root)
   289  		tempF := *f
   290  		tempF.dirCache = dircache.New(newRoot, rootID, &tempF)
   291  		tempF.root = newRoot
   292  		// Make new Fs which is the parent
   293  		err = tempF.dirCache.FindRoot(ctx, false)
   294  		if err != nil {
   295  			// No root so return old f
   296  			return f, nil
   297  		}
   298  		_, err := tempF.newObjectWithInfo(ctx, remote, nil)
   299  		if err != nil {
   300  			if err == fs.ErrorObjectNotFound {
   301  				// File doesn't exist so return old f
   302  				return f, nil
   303  			}
   304  			return nil, err
   305  		}
   306  		f.features.Fill(&tempF)
   307  		// XXX: update the old f here instead of returning tempF, since
   308  		// `features` were already filled with functions having *f as a receiver.
   309  		// See https://github.com/rclone/rclone/issues/2182
   310  		f.dirCache = tempF.dirCache
   311  		f.root = tempF.root
   312  		// return an error with an fs which points to the parent
   313  		return f, fs.ErrorIsFile
   314  	}
   315  	return f, nil
   316  }
   317  
   318  // Return an Object from a path
   319  //
   320  // If it can't be found it returns the error fs.ErrorObjectNotFound.
   321  func (f *Fs) newObjectWithInfo(ctx context.Context, remote string, info *api.Item) (fs.Object, error) {
   322  	o := &Object{
   323  		fs:     f,
   324  		remote: remote,
   325  	}
   326  	var err error
   327  	if info != nil {
   328  		// Set info
   329  		err = o.setMetaData(info)
   330  	} else {
   331  		err = o.readMetaData(ctx) // reads info and meta, returning an error
   332  	}
   333  	if err != nil {
   334  		return nil, err
   335  	}
   336  	return o, nil
   337  }
   338  
   339  // NewObject finds the Object at remote.  If it can't be found
   340  // it returns the error fs.ErrorObjectNotFound.
   341  func (f *Fs) NewObject(ctx context.Context, remote string) (fs.Object, error) {
   342  	return f.newObjectWithInfo(ctx, remote, nil)
   343  }
   344  
   345  // FindLeaf finds a directory of name leaf in the folder with ID pathID
   346  func (f *Fs) FindLeaf(ctx context.Context, pathID, leaf string) (pathIDOut string, found bool, err error) {
   347  	// Find the leaf in pathID
   348  	found, err = f.listAll(ctx, pathID, true, false, func(item *api.Item) bool {
   349  		if item.Name == leaf {
   350  			pathIDOut = item.ID
   351  			return true
   352  		}
   353  		return false
   354  	})
   355  	return pathIDOut, found, err
   356  }
   357  
   358  // CreateDir makes a directory with pathID as parent and name leaf
   359  func (f *Fs) CreateDir(ctx context.Context, pathID, leaf string) (newID string, err error) {
   360  	// fs.Debugf(f, "CreateDir(%q, %q)\n", pathID, leaf)
   361  	var resp *http.Response
   362  	var info api.FolderCreateResponse
   363  	opts := rest.Opts{
   364  		Method:     "POST",
   365  		Path:       "/folder/create",
   366  		Parameters: f.baseParams(),
   367  		MultipartParams: url.Values{
   368  			"name":      {f.opt.Enc.FromStandardName(leaf)},
   369  			"parent_id": {pathID},
   370  		},
   371  	}
   372  	err = f.pacer.Call(func() (bool, error) {
   373  		resp, err = f.srv.CallJSON(ctx, &opts, nil, &info)
   374  		return shouldRetry(resp, err)
   375  	})
   376  	if err != nil {
   377  		//fmt.Printf("...Error %v\n", err)
   378  		return "", errors.Wrap(err, "CreateDir http")
   379  	}
   380  	if err = info.AsErr(); err != nil {
   381  		return "", errors.Wrap(err, "CreateDir")
   382  	}
   383  	// fmt.Printf("...Id %q\n", *info.Id)
   384  	return info.ID, nil
   385  }
   386  
   387  // list the objects into the function supplied
   388  //
   389  // If directories is set it only sends directories
   390  // User function to process a File item from listAll
   391  //
   392  // Should return true to finish processing
   393  type listAllFn func(*api.Item) bool
   394  
   395  // Lists the directory required calling the user function on each item found
   396  //
   397  // If the user fn ever returns true then it early exits with found = true
   398  func (f *Fs) listAll(ctx context.Context, dirID string, directoriesOnly bool, filesOnly bool, fn listAllFn) (found bool, err error) {
   399  	opts := rest.Opts{
   400  		Method:     "GET",
   401  		Path:       "/folder/list",
   402  		Parameters: f.baseParams(),
   403  	}
   404  	opts.Parameters.Set("id", dirID)
   405  	opts.Parameters.Set("includebreadcrumbs", "false")
   406  
   407  	var result api.FolderListResponse
   408  	var resp *http.Response
   409  	err = f.pacer.Call(func() (bool, error) {
   410  		resp, err = f.srv.CallJSON(ctx, &opts, nil, &result)
   411  		return shouldRetry(resp, err)
   412  	})
   413  	if err != nil {
   414  		return found, errors.Wrap(err, "couldn't list files")
   415  	}
   416  	if err = result.AsErr(); err != nil {
   417  		return found, errors.Wrap(err, "error while listing")
   418  	}
   419  	for i := range result.Content {
   420  		item := &result.Content[i]
   421  		if item.Type == api.ItemTypeFolder {
   422  			if filesOnly {
   423  				continue
   424  			}
   425  		} else if item.Type == api.ItemTypeFile {
   426  			if directoriesOnly {
   427  				continue
   428  			}
   429  		} else {
   430  			fs.Debugf(f, "Ignoring %q - unknown type %q", item.Name, item.Type)
   431  			continue
   432  		}
   433  		item.Name = f.opt.Enc.ToStandardName(item.Name)
   434  		if fn(item) {
   435  			found = true
   436  			break
   437  		}
   438  	}
   439  
   440  	return
   441  }
   442  
   443  // List the objects and directories in dir into entries.  The
   444  // entries can be returned in any order but should be for a
   445  // complete directory.
   446  //
   447  // dir should be "" to list the root, and should not have
   448  // trailing slashes.
   449  //
   450  // This should return ErrDirNotFound if the directory isn't
   451  // found.
   452  func (f *Fs) List(ctx context.Context, dir string) (entries fs.DirEntries, err error) {
   453  	err = f.dirCache.FindRoot(ctx, false)
   454  	if err != nil {
   455  		return nil, err
   456  	}
   457  	directoryID, err := f.dirCache.FindDir(ctx, dir, false)
   458  	if err != nil {
   459  		return nil, err
   460  	}
   461  	var iErr error
   462  	_, err = f.listAll(ctx, directoryID, false, false, func(info *api.Item) bool {
   463  		remote := path.Join(dir, info.Name)
   464  		if info.Type == api.ItemTypeFolder {
   465  			// cache the directory ID for later lookups
   466  			f.dirCache.Put(remote, info.ID)
   467  			d := fs.NewDir(remote, time.Unix(info.CreatedAt, 0)).SetID(info.ID)
   468  			entries = append(entries, d)
   469  		} else if info.Type == api.ItemTypeFile {
   470  			o, err := f.newObjectWithInfo(ctx, remote, info)
   471  			if err != nil {
   472  				iErr = err
   473  				return true
   474  			}
   475  			entries = append(entries, o)
   476  		}
   477  		return false
   478  	})
   479  	if err != nil {
   480  		return nil, err
   481  	}
   482  	if iErr != nil {
   483  		return nil, iErr
   484  	}
   485  	return entries, nil
   486  }
   487  
   488  // Creates from the parameters passed in a half finished Object which
   489  // must have setMetaData called on it
   490  //
   491  // Returns the object, leaf, directoryID and error
   492  //
   493  // Used to create new objects
   494  func (f *Fs) createObject(ctx context.Context, remote string, modTime time.Time, size int64) (o *Object, leaf string, directoryID string, err error) {
   495  	// Create the directory for the object if it doesn't exist
   496  	leaf, directoryID, err = f.dirCache.FindRootAndPath(ctx, remote, true)
   497  	if err != nil {
   498  		return
   499  	}
   500  	// Temporary Object under construction
   501  	o = &Object{
   502  		fs:     f,
   503  		remote: remote,
   504  	}
   505  	return o, leaf, directoryID, nil
   506  }
   507  
   508  // Put the object
   509  //
   510  // Copy the reader in to the new object which is returned
   511  //
   512  // The new object may have been created if an error is returned
   513  func (f *Fs) Put(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) (fs.Object, error) {
   514  	existingObj, err := f.newObjectWithInfo(ctx, src.Remote(), nil)
   515  	switch err {
   516  	case nil:
   517  		return existingObj, existingObj.Update(ctx, in, src, options...)
   518  	case fs.ErrorObjectNotFound:
   519  		// Not found so create it
   520  		return f.PutUnchecked(ctx, in, src, options...)
   521  	default:
   522  		return nil, err
   523  	}
   524  }
   525  
   526  // PutUnchecked the object into the container
   527  //
   528  // This will produce an error if the object already exists
   529  //
   530  // Copy the reader in to the new object which is returned
   531  //
   532  // The new object may have been created if an error is returned
   533  func (f *Fs) PutUnchecked(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) (fs.Object, error) {
   534  	remote := src.Remote()
   535  	size := src.Size()
   536  	modTime := src.ModTime(ctx)
   537  
   538  	o, _, _, err := f.createObject(ctx, remote, modTime, size)
   539  	if err != nil {
   540  		return nil, err
   541  	}
   542  	return o, o.Update(ctx, in, src, options...)
   543  }
   544  
   545  // Mkdir creates the container if it doesn't exist
   546  func (f *Fs) Mkdir(ctx context.Context, dir string) error {
   547  	err := f.dirCache.FindRoot(ctx, true)
   548  	if err != nil {
   549  		return err
   550  	}
   551  	if dir != "" {
   552  		_, err = f.dirCache.FindDir(ctx, dir, true)
   553  	}
   554  	return err
   555  }
   556  
   557  // purgeCheck removes the root directory, if check is set then it
   558  // refuses to do so if it has anything in
   559  func (f *Fs) purgeCheck(ctx context.Context, dir string, check bool) error {
   560  	root := path.Join(f.root, dir)
   561  	if root == "" {
   562  		return errors.New("can't purge root directory")
   563  	}
   564  	dc := f.dirCache
   565  	err := dc.FindRoot(ctx, false)
   566  	if err != nil {
   567  		return err
   568  	}
   569  	rootID, err := dc.FindDir(ctx, dir, false)
   570  	if err != nil {
   571  		return err
   572  	}
   573  
   574  	// need to check if empty as it will delete recursively by default
   575  	if check {
   576  		found, err := f.listAll(ctx, rootID, false, false, func(item *api.Item) bool {
   577  			return true
   578  		})
   579  		if err != nil {
   580  			return errors.Wrap(err, "purgeCheck")
   581  		}
   582  		if found {
   583  			return fs.ErrorDirectoryNotEmpty
   584  		}
   585  	}
   586  
   587  	opts := rest.Opts{
   588  		Method: "POST",
   589  		Path:   "/folder/delete",
   590  		MultipartParams: url.Values{
   591  			"id": {rootID},
   592  		},
   593  		Parameters: f.baseParams(),
   594  	}
   595  	var resp *http.Response
   596  	var result api.Response
   597  	err = f.pacer.Call(func() (bool, error) {
   598  		resp, err = f.srv.CallJSON(ctx, &opts, nil, &result)
   599  		return shouldRetry(resp, err)
   600  	})
   601  	if err != nil {
   602  		return errors.Wrap(err, "rmdir failed")
   603  	}
   604  	if err = result.AsErr(); err != nil {
   605  		return errors.Wrap(err, "rmdir")
   606  	}
   607  	f.dirCache.FlushDir(dir)
   608  	if err != nil {
   609  		return err
   610  	}
   611  	return nil
   612  }
   613  
   614  // Rmdir deletes the root folder
   615  //
   616  // Returns an error if it isn't empty
   617  func (f *Fs) Rmdir(ctx context.Context, dir string) error {
   618  	return f.purgeCheck(ctx, dir, true)
   619  }
   620  
   621  // Precision return the precision of this Fs
   622  func (f *Fs) Precision() time.Duration {
   623  	return fs.ModTimeNotSupported
   624  }
   625  
   626  // Purge deletes all the files and the container
   627  //
   628  // Optional interface: Only implement this if you have a way of
   629  // deleting all the files quicker than just running Remove() on the
   630  // result of List()
   631  func (f *Fs) Purge(ctx context.Context) error {
   632  	return f.purgeCheck(ctx, "", false)
   633  }
   634  
   635  // move a file or folder
   636  //
   637  // This is complicated by the fact that there is an API to move files
   638  // between directories and a separate one to rename them.  We try to
   639  // call the minimum number of API calls.
   640  func (f *Fs) move(ctx context.Context, isFile bool, id, oldLeaf, newLeaf, oldDirectoryID, newDirectoryID string) (err error) {
   641  	newLeaf = f.opt.Enc.FromStandardName(newLeaf)
   642  	oldLeaf = f.opt.Enc.FromStandardName(oldLeaf)
   643  	doRenameLeaf := oldLeaf != newLeaf
   644  	doMove := oldDirectoryID != newDirectoryID
   645  
   646  	// Now rename the leaf to a temporary name if we are moving to
   647  	// another directory to make sure we don't overwrite something
   648  	// in the destination directory by accident
   649  	if doRenameLeaf && doMove {
   650  		tmpLeaf := newLeaf + "." + random.String(8)
   651  		err = f.renameLeaf(ctx, isFile, id, tmpLeaf)
   652  		if err != nil {
   653  			return errors.Wrap(err, "Move rename leaf")
   654  		}
   655  	}
   656  
   657  	// Move the object to a new directory (with the existing name)
   658  	// if required
   659  	if doMove {
   660  		opts := rest.Opts{
   661  			Method:     "POST",
   662  			Path:       "/folder/paste",
   663  			Parameters: f.baseParams(),
   664  			MultipartParams: url.Values{
   665  				"id": {newDirectoryID},
   666  			},
   667  		}
   668  		if isFile {
   669  			opts.MultipartParams.Set("files[]", id)
   670  		} else {
   671  			opts.MultipartParams.Set("folders[]", id)
   672  		}
   673  		//replacedLeaf := enc.FromStandardName(leaf)
   674  		var resp *http.Response
   675  		var result api.Response
   676  		err = f.pacer.Call(func() (bool, error) {
   677  			resp, err = f.srv.CallJSON(ctx, &opts, nil, &result)
   678  			return shouldRetry(resp, err)
   679  		})
   680  		if err != nil {
   681  			return errors.Wrap(err, "Move http")
   682  		}
   683  		if err = result.AsErr(); err != nil {
   684  			return errors.Wrap(err, "Move")
   685  		}
   686  	}
   687  
   688  	// Rename the leaf to its final name if required
   689  	if doRenameLeaf {
   690  		err = f.renameLeaf(ctx, isFile, id, newLeaf)
   691  		if err != nil {
   692  			return errors.Wrap(err, "Move rename leaf")
   693  		}
   694  	}
   695  
   696  	return nil
   697  }
   698  
   699  // Move src to this remote using server side move operations.
   700  //
   701  // This is stored with the remote path given
   702  //
   703  // It returns the destination Object and a possible error
   704  //
   705  // Will only be called if src.Fs().Name() == f.Name()
   706  //
   707  // If it isn't possible then return fs.ErrorCantMove
   708  func (f *Fs) Move(ctx context.Context, src fs.Object, remote string) (fs.Object, error) {
   709  	srcObj, ok := src.(*Object)
   710  	if !ok {
   711  		fs.Debugf(src, "Can't move - not same remote type")
   712  		return nil, fs.ErrorCantMove
   713  	}
   714  
   715  	// Create temporary object
   716  	dstObj, leaf, directoryID, err := f.createObject(ctx, remote, srcObj.modTime, srcObj.size)
   717  	if err != nil {
   718  		return nil, err
   719  	}
   720  
   721  	// Do the move
   722  	err = f.move(ctx, true, srcObj.id, path.Base(srcObj.remote), leaf, srcObj.parentID, directoryID)
   723  	if err != nil {
   724  		return nil, err
   725  	}
   726  
   727  	err = dstObj.readMetaData(ctx)
   728  	if err != nil {
   729  		return nil, err
   730  	}
   731  	return dstObj, nil
   732  }
   733  
   734  // DirMove moves src, srcRemote to this remote at dstRemote
   735  // using server side move operations.
   736  //
   737  // Will only be called if src.Fs().Name() == f.Name()
   738  //
   739  // If it isn't possible then return fs.ErrorCantDirMove
   740  //
   741  // If destination exists then return fs.ErrorDirExists
   742  func (f *Fs) DirMove(ctx context.Context, src fs.Fs, srcRemote, dstRemote string) error {
   743  	srcFs, ok := src.(*Fs)
   744  	if !ok {
   745  		fs.Debugf(srcFs, "Can't move directory - not same remote type")
   746  		return fs.ErrorCantDirMove
   747  	}
   748  	srcPath := path.Join(srcFs.root, srcRemote)
   749  	dstPath := path.Join(f.root, dstRemote)
   750  
   751  	// Refuse to move to or from the root
   752  	if srcPath == "" || dstPath == "" {
   753  		fs.Debugf(src, "DirMove error: Can't move root")
   754  		return errors.New("can't move root directory")
   755  	}
   756  
   757  	// find the root src directory
   758  	err := srcFs.dirCache.FindRoot(ctx, false)
   759  	if err != nil {
   760  		return err
   761  	}
   762  
   763  	// find the root dst directory
   764  	if dstRemote != "" {
   765  		err = f.dirCache.FindRoot(ctx, true)
   766  		if err != nil {
   767  			return err
   768  		}
   769  	} else {
   770  		if f.dirCache.FoundRoot() {
   771  			return fs.ErrorDirExists
   772  		}
   773  	}
   774  
   775  	// Find ID of dst parent, creating subdirs if necessary
   776  	var leaf, directoryID string
   777  	findPath := dstRemote
   778  	if dstRemote == "" {
   779  		findPath = f.root
   780  	}
   781  	leaf, directoryID, err = f.dirCache.FindPath(ctx, findPath, true)
   782  	if err != nil {
   783  		return err
   784  	}
   785  
   786  	// Check destination does not exist
   787  	if dstRemote != "" {
   788  		_, err = f.dirCache.FindDir(ctx, dstRemote, false)
   789  		if err == fs.ErrorDirNotFound {
   790  			// OK
   791  		} else if err != nil {
   792  			return err
   793  		} else {
   794  			return fs.ErrorDirExists
   795  		}
   796  	}
   797  
   798  	// Find ID of src
   799  	srcID, err := srcFs.dirCache.FindDir(ctx, srcRemote, false)
   800  	if err != nil {
   801  		return err
   802  	}
   803  
   804  	// Find ID of src parent, not creating subdirs
   805  	var srcLeaf, srcDirectoryID string
   806  	findPath = srcRemote
   807  	if srcRemote == "" {
   808  		findPath = srcFs.root
   809  	}
   810  	srcLeaf, srcDirectoryID, err = srcFs.dirCache.FindPath(ctx, findPath, false)
   811  	if err != nil {
   812  		return err
   813  	}
   814  
   815  	// Do the move
   816  	err = f.move(ctx, false, srcID, srcLeaf, leaf, srcDirectoryID, directoryID)
   817  	if err != nil {
   818  		return err
   819  	}
   820  	srcFs.dirCache.FlushDir(srcRemote)
   821  	return nil
   822  }
   823  
   824  // PublicLink adds a "readable by anyone with link" permission on the given file or folder.
   825  func (f *Fs) PublicLink(ctx context.Context, remote string) (string, error) {
   826  	_, err := f.dirCache.FindDir(ctx, remote, false)
   827  	if err == nil {
   828  		return "", fs.ErrorCantShareDirectories
   829  	}
   830  	o, err := f.NewObject(ctx, remote)
   831  	if err != nil {
   832  		return "", err
   833  	}
   834  	return o.(*Object).url, nil
   835  }
   836  
   837  // About gets quota information
   838  func (f *Fs) About(ctx context.Context) (usage *fs.Usage, err error) {
   839  	var resp *http.Response
   840  	var info api.AccountInfoResponse
   841  	opts := rest.Opts{
   842  		Method:     "POST",
   843  		Path:       "/account/info",
   844  		Parameters: f.baseParams(),
   845  	}
   846  	err = f.pacer.Call(func() (bool, error) {
   847  		resp, err = f.srv.CallJSON(ctx, &opts, nil, &info)
   848  		return shouldRetry(resp, err)
   849  	})
   850  	if err != nil {
   851  		return nil, errors.Wrap(err, "CreateDir http")
   852  	}
   853  	if err = info.AsErr(); err != nil {
   854  		return nil, errors.Wrap(err, "CreateDir")
   855  	}
   856  	usage = &fs.Usage{
   857  		Used: fs.NewUsageValue(int64(info.SpaceUsed)),
   858  	}
   859  	return usage, nil
   860  }
   861  
   862  // DirCacheFlush resets the directory cache - used in testing as an
   863  // optional interface
   864  func (f *Fs) DirCacheFlush() {
   865  	f.dirCache.ResetRoot()
   866  }
   867  
   868  // Hashes returns the supported hash sets.
   869  func (f *Fs) Hashes() hash.Set {
   870  	return hash.Set(hash.None)
   871  }
   872  
   873  // ------------------------------------------------------------
   874  
   875  // Fs returns the parent Fs
   876  func (o *Object) Fs() fs.Info {
   877  	return o.fs
   878  }
   879  
   880  // Return a string version
   881  func (o *Object) String() string {
   882  	if o == nil {
   883  		return "<nil>"
   884  	}
   885  	return o.remote
   886  }
   887  
   888  // Remote returns the remote path
   889  func (o *Object) Remote() string {
   890  	return o.remote
   891  }
   892  
   893  // Hash returns the SHA-1 of an object returning a lowercase hex string
   894  func (o *Object) Hash(ctx context.Context, t hash.Type) (string, error) {
   895  	return "", hash.ErrUnsupported
   896  }
   897  
   898  // Size returns the size of an object in bytes
   899  func (o *Object) Size() int64 {
   900  	err := o.readMetaData(context.TODO())
   901  	if err != nil {
   902  		fs.Logf(o, "Failed to read metadata: %v", err)
   903  		return 0
   904  	}
   905  	return o.size
   906  }
   907  
   908  // setMetaData sets the metadata from info
   909  func (o *Object) setMetaData(info *api.Item) (err error) {
   910  	if info.Type != "file" {
   911  		return errors.Wrapf(fs.ErrorNotAFile, "%q is %q", o.remote, info.Type)
   912  	}
   913  	o.hasMetaData = true
   914  	o.size = info.Size
   915  	o.modTime = time.Unix(info.CreatedAt, 0)
   916  	o.id = info.ID
   917  	o.mimeType = info.MimeType
   918  	o.url = info.Link
   919  	return nil
   920  }
   921  
   922  // readMetaData gets the metadata if it hasn't already been fetched
   923  //
   924  // it also sets the info
   925  func (o *Object) readMetaData(ctx context.Context) (err error) {
   926  	if o.hasMetaData {
   927  		return nil
   928  	}
   929  	info, err := o.fs.readMetaDataForPath(ctx, o.remote, false, true)
   930  	if err != nil {
   931  		return err
   932  	}
   933  	return o.setMetaData(info)
   934  }
   935  
   936  // ModTime returns the modification time of the object
   937  //
   938  //
   939  // It attempts to read the objects mtime and if that isn't present the
   940  // LastModified returned in the http headers
   941  func (o *Object) ModTime(ctx context.Context) time.Time {
   942  	err := o.readMetaData(ctx)
   943  	if err != nil {
   944  		fs.Logf(o, "Failed to read metadata: %v", err)
   945  		return time.Now()
   946  	}
   947  	return o.modTime
   948  }
   949  
   950  // SetModTime sets the modification time of the local fs object
   951  func (o *Object) SetModTime(ctx context.Context, modTime time.Time) error {
   952  	return fs.ErrorCantSetModTime
   953  }
   954  
   955  // Storable returns a boolean showing whether this object storable
   956  func (o *Object) Storable() bool {
   957  	return true
   958  }
   959  
   960  // Open an object for read
   961  func (o *Object) Open(ctx context.Context, options ...fs.OpenOption) (in io.ReadCloser, err error) {
   962  	if o.url == "" {
   963  		return nil, errors.New("can't download - no URL")
   964  	}
   965  	fs.FixRangeOption(options, o.size)
   966  	var resp *http.Response
   967  	opts := rest.Opts{
   968  		Path:    "",
   969  		RootURL: o.url,
   970  		Method:  "GET",
   971  		Options: options,
   972  	}
   973  	err = o.fs.pacer.Call(func() (bool, error) {
   974  		resp, err = o.fs.srv.Call(ctx, &opts)
   975  		return shouldRetry(resp, err)
   976  	})
   977  	if err != nil {
   978  		return nil, err
   979  	}
   980  	return resp.Body, err
   981  }
   982  
   983  // Update the object with the contents of the io.Reader, modTime and size
   984  //
   985  // If existing is set then it updates the object rather than creating a new one
   986  //
   987  // The new object may have been created if an error is returned
   988  func (o *Object) Update(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) (err error) {
   989  	remote := o.Remote()
   990  	size := src.Size()
   991  
   992  	// Create the directory for the object if it doesn't exist
   993  	leaf, directoryID, err := o.fs.dirCache.FindRootAndPath(ctx, remote, true)
   994  	if err != nil {
   995  		return err
   996  	}
   997  	leaf = o.fs.opt.Enc.FromStandardName(leaf)
   998  
   999  	var resp *http.Response
  1000  	var info api.FolderUploadinfoResponse
  1001  	opts := rest.Opts{
  1002  		Method:     "POST",
  1003  		Path:       "/folder/uploadinfo",
  1004  		Parameters: o.fs.baseParams(),
  1005  		Options:    options,
  1006  		MultipartParams: url.Values{
  1007  			"id": {directoryID},
  1008  		},
  1009  	}
  1010  	err = o.fs.pacer.Call(func() (bool, error) {
  1011  		resp, err = o.fs.srv.CallJSON(ctx, &opts, nil, &info)
  1012  		if err != nil {
  1013  			return shouldRetry(resp, err)
  1014  		}
  1015  		// Just check the download URL resolves - sometimes
  1016  		// the URLs returned by premiumize.me don't resolve so
  1017  		// this needs a retry.
  1018  		var u *url.URL
  1019  		u, err = url.Parse(info.URL)
  1020  		if err != nil {
  1021  			return true, errors.Wrap(err, "failed to parse download URL")
  1022  		}
  1023  		_, err = net.LookupIP(u.Hostname())
  1024  		if err != nil {
  1025  			return true, errors.Wrap(err, "failed to resolve download URL")
  1026  		}
  1027  		return false, nil
  1028  	})
  1029  	if err != nil {
  1030  		return errors.Wrap(err, "upload get URL http")
  1031  	}
  1032  	if err = info.AsErr(); err != nil {
  1033  		return errors.Wrap(err, "upload get URL")
  1034  	}
  1035  
  1036  	// if file exists then rename it out the way otherwise uploads can fail
  1037  	uploaded := false
  1038  	var oldID = o.id
  1039  	if o.hasMetaData {
  1040  		newLeaf := leaf + "." + random.String(8)
  1041  		fs.Debugf(o, "Moving old file out the way to %q", newLeaf)
  1042  		err = o.fs.renameLeaf(ctx, true, oldID, newLeaf)
  1043  		if err != nil {
  1044  			return errors.Wrap(err, "upload rename old file")
  1045  		}
  1046  		defer func() {
  1047  			// on failed upload rename old file back
  1048  			if !uploaded {
  1049  				fs.Debugf(o, "Renaming old file back (from %q to %q) since upload failed", leaf, newLeaf)
  1050  				newErr := o.fs.renameLeaf(ctx, true, oldID, leaf)
  1051  				if newErr != nil && err == nil {
  1052  					err = errors.Wrap(newErr, "upload renaming old file back")
  1053  				}
  1054  			}
  1055  		}()
  1056  	}
  1057  
  1058  	opts = rest.Opts{
  1059  		Method:  "POST",
  1060  		RootURL: info.URL,
  1061  		Body:    in,
  1062  		MultipartParams: url.Values{
  1063  			"token": {info.Token},
  1064  		},
  1065  		MultipartContentName: "file", // ..name of the parameter which is the attached file
  1066  		MultipartFileName:    leaf,   // ..name of the file for the attached file
  1067  		ContentLength:        &size,
  1068  	}
  1069  	var result api.Response
  1070  	err = o.fs.pacer.CallNoRetry(func() (bool, error) {
  1071  		resp, err = o.fs.srv.CallJSON(ctx, &opts, nil, &result)
  1072  		return shouldRetry(resp, err)
  1073  	})
  1074  	if err != nil {
  1075  		return errors.Wrap(err, "upload file http")
  1076  	}
  1077  	if err = result.AsErr(); err != nil {
  1078  		return errors.Wrap(err, "upload file")
  1079  	}
  1080  
  1081  	// on successful upload, remove old file if it exists
  1082  	uploaded = true
  1083  	if o.hasMetaData {
  1084  		fs.Debugf(o, "Removing old file")
  1085  		err := o.fs.remove(ctx, oldID)
  1086  		if err != nil {
  1087  			return errors.Wrap(err, "upload remove old file")
  1088  		}
  1089  	}
  1090  
  1091  	o.hasMetaData = false
  1092  	return o.readMetaData(ctx)
  1093  }
  1094  
  1095  // Rename the leaf of a file or directory in a directory
  1096  func (f *Fs) renameLeaf(ctx context.Context, isFile bool, id string, newLeaf string) (err error) {
  1097  	opts := rest.Opts{
  1098  		Method: "POST",
  1099  		MultipartParams: url.Values{
  1100  			"id":   {id},
  1101  			"name": {newLeaf},
  1102  		},
  1103  		Parameters: f.baseParams(),
  1104  	}
  1105  	if isFile {
  1106  		opts.Path = "/item/rename"
  1107  	} else {
  1108  		opts.Path = "/folder/rename"
  1109  	}
  1110  	var resp *http.Response
  1111  	var result api.Response
  1112  	err = f.pacer.Call(func() (bool, error) {
  1113  		resp, err = f.srv.CallJSON(ctx, &opts, nil, &result)
  1114  		return shouldRetry(resp, err)
  1115  	})
  1116  	if err != nil {
  1117  		return errors.Wrap(err, "rename http")
  1118  	}
  1119  	if err = result.AsErr(); err != nil {
  1120  		return errors.Wrap(err, "rename")
  1121  	}
  1122  	return nil
  1123  }
  1124  
  1125  // Remove an object by ID
  1126  func (f *Fs) remove(ctx context.Context, id string) (err error) {
  1127  	opts := rest.Opts{
  1128  		Method: "POST",
  1129  		Path:   "/item/delete",
  1130  		MultipartParams: url.Values{
  1131  			"id": {id},
  1132  		},
  1133  		Parameters: f.baseParams(),
  1134  	}
  1135  	var resp *http.Response
  1136  	var result api.Response
  1137  	err = f.pacer.Call(func() (bool, error) {
  1138  		resp, err = f.srv.CallJSON(ctx, &opts, nil, &result)
  1139  		return shouldRetry(resp, err)
  1140  	})
  1141  	if err != nil {
  1142  		return errors.Wrap(err, "remove http")
  1143  	}
  1144  	if err = result.AsErr(); err != nil {
  1145  		return errors.Wrap(err, "remove")
  1146  	}
  1147  	return nil
  1148  }
  1149  
  1150  // Remove an object
  1151  func (o *Object) Remove(ctx context.Context) error {
  1152  	err := o.readMetaData(ctx)
  1153  	if err != nil {
  1154  		return errors.Wrap(err, "Remove: Failed to read metadata")
  1155  	}
  1156  	return o.fs.remove(ctx, o.id)
  1157  }
  1158  
  1159  // MimeType of an Object if known, "" otherwise
  1160  func (o *Object) MimeType(ctx context.Context) string {
  1161  	return o.mimeType
  1162  }
  1163  
  1164  // ID returns the ID of the Object if known, or "" if not
  1165  func (o *Object) ID() string {
  1166  	return o.id
  1167  }
  1168  
  1169  // Check the interfaces are satisfied
  1170  var (
  1171  	_ fs.Fs              = (*Fs)(nil)
  1172  	_ fs.Purger          = (*Fs)(nil)
  1173  	_ fs.Mover           = (*Fs)(nil)
  1174  	_ fs.DirMover        = (*Fs)(nil)
  1175  	_ fs.DirCacheFlusher = (*Fs)(nil)
  1176  	_ fs.Abouter         = (*Fs)(nil)
  1177  	_ fs.PublicLinker    = (*Fs)(nil)
  1178  	_ fs.Object          = (*Object)(nil)
  1179  	_ fs.MimeTyper       = (*Object)(nil)
  1180  	_ fs.IDer            = (*Object)(nil)
  1181  )