github.com/xhghs/rclone@v1.51.1-0.20200430155106-e186a28cced8/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)
    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  // rootSlash returns root with a slash on if it is empty, otherwise empty string
   319  func (f *Fs) rootSlash() string {
   320  	if f.root == "" {
   321  		return f.root
   322  	}
   323  	return f.root + "/"
   324  }
   325  
   326  // Return an Object from a path
   327  //
   328  // If it can't be found it returns the error fs.ErrorObjectNotFound.
   329  func (f *Fs) newObjectWithInfo(ctx context.Context, remote string, info *api.Item) (fs.Object, error) {
   330  	o := &Object{
   331  		fs:     f,
   332  		remote: remote,
   333  	}
   334  	var err error
   335  	if info != nil {
   336  		// Set info
   337  		err = o.setMetaData(info)
   338  	} else {
   339  		err = o.readMetaData(ctx) // reads info and meta, returning an error
   340  	}
   341  	if err != nil {
   342  		return nil, err
   343  	}
   344  	return o, nil
   345  }
   346  
   347  // NewObject finds the Object at remote.  If it can't be found
   348  // it returns the error fs.ErrorObjectNotFound.
   349  func (f *Fs) NewObject(ctx context.Context, remote string) (fs.Object, error) {
   350  	return f.newObjectWithInfo(ctx, remote, nil)
   351  }
   352  
   353  // FindLeaf finds a directory of name leaf in the folder with ID pathID
   354  func (f *Fs) FindLeaf(ctx context.Context, pathID, leaf string) (pathIDOut string, found bool, err error) {
   355  	// Find the leaf in pathID
   356  	found, err = f.listAll(ctx, pathID, true, false, func(item *api.Item) bool {
   357  		if item.Name == leaf {
   358  			pathIDOut = item.ID
   359  			return true
   360  		}
   361  		return false
   362  	})
   363  	return pathIDOut, found, err
   364  }
   365  
   366  // CreateDir makes a directory with pathID as parent and name leaf
   367  func (f *Fs) CreateDir(ctx context.Context, pathID, leaf string) (newID string, err error) {
   368  	// fs.Debugf(f, "CreateDir(%q, %q)\n", pathID, leaf)
   369  	var resp *http.Response
   370  	var info api.FolderCreateResponse
   371  	opts := rest.Opts{
   372  		Method:     "POST",
   373  		Path:       "/folder/create",
   374  		Parameters: f.baseParams(),
   375  		MultipartParams: url.Values{
   376  			"name":      {f.opt.Enc.FromStandardName(leaf)},
   377  			"parent_id": {pathID},
   378  		},
   379  	}
   380  	err = f.pacer.Call(func() (bool, error) {
   381  		resp, err = f.srv.CallJSON(ctx, &opts, nil, &info)
   382  		return shouldRetry(resp, err)
   383  	})
   384  	if err != nil {
   385  		//fmt.Printf("...Error %v\n", err)
   386  		return "", errors.Wrap(err, "CreateDir http")
   387  	}
   388  	if err = info.AsErr(); err != nil {
   389  		return "", errors.Wrap(err, "CreateDir")
   390  	}
   391  	// fmt.Printf("...Id %q\n", *info.Id)
   392  	return info.ID, nil
   393  }
   394  
   395  // list the objects into the function supplied
   396  //
   397  // If directories is set it only sends directories
   398  // User function to process a File item from listAll
   399  //
   400  // Should return true to finish processing
   401  type listAllFn func(*api.Item) bool
   402  
   403  // Lists the directory required calling the user function on each item found
   404  //
   405  // If the user fn ever returns true then it early exits with found = true
   406  func (f *Fs) listAll(ctx context.Context, dirID string, directoriesOnly bool, filesOnly bool, fn listAllFn) (found bool, err error) {
   407  	opts := rest.Opts{
   408  		Method:     "GET",
   409  		Path:       "/folder/list",
   410  		Parameters: f.baseParams(),
   411  	}
   412  	opts.Parameters.Set("id", dirID)
   413  	opts.Parameters.Set("includebreadcrumbs", "false")
   414  
   415  	var result api.FolderListResponse
   416  	var resp *http.Response
   417  	err = f.pacer.Call(func() (bool, error) {
   418  		resp, err = f.srv.CallJSON(ctx, &opts, nil, &result)
   419  		return shouldRetry(resp, err)
   420  	})
   421  	if err != nil {
   422  		return found, errors.Wrap(err, "couldn't list files")
   423  	}
   424  	if err = result.AsErr(); err != nil {
   425  		return found, errors.Wrap(err, "error while listing")
   426  	}
   427  	for i := range result.Content {
   428  		item := &result.Content[i]
   429  		if item.Type == api.ItemTypeFolder {
   430  			if filesOnly {
   431  				continue
   432  			}
   433  		} else if item.Type == api.ItemTypeFile {
   434  			if directoriesOnly {
   435  				continue
   436  			}
   437  		} else {
   438  			fs.Debugf(f, "Ignoring %q - unknown type %q", item.Name, item.Type)
   439  			continue
   440  		}
   441  		item.Name = f.opt.Enc.ToStandardName(item.Name)
   442  		if fn(item) {
   443  			found = true
   444  			break
   445  		}
   446  	}
   447  
   448  	return
   449  }
   450  
   451  // List the objects and directories in dir into entries.  The
   452  // entries can be returned in any order but should be for a
   453  // complete directory.
   454  //
   455  // dir should be "" to list the root, and should not have
   456  // trailing slashes.
   457  //
   458  // This should return ErrDirNotFound if the directory isn't
   459  // found.
   460  func (f *Fs) List(ctx context.Context, dir string) (entries fs.DirEntries, err error) {
   461  	err = f.dirCache.FindRoot(ctx, false)
   462  	if err != nil {
   463  		return nil, err
   464  	}
   465  	directoryID, err := f.dirCache.FindDir(ctx, dir, false)
   466  	if err != nil {
   467  		return nil, err
   468  	}
   469  	var iErr error
   470  	_, err = f.listAll(ctx, directoryID, false, false, func(info *api.Item) bool {
   471  		remote := path.Join(dir, info.Name)
   472  		if info.Type == api.ItemTypeFolder {
   473  			// cache the directory ID for later lookups
   474  			f.dirCache.Put(remote, info.ID)
   475  			d := fs.NewDir(remote, time.Unix(info.CreatedAt, 0)).SetID(info.ID)
   476  			entries = append(entries, d)
   477  		} else if info.Type == api.ItemTypeFile {
   478  			o, err := f.newObjectWithInfo(ctx, remote, info)
   479  			if err != nil {
   480  				iErr = err
   481  				return true
   482  			}
   483  			entries = append(entries, o)
   484  		}
   485  		return false
   486  	})
   487  	if err != nil {
   488  		return nil, err
   489  	}
   490  	if iErr != nil {
   491  		return nil, iErr
   492  	}
   493  	return entries, nil
   494  }
   495  
   496  // Creates from the parameters passed in a half finished Object which
   497  // must have setMetaData called on it
   498  //
   499  // Returns the object, leaf, directoryID and error
   500  //
   501  // Used to create new objects
   502  func (f *Fs) createObject(ctx context.Context, remote string, modTime time.Time, size int64) (o *Object, leaf string, directoryID string, err error) {
   503  	// Create the directory for the object if it doesn't exist
   504  	leaf, directoryID, err = f.dirCache.FindRootAndPath(ctx, remote, true)
   505  	if err != nil {
   506  		return
   507  	}
   508  	// Temporary Object under construction
   509  	o = &Object{
   510  		fs:     f,
   511  		remote: remote,
   512  	}
   513  	return o, leaf, directoryID, nil
   514  }
   515  
   516  // Put the object
   517  //
   518  // Copy the reader in to the new object which is returned
   519  //
   520  // The new object may have been created if an error is returned
   521  func (f *Fs) Put(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) (fs.Object, error) {
   522  	existingObj, err := f.newObjectWithInfo(ctx, src.Remote(), nil)
   523  	switch err {
   524  	case nil:
   525  		return existingObj, existingObj.Update(ctx, in, src, options...)
   526  	case fs.ErrorObjectNotFound:
   527  		// Not found so create it
   528  		return f.PutUnchecked(ctx, in, src)
   529  	default:
   530  		return nil, err
   531  	}
   532  }
   533  
   534  // PutUnchecked the object into the container
   535  //
   536  // This will produce an error if the object already exists
   537  //
   538  // Copy the reader in to the new object which is returned
   539  //
   540  // The new object may have been created if an error is returned
   541  func (f *Fs) PutUnchecked(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) (fs.Object, error) {
   542  	remote := src.Remote()
   543  	size := src.Size()
   544  	modTime := src.ModTime(ctx)
   545  
   546  	o, _, _, err := f.createObject(ctx, remote, modTime, size)
   547  	if err != nil {
   548  		return nil, err
   549  	}
   550  	return o, o.Update(ctx, in, src, options...)
   551  }
   552  
   553  // Mkdir creates the container if it doesn't exist
   554  func (f *Fs) Mkdir(ctx context.Context, dir string) error {
   555  	err := f.dirCache.FindRoot(ctx, true)
   556  	if err != nil {
   557  		return err
   558  	}
   559  	if dir != "" {
   560  		_, err = f.dirCache.FindDir(ctx, dir, true)
   561  	}
   562  	return err
   563  }
   564  
   565  // purgeCheck removes the root directory, if check is set then it
   566  // refuses to do so if it has anything in
   567  func (f *Fs) purgeCheck(ctx context.Context, dir string, check bool) error {
   568  	root := path.Join(f.root, dir)
   569  	if root == "" {
   570  		return errors.New("can't purge root directory")
   571  	}
   572  	dc := f.dirCache
   573  	err := dc.FindRoot(ctx, false)
   574  	if err != nil {
   575  		return err
   576  	}
   577  	rootID, err := dc.FindDir(ctx, dir, false)
   578  	if err != nil {
   579  		return err
   580  	}
   581  
   582  	// need to check if empty as it will delete recursively by default
   583  	if check {
   584  		found, err := f.listAll(ctx, rootID, false, false, func(item *api.Item) bool {
   585  			return true
   586  		})
   587  		if err != nil {
   588  			return errors.Wrap(err, "purgeCheck")
   589  		}
   590  		if found {
   591  			return fs.ErrorDirectoryNotEmpty
   592  		}
   593  	}
   594  
   595  	opts := rest.Opts{
   596  		Method: "POST",
   597  		Path:   "/folder/delete",
   598  		MultipartParams: url.Values{
   599  			"id": {rootID},
   600  		},
   601  		Parameters: f.baseParams(),
   602  	}
   603  	var resp *http.Response
   604  	var result api.Response
   605  	err = f.pacer.Call(func() (bool, error) {
   606  		resp, err = f.srv.CallJSON(ctx, &opts, nil, &result)
   607  		return shouldRetry(resp, err)
   608  	})
   609  	if err != nil {
   610  		return errors.Wrap(err, "rmdir failed")
   611  	}
   612  	if err = result.AsErr(); err != nil {
   613  		return errors.Wrap(err, "rmdir")
   614  	}
   615  	f.dirCache.FlushDir(dir)
   616  	if err != nil {
   617  		return err
   618  	}
   619  	return nil
   620  }
   621  
   622  // Rmdir deletes the root folder
   623  //
   624  // Returns an error if it isn't empty
   625  func (f *Fs) Rmdir(ctx context.Context, dir string) error {
   626  	return f.purgeCheck(ctx, dir, true)
   627  }
   628  
   629  // Precision return the precision of this Fs
   630  func (f *Fs) Precision() time.Duration {
   631  	return fs.ModTimeNotSupported
   632  }
   633  
   634  // Purge deletes all the files and the container
   635  //
   636  // Optional interface: Only implement this if you have a way of
   637  // deleting all the files quicker than just running Remove() on the
   638  // result of List()
   639  func (f *Fs) Purge(ctx context.Context) error {
   640  	return f.purgeCheck(ctx, "", false)
   641  }
   642  
   643  // move a file or folder
   644  //
   645  // This is complicated by the fact that there is an API to move files
   646  // between directories and a separate one to rename them.  We try to
   647  // call the minimum number of API calls.
   648  func (f *Fs) move(ctx context.Context, isFile bool, id, oldLeaf, newLeaf, oldDirectoryID, newDirectoryID string) (err error) {
   649  	newLeaf = f.opt.Enc.FromStandardName(newLeaf)
   650  	oldLeaf = f.opt.Enc.FromStandardName(oldLeaf)
   651  	doRenameLeaf := oldLeaf != newLeaf
   652  	doMove := oldDirectoryID != newDirectoryID
   653  
   654  	// Now rename the leaf to a temporary name if we are moving to
   655  	// another directory to make sure we don't overwrite something
   656  	// in the destination directory by accident
   657  	if doRenameLeaf && doMove {
   658  		tmpLeaf := newLeaf + "." + random.String(8)
   659  		err = f.renameLeaf(ctx, isFile, id, tmpLeaf)
   660  		if err != nil {
   661  			return errors.Wrap(err, "Move rename leaf")
   662  		}
   663  	}
   664  
   665  	// Move the object to a new directory (with the existing name)
   666  	// if required
   667  	if doMove {
   668  		opts := rest.Opts{
   669  			Method:     "POST",
   670  			Path:       "/folder/paste",
   671  			Parameters: f.baseParams(),
   672  			MultipartParams: url.Values{
   673  				"id": {newDirectoryID},
   674  			},
   675  		}
   676  		if isFile {
   677  			opts.MultipartParams.Set("files[]", id)
   678  		} else {
   679  			opts.MultipartParams.Set("folders[]", id)
   680  		}
   681  		//replacedLeaf := enc.FromStandardName(leaf)
   682  		var resp *http.Response
   683  		var result api.Response
   684  		err = f.pacer.Call(func() (bool, error) {
   685  			resp, err = f.srv.CallJSON(ctx, &opts, nil, &result)
   686  			return shouldRetry(resp, err)
   687  		})
   688  		if err != nil {
   689  			return errors.Wrap(err, "Move http")
   690  		}
   691  		if err = result.AsErr(); err != nil {
   692  			return errors.Wrap(err, "Move")
   693  		}
   694  	}
   695  
   696  	// Rename the leaf to its final name if required
   697  	if doRenameLeaf {
   698  		err = f.renameLeaf(ctx, isFile, id, newLeaf)
   699  		if err != nil {
   700  			return errors.Wrap(err, "Move rename leaf")
   701  		}
   702  	}
   703  
   704  	return nil
   705  }
   706  
   707  // Move src to this remote using server side move operations.
   708  //
   709  // This is stored with the remote path given
   710  //
   711  // It returns the destination Object and a possible error
   712  //
   713  // Will only be called if src.Fs().Name() == f.Name()
   714  //
   715  // If it isn't possible then return fs.ErrorCantMove
   716  func (f *Fs) Move(ctx context.Context, src fs.Object, remote string) (fs.Object, error) {
   717  	srcObj, ok := src.(*Object)
   718  	if !ok {
   719  		fs.Debugf(src, "Can't move - not same remote type")
   720  		return nil, fs.ErrorCantMove
   721  	}
   722  
   723  	// Create temporary object
   724  	dstObj, leaf, directoryID, err := f.createObject(ctx, remote, srcObj.modTime, srcObj.size)
   725  	if err != nil {
   726  		return nil, err
   727  	}
   728  
   729  	// Do the move
   730  	err = f.move(ctx, true, srcObj.id, path.Base(srcObj.remote), leaf, srcObj.parentID, directoryID)
   731  	if err != nil {
   732  		return nil, err
   733  	}
   734  
   735  	err = dstObj.readMetaData(ctx)
   736  	if err != nil {
   737  		return nil, err
   738  	}
   739  	return dstObj, nil
   740  }
   741  
   742  // DirMove moves src, srcRemote to this remote at dstRemote
   743  // using server side move operations.
   744  //
   745  // Will only be called if src.Fs().Name() == f.Name()
   746  //
   747  // If it isn't possible then return fs.ErrorCantDirMove
   748  //
   749  // If destination exists then return fs.ErrorDirExists
   750  func (f *Fs) DirMove(ctx context.Context, src fs.Fs, srcRemote, dstRemote string) error {
   751  	srcFs, ok := src.(*Fs)
   752  	if !ok {
   753  		fs.Debugf(srcFs, "Can't move directory - not same remote type")
   754  		return fs.ErrorCantDirMove
   755  	}
   756  	srcPath := path.Join(srcFs.root, srcRemote)
   757  	dstPath := path.Join(f.root, dstRemote)
   758  
   759  	// Refuse to move to or from the root
   760  	if srcPath == "" || dstPath == "" {
   761  		fs.Debugf(src, "DirMove error: Can't move root")
   762  		return errors.New("can't move root directory")
   763  	}
   764  
   765  	// find the root src directory
   766  	err := srcFs.dirCache.FindRoot(ctx, false)
   767  	if err != nil {
   768  		return err
   769  	}
   770  
   771  	// find the root dst directory
   772  	if dstRemote != "" {
   773  		err = f.dirCache.FindRoot(ctx, true)
   774  		if err != nil {
   775  			return err
   776  		}
   777  	} else {
   778  		if f.dirCache.FoundRoot() {
   779  			return fs.ErrorDirExists
   780  		}
   781  	}
   782  
   783  	// Find ID of dst parent, creating subdirs if necessary
   784  	var leaf, directoryID string
   785  	findPath := dstRemote
   786  	if dstRemote == "" {
   787  		findPath = f.root
   788  	}
   789  	leaf, directoryID, err = f.dirCache.FindPath(ctx, findPath, true)
   790  	if err != nil {
   791  		return err
   792  	}
   793  
   794  	// Check destination does not exist
   795  	if dstRemote != "" {
   796  		_, err = f.dirCache.FindDir(ctx, dstRemote, false)
   797  		if err == fs.ErrorDirNotFound {
   798  			// OK
   799  		} else if err != nil {
   800  			return err
   801  		} else {
   802  			return fs.ErrorDirExists
   803  		}
   804  	}
   805  
   806  	// Find ID of src
   807  	srcID, err := srcFs.dirCache.FindDir(ctx, srcRemote, false)
   808  	if err != nil {
   809  		return err
   810  	}
   811  
   812  	// Find ID of src parent, not creating subdirs
   813  	var srcLeaf, srcDirectoryID string
   814  	findPath = srcRemote
   815  	if srcRemote == "" {
   816  		findPath = srcFs.root
   817  	}
   818  	srcLeaf, srcDirectoryID, err = srcFs.dirCache.FindPath(ctx, findPath, false)
   819  	if err != nil {
   820  		return err
   821  	}
   822  
   823  	// Do the move
   824  	err = f.move(ctx, false, srcID, srcLeaf, leaf, srcDirectoryID, directoryID)
   825  	if err != nil {
   826  		return err
   827  	}
   828  	srcFs.dirCache.FlushDir(srcRemote)
   829  	return nil
   830  }
   831  
   832  // PublicLink adds a "readable by anyone with link" permission on the given file or folder.
   833  func (f *Fs) PublicLink(ctx context.Context, remote string) (string, error) {
   834  	_, err := f.dirCache.FindDir(ctx, remote, false)
   835  	if err == nil {
   836  		return "", fs.ErrorCantShareDirectories
   837  	}
   838  	o, err := f.NewObject(ctx, remote)
   839  	if err != nil {
   840  		return "", err
   841  	}
   842  	return o.(*Object).url, nil
   843  }
   844  
   845  // About gets quota information
   846  func (f *Fs) About(ctx context.Context) (usage *fs.Usage, err error) {
   847  	var resp *http.Response
   848  	var info api.AccountInfoResponse
   849  	opts := rest.Opts{
   850  		Method:     "POST",
   851  		Path:       "/account/info",
   852  		Parameters: f.baseParams(),
   853  	}
   854  	err = f.pacer.Call(func() (bool, error) {
   855  		resp, err = f.srv.CallJSON(ctx, &opts, nil, &info)
   856  		return shouldRetry(resp, err)
   857  	})
   858  	if err != nil {
   859  		return nil, errors.Wrap(err, "CreateDir http")
   860  	}
   861  	if err = info.AsErr(); err != nil {
   862  		return nil, errors.Wrap(err, "CreateDir")
   863  	}
   864  	usage = &fs.Usage{
   865  		Used: fs.NewUsageValue(int64(info.SpaceUsed)),
   866  	}
   867  	return usage, nil
   868  }
   869  
   870  // DirCacheFlush resets the directory cache - used in testing as an
   871  // optional interface
   872  func (f *Fs) DirCacheFlush() {
   873  	f.dirCache.ResetRoot()
   874  }
   875  
   876  // Hashes returns the supported hash sets.
   877  func (f *Fs) Hashes() hash.Set {
   878  	return hash.Set(hash.None)
   879  }
   880  
   881  // ------------------------------------------------------------
   882  
   883  // Fs returns the parent Fs
   884  func (o *Object) Fs() fs.Info {
   885  	return o.fs
   886  }
   887  
   888  // Return a string version
   889  func (o *Object) String() string {
   890  	if o == nil {
   891  		return "<nil>"
   892  	}
   893  	return o.remote
   894  }
   895  
   896  // Remote returns the remote path
   897  func (o *Object) Remote() string {
   898  	return o.remote
   899  }
   900  
   901  // srvPath returns a path for use in server
   902  func (o *Object) srvPath() string {
   903  	return o.fs.opt.Enc.FromStandardPath(o.fs.rootSlash() + o.remote)
   904  }
   905  
   906  // Hash returns the SHA-1 of an object returning a lowercase hex string
   907  func (o *Object) Hash(ctx context.Context, t hash.Type) (string, error) {
   908  	return "", hash.ErrUnsupported
   909  }
   910  
   911  // Size returns the size of an object in bytes
   912  func (o *Object) Size() int64 {
   913  	err := o.readMetaData(context.TODO())
   914  	if err != nil {
   915  		fs.Logf(o, "Failed to read metadata: %v", err)
   916  		return 0
   917  	}
   918  	return o.size
   919  }
   920  
   921  // setMetaData sets the metadata from info
   922  func (o *Object) setMetaData(info *api.Item) (err error) {
   923  	if info.Type != "file" {
   924  		return errors.Wrapf(fs.ErrorNotAFile, "%q is %q", o.remote, info.Type)
   925  	}
   926  	o.hasMetaData = true
   927  	o.size = info.Size
   928  	o.modTime = time.Unix(info.CreatedAt, 0)
   929  	o.id = info.ID
   930  	o.mimeType = info.MimeType
   931  	o.url = info.Link
   932  	return nil
   933  }
   934  
   935  // readMetaData gets the metadata if it hasn't already been fetched
   936  //
   937  // it also sets the info
   938  func (o *Object) readMetaData(ctx context.Context) (err error) {
   939  	if o.hasMetaData {
   940  		return nil
   941  	}
   942  	info, err := o.fs.readMetaDataForPath(ctx, o.remote, false, true)
   943  	if err != nil {
   944  		return err
   945  	}
   946  	return o.setMetaData(info)
   947  }
   948  
   949  // ModTime returns the modification time of the object
   950  //
   951  //
   952  // It attempts to read the objects mtime and if that isn't present the
   953  // LastModified returned in the http headers
   954  func (o *Object) ModTime(ctx context.Context) time.Time {
   955  	err := o.readMetaData(ctx)
   956  	if err != nil {
   957  		fs.Logf(o, "Failed to read metadata: %v", err)
   958  		return time.Now()
   959  	}
   960  	return o.modTime
   961  }
   962  
   963  // SetModTime sets the modification time of the local fs object
   964  func (o *Object) SetModTime(ctx context.Context, modTime time.Time) error {
   965  	return fs.ErrorCantSetModTime
   966  }
   967  
   968  // Storable returns a boolean showing whether this object storable
   969  func (o *Object) Storable() bool {
   970  	return true
   971  }
   972  
   973  // Open an object for read
   974  func (o *Object) Open(ctx context.Context, options ...fs.OpenOption) (in io.ReadCloser, err error) {
   975  	if o.url == "" {
   976  		return nil, errors.New("can't download - no URL")
   977  	}
   978  	fs.FixRangeOption(options, o.size)
   979  	var resp *http.Response
   980  	opts := rest.Opts{
   981  		Path:    "",
   982  		RootURL: o.url,
   983  		Method:  "GET",
   984  		Options: options,
   985  	}
   986  	err = o.fs.pacer.Call(func() (bool, error) {
   987  		resp, err = o.fs.srv.Call(ctx, &opts)
   988  		return shouldRetry(resp, err)
   989  	})
   990  	if err != nil {
   991  		return nil, err
   992  	}
   993  	return resp.Body, err
   994  }
   995  
   996  // metaHash returns a rough hash of metadata to detect if object has been updated
   997  func (o *Object) metaHash() string {
   998  	if !o.hasMetaData {
   999  		return ""
  1000  	}
  1001  	return fmt.Sprintf("remote=%q, size=%d, modTime=%v, id=%q, mimeType=%q", o.remote, o.size, o.modTime, o.id, o.mimeType)
  1002  }
  1003  
  1004  // Update the object with the contents of the io.Reader, modTime and size
  1005  //
  1006  // If existing is set then it updates the object rather than creating a new one
  1007  //
  1008  // The new object may have been created if an error is returned
  1009  func (o *Object) Update(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) (err error) {
  1010  	remote := o.Remote()
  1011  	size := src.Size()
  1012  
  1013  	// Create the directory for the object if it doesn't exist
  1014  	leaf, directoryID, err := o.fs.dirCache.FindRootAndPath(ctx, remote, true)
  1015  	if err != nil {
  1016  		return err
  1017  	}
  1018  	leaf = o.fs.opt.Enc.FromStandardName(leaf)
  1019  
  1020  	var resp *http.Response
  1021  	var info api.FolderUploadinfoResponse
  1022  	opts := rest.Opts{
  1023  		Method:     "POST",
  1024  		Path:       "/folder/uploadinfo",
  1025  		Parameters: o.fs.baseParams(),
  1026  		MultipartParams: url.Values{
  1027  			"id": {directoryID},
  1028  		},
  1029  	}
  1030  	err = o.fs.pacer.Call(func() (bool, error) {
  1031  		resp, err = o.fs.srv.CallJSON(ctx, &opts, nil, &info)
  1032  		if err != nil {
  1033  			return shouldRetry(resp, err)
  1034  		}
  1035  		// Just check the download URL resolves - sometimes
  1036  		// the URLs returned by premiumize.me don't resolve so
  1037  		// this needs a retry.
  1038  		var u *url.URL
  1039  		u, err = url.Parse(info.URL)
  1040  		if err != nil {
  1041  			return true, errors.Wrap(err, "failed to parse download URL")
  1042  		}
  1043  		_, err = net.LookupIP(u.Hostname())
  1044  		if err != nil {
  1045  			return true, errors.Wrap(err, "failed to resolve download URL")
  1046  		}
  1047  		return false, nil
  1048  	})
  1049  	if err != nil {
  1050  		return errors.Wrap(err, "upload get URL http")
  1051  	}
  1052  	if err = info.AsErr(); err != nil {
  1053  		return errors.Wrap(err, "upload get URL")
  1054  	}
  1055  
  1056  	// if file exists then rename it out the way otherwise uploads can fail
  1057  	uploaded := false
  1058  	var oldID = o.id
  1059  	if o.hasMetaData {
  1060  		newLeaf := leaf + "." + random.String(8)
  1061  		fs.Debugf(o, "Moving old file out the way to %q", newLeaf)
  1062  		err = o.fs.renameLeaf(ctx, true, oldID, newLeaf)
  1063  		if err != nil {
  1064  			return errors.Wrap(err, "upload rename old file")
  1065  		}
  1066  		defer func() {
  1067  			// on failed upload rename old file back
  1068  			if !uploaded {
  1069  				fs.Debugf(o, "Renaming old file back (from %q to %q) since upload failed", leaf, newLeaf)
  1070  				newErr := o.fs.renameLeaf(ctx, true, oldID, leaf)
  1071  				if newErr != nil && err == nil {
  1072  					err = errors.Wrap(newErr, "upload renaming old file back")
  1073  				}
  1074  			}
  1075  		}()
  1076  	}
  1077  
  1078  	opts = rest.Opts{
  1079  		Method:  "POST",
  1080  		RootURL: info.URL,
  1081  		Body:    in,
  1082  		MultipartParams: url.Values{
  1083  			"token": {info.Token},
  1084  		},
  1085  		MultipartContentName: "file", // ..name of the parameter which is the attached file
  1086  		MultipartFileName:    leaf,   // ..name of the file for the attached file
  1087  		ContentLength:        &size,
  1088  	}
  1089  	var result api.Response
  1090  	err = o.fs.pacer.CallNoRetry(func() (bool, error) {
  1091  		resp, err = o.fs.srv.CallJSON(ctx, &opts, nil, &result)
  1092  		return shouldRetry(resp, err)
  1093  	})
  1094  	if err != nil {
  1095  		return errors.Wrap(err, "upload file http")
  1096  	}
  1097  	if err = result.AsErr(); err != nil {
  1098  		return errors.Wrap(err, "upload file")
  1099  	}
  1100  
  1101  	// on successful upload, remove old file if it exists
  1102  	uploaded = true
  1103  	if o.hasMetaData {
  1104  		fs.Debugf(o, "Removing old file")
  1105  		err := o.fs.remove(ctx, oldID)
  1106  		if err != nil {
  1107  			return errors.Wrap(err, "upload remove old file")
  1108  		}
  1109  	}
  1110  
  1111  	o.hasMetaData = false
  1112  	return o.readMetaData(ctx)
  1113  }
  1114  
  1115  // Rename the leaf of a file or directory in a directory
  1116  func (f *Fs) renameLeaf(ctx context.Context, isFile bool, id string, newLeaf string) (err error) {
  1117  	opts := rest.Opts{
  1118  		Method: "POST",
  1119  		MultipartParams: url.Values{
  1120  			"id":   {id},
  1121  			"name": {newLeaf},
  1122  		},
  1123  		Parameters: f.baseParams(),
  1124  	}
  1125  	if isFile {
  1126  		opts.Path = "/item/rename"
  1127  	} else {
  1128  		opts.Path = "/folder/rename"
  1129  	}
  1130  	var resp *http.Response
  1131  	var result api.Response
  1132  	err = f.pacer.Call(func() (bool, error) {
  1133  		resp, err = f.srv.CallJSON(ctx, &opts, nil, &result)
  1134  		return shouldRetry(resp, err)
  1135  	})
  1136  	if err != nil {
  1137  		return errors.Wrap(err, "rename http")
  1138  	}
  1139  	if err = result.AsErr(); err != nil {
  1140  		return errors.Wrap(err, "rename")
  1141  	}
  1142  	return nil
  1143  }
  1144  
  1145  // Remove an object by ID
  1146  func (f *Fs) remove(ctx context.Context, id string) (err error) {
  1147  	opts := rest.Opts{
  1148  		Method: "POST",
  1149  		Path:   "/item/delete",
  1150  		MultipartParams: url.Values{
  1151  			"id": {id},
  1152  		},
  1153  		Parameters: f.baseParams(),
  1154  	}
  1155  	var resp *http.Response
  1156  	var result api.Response
  1157  	err = f.pacer.Call(func() (bool, error) {
  1158  		resp, err = f.srv.CallJSON(ctx, &opts, nil, &result)
  1159  		return shouldRetry(resp, err)
  1160  	})
  1161  	if err != nil {
  1162  		return errors.Wrap(err, "remove http")
  1163  	}
  1164  	if err = result.AsErr(); err != nil {
  1165  		return errors.Wrap(err, "remove")
  1166  	}
  1167  	return nil
  1168  }
  1169  
  1170  // Remove an object
  1171  func (o *Object) Remove(ctx context.Context) error {
  1172  	err := o.readMetaData(ctx)
  1173  	if err != nil {
  1174  		return errors.Wrap(err, "Remove: Failed to read metadata")
  1175  	}
  1176  	return o.fs.remove(ctx, o.id)
  1177  }
  1178  
  1179  // MimeType of an Object if known, "" otherwise
  1180  func (o *Object) MimeType(ctx context.Context) string {
  1181  	return o.mimeType
  1182  }
  1183  
  1184  // ID returns the ID of the Object if known, or "" if not
  1185  func (o *Object) ID() string {
  1186  	return o.id
  1187  }
  1188  
  1189  // Check the interfaces are satisfied
  1190  var (
  1191  	_ fs.Fs              = (*Fs)(nil)
  1192  	_ fs.Purger          = (*Fs)(nil)
  1193  	_ fs.Mover           = (*Fs)(nil)
  1194  	_ fs.DirMover        = (*Fs)(nil)
  1195  	_ fs.DirCacheFlusher = (*Fs)(nil)
  1196  	_ fs.Abouter         = (*Fs)(nil)
  1197  	_ fs.PublicLinker    = (*Fs)(nil)
  1198  	_ fs.Object          = (*Object)(nil)
  1199  	_ fs.MimeTyper       = (*Object)(nil)
  1200  	_ fs.IDer            = (*Object)(nil)
  1201  )