webdav: set modtime using propset for owncloud and nextcloud

This commit is contained in:
WeidiDeng 2023-04-29 00:38:49 +08:00 committed by GitHub
parent f5bab284c3
commit ae6874170f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -175,6 +175,7 @@ type Fs struct {
precision time.Duration // mod time precision precision time.Duration // mod time precision
canStream bool // set if can stream canStream bool // set if can stream
useOCMtime bool // set if can use X-OC-Mtime useOCMtime bool // set if can use X-OC-Mtime
propsetMtime bool // set if can use propset
retryWithZeroDepth bool // some vendors (sharepoint) won't list files when Depth is 1 (our default) retryWithZeroDepth bool // some vendors (sharepoint) won't list files when Depth is 1 (our default)
checkBeforePurge bool // enables extra check that directory to purge really exists checkBeforePurge bool // enables extra check that directory to purge really exists
hasOCMD5 bool // set if can use owncloud style checksums for MD5 hasOCMD5 bool // set if can use owncloud style checksums for MD5
@ -582,11 +583,13 @@ func (f *Fs) setQuirks(ctx context.Context, vendor string) error {
f.canStream = true f.canStream = true
f.precision = time.Second f.precision = time.Second
f.useOCMtime = true f.useOCMtime = true
f.propsetMtime = true
f.hasOCMD5 = true f.hasOCMD5 = true
f.hasOCSHA1 = true f.hasOCSHA1 = true
case "nextcloud": case "nextcloud":
f.precision = time.Second f.precision = time.Second
f.useOCMtime = true f.useOCMtime = true
f.propsetMtime = true
f.hasOCSHA1 = true f.hasOCSHA1 = true
f.canChunk = true f.canChunk = true
if err := f.verifyChunkConfig(); err != nil { if err := f.verifyChunkConfig(); err != nil {
@ -1299,8 +1302,53 @@ func (o *Object) ModTime(ctx context.Context) time.Time {
return o.modTime return o.modTime
} }
// Set modified time using propset
//
// <d:multistatus xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns"><d:response><d:href>/ocm/remote.php/webdav/office/wir.jpg</d:href><d:propstat><d:prop><d:lastmodified/></d:prop><d:status>HTTP/1.1 200 OK</d:status></d:propstat></d:response></d:multistatus>
var owncloudPropset = `<?xml version="1.0" encoding="utf-8" ?>
<D:propertyupdate xmlns:D="DAV:">
<D:set>
<D:prop>
<lastmodified xmlns="DAV:">%d</lastmodified>
</D:prop>
</D:set>
</D:propertyupdate>
`
// SetModTime sets the modification time of the local fs object // SetModTime sets the modification time of the local fs object
func (o *Object) SetModTime(ctx context.Context, modTime time.Time) error { func (o *Object) SetModTime(ctx context.Context, modTime time.Time) error {
if o.fs.propsetMtime {
opts := rest.Opts{
Method: "PROPPATCH",
Path: o.filePath(),
NoRedirect: true,
Body: strings.NewReader(fmt.Sprintf(owncloudPropset, modTime.Unix())),
}
var result api.Multistatus
var resp *http.Response
var err error
err = o.fs.pacer.Call(func() (bool, error) {
resp, err = o.fs.srv.CallXML(ctx, &opts, nil, &result)
return o.fs.shouldRetry(ctx, resp, err)
})
if err != nil {
if apiErr, ok := err.(*api.Error); ok {
// does not exist
if apiErr.StatusCode == http.StatusNotFound {
return fs.ErrorObjectNotFound
}
}
return fmt.Errorf("couldn't set modified time: %w", err)
}
// FIXME check if response is valid
if len(result.Responses) == 1 && result.Responses[0].Props.StatusOK() {
// update cached modtime
o.modTime = modTime
return nil
}
// fallback
return fs.ErrorCantSetModTime
}
return fs.ErrorCantSetModTime return fs.ErrorCantSetModTime
} }