diff --git a/changelog/unreleased/bump-reva.md b/changelog/unreleased/bump-reva.md new file mode 100644 index 0000000000..66115d4d19 --- /dev/null +++ b/changelog/unreleased/bump-reva.md @@ -0,0 +1,5 @@ +Enhancement: Bump Reva + +Bumps the reva version + +https://github.com/owncloud/ocis/pull/10830 diff --git a/go.mod b/go.mod index a4b6782ed1..563cac135e 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,7 @@ require ( github.com/cenkalti/backoff v2.2.1+incompatible github.com/coreos/go-oidc/v3 v3.11.0 github.com/cs3org/go-cs3apis v0.0.0-20241105092511-3ad35d174fc1 - github.com/cs3org/reva/v2 v2.27.0 + github.com/cs3org/reva/v2 v2.27.1-0.20250107124628-bd76241aa776 github.com/davidbyttow/govips/v2 v2.15.0 github.com/dhowden/tag v0.0.0-20240417053706-3d75831295e8 github.com/dutchcoders/go-clamd v0.0.0-20170520113014-b970184f4d9e @@ -340,7 +340,7 @@ require ( sigs.k8s.io/yaml v1.4.0 // indirect ) -replace github.com/studio-b12/gowebdav => github.com/aduffeck/gowebdav v0.0.0-20231215102054-212d4a4374f6 +replace github.com/studio-b12/gowebdav => github.com/kobergj/gowebdav v0.0.0-20250102091030-aa65266db202 replace github.com/egirna/icap-client => github.com/fschade/icap-client v0.0.0-20240802074440-aade4a234387 diff --git a/go.sum b/go.sum index 249bbe2cdd..a137461062 100644 --- a/go.sum +++ b/go.sum @@ -93,8 +93,6 @@ github.com/RoaringBitmap/roaring v1.9.3 h1:t4EbC5qQwnisr5PrP9nt0IRhRTb9gMUgQF4t4 github.com/RoaringBitmap/roaring v1.9.3/go.mod h1:6AXUsoIEzDTFFQCe1RbGA6uFONMhvejWj5rqITANK90= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= -github.com/aduffeck/gowebdav v0.0.0-20231215102054-212d4a4374f6 h1:ws0yvsikTQdmheKINP16tBzAHdttrHwbz/q3Fgl9X1Y= -github.com/aduffeck/gowebdav v0.0.0-20231215102054-212d4a4374f6/go.mod h1:bHA7t77X/QFExdeAnDzK6vKM34kEZAcE1OX4MfiwjkE= github.com/agnivade/levenshtein v1.2.0 h1:U9L4IOT0Y3i0TIlUIDJ7rVUziKi/zPbrJGaFrtYH3SY= github.com/agnivade/levenshtein v1.2.0/go.mod h1:QVVI16kDrtSuwcpd0p1+xMC6Z/VfhtCyDIjcwga4/DU= github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU= @@ -255,8 +253,8 @@ github.com/crewjam/saml v0.4.14 h1:g9FBNx62osKusnFzs3QTN5L9CVA/Egfgm+stJShzw/c= github.com/crewjam/saml v0.4.14/go.mod h1:UVSZCf18jJkk6GpWNVqcyQJMD5HsRugBPf4I1nl2mME= github.com/cs3org/go-cs3apis v0.0.0-20241105092511-3ad35d174fc1 h1:RU6LT6mkD16xZs011+8foU7T3LrPvTTSWeTQ9OgfhkA= github.com/cs3org/go-cs3apis v0.0.0-20241105092511-3ad35d174fc1/go.mod h1:DedpcqXl193qF/08Y04IO0PpxyyMu8+GrkD6kWK2MEQ= -github.com/cs3org/reva/v2 v2.27.0 h1:Lq8VBRjQmyCfCO6+wC80Z4vH2Q1MfgORggg1e6mEcdM= -github.com/cs3org/reva/v2 v2.27.0/go.mod h1:fJWmn7EkttWOWphZfiKdFOcHuthcUsU55aSN1VeTOhU= +github.com/cs3org/reva/v2 v2.27.1-0.20250107124628-bd76241aa776 h1:tNLN9UCR9OHhZUHftWKfJyNPPaS0HaT0QOH4zGln/UE= +github.com/cs3org/reva/v2 v2.27.1-0.20250107124628-bd76241aa776/go.mod h1:OrCNgIFkp3ROHiEjNCj8eZHACmYCD4owtFRuYFlYZkU= github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4= github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg= github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= @@ -704,6 +702,8 @@ github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90 github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM= github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/kobergj/gowebdav v0.0.0-20250102091030-aa65266db202 h1:A1xJ2NKgiYFiaHiLl9B5yw/gUBACSs9crDykTS3GuQI= +github.com/kobergj/gowebdav v0.0.0-20250102091030-aa65266db202/go.mod h1:bHA7t77X/QFExdeAnDzK6vKM34kEZAcE1OX4MfiwjkE= github.com/kobergj/plugins/v4/store/nats-js-kv v0.0.0-20240807130109-f62bb67e8c90 h1:pfI8Z5yavO6fU6vDGlWhZ4BgDlvj8c6xB7J57HfTPwA= github.com/kobergj/plugins/v4/store/nats-js-kv v0.0.0-20240807130109-f62bb67e8c90/go.mod h1:pjcozWijkNPbEtX5SIQaxEW/h8VAVZYTLx+70bmB3LY= github.com/kolo/xmlrpc v0.0.0-20200310150728-e0350524596b/go.mod h1:o03bZfuBwAXHetKXuInt4S7omeXUu62/A845kiycsSQ= diff --git a/vendor/github.com/cs3org/reva/v2/internal/grpc/services/publicstorageprovider/publicstorageprovider.go b/vendor/github.com/cs3org/reva/v2/internal/grpc/services/publicstorageprovider/publicstorageprovider.go index c743372295..114c674c95 100644 --- a/vendor/github.com/cs3org/reva/v2/internal/grpc/services/publicstorageprovider/publicstorageprovider.go +++ b/vendor/github.com/cs3org/reva/v2/internal/grpc/services/publicstorageprovider/publicstorageprovider.go @@ -315,6 +315,7 @@ func (s *service) InitiateFileUpload(ctx context.Context, req *provider.Initiate uReq := &provider.InitiateFileUploadRequest{ Ref: cs3Ref, Opaque: req.Opaque, + LockId: req.LockId, } gatewayClient, err := s.gatewaySelector.Next() diff --git a/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/locks.go b/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/locks.go index 5458ec4e58..da98699615 100644 --- a/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/locks.go +++ b/vendor/github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/locks.go @@ -84,6 +84,7 @@ type lockInfo struct { Shared *struct{} `xml:"lockscope>shared"` Write *struct{} `xml:"locktype>write"` Owner owner `xml:"owner"` + LockID string `xml:"locktoken>href"` } // http://www.webdav.org/specs/rfc4918.html#ELEMENT_owner @@ -144,7 +145,7 @@ type LockSystem interface { // // See http://www.webdav.org/specs/rfc4918.html#rfc.section.9.10.6 for // when to use each error. - Refresh(ctx context.Context, now time.Time, token string, duration time.Duration) (LockDetails, error) + Refresh(ctx context.Context, now time.Time, ref *provider.Reference, token string) error // Unlock unlocks the lock with the given token. // @@ -184,13 +185,6 @@ func (cls *cs3LS) Create(ctx context.Context, now time.Time, details LockDetails } */ - // Having a lock token provides no special access rights. Anyone can find out anyone - // else's lock token by performing lock discovery. Locks must be enforced based upon - // whatever authentication mechanism is used by the server, not based on the secrecy - // of the token values. - // see: http://www.webdav.org/specs/rfc2518.html#n-lock-tokens - token := uuid.New() - u := ctxpkg.ContextMustGetUser(ctx) // add metadata via opaque @@ -198,6 +192,17 @@ func (cls *cs3LS) Create(ctx context.Context, now time.Time, details LockDetails o := utils.AppendPlainToOpaque(nil, "lockownername", u.GetDisplayName()) o = utils.AppendPlainToOpaque(o, "locktime", now.Format(time.RFC3339)) + lockid := details.LockID + if lockid == "" { + // Having a lock token provides no special access rights. Anyone can find out anyone + // else's lock token by performing lock discovery. Locks must be enforced based upon + // whatever authentication mechanism is used by the server, not based on the secrecy + // of the token values. + // see: http://www.webdav.org/specs/rfc2518.html#n-lock-tokens + token := uuid.New() + + lockid = lockTokenPrefix + token.String() + } r := &provider.SetLockRequest{ Ref: details.Root, Lock: &provider.Lock{ @@ -205,7 +210,7 @@ func (cls *cs3LS) Create(ctx context.Context, now time.Time, details LockDetails Type: provider.LockType_LOCK_TYPE_EXCL, User: details.UserID, // no way to set an app lock? TODO maybe via the ownerxml //AppName: , // TODO use a urn scheme? - LockId: lockTokenPrefix + token.String(), // can be a token or a Coded-URL + LockId: lockid, }, } if details.Duration > 0 { @@ -227,15 +232,52 @@ func (cls *cs3LS) Create(ctx context.Context, now time.Time, details LockDetails } switch res.GetStatus().GetCode() { case rpc.Code_CODE_OK: - return lockTokenPrefix + token.String(), nil + return lockid, nil default: return "", ocdavErrors.NewErrFromStatus(res.GetStatus()) } } -func (cls *cs3LS) Refresh(ctx context.Context, now time.Time, token string, duration time.Duration) (LockDetails, error) { - return LockDetails{}, ocdavErrors.ErrNotImplemented +func (cls *cs3LS) Refresh(ctx context.Context, now time.Time, ref *provider.Reference, token string) error { + u := ctxpkg.ContextMustGetUser(ctx) + + // add metadata via opaque + // TODO: upate cs3api: https://github.com/cs3org/cs3apis/issues/213 + o := utils.AppendPlainToOpaque(nil, "lockownername", u.GetDisplayName()) + o = utils.AppendPlainToOpaque(o, "locktime", now.Format(time.RFC3339)) + + if token == "" { + return errors.New("token is empty") + } + + r := &provider.RefreshLockRequest{ + Ref: ref, + Lock: &provider.Lock{ + Opaque: o, + Type: provider.LockType_LOCK_TYPE_EXCL, + //AppName: , // TODO use a urn scheme? + LockId: token, + User: u.GetId(), + }, + } + + client, err := cls.selector.Next() + if err != nil { + return err + } + + res, err := client.RefreshLock(ctx, r) + if err != nil { + return err + } + switch res.GetStatus().GetCode() { + case rpc.Code_CODE_OK: + return nil + + default: + return ocdavErrors.NewErrFromStatus(res.GetStatus()) + } } func (cls *cs3LS) Unlock(ctx context.Context, now time.Time, ref *provider.Reference, token string) error { @@ -287,6 +329,8 @@ type LockDetails struct { OwnerName string // Locktime is the time the lock was created Locktime time.Time + // LockID is the lock token + LockID string } func readLockInfo(r io.Reader) (li lockInfo, status int, err error) { @@ -450,7 +494,7 @@ func (s *svc) lockReference(ctx context.Context, w http.ResponseWriter, r *http. u := ctxpkg.ContextMustGetUser(ctx) token, now, created := "", time.Now(), false - ld := LockDetails{UserID: u.Id, Root: ref, Duration: duration, OwnerName: u.GetDisplayName(), Locktime: now} + ld := LockDetails{UserID: u.Id, Root: ref, Duration: duration, OwnerName: u.GetDisplayName(), Locktime: now, LockID: li.LockID} if li == (lockInfo{}) { // An empty lockInfo means to refresh the lock. ih, ok := parseIfHeader(r.Header.Get(net.HeaderIf)) @@ -463,7 +507,7 @@ func (s *svc) lockReference(ctx context.Context, w http.ResponseWriter, r *http. if token == "" { return http.StatusBadRequest, ocdavErrors.ErrInvalidLockToken } - ld, err = s.LockSystem.Refresh(ctx, now, token, duration) + err = s.LockSystem.Refresh(ctx, now, ref, token) if err != nil { if err == ocdavErrors.ErrNoSuchLock { return http.StatusPreconditionFailed, err @@ -471,6 +515,8 @@ func (s *svc) lockReference(ctx context.Context, w http.ResponseWriter, r *http. return http.StatusInternalServerError, err } + ld.LockID = token + } else { // Section 9.10.3 says that "If no Depth header is submitted on a LOCK request, // then the request MUST act as if a "Depth:infinity" had been submitted." diff --git a/vendor/github.com/cs3org/reva/v2/pkg/ocm/provider/authorizer/json/json.go b/vendor/github.com/cs3org/reva/v2/pkg/ocm/provider/authorizer/json/json.go index fc3eb0c891..c56b951c0e 100644 --- a/vendor/github.com/cs3org/reva/v2/pkg/ocm/provider/authorizer/json/json.go +++ b/vendor/github.com/cs3org/reva/v2/pkg/ocm/provider/authorizer/json/json.go @@ -24,6 +24,7 @@ import ( "net" "net/url" "os" + "regexp" "strings" "sync" @@ -130,7 +131,7 @@ func (a *authorizer) IsProviderAllowed(ctx context.Context, pi *ocmprovider.Prov var providerAuthorized bool if normalizedDomain != "" { for _, p := range a.providers { - if p.Domain == normalizedDomain { + if ok, err := regexp.MatchString(p.Domain, normalizedDomain); ok && err == nil { providerAuthorized = true break } diff --git a/vendor/github.com/cs3org/reva/v2/pkg/ocm/storage/received/ocm.go b/vendor/github.com/cs3org/reva/v2/pkg/ocm/storage/received/ocm.go index 1b595eb017..02b5d8235a 100644 --- a/vendor/github.com/cs3org/reva/v2/pkg/ocm/storage/received/ocm.go +++ b/vendor/github.com/cs3org/reva/v2/pkg/ocm/storage/received/ocm.go @@ -474,20 +474,47 @@ func (d *driver) UnsetArbitraryMetadata(ctx context.Context, ref *provider.Refer return errtypes.NotSupported("operation not supported") } +// SetLock sets a lock on a file func (d *driver) SetLock(ctx context.Context, ref *provider.Reference, lock *provider.Lock) error { - return errtypes.NotSupported("operation not supported") + client, _, rel, err := d.webdavClient(ctx, nil, ref) + if err != nil { + return err + } + + return client.Lock(rel, lock.GetLockId()) } func (d *driver) GetLock(ctx context.Context, ref *provider.Reference) (*provider.Lock, error) { - return nil, errtypes.NotSupported("operation not supported") + client, _, rel, err := d.webdavClient(ctx, nil, ref) + if err != nil { + return nil, err + } + + token, err := client.GetLock(rel) + if err != nil { + return nil, err + } + + return &provider.Lock{LockId: token, Type: provider.LockType_LOCK_TYPE_EXCL}, nil } func (d *driver) RefreshLock(ctx context.Context, ref *provider.Reference, lock *provider.Lock, existingLockID string) error { - return errtypes.NotSupported("operation not supported") + client, _, rel, err := d.webdavClient(ctx, nil, ref) + if err != nil { + return err + } + + return client.RefreshLock(rel, lock.GetLockId()) } +// Unlock removes a lock from a file func (d *driver) Unlock(ctx context.Context, ref *provider.Reference, lock *provider.Lock) error { - return errtypes.NotSupported("operation not supported") + client, _, rel, err := d.webdavClient(ctx, nil, ref) + if err != nil { + return err + } + + return client.Unlock(rel, lock.GetLockId()) } func (d *driver) ListStorageSpaces(ctx context.Context, filters []*provider.ListStorageSpacesRequest_Filter, _ bool) ([]*provider.StorageSpace, error) { diff --git a/vendor/github.com/cs3org/reva/v2/pkg/ocm/storage/received/upload.go b/vendor/github.com/cs3org/reva/v2/pkg/ocm/storage/received/upload.go index 05556ede47..046a5fcc66 100644 --- a/vendor/github.com/cs3org/reva/v2/pkg/ocm/storage/received/upload.go +++ b/vendor/github.com/cs3org/reva/v2/pkg/ocm/storage/received/upload.go @@ -106,7 +106,8 @@ func (d *driver) Upload(ctx context.Context, req storage.UploadRequest, _ storag } }) - return &provider.ResourceInfo{}, client.WriteStream(rel, req.Body, 0) + locktoken, _ := ctxpkg.ContextGetLockID(ctx) + return &provider.ResourceInfo{}, client.WriteStream(rel, req.Body, 0, locktoken) } // UseIn tells the tus upload middleware which extensions it supports. @@ -356,7 +357,7 @@ func (u *upload) FinishUpload(ctx context.Context) error { return err } defer f.Close() - return client.WriteStream(rel, f, 0) + return client.WriteStream(rel, f, 0, "") } func (u *upload) Terminate(ctx context.Context) error { diff --git a/vendor/github.com/cs3org/reva/v2/pkg/owncloud/ocs/capabilities.go b/vendor/github.com/cs3org/reva/v2/pkg/owncloud/ocs/capabilities.go index 1f4bcdab6f..8d754cd750 100644 --- a/vendor/github.com/cs3org/reva/v2/pkg/owncloud/ocs/capabilities.go +++ b/vendor/github.com/cs3org/reva/v2/pkg/owncloud/ocs/capabilities.go @@ -288,7 +288,8 @@ type CapabilitiesFilesSharingFederation struct { // CapabilitiesNotifications holds a list of notification endpoints type CapabilitiesNotifications struct { - Endpoints []string `json:"ocs-endpoints,omitempty" xml:"ocs-endpoints>element,omitempty" mapstructure:"endpoints"` + Endpoints []string `json:"ocs-endpoints,omitempty" xml:"ocs-endpoints>element,omitempty" mapstructure:"endpoints"` + Configurable bool `json:"configurable" xml:"configurable,omitempty" mapstructure:"configurable"` } // CapabilitiesTheme holds theming capabilities diff --git a/vendor/github.com/cs3org/reva/v2/pkg/rhttp/datatx/manager/simple/simple.go b/vendor/github.com/cs3org/reva/v2/pkg/rhttp/datatx/manager/simple/simple.go index 5074b3861d..5cfec2ca4e 100644 --- a/vendor/github.com/cs3org/reva/v2/pkg/rhttp/datatx/manager/simple/simple.go +++ b/vendor/github.com/cs3org/reva/v2/pkg/rhttp/datatx/manager/simple/simple.go @@ -24,6 +24,7 @@ import ( userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" + ctxpkg "github.com/cs3org/reva/v2/pkg/ctx" "github.com/mitchellh/mapstructure" "github.com/pkg/errors" "github.com/rs/zerolog" @@ -109,6 +110,10 @@ func (m *manager) Handler(fs storage.FS) (http.Handler, error) { ref := &provider.Reference{Path: fn} + if lockID := r.Header.Get("X-Lock-Id"); lockID != "" { + ctx = ctxpkg.ContextSetLockID(ctx, lockID) + } + info, err := fs.Upload(ctx, storage.UploadRequest{ Ref: ref, Body: r.Body, diff --git a/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/spaces.go b/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/spaces.go index 760ad96552..0a369a61f9 100644 --- a/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/spaces.go +++ b/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/spaces.go @@ -50,6 +50,7 @@ import ( "github.com/cs3org/reva/v2/pkg/storagespace" "github.com/cs3org/reva/v2/pkg/utils" "github.com/pkg/errors" + "github.com/shamaton/msgpack/v2" "golang.org/x/sync/errgroup" ) @@ -744,12 +745,58 @@ func (fs *Decomposedfs) DeleteStorageSpace(ctx context.Context, req *provider.De return err } - // remove space metadata - if err := os.RemoveAll(fs.getSpaceRoot(spaceID)); err != nil { + root := fs.getSpaceRoot(spaceID) + + // walkfn will delete the blob if the node has one + walkfn := func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + + if filepath.Ext(path) != ".mpk" { + return nil + } + + b, err := os.ReadFile(path) + if err != nil { + return err + } + + m := map[string][]byte{} + if err := msgpack.Unmarshal(b, &m); err != nil { + return err + } + + bid := m["user.ocis.blobid"] + if string(bid) == "" { + return nil + } + + if err := fs.tp.DeleteBlob(&node.Node{ + BlobID: string(bid), + SpaceID: spaceID, + }); err != nil { + return err + } + + // remove .mpk file so subsequent attempts will not try to delete the blob again + return os.Remove(path) + } + + // This is deletes all blobs of the space + // NOTE: This isn't needed when no s3 is used, but we can't differentiate that here... + if err := filepath.Walk(root, walkfn); err != nil { return err } - // TODO remove space blobs with s3 backend by adding a purge method to the Blobstore interface + // remove space metadata + if err := os.RemoveAll(root); err != nil { + return err + } + + // try removing the space root node + // Note that this will fail when there are other spaceids starting with the same two digits. + _ = os.Remove(filepath.Dir(root)) return nil } diff --git a/vendor/github.com/cs3org/reva/v2/pkg/storagespace/storagespace.go b/vendor/github.com/cs3org/reva/v2/pkg/storagespace/storagespace.go index 78fa52df09..e6412f4ae5 100644 --- a/vendor/github.com/cs3org/reva/v2/pkg/storagespace/storagespace.go +++ b/vendor/github.com/cs3org/reva/v2/pkg/storagespace/storagespace.go @@ -75,8 +75,8 @@ func SplitStorageID(sid string) (storageID, spaceID string) { // The result format will look like: // $! func FormatResourceID(sid *provider.ResourceId) string { - if sid.OpaqueId == "" { - return FormatStorageID(sid.StorageId, sid.SpaceId) + if sid.GetOpaqueId() == "" { + return FormatStorageID(sid.GetStorageId(), sid.GetSpaceId()) } return strings.Join([]string{FormatStorageID(sid.StorageId, sid.SpaceId), sid.OpaqueId}, _idDelimiter) } diff --git a/vendor/github.com/studio-b12/gowebdav/client.go b/vendor/github.com/studio-b12/gowebdav/client.go index 99cf24adab..7ab8e41fa4 100644 --- a/vendor/github.com/studio-b12/gowebdav/client.go +++ b/vendor/github.com/studio-b12/gowebdav/client.go @@ -3,6 +3,7 @@ package gowebdav import ( "bytes" "encoding/xml" + "errors" "fmt" "io" "net/http" @@ -159,6 +160,10 @@ func (p *propstat) Modified() time.Time { return time.Unix(0, 0) } +func (p *propstat) Lock() string { + return p.Props.GetString(xml.Name{Space: "DAV:", Local: "lockdiscovery"}) +} + func (p *propstat) StatusCode() int { parts := strings.Split(p.Status, " ") if len(parts) < 2 { @@ -380,6 +385,37 @@ func (c *Client) Copy(oldpath, newpath string, overwrite bool) error { return c.copymove("COPY", oldpath, newpath, overwrite) } +// GetLock gets a lock +func (c *Client) GetLock(path string) (string, error) { + fi, err := c.Stat(path) + if err != nil { + return "", err + } + + f, ok := fi.(File) + if !ok { + // This won't happen unless implementation is changed + return "", errors.New("Stat did not return a File") + } + + return f.Lock(), nil +} + +// Lock locks a file +func (c *Client) Lock(path string, token string) error { + return c.lock(path, token, false) +} + +// RefreshLock refreshes a lock +func (c *Client) RefreshLock(path string, token string) error { + return c.lock(path, token, true) +} + +// Unlock unlocks a file +func (c *Client) Unlock(path string, token string) error { + return c.unlock(path, token) +} + // Read reads the contents of a remote file func (c *Client) Read(path string) ([]byte, error) { var stream io.ReadCloser @@ -456,7 +492,7 @@ func (c *Client) ReadStreamRange(path string, offset, length int64) (io.ReadClos // Write writes data to a given path func (c *Client) Write(path string, data []byte, _ os.FileMode) (err error) { - s, err := c.put(path, bytes.NewReader(data)) + s, err := c.put(path, bytes.NewReader(data), "") if err != nil { return } @@ -472,7 +508,7 @@ func (c *Client) Write(path string, data []byte, _ os.FileMode) (err error) { return } - s, err = c.put(path, bytes.NewReader(data)) + s, err = c.put(path, bytes.NewReader(data), "") if err != nil { return } @@ -485,14 +521,14 @@ func (c *Client) Write(path string, data []byte, _ os.FileMode) (err error) { } // WriteStream writes a stream -func (c *Client) WriteStream(path string, stream io.Reader, _ os.FileMode) (err error) { +func (c *Client) WriteStream(path string, stream io.Reader, _ os.FileMode, locktoken string) (err error) { err = c.createParentCollection(path) if err != nil { return err } - s, err := c.put(path, stream) + s, err := c.put(path, stream, locktoken) if err != nil { return err } diff --git a/vendor/github.com/studio-b12/gowebdav/file.go b/vendor/github.com/studio-b12/gowebdav/file.go index e66d1a4ecf..15f4547343 100644 --- a/vendor/github.com/studio-b12/gowebdav/file.go +++ b/vendor/github.com/studio-b12/gowebdav/file.go @@ -31,7 +31,7 @@ func newFile(path, name string, p *propstat) *File { } path = FixSlashes(path) - f.name = name + f.name = filepath.Base(name) f.path = filepath.Clean(filepath.Join(path, f.name)) f.modified = p.Modified() f.etag = p.ETag() @@ -98,6 +98,11 @@ func (f File) Sys() interface{} { return f.propstat.Props } +// Lock returns the files lock +func (f File) Lock() string { + return f.propstat.Lock() +} + func (f File) StatusCode() int { return f.propstat.StatusCode() } diff --git a/vendor/github.com/studio-b12/gowebdav/requests.go b/vendor/github.com/studio-b12/gowebdav/requests.go index 8e362e8664..26d6038b0d 100644 --- a/vendor/github.com/studio-b12/gowebdav/requests.go +++ b/vendor/github.com/studio-b12/gowebdav/requests.go @@ -105,6 +105,63 @@ func (c *Client) propfind(path string, self bool, body string, resp interface{}, return parseXML(rs.Body, resp, parse) } +func (c *Client) lock(path string, token string, refresh bool) error { + var body io.Reader + if !refresh { + b := strings.Builder{} + + // build the lockinfo xml + b.WriteString("") + // always use an exclusive lock + b.WriteString("") + // always use a write lock + b.WriteString("") + // add the lock token + b.WriteString("") + b.WriteString(token) + b.WriteString("") + // close the lockinfo xml + b.WriteString("") + + body = strings.NewReader(b.String()) + } + + rs, err := c.req("LOCK", path, body, func(rq *http.Request) { + rq.Header.Add("Content-Type", "application/xml;charset=UTF-8") + rq.Header.Add("Accept", "application/xml,text/xml") + rq.Header.Add("Accept-Charset", "utf-8") + rq.Header.Add("Accept-Encoding", "") + if refresh { + rq.Header.Add("If", "("+token+")") + } + }) + if err != nil { + return err + } + defer rs.Body.Close() + + if rs.StatusCode != 200 { + return NewPathError("LOCK", path, rs.StatusCode) + } + + return nil +} + +func (c *Client) unlock(path string, token string) error { + rs, err := c.req("UNLOCK", path, nil, func(rq *http.Request) { + rq.Header.Add("Lock-Token", "<"+token+">") + }) + if err != nil { + return err + } + defer rs.Body.Close() + + if rs.StatusCode != http.StatusNoContent { + return NewPathError("UNLOCK", path, rs.StatusCode) + } + return nil +} + func (c *Client) doCopyMove( method string, oldpath string, @@ -160,8 +217,10 @@ func (c *Client) copymove(method string, oldpath string, newpath string, overwri return NewPathError(method, oldpath, s) } -func (c *Client) put(path string, stream io.Reader) (status int, err error) { - rs, err := c.req("PUT", path, stream, nil) +func (c *Client) put(path string, stream io.Reader, locktoken string) (status int, err error) { + rs, err := c.req("PUT", path, stream, func(rq *http.Request) { + rq.Header.Add("Lock-Token", "<"+locktoken+">") + }) if err != nil { return } diff --git a/vendor/modules.txt b/vendor/modules.txt index d0941674f9..1b015a15e6 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -367,7 +367,7 @@ github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1 github.com/cs3org/go-cs3apis/cs3/storage/registry/v1beta1 github.com/cs3org/go-cs3apis/cs3/tx/v1beta1 github.com/cs3org/go-cs3apis/cs3/types/v1beta1 -# github.com/cs3org/reva/v2 v2.27.0 +# github.com/cs3org/reva/v2 v2.27.1-0.20250107124628-bd76241aa776 ## explicit; go 1.22.0 github.com/cs3org/reva/v2/cmd/revad/internal/grace github.com/cs3org/reva/v2/cmd/revad/runtime @@ -1813,7 +1813,7 @@ github.com/stretchr/testify/assert github.com/stretchr/testify/assert/yaml github.com/stretchr/testify/mock github.com/stretchr/testify/require -# github.com/studio-b12/gowebdav v0.9.0 => github.com/aduffeck/gowebdav v0.0.0-20231215102054-212d4a4374f6 +# github.com/studio-b12/gowebdav v0.9.0 => github.com/kobergj/gowebdav v0.0.0-20250102091030-aa65266db202 ## explicit; go 1.17 github.com/studio-b12/gowebdav # github.com/tchap/go-patricia/v2 v2.3.1 @@ -2398,7 +2398,7 @@ sigs.k8s.io/yaml/goyaml.v2 # stash.kopano.io/kgol/rndm v1.1.2 ## explicit; go 1.13 stash.kopano.io/kgol/rndm -# github.com/studio-b12/gowebdav => github.com/aduffeck/gowebdav v0.0.0-20231215102054-212d4a4374f6 +# github.com/studio-b12/gowebdav => github.com/kobergj/gowebdav v0.0.0-20250102091030-aa65266db202 # github.com/egirna/icap-client => github.com/fschade/icap-client v0.0.0-20240802074440-aade4a234387 # github.com/unrolled/secure => github.com/DeepDiver1975/secure v0.0.0-20240611112133-abc838fb797c # github.com/go-micro/plugins/v4/store/nats-js-kv => github.com/kobergj/plugins/v4/store/nats-js-kv v0.0.0-20240807130109-f62bb67e8c90