Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

allow setting favorites, mtime and a temporary etag #1393

Merged
merged 5 commits into from
Jan 14, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions changelog/unreleased/ocis-favorites-etags-mtime-metadata.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Enhancement: allow setting favorites, mtime and a temporary etag

We now let the ocis driver persist favorites, set temporary etags and the mtime as arbitrary metadata.

https://github.com/cs3org/reva/pull/1393
https://github.com/owncloud/ocis/issues/567
https://github.com/cs3org/reva/issues/1394
69 changes: 53 additions & 16 deletions internal/http/services/owncloud/ocdav/propfind.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,14 @@ import (
"github.com/pkg/errors"
)

const (
_nsDav = "DAV:"
_nsOwncloud = "http://owncloud.org/ns"
_nsOCS = "http://open-collaboration-services.org/ns"

_propOcFavorite = "http://owncloud.org/ns/favorite"
)

// ns is the namespace that is prefixed to the path in the cs3 namespace
func (s *svc) handlePropfind(w http.ResponseWriter, r *http.Request, ns string) {
ctx := r.Context()
Expand Down Expand Up @@ -98,14 +106,23 @@ func (s *svc) handlePropfind(w http.ResponseWriter, r *http.Request, ns string)
return
}

metadataKeys := []string{}
if pf.Allprop != nil {
metadataKeys = append(metadataKeys, "*")
} else {
for i := range pf.Prop {
if requiresExplicitFetching(&pf.Prop[i]) {
metadataKeys = append(metadataKeys, metadataKeyOf(&pf.Prop[i]))
}
}
}

info := res.Info
infos := []*provider.ResourceInfo{info}
if info.Type == provider.ResourceType_RESOURCE_TYPE_CONTAINER && depth == "1" {
req := &provider.ListContainerRequest{
Ref: ref,
ArbitraryMetadataKeys: []string{
"http://owncloud.org/ns/share-types",
},
Ref: ref,
ArbitraryMetadataKeys: metadataKeys,
}
res, err := client.ListContainer(ctx, req)
if err != nil {
Expand All @@ -130,10 +147,8 @@ func (s *svc) handlePropfind(w http.ResponseWriter, r *http.Request, ns string)
Spec: &provider.Reference_Path{Path: path},
}
req := &provider.ListContainerRequest{
Ref: ref,
ArbitraryMetadataKeys: []string{
"http://owncloud.org/ns/share-types",
},
Ref: ref,
ArbitraryMetadataKeys: metadataKeys,
}
res, err := client.ListContainer(ctx, req)
if err != nil {
Expand Down Expand Up @@ -188,6 +203,23 @@ func (s *svc) handlePropfind(w http.ResponseWriter, r *http.Request, ns string)
}
}

func requiresExplicitFetching(n *xml.Name) bool {
switch n.Space {
case _nsDav:
return false
case _nsOwncloud:
switch n.Local {
case "favorite", "share-types":
return true
default:
return false
}
case _nsOCS:
return false
}
return true
}

// from https://github.com/golang/net/blob/e514e69ffb8bc3c76a71ae40de0118d794855992/webdav/xml.go#L178-L205
func readPropfind(r io.Reader) (pf propfindXML, status int, err error) {
c := countingReader{r: r}
Expand Down Expand Up @@ -253,6 +285,7 @@ func (s *svc) newPropNS(namespace string, local string, val string) *propertyXML
}
}

