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