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

     1  // Package webdav provides an interface to the Webdav
     2  // object storage system.
     3  package webdav
     4  
     5  // SetModTime might be possible
     6  // https://stackoverflow.com/questions/3579608/webdav-can-a-client-modify-the-mtime-of-a-file
     7  // ...support for a PROPSET to lastmodified (mind the missing get) which does the utime() call might be an option.
     8  // For example the ownCloud WebDAV server does it that way.
     9  
    10  import (
    11  	"bytes"
    12  	"context"
    13  	"encoding/xml"
    14  	"fmt"
    15  	"io"
    16  	"net/http"
    17  	"net/url"
    18  	"os/exec"
    19  	"path"
    20  	"strconv"
    21  	"strings"
    22  	"time"
    23  
    24  	"github.com/pkg/errors"
    25  	"github.com/rclone/rclone/backend/webdav/api"
    26  	"github.com/rclone/rclone/backend/webdav/odrvcookie"
    27  	"github.com/rclone/rclone/fs"
    28  	"github.com/rclone/rclone/fs/config/configmap"
    29  	"github.com/rclone/rclone/fs/config/configstruct"
    30  	"github.com/rclone/rclone/fs/config/obscure"
    31  	"github.com/rclone/rclone/fs/fserrors"
    32  	"github.com/rclone/rclone/fs/fshttp"
    33  	"github.com/rclone/rclone/fs/hash"
    34  	"github.com/rclone/rclone/lib/pacer"
    35  	"github.com/rclone/rclone/lib/rest"
    36  )
    37  
    38  const (
    39  	minSleep      = 10 * time.Millisecond
    40  	maxSleep      = 2 * time.Second
    41  	decayConstant = 2   // bigger for slower decay, exponential
    42  	defaultDepth  = "1" // depth for PROPFIND
    43  )
    44  
    45  // Register with Fs
    46  func init() {
    47  	fs.Register(&fs.RegInfo{
    48  		Name:        "webdav",
    49  		Description: "Webdav",
    50  		NewFs:       NewFs,
    51  		Options: []fs.Option{{
    52  			Name:     "url",
    53  			Help:     "URL of http host to connect to",
    54  			Required: true,
    55  			Examples: []fs.OptionExample{{
    56  				Value: "https://example.com",
    57  				Help:  "Connect to example.com",
    58  			}},
    59  		}, {
    60  			Name: "vendor",
    61  			Help: "Name of the Webdav site/service/software you are using",
    62  			Examples: []fs.OptionExample{{
    63  				Value: "nextcloud",
    64  				Help:  "Nextcloud",
    65  			}, {
    66  				Value: "owncloud",
    67  				Help:  "Owncloud",
    68  			}, {
    69  				Value: "sharepoint",
    70  				Help:  "Sharepoint",
    71  			}, {
    72  				Value: "other",
    73  				Help:  "Other site/service or software",
    74  			}},
    75  		}, {
    76  			Name: "user",
    77  			Help: "User name",
    78  		}, {
    79  			Name:       "pass",
    80  			Help:       "Password.",
    81  			IsPassword: true,
    82  		}, {
    83  			Name: "bearer_token",
    84  			Help: "Bearer token instead of user/pass (eg a Macaroon)",
    85  		}, {
    86  			Name:     "bearer_token_command",
    87  			Help:     "Command to run to get a bearer token",
    88  			Advanced: true,
    89  		}},
    90  	})
    91  }
    92  
    93  // Options defines the configuration for this backend
    94  type Options struct {
    95  	URL                string `config:"url"`
    96  	Vendor             string `config:"vendor"`
    97  	User               string `config:"user"`
    98  	Pass               string `config:"pass"`
    99  	BearerToken        string `config:"bearer_token"`
   100  	BearerTokenCommand string `config:"bearer_token_command"`
   101  }
   102  
   103  // Fs represents a remote webdav
   104  type Fs struct {
   105  	name               string        // name of this remote
   106  	root               string        // the path we are working on
   107  	opt                Options       // parsed options
   108  	features           *fs.Features  // optional features
   109  	endpoint           *url.URL      // URL of the host
   110  	endpointURL        string        // endpoint as a string
   111  	srv                *rest.Client  // the connection to the one drive server
   112  	pacer              *fs.Pacer     // pacer for API calls
   113  	precision          time.Duration // mod time precision
   114  	canStream          bool          // set if can stream
   115  	useOCMtime         bool          // set if can use X-OC-Mtime
   116  	retryWithZeroDepth bool          // some vendors (sharepoint) won't list files when Depth is 1 (our default)
   117  	hasMD5             bool          // set if can use owncloud style checksums for MD5
   118  	hasSHA1            bool          // set if can use owncloud style checksums for SHA1
   119  }
   120  
   121  // Object describes a webdav object
   122  //
   123  // Will definitely have info but maybe not meta
   124  type Object struct {
   125  	fs          *Fs       // what this object is part of
   126  	remote      string    // The remote path
   127  	hasMetaData bool      // whether info below has been set
   128  	size        int64     // size of the object
   129  	modTime     time.Time // modification time of the object
   130  	sha1        string    // SHA-1 of the object content if known
   131  	md5         string    // MD5 of the object content if known
   132  }
   133  
   134  // ------------------------------------------------------------
   135  
   136  // Name of the remote (as passed into NewFs)
   137  func (f *Fs) Name() string {
   138  	return f.name
   139  }
   140  
   141  // Root of the remote (as passed into NewFs)
   142  func (f *Fs) Root() string {
   143  	return f.root
   144  }
   145  
   146  // String converts this Fs to a string
   147  func (f *Fs) String() string {
   148  	return fmt.Sprintf("webdav root '%s'", f.root)
   149  }
   150  
   151  // Features returns the optional features of this Fs
   152  func (f *Fs) Features() *fs.Features {
   153  	return f.features
   154  }
   155  
   156  // retryErrorCodes is a slice of error codes that we will retry
   157  var retryErrorCodes = []int{
   158  	423, // Locked
   159  	429, // Too Many Requests.
   160  	500, // Internal Server Error
   161  	502, // Bad Gateway
   162  	503, // Service Unavailable
   163  	504, // Gateway Timeout
   164  	509, // Bandwidth Limit Exceeded
   165  }
   166  
   167  // shouldRetry returns a boolean as to whether this resp and err
   168  // deserve to be retried.  It returns the err as a convenience
   169  func (f *Fs) shouldRetry(resp *http.Response, err error) (bool, error) {
   170  	// If we have a bearer token command and it has expired then refresh it
   171  	if f.opt.BearerTokenCommand != "" && resp != nil && resp.StatusCode == 401 {
   172  		fs.Debugf(f, "Bearer token expired: %v", err)
   173  		authErr := f.fetchAndSetBearerToken()
   174  		if authErr != nil {
   175  			err = authErr
   176  		}
   177  		return true, err
   178  	}
   179  	return fserrors.ShouldRetry(err) || fserrors.ShouldRetryHTTP(resp, retryErrorCodes), err
   180  }
   181  
   182  // itemIsDir returns true if the item is a directory
   183  //
   184  // When a client sees a resourcetype it doesn't recognize it should
   185  // assume it is a regular non-collection resource.  [WebDav book by
   186  // Lisa Dusseault ch 7.5.8 p170]
   187  func itemIsDir(item *api.Response) bool {
   188  	if t := item.Props.Type; t != nil {
   189  		if t.Space == "DAV:" && t.Local == "collection" {
   190  			return true
   191  		}
   192  		fs.Debugf(nil, "Unknown resource type %q/%q on %q", t.Space, t.Local, item.Props.Name)
   193  	}
   194  	// the iscollection prop is a Microsoft extension, but if present it is a reliable indicator
   195  	// if the above check failed - see #2716. This can be an integer or a boolean - see #2964
   196  	if t := item.Props.IsCollection; t != nil {
   197  		switch x := strings.ToLower(*t); x {
   198  		case "0", "false":
   199  			return false
   200  		case "1", "true":
   201  			return true
   202  		default:
   203  			fs.Debugf(nil, "Unknown value %q for IsCollection", x)
   204  		}
   205  	}
   206  	return false
   207  }
   208  
   209  // readMetaDataForPath reads the metadata from the path
   210  func (f *Fs) readMetaDataForPath(ctx context.Context, path string, depth string) (info *api.Prop, err error) {
   211  	// FIXME how do we read back additional properties?
   212  	opts := rest.Opts{
   213  		Method: "PROPFIND",
   214  		Path:   f.filePath(path),
   215  		ExtraHeaders: map[string]string{
   216  			"Depth": depth,
   217  		},
   218  		NoRedirect: true,
   219  	}
   220  	if f.hasMD5 || f.hasSHA1 {
   221  		opts.Body = bytes.NewBuffer(owncloudProps)
   222  	}
   223  	var result api.Multistatus
   224  	var resp *http.Response
   225  	err = f.pacer.Call(func() (bool, error) {
   226  		resp, err = f.srv.CallXML(ctx, &opts, nil, &result)
   227  		return f.shouldRetry(resp, err)
   228  	})
   229  	if apiErr, ok := err.(*api.Error); ok {
   230  		// does not exist
   231  		switch apiErr.StatusCode {
   232  		case http.StatusNotFound:
   233  			if f.retryWithZeroDepth && depth != "0" {
   234  				return f.readMetaDataForPath(ctx, path, "0")
   235  			}
   236  			return nil, fs.ErrorObjectNotFound
   237  		case http.StatusMovedPermanently, http.StatusFound, http.StatusSeeOther:
   238  			// Some sort of redirect - go doesn't deal with these properly (it resets
   239  			// the method to GET).  However we can assume that if it was redirected the
   240  			// object was not found.
   241  			return nil, fs.ErrorObjectNotFound
   242  		}
   243  	}
   244  	if err != nil {
   245  		return nil, errors.Wrap(err, "read metadata failed")
   246  	}
   247  	if len(result.Responses) < 1 {
   248  		return nil, fs.ErrorObjectNotFound
   249  	}
   250  	item := result.Responses[0]
   251  	if !item.Props.StatusOK() {
   252  		return nil, fs.ErrorObjectNotFound
   253  	}
   254  	if itemIsDir(&item) {
   255  		return nil, fs.ErrorNotAFile
   256  	}
   257  	return &item.Props, nil
   258  }
   259  
   260  // errorHandler parses a non 2xx error response into an error
   261  func errorHandler(resp *http.Response) error {
   262  	body, err := rest.ReadBody(resp)
   263  	if err != nil {
   264  		return errors.Wrap(err, "error when trying to read error from body")
   265  	}
   266  	// Decode error response
   267  	errResponse := new(api.Error)
   268  	err = xml.Unmarshal(body, &errResponse)
   269  	if err != nil {
   270  		// set the Message to be the body if can't parse the XML
   271  		errResponse.Message = strings.TrimSpace(string(body))
   272  	}
   273  	errResponse.Status = resp.Status
   274  	errResponse.StatusCode = resp.StatusCode
   275  	return errResponse
   276  }
   277  
   278  // addSlash makes sure s is terminated with a / if non empty
   279  func addSlash(s string) string {
   280  	if s != "" && !strings.HasSuffix(s, "/") {
   281  		s += "/"
   282  	}
   283  	return s
   284  }
   285  
   286  // filePath returns a file path (f.root, file)
   287  func (f *Fs) filePath(file string) string {
   288  	return rest.URLPathEscape(path.Join(f.root, file))
   289  }
   290  
   291  // dirPath returns a directory path (f.root, dir)
   292  func (f *Fs) dirPath(dir string) string {
   293  	return addSlash(f.filePath(dir))
   294  }
   295  
   296  // filePath returns a file path (f.root, remote)
   297  func (o *Object) filePath() string {
   298  	return o.fs.filePath(o.remote)
   299  }
   300  
   301  // NewFs constructs an Fs from the path, container:path
   302  func NewFs(name, root string, m configmap.Mapper) (fs.Fs, error) {
   303  	ctx := context.Background()
   304  	// Parse config into Options struct
   305  	opt := new(Options)
   306  	err := configstruct.Set(m, opt)
   307  	if err != nil {
   308  		return nil, err
   309  	}
   310  	rootIsDir := strings.HasSuffix(root, "/")
   311  	root = strings.Trim(root, "/")
   312  
   313  	if !strings.HasSuffix(opt.URL, "/") {
   314  		opt.URL += "/"
   315  	}
   316  	if opt.Pass != "" {
   317  		var err error
   318  		opt.Pass, err = obscure.Reveal(opt.Pass)
   319  		if err != nil {
   320  			return nil, errors.Wrap(err, "couldn't decrypt password")
   321  		}
   322  	}
   323  	if opt.Vendor == "" {
   324  		opt.Vendor = "other"
   325  	}
   326  	root = strings.Trim(root, "/")
   327  
   328  	// Parse the endpoint
   329  	u, err := url.Parse(opt.URL)
   330  	if err != nil {
   331  		return nil, err
   332  	}
   333  
   334  	f := &Fs{
   335  		name:        name,
   336  		root:        root,
   337  		opt:         *opt,
   338  		endpoint:    u,
   339  		endpointURL: u.String(),
   340  		srv:         rest.NewClient(fshttp.NewClient(fs.Config)).SetRoot(u.String()),
   341  		pacer:       fs.NewPacer(pacer.NewDefault(pacer.MinSleep(minSleep), pacer.MaxSleep(maxSleep), pacer.DecayConstant(decayConstant))),
   342  		precision:   fs.ModTimeNotSupported,
   343  	}
   344  	f.features = (&fs.Features{
   345  		CanHaveEmptyDirectories: true,
   346  	}).Fill(f)
   347  	if opt.User != "" || opt.Pass != "" {
   348  		f.srv.SetUserPass(opt.User, opt.Pass)
   349  	} else if opt.BearerToken != "" {
   350  		f.setBearerToken(opt.BearerToken)
   351  	} else if f.opt.BearerTokenCommand != "" {
   352  		err = f.fetchAndSetBearerToken()
   353  		if err != nil {
   354  			return nil, err
   355  		}
   356  	}
   357  	f.srv.SetErrorHandler(errorHandler)
   358  	err = f.setQuirks(ctx, opt.Vendor)
   359  	if err != nil {
   360  		return nil, err
   361  	}
   362  	f.srv.SetHeader("Referer", u.String())
   363  
   364  	if root != "" && !rootIsDir {
   365  		// Check to see if the root actually an existing file
   366  		remote := path.Base(root)
   367  		f.root = path.Dir(root)
   368  		if f.root == "." {
   369  			f.root = ""
   370  		}
   371  		_, err := f.NewObject(ctx, remote)
   372  		if err != nil {
   373  			if errors.Cause(err) == fs.ErrorObjectNotFound || errors.Cause(err) == fs.ErrorNotAFile {
   374  				// File doesn't exist so return old f
   375  				f.root = root
   376  				return f, nil
   377  			}
   378  			return nil, err
   379  		}
   380  		// return an error with an fs which points to the parent
   381  		return f, fs.ErrorIsFile
   382  	}
   383  	return f, nil
   384  }
   385  
   386  // sets the BearerToken up
   387  func (f *Fs) setBearerToken(token string) {
   388  	f.opt.BearerToken = token
   389  	f.srv.SetHeader("Authorization", "Bearer "+token)
   390  }
   391  
   392  // fetch the bearer token using the command
   393  func (f *Fs) fetchBearerToken(cmd string) (string, error) {
   394  	var (
   395  		args   = strings.Split(cmd, " ")
   396  		stdout bytes.Buffer
   397  		stderr bytes.Buffer
   398  		c      = exec.Command(args[0], args[1:]...)
   399  	)
   400  	c.Stdout = &stdout
   401  	c.Stderr = &stderr
   402  	var (
   403  		err          = c.Run()
   404  		stdoutString = strings.TrimSpace(stdout.String())
   405  		stderrString = strings.TrimSpace(stderr.String())
   406  	)
   407  	if err != nil {
   408  		if stderrString == "" {
   409  			stderrString = stdoutString
   410  		}
   411  		return "", errors.Wrapf(err, "failed to get bearer token using %q: %s", f.opt.BearerTokenCommand, stderrString)
   412  	}
   413  	return stdoutString, nil
   414  }
   415  
   416  // fetch the bearer token and set it if successful
   417  func (f *Fs) fetchAndSetBearerToken() error {
   418  	if f.opt.BearerTokenCommand == "" {
   419  		return nil
   420  	}
   421  	token, err := f.fetchBearerToken(f.opt.BearerTokenCommand)
   422  	if err != nil {
   423  		return err
   424  	}
   425  	f.setBearerToken(token)
   426  	return nil
   427  }
   428  
   429  // setQuirks adjusts the Fs for the vendor passed in
   430  func (f *Fs) setQuirks(ctx context.Context, vendor string) error {
   431  	switch vendor {
   432  	case "owncloud":
   433  		f.canStream = true
   434  		f.precision = time.Second
   435  		f.useOCMtime = true
   436  		f.hasMD5 = true
   437  		f.hasSHA1 = true
   438  	case "nextcloud":
   439  		f.precision = time.Second
   440  		f.useOCMtime = true
   441  		f.hasSHA1 = true
   442  	case "sharepoint":
   443  		// To mount sharepoint, two Cookies are required
   444  		// They have to be set instead of BasicAuth
   445  		f.srv.RemoveHeader("Authorization") // We don't need this Header if using cookies
   446  		spCk := odrvcookie.New(f.opt.User, f.opt.Pass, f.endpointURL)
   447  		spCookies, err := spCk.Cookies(ctx)
   448  		if err != nil {
   449  			return err
   450  		}
   451  
   452  		odrvcookie.NewRenew(12*time.Hour, func() {
   453  			spCookies, err := spCk.Cookies(ctx)
   454  			if err != nil {
   455  				fs.Errorf("could not renew cookies: %s", err.Error())
   456  				return
   457  			}
   458  			f.srv.SetCookie(&spCookies.FedAuth, &spCookies.RtFa)
   459  			fs.Debugf(spCookies, "successfully renewed sharepoint cookies")
   460  		})
   461  
   462  		f.srv.SetCookie(&spCookies.FedAuth, &spCookies.RtFa)
   463  
   464  		// sharepoint, unlike the other vendors, only lists files if the depth header is set to 0
   465  		// however, rclone defaults to 1 since it provides recursive directory listing
   466  		// to determine if we may have found a file, the request has to be resent
   467  		// with the depth set to 0
   468  		f.retryWithZeroDepth = true
   469  	case "other":
   470  	default:
   471  		fs.Debugf(f, "Unknown vendor %q", vendor)
   472  	}
   473  
   474  	// Remove PutStream from optional features
   475  	if !f.canStream {
   476  		f.features.PutStream = nil
   477  	}
   478  	return nil
   479  }
   480  
   481  // Return an Object from a path
   482  //
   483  // If it can't be found it returns the error fs.ErrorObjectNotFound.
   484  func (f *Fs) newObjectWithInfo(ctx context.Context, remote string, info *api.Prop) (fs.Object, error) {
   485  	o := &Object{
   486  		fs:     f,
   487  		remote: remote,
   488  	}
   489  	var err error
   490  	if info != nil {
   491  		// Set info
   492  		err = o.setMetaData(info)
   493  	} else {
   494  		err = o.readMetaData(ctx) // reads info and meta, returning an error
   495  	}
   496  	if err != nil {
   497  		return nil, err
   498  	}
   499  	return o, nil
   500  }
   501  
   502  // NewObject finds the Object at remote.  If it can't be found
   503  // it returns the error fs.ErrorObjectNotFound.
   504  func (f *Fs) NewObject(ctx context.Context, remote string) (fs.Object, error) {
   505  	return f.newObjectWithInfo(ctx, remote, nil)
   506  }
   507  
   508  // Read the normal props, plus the checksums
   509  //
   510  // <oc:checksums><oc:checksum>SHA1:f572d396fae9206628714fb2ce00f72e94f2258f MD5:b1946ac92492d2347c6235b4d2611184 ADLER32:084b021f</oc:checksum></oc:checksums>
   511  var owncloudProps = []byte(`<?xml version="1.0"?>
   512  <d:propfind  xmlns:d="DAV:" xmlns:oc="http://owncloud.org/ns" xmlns:nc="http://nextcloud.org/ns">
   513   <d:prop>
   514    <d:displayname />
   515    <d:getlastmodified />
   516    <d:getcontentlength />
   517    <d:resourcetype />
   518    <d:getcontenttype />
   519    <oc:checksums />
   520   </d:prop>
   521  </d:propfind>
   522  `)
   523  
   524  // list the objects into the function supplied
   525  //
   526  // If directories is set it only sends directories
   527  // User function to process a File item from listAll
   528  //
   529  // Should return true to finish processing
   530  type listAllFn func(string, bool, *api.Prop) bool
   531  
   532  // Lists the directory required calling the user function on each item found
   533  //
   534  // If the user fn ever returns true then it early exits with found = true
   535  func (f *Fs) listAll(ctx context.Context, dir string, directoriesOnly bool, filesOnly bool, depth string, fn listAllFn) (found bool, err error) {
   536  	opts := rest.Opts{
   537  		Method: "PROPFIND",
   538  		Path:   f.dirPath(dir), // FIXME Should not start with /
   539  		ExtraHeaders: map[string]string{
   540  			"Depth": depth,
   541  		},
   542  	}
   543  	if f.hasMD5 || f.hasSHA1 {
   544  		opts.Body = bytes.NewBuffer(owncloudProps)
   545  	}
   546  	var result api.Multistatus
   547  	var resp *http.Response
   548  	err = f.pacer.Call(func() (bool, error) {
   549  		resp, err = f.srv.CallXML(ctx, &opts, nil, &result)
   550  		return f.shouldRetry(resp, err)
   551  	})
   552  	if err != nil {
   553  		if apiErr, ok := err.(*api.Error); ok {
   554  			// does not exist
   555  			if apiErr.StatusCode == http.StatusNotFound {
   556  				if f.retryWithZeroDepth && depth != "0" {
   557  					return f.listAll(ctx, dir, directoriesOnly, filesOnly, "0", fn)
   558  				}
   559  				return found, fs.ErrorDirNotFound
   560  			}
   561  		}
   562  		return found, errors.Wrap(err, "couldn't list files")
   563  	}
   564  	//fmt.Printf("result = %#v", &result)
   565  	baseURL, err := rest.URLJoin(f.endpoint, opts.Path)
   566  	if err != nil {
   567  		return false, errors.Wrap(err, "couldn't join URL")
   568  	}
   569  	for i := range result.Responses {
   570  		item := &result.Responses[i]
   571  		isDir := itemIsDir(item)
   572  
   573  		// Find name
   574  		u, err := rest.URLJoin(baseURL, item.Href)
   575  		if err != nil {
   576  			fs.Errorf(nil, "URL Join failed for %q and %q: %v", baseURL, item.Href, err)
   577  			continue
   578  		}
   579  		// Make sure directories end with a /
   580  		if isDir {
   581  			u.Path = addSlash(u.Path)
   582  		}
   583  		if !strings.HasPrefix(u.Path, baseURL.Path) {
   584  			fs.Debugf(nil, "Item with unknown path received: %q, %q", u.Path, baseURL.Path)
   585  			continue
   586  		}
   587  		remote := path.Join(dir, u.Path[len(baseURL.Path):])
   588  		if strings.HasSuffix(remote, "/") {
   589  			remote = remote[:len(remote)-1]
   590  		}
   591  
   592  		// the listing contains info about itself which we ignore
   593  		if remote == dir {
   594  			continue
   595  		}
   596  
   597  		// Check OK
   598  		if !item.Props.StatusOK() {
   599  			fs.Debugf(remote, "Ignoring item with bad status %q", item.Props.Status)
   600  			continue
   601  		}
   602  
   603  		if isDir {
   604  			if filesOnly {
   605  				continue
   606  			}
   607  		} else {
   608  			if directoriesOnly {
   609  				continue
   610  			}
   611  		}
   612  		// 	item.Name = restoreReservedChars(item.Name)
   613  		if fn(remote, isDir, &item.Props) {
   614  			found = true
   615  			break
   616  		}
   617  	}
   618  	return
   619  }
   620  
   621  // List the objects and directories in dir into entries.  The
   622  // entries can be returned in any order but should be for a
   623  // complete directory.
   624  //
   625  // dir should be "" to list the root, and should not have
   626  // trailing slashes.
   627  //
   628  // This should return ErrDirNotFound if the directory isn't
   629  // found.
   630  func (f *Fs) List(ctx context.Context, dir string) (entries fs.DirEntries, err error) {
   631  	var iErr error
   632  	_, err = f.listAll(ctx, dir, false, false, defaultDepth, func(remote string, isDir bool, info *api.Prop) bool {
   633  		if isDir {
   634  			d := fs.NewDir(remote, time.Time(info.Modified))
   635  			// .SetID(info.ID)
   636  			// FIXME more info from dir? can set size, items?
   637  			entries = append(entries, d)
   638  		} else {
   639  			o, err := f.newObjectWithInfo(ctx, remote, info)
   640  			if err != nil {
   641  				iErr = err
   642  				return true
   643  			}
   644  			entries = append(entries, o)
   645  		}
   646  		return false
   647  	})
   648  	if err != nil {
   649  		return nil, err
   650  	}
   651  	if iErr != nil {
   652  		return nil, iErr
   653  	}
   654  	return entries, nil
   655  }
   656  
   657  // Creates from the parameters passed in a half finished Object which
   658  // must have setMetaData called on it
   659  //
   660  // Used to create new objects
   661  func (f *Fs) createObject(remote string, modTime time.Time, size int64) (o *Object) {
   662  	// Temporary Object under construction
   663  	o = &Object{
   664  		fs:      f,
   665  		remote:  remote,
   666  		size:    size,
   667  		modTime: modTime,
   668  	}
   669  	return o
   670  }
   671  
   672  // Put the object
   673  //
   674  // Copy the reader in to the new object which is returned
   675  //
   676  // The new object may have been created if an error is returned
   677  func (f *Fs) Put(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) (fs.Object, error) {
   678  	o := f.createObject(src.Remote(), src.ModTime(ctx), src.Size())
   679  	return o, o.Update(ctx, in, src, options...)
   680  }
   681  
   682  // PutStream uploads to the remote path with the modTime given of indeterminate size
   683  func (f *Fs) PutStream(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) (fs.Object, error) {
   684  	return f.Put(ctx, in, src, options...)
   685  }
   686  
   687  // mkParentDir makes the parent of the native path dirPath if
   688  // necessary and any directories above that
   689  func (f *Fs) mkParentDir(ctx context.Context, dirPath string) error {
   690  	// defer log.Trace(dirPath, "")("")
   691  	// chop off trailing / if it exists
   692  	if strings.HasSuffix(dirPath, "/") {
   693  		dirPath = dirPath[:len(dirPath)-1]
   694  	}
   695  	parent := path.Dir(dirPath)
   696  	if parent == "." {
   697  		parent = ""
   698  	}
   699  	return f.mkdir(ctx, parent)
   700  }
   701  
   702  // low level mkdir, only makes the directory, doesn't attempt to create parents
   703  func (f *Fs) _mkdir(ctx context.Context, dirPath string) error {
   704  	// We assume the root is already created
   705  	if dirPath == "" {
   706  		return nil
   707  	}
   708  	// Collections must end with /
   709  	if !strings.HasSuffix(dirPath, "/") {
   710  		dirPath += "/"
   711  	}
   712  	opts := rest.Opts{
   713  		Method:     "MKCOL",
   714  		Path:       dirPath,
   715  		NoResponse: true,
   716  	}
   717  	err := f.pacer.Call(func() (bool, error) {
   718  		resp, err := f.srv.Call(ctx, &opts)
   719  		return f.shouldRetry(resp, err)
   720  	})
   721  	if apiErr, ok := err.(*api.Error); ok {
   722  		// already exists
   723  		// owncloud returns 423/StatusLocked if the create is already in progress
   724  		if apiErr.StatusCode == http.StatusMethodNotAllowed || apiErr.StatusCode == http.StatusNotAcceptable || apiErr.StatusCode == http.StatusLocked {
   725  			return nil
   726  		}
   727  	}
   728  	return err
   729  }
   730  
   731  // mkdir makes the directory and parents using native paths
   732  func (f *Fs) mkdir(ctx context.Context, dirPath string) error {
   733  	// defer log.Trace(dirPath, "")("")
   734  	err := f._mkdir(ctx, dirPath)
   735  	if apiErr, ok := err.(*api.Error); ok {
   736  		// parent does not exist so create it first then try again
   737  		if apiErr.StatusCode == http.StatusConflict {
   738  			err = f.mkParentDir(ctx, dirPath)
   739  			if err == nil {
   740  				err = f._mkdir(ctx, dirPath)
   741  			}
   742  		}
   743  	}
   744  	return err
   745  }
   746  
   747  // Mkdir creates the directory if it doesn't exist
   748  func (f *Fs) Mkdir(ctx context.Context, dir string) error {
   749  	dirPath := f.dirPath(dir)
   750  	return f.mkdir(ctx, dirPath)
   751  }
   752  
   753  // dirNotEmpty returns true if the directory exists and is not Empty
   754  //
   755  // if the directory does not exist then err will be ErrorDirNotFound
   756  func (f *Fs) dirNotEmpty(ctx context.Context, dir string) (found bool, err error) {
   757  	return f.listAll(ctx, dir, false, false, defaultDepth, func(remote string, isDir bool, info *api.Prop) bool {
   758  		return true
   759  	})
   760  }
   761  
   762  // purgeCheck removes the root directory, if check is set then it
   763  // refuses to do so if it has anything in
   764  func (f *Fs) purgeCheck(ctx context.Context, dir string, check bool) error {
   765  	if check {
   766  		notEmpty, err := f.dirNotEmpty(ctx, dir)
   767  		if err != nil {
   768  			return err
   769  		}
   770  		if notEmpty {
   771  			return fs.ErrorDirectoryNotEmpty
   772  		}
   773  	}
   774  	opts := rest.Opts{
   775  		Method:     "DELETE",
   776  		Path:       f.dirPath(dir),
   777  		NoResponse: true,
   778  	}
   779  	var resp *http.Response
   780  	var err error
   781  	err = f.pacer.Call(func() (bool, error) {
   782  		resp, err = f.srv.CallXML(ctx, &opts, nil, nil)
   783  		return f.shouldRetry(resp, err)
   784  	})
   785  	if err != nil {
   786  		return errors.Wrap(err, "rmdir failed")
   787  	}
   788  	// FIXME parse Multistatus response
   789  	return nil
   790  }
   791  
   792  // Rmdir deletes the root folder
   793  //
   794  // Returns an error if it isn't empty
   795  func (f *Fs) Rmdir(ctx context.Context, dir string) error {
   796  	return f.purgeCheck(ctx, dir, true)
   797  }
   798  
   799  // Precision return the precision of this Fs
   800  func (f *Fs) Precision() time.Duration {
   801  	return f.precision
   802  }
   803  
   804  // Copy or Move src to this remote using server side copy operations.
   805  //
   806  // This is stored with the remote path given
   807  //
   808  // It returns the destination Object and a possible error
   809  //
   810  // Will only be called if src.Fs().Name() == f.Name()
   811  //
   812  // If it isn't possible then return fs.ErrorCantCopy/fs.ErrorCantMove
   813  func (f *Fs) copyOrMove(ctx context.Context, src fs.Object, remote string, method string) (fs.Object, error) {
   814  	srcObj, ok := src.(*Object)
   815  	if !ok {
   816  		fs.Debugf(src, "Can't copy - not same remote type")
   817  		if method == "COPY" {
   818  			return nil, fs.ErrorCantCopy
   819  		}
   820  		return nil, fs.ErrorCantMove
   821  	}
   822  	dstPath := f.filePath(remote)
   823  	err := f.mkParentDir(ctx, dstPath)
   824  	if err != nil {
   825  		return nil, errors.Wrap(err, "Copy mkParentDir failed")
   826  	}
   827  	destinationURL, err := rest.URLJoin(f.endpoint, dstPath)
   828  	if err != nil {
   829  		return nil, errors.Wrap(err, "copyOrMove couldn't join URL")
   830  	}
   831  	var resp *http.Response
   832  	opts := rest.Opts{
   833  		Method:     method,
   834  		Path:       srcObj.filePath(),
   835  		NoResponse: true,
   836  		ExtraHeaders: map[string]string{
   837  			"Destination": destinationURL.String(),
   838  			"Overwrite":   "F",
   839  		},
   840  	}
   841  	if f.useOCMtime {
   842  		opts.ExtraHeaders["X-OC-Mtime"] = fmt.Sprintf("%d", src.ModTime(ctx).Unix())
   843  	}
   844  	err = f.pacer.Call(func() (bool, error) {
   845  		resp, err = f.srv.Call(ctx, &opts)
   846  		return f.shouldRetry(resp, err)
   847  	})
   848  	if err != nil {
   849  		return nil, errors.Wrap(err, "Copy call failed")
   850  	}
   851  	dstObj, err := f.NewObject(ctx, remote)
   852  	if err != nil {
   853  		return nil, errors.Wrap(err, "Copy NewObject failed")
   854  	}
   855  	return dstObj, nil
   856  }
   857  
   858  // Copy src to this remote using server side copy operations.
   859  //
   860  // This is stored with the remote path given
   861  //
   862  // It returns the destination Object and a possible error
   863  //
   864  // Will only be called if src.Fs().Name() == f.Name()
   865  //
   866  // If it isn't possible then return fs.ErrorCantCopy
   867  func (f *Fs) Copy(ctx context.Context, src fs.Object, remote string) (fs.Object, error) {
   868  	return f.copyOrMove(ctx, src, remote, "COPY")
   869  }
   870  
   871  // Purge deletes all the files and the container
   872  //
   873  // Optional interface: Only implement this if you have a way of
   874  // deleting all the files quicker than just running Remove() on the
   875  // result of List()
   876  func (f *Fs) Purge(ctx context.Context) error {
   877  	return f.purgeCheck(ctx, "", false)
   878  }
   879  
   880  // Move src to this remote using server side move operations.
   881  //
   882  // This is stored with the remote path given
   883  //
   884  // It returns the destination Object and a possible error
   885  //
   886  // Will only be called if src.Fs().Name() == f.Name()
   887  //
   888  // If it isn't possible then return fs.ErrorCantMove
   889  func (f *Fs) Move(ctx context.Context, src fs.Object, remote string) (fs.Object, error) {
   890  	return f.copyOrMove(ctx, src, remote, "MOVE")
   891  }
   892  
   893  // DirMove moves src, srcRemote to this remote at dstRemote
   894  // using server side move operations.
   895  //
   896  // Will only be called if src.Fs().Name() == f.Name()
   897  //
   898  // If it isn't possible then return fs.ErrorCantDirMove
   899  //
   900  // If destination exists then return fs.ErrorDirExists
   901  func (f *Fs) DirMove(ctx context.Context, src fs.Fs, srcRemote, dstRemote string) error {
   902  	srcFs, ok := src.(*Fs)
   903  	if !ok {
   904  		fs.Debugf(srcFs, "Can't move directory - not same remote type")
   905  		return fs.ErrorCantDirMove
   906  	}
   907  	srcPath := srcFs.filePath(srcRemote)
   908  	dstPath := f.filePath(dstRemote)
   909  
   910  	// Check if destination exists
   911  	_, err := f.dirNotEmpty(ctx, dstRemote)
   912  	if err == nil {
   913  		return fs.ErrorDirExists
   914  	}
   915  	if err != fs.ErrorDirNotFound {
   916  		return errors.Wrap(err, "DirMove dirExists dst failed")
   917  	}
   918  
   919  	// Make sure the parent directory exists
   920  	err = f.mkParentDir(ctx, dstPath)
   921  	if err != nil {
   922  		return errors.Wrap(err, "DirMove mkParentDir dst failed")
   923  	}
   924  
   925  	destinationURL, err := rest.URLJoin(f.endpoint, dstPath)
   926  	if err != nil {
   927  		return errors.Wrap(err, "DirMove couldn't join URL")
   928  	}
   929  
   930  	var resp *http.Response
   931  	opts := rest.Opts{
   932  		Method:     "MOVE",
   933  		Path:       addSlash(srcPath),
   934  		NoResponse: true,
   935  		ExtraHeaders: map[string]string{
   936  			"Destination": addSlash(destinationURL.String()),
   937  			"Overwrite":   "F",
   938  		},
   939  	}
   940  	err = f.pacer.Call(func() (bool, error) {
   941  		resp, err = f.srv.Call(ctx, &opts)
   942  		return f.shouldRetry(resp, err)
   943  	})
   944  	if err != nil {
   945  		return errors.Wrap(err, "DirMove MOVE call failed")
   946  	}
   947  	return nil
   948  }
   949  
   950  // Hashes returns the supported hash sets.
   951  func (f *Fs) Hashes() hash.Set {
   952  	hashes := hash.Set(hash.None)
   953  	if f.hasMD5 {
   954  		hashes.Add(hash.MD5)
   955  	}
   956  	if f.hasSHA1 {
   957  		hashes.Add(hash.SHA1)
   958  	}
   959  	return hashes
   960  }
   961  
   962  // About gets quota information
   963  func (f *Fs) About(ctx context.Context) (*fs.Usage, error) {
   964  	opts := rest.Opts{
   965  		Method: "PROPFIND",
   966  		Path:   "",
   967  		ExtraHeaders: map[string]string{
   968  			"Depth": "0",
   969  		},
   970  	}
   971  	opts.Body = bytes.NewBuffer([]byte(`<?xml version="1.0" ?>
   972  <D:propfind xmlns:D="DAV:">
   973   <D:prop>
   974    <D:quota-available-bytes/>
   975    <D:quota-used-bytes/>
   976   </D:prop>
   977  </D:propfind>
   978  `))
   979  	var q api.Quota
   980  	var resp *http.Response
   981  	var err error
   982  	err = f.pacer.Call(func() (bool, error) {
   983  		resp, err = f.srv.CallXML(ctx, &opts, nil, &q)
   984  		return f.shouldRetry(resp, err)
   985  	})
   986  	if err != nil {
   987  		return nil, errors.Wrap(err, "about call failed")
   988  	}
   989  	usage := &fs.Usage{}
   990  	if i, err := strconv.ParseInt(q.Used, 10, 64); err == nil && i >= 0 {
   991  		usage.Used = fs.NewUsageValue(i)
   992  	}
   993  	if i, err := strconv.ParseInt(q.Available, 10, 64); err == nil && i >= 0 {
   994  		usage.Free = fs.NewUsageValue(i)
   995  	}
   996  	if usage.Used != nil && usage.Free != nil {
   997  		usage.Total = fs.NewUsageValue(*usage.Used + *usage.Free)
   998  	}
   999  	return usage, nil
  1000  }
  1001  
  1002  // ------------------------------------------------------------
  1003  
  1004  // Fs returns the parent Fs
  1005  func (o *Object) Fs() fs.Info {
  1006  	return o.fs
  1007  }
  1008  
  1009  // Return a string version
  1010  func (o *Object) String() string {
  1011  	if o == nil {
  1012  		return "<nil>"
  1013  	}
  1014  	return o.remote
  1015  }
  1016  
  1017  // Remote returns the remote path
  1018  func (o *Object) Remote() string {
  1019  	return o.remote
  1020  }
  1021  
  1022  // Hash returns the SHA1 or MD5 of an object returning a lowercase hex string
  1023  func (o *Object) Hash(ctx context.Context, t hash.Type) (string, error) {
  1024  	if t == hash.MD5 && o.fs.hasMD5 {
  1025  		return o.md5, nil
  1026  	}
  1027  	if t == hash.SHA1 && o.fs.hasSHA1 {
  1028  		return o.sha1, nil
  1029  	}
  1030  	return "", hash.ErrUnsupported
  1031  }
  1032  
  1033  // Size returns the size of an object in bytes
  1034  func (o *Object) Size() int64 {
  1035  	ctx := context.TODO()
  1036  	err := o.readMetaData(ctx)
  1037  	if err != nil {
  1038  		fs.Logf(o, "Failed to read metadata: %v", err)
  1039  		return 0
  1040  	}
  1041  	return o.size
  1042  }
  1043  
  1044  // setMetaData sets the metadata from info
  1045  func (o *Object) setMetaData(info *api.Prop) (err error) {
  1046  	o.hasMetaData = true
  1047  	o.size = info.Size
  1048  	o.modTime = time.Time(info.Modified)
  1049  	if o.fs.hasMD5 || o.fs.hasSHA1 {
  1050  		hashes := info.Hashes()
  1051  		if o.fs.hasSHA1 {
  1052  			o.sha1 = hashes[hash.SHA1]
  1053  		}
  1054  		if o.fs.hasMD5 {
  1055  			o.md5 = hashes[hash.MD5]
  1056  		}
  1057  	}
  1058  	return nil
  1059  }
  1060  
  1061  // readMetaData gets the metadata if it hasn't already been fetched
  1062  //
  1063  // it also sets the info
  1064  func (o *Object) readMetaData(ctx context.Context) (err error) {
  1065  	if o.hasMetaData {
  1066  		return nil
  1067  	}
  1068  	info, err := o.fs.readMetaDataForPath(ctx, o.remote, defaultDepth)
  1069  	if err != nil {
  1070  		return err
  1071  	}
  1072  	return o.setMetaData(info)
  1073  }
  1074  
  1075  // ModTime returns the modification time of the object
  1076  //
  1077  // It attempts to read the objects mtime and if that isn't present the
  1078  // LastModified returned in the http headers
  1079  func (o *Object) ModTime(ctx context.Context) time.Time {
  1080  	err := o.readMetaData(ctx)
  1081  	if err != nil {
  1082  		fs.Logf(o, "Failed to read metadata: %v", err)
  1083  		return time.Now()
  1084  	}
  1085  	return o.modTime
  1086  }
  1087  
  1088  // SetModTime sets the modification time of the local fs object
  1089  func (o *Object) SetModTime(ctx context.Context, modTime time.Time) error {
  1090  	return fs.ErrorCantSetModTime
  1091  }
  1092  
  1093  // Storable returns a boolean showing whether this object storable
  1094  func (o *Object) Storable() bool {
  1095  	return true
  1096  }
  1097  
  1098  // Open an object for read
  1099  func (o *Object) Open(ctx context.Context, options ...fs.OpenOption) (in io.ReadCloser, err error) {
  1100  	var resp *http.Response
  1101  	opts := rest.Opts{
  1102  		Method:  "GET",
  1103  		Path:    o.filePath(),
  1104  		Options: options,
  1105  	}
  1106  	err = o.fs.pacer.Call(func() (bool, error) {
  1107  		resp, err = o.fs.srv.Call(ctx, &opts)
  1108  		return o.fs.shouldRetry(resp, err)
  1109  	})
  1110  	if err != nil {
  1111  		return nil, err
  1112  	}
  1113  	return resp.Body, err
  1114  }
  1115  
  1116  // Update the object with the contents of the io.Reader, modTime and size
  1117  //
  1118  // If existing is set then it updates the object rather than creating a new one
  1119  //
  1120  // The new object may have been created if an error is returned
  1121  func (o *Object) Update(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) (err error) {
  1122  	err = o.fs.mkParentDir(ctx, o.filePath())
  1123  	if err != nil {
  1124  		return errors.Wrap(err, "Update mkParentDir failed")
  1125  	}
  1126  
  1127  	size := src.Size()
  1128  	var resp *http.Response
  1129  	opts := rest.Opts{
  1130  		Method:        "PUT",
  1131  		Path:          o.filePath(),
  1132  		Body:          in,
  1133  		NoResponse:    true,
  1134  		ContentLength: &size, // FIXME this isn't necessary with owncloud - See https://github.com/nextcloud/nextcloud-snap/issues/365
  1135  		ContentType:   fs.MimeType(ctx, src),
  1136  		Options:       options,
  1137  	}
  1138  	if o.fs.useOCMtime || o.fs.hasMD5 || o.fs.hasSHA1 {
  1139  		opts.ExtraHeaders = map[string]string{}
  1140  		if o.fs.useOCMtime {
  1141  			opts.ExtraHeaders["X-OC-Mtime"] = fmt.Sprintf("%d", src.ModTime(ctx).Unix())
  1142  		}
  1143  		// Set one upload checksum
  1144  		// Owncloud uses one checksum only to check the upload and stores its own SHA1 and MD5
  1145  		// Nextcloud stores the checksum you supply (SHA1 or MD5) but only stores one
  1146  		if o.fs.hasSHA1 {
  1147  			if sha1, _ := src.Hash(ctx, hash.SHA1); sha1 != "" {
  1148  				opts.ExtraHeaders["OC-Checksum"] = "SHA1:" + sha1
  1149  			}
  1150  		}
  1151  		if o.fs.hasMD5 && opts.ExtraHeaders["OC-Checksum"] == "" {
  1152  			if md5, _ := src.Hash(ctx, hash.MD5); md5 != "" {
  1153  				opts.ExtraHeaders["OC-Checksum"] = "MD5:" + md5
  1154  			}
  1155  		}
  1156  	}
  1157  	err = o.fs.pacer.CallNoRetry(func() (bool, error) {
  1158  		resp, err = o.fs.srv.Call(ctx, &opts)
  1159  		return o.fs.shouldRetry(resp, err)
  1160  	})
  1161  	if err != nil {
  1162  		// Give the WebDAV server a chance to get its internal state in order after the
  1163  		// error.  The error may have been local in which case we closed the connection.
  1164  		// The server may still be dealing with it for a moment. A sleep isn't ideal but I
  1165  		// haven't been able to think of a better method to find out if the server has
  1166  		// finished - ncw
  1167  		time.Sleep(1 * time.Second)
  1168  		// Remove failed upload
  1169  		_ = o.Remove(ctx)
  1170  		return err
  1171  	}
  1172  	// read metadata from remote
  1173  	o.hasMetaData = false
  1174  	return o.readMetaData(ctx)
  1175  }
  1176  
  1177  // Remove an object
  1178  func (o *Object) Remove(ctx context.Context) error {
  1179  	opts := rest.Opts{
  1180  		Method:     "DELETE",
  1181  		Path:       o.filePath(),
  1182  		NoResponse: true,
  1183  	}
  1184  	return o.fs.pacer.Call(func() (bool, error) {
  1185  		resp, err := o.fs.srv.Call(ctx, &opts)
  1186  		return o.fs.shouldRetry(resp, err)
  1187  	})
  1188  }
  1189  
  1190  // Check the interfaces are satisfied
  1191  var (
  1192  	_ fs.Fs          = (*Fs)(nil)
  1193  	_ fs.Purger      = (*Fs)(nil)
  1194  	_ fs.PutStreamer = (*Fs)(nil)
  1195  	_ fs.Copier      = (*Fs)(nil)
  1196  	_ fs.Mover       = (*Fs)(nil)
  1197  	_ fs.DirMover    = (*Fs)(nil)
  1198  	_ fs.Abouter     = (*Fs)(nil)
  1199  	_ fs.Object      = (*Object)(nil)
  1200  )