// TODO properly use the space
func (s *svc) newProp(key, val string) *propertyXML {
return &propertyXML{
XMLName: xml.Name{Space: "", Local: key},
Expand Down Expand Up @@ -368,8 +401,8 @@ func (s *svc) mdToPropResponse(ctx context.Context, pf *propfindXML, md *provide
response.Propstat[0].Prop = append(response.Propstat[0].Prop, s.newProp("oc:favorite", "0"))
} else if amd := k.GetMetadata(); amd == nil {
response.Propstat[0].Prop = append(response.Propstat[0].Prop, s.newProp("oc:favorite", "0"))
} else if v, ok := amd["http://owncloud.org/ns/favorite"]; ok && v != "" {
response.Propstat[0].Prop = append(response.Propstat[0].Prop, s.newProp("oc:favorite", "1"))
} else if v, ok := amd[_propOcFavorite]; ok && v != "" {
response.Propstat[0].Prop = append(response.Propstat[0].Prop, s.newProp("oc:favorite", v))
} else {
response.Propstat[0].Prop = append(response.Propstat[0].Prop, s.newProp("oc:favorite", "0"))
}
Expand All @@ -387,7 +420,7 @@ func (s *svc) mdToPropResponse(ctx context.Context, pf *propfindXML, md *provide
size := fmt.Sprintf("%d", md.Size)
for i := range pf.Prop {
switch pf.Prop[i].Space {
case "http://owncloud.org/ns":
case _nsOwncloud:
switch pf.Prop[i].Local {
// TODO(jfd): maybe phoenix and the other clients can just use this id as an opaque string?
// I tested the desktop client and phoenix to annotate which properties are requestted, see below cases
Expand Down Expand Up @@ -491,7 +524,7 @@ func (s *svc) mdToPropResponse(ctx context.Context, pf *propfindXML, md *provide
propstatOK.Prop = append(propstatOK.Prop, s.newProp("oc:favorite", "0"))
} else if amd := k.GetMetadata(); amd == nil {
propstatOK.Prop = append(propstatOK.Prop, s.newProp("oc:favorite", "0"))
} else if v, ok := amd["http://owncloud.org/ns/favorite"]; ok && v != "" {
} else if v, ok := amd[_propOcFavorite]; ok && v != "" {
propstatOK.Prop = append(propstatOK.Prop, s.newProp("oc:favorite", "1"))
} else {
propstatOK.Prop = append(propstatOK.Prop, s.newProp("oc:favorite", "0"))
Expand All @@ -511,7 +544,7 @@ func (s *svc) mdToPropResponse(ctx context.Context, pf *propfindXML, md *provide
case "share-types": // desktop
k := md.GetArbitraryMetadata()
amd := k.GetMetadata()
if amdv, ok := amd[fmt.Sprintf("%s/%s", pf.Prop[i].Space, pf.Prop[i].Local)]; ok {
if amdv, ok := amd[metadataKeyOf(&pf.Prop[i])]; ok {
st := fmt.Sprintf("<oc:share-type>%s</oc:share-type>", amdv)
propstatOK.Prop = append(propstatOK.Prop, s.newProp("oc:share-types", st))
} else {
Expand Down Expand Up @@ -546,7 +579,7 @@ func (s *svc) mdToPropResponse(ctx context.Context, pf *propfindXML, md *provide
default:
propstatNotFound.Prop = append(propstatNotFound.Prop, s.newProp("oc:"+pf.Prop[i].Local, ""))
}
case "DAV:":
case _nsDav:
switch pf.Prop[i].Local {
case "getetag": // both
if md.Etag != "" {
Expand Down Expand Up @@ -586,7 +619,7 @@ func (s *svc) mdToPropResponse(ctx context.Context, pf *propfindXML, md *provide
default:
propstatNotFound.Prop = append(propstatNotFound.Prop, s.newProp("d:"+pf.Prop[i].Local, ""))
}
case "http://open-collaboration-services.org/ns":
case _nsOCS:
switch pf.Prop[i].Local {
// ocs:share-permissions indicate clients the maximum permissions that can be granted:
// 1 = read
Expand Down Expand Up @@ -614,7 +647,7 @@ func (s *svc) mdToPropResponse(ctx context.Context, pf *propfindXML, md *provide
propstatNotFound.Prop = append(propstatNotFound.Prop, s.newPropNS(pf.Prop[i].Space, pf.Prop[i].Local, ""))
} else if amd := k.GetMetadata(); amd == nil {
propstatNotFound.Prop = append(propstatNotFound.Prop, s.newPropNS(pf.Prop[i].Space, pf.Prop[i].Local, ""))
} else if v, ok := amd[fmt.Sprintf("%s/%s", pf.Prop[i].Space, pf.Prop[i].Local)]; ok && v != "" {
} else if v, ok := amd[metadataKeyOf(&pf.Prop[i])]; ok && v != "" {
propstatOK.Prop = append(propstatOK.Prop, s.newPropNS(pf.Prop[i].Space, pf.Prop[i].Local, v))
} else {
propstatNotFound.Prop = append(propstatNotFound.Prop, s.newPropNS(pf.Prop[i].Space, pf.Prop[i].Local, ""))
Expand Down Expand Up @@ -654,6 +687,10 @@ func (c *countingReader) Read(p []byte) (int, error) {
return n, err
}

func metadataKeyOf(n *xml.Name) string {
return fmt.Sprintf("%s/%s", n.Space, n.Local)
}

// http://www.webdav.org/specs/rfc4918.html#ELEMENT_prop (for propfind)
type propfindProps []xml.Name

Expand Down
6 changes: 3 additions & 3 deletions internal/http/services/owncloud/ocdav/proppatch.go
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ func (s *svc) formatProppatchResponse(ctx context.Context, acceptedProps []xml.N

func (s *svc) isBooleanProperty(prop string) bool {
// TODO add other properties we know to be boolean?
return prop == "http://owncloud.org/ns/favorite"
return prop == _propOcFavorite
}

func (s *svc) as0or1(val string) string {
Expand Down Expand Up @@ -307,9 +307,9 @@ func readProppatch(r io.Reader) (patches []Proppatch, status int, err error) {
for _, op := range pu.SetRemove {
remove := false
switch op.XMLName {
case xml.Name{Space: "DAV:", Local: "set"}:
case xml.Name{Space: _nsDav, Local: "set"}:
// No-op.
case xml.Name{Space: "DAV:", Local: "remove"}:
case xml.Name{Space: _nsDav, Local: "remove"}:
for _, p := range op.Prop {
if len(p.InnerXML) > 0 {
return nil, http.StatusBadRequest, errInvalidProppatch
Expand Down
4 changes: 2 additions & 2 deletions internal/http/services/owncloud/ocdav/trashbin.go
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,7 @@ func (h *TrashbinHandler) itemToPropResponse(ctx context.Context, s *svc, pf *pr
size := fmt.Sprintf("%d", item.Size)
for i := range pf.Prop {
switch pf.Prop[i].Space {
case "http://owncloud.org/ns":
case _nsOwncloud:
switch pf.Prop[i].Local {
case "oc:size":
if item.Type == provider.ResourceType_RESOURCE_TYPE_CONTAINER {
Expand All @@ -314,7 +314,7 @@ func (h *TrashbinHandler) itemToPropResponse(ctx context.Context, s *svc, pf *pr
default:
propstatNotFound.Prop = append(propstatNotFound.Prop, s.newProp("oc:"+pf.Prop[i].Local, ""))
}
case "DAV:":
case _nsDav:
switch pf.Prop[i].Local {
case "getcontentlength":
if item.Type == provider.ResourceType_RESOURCE_TYPE_CONTAINER {
Expand Down
1 change: 1 addition & 0 deletions internal/http/services/owncloud/ocs/data/capabilities.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ type CapabilitiesFiles struct {
BigFileChunking ocsBool `json:"bigfilechunking" xml:"bigfilechunking"`
Undelete ocsBool `json:"undelete" xml:"undelete"`
Versioning ocsBool `json:"versioning" xml:"versioning"`
Favorites ocsBool `json:"favorites" xml:"favorites"`
BlacklistedFiles []string `json:"blacklisted_files" xml:"blacklisted_files>element" mapstructure:"blacklisted_files"`
TusSupport *CapabilitiesFilesTusSupport `json:"tus_support" xml:"tus_support" mapstructure:"tus_support"`
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ func (h *Handler) Init(c *config.Config) {
}
// h.c.Capabilities.Files.Undelete is boolean
// h.c.Capabilities.Files.Versioning is boolean
// h.c.Capabilities.Files.Favorites is boolean

// dav

Expand Down
Loading