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