Skip to content

Commit

Permalink
request unknown properties as arbitrary metadata
Browse files Browse the repository at this point in the history
Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>
  • Loading branch information
butonic committed Jan 11, 2021
1 parent 733d40a commit 2ab71a9
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 16 deletions.
60 changes: 51 additions & 9 deletions internal/http/services/owncloud/ocdav/propfind.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,15 @@ 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"
_propOcShareTypes = "http://owncloud.org/ns/share-types"
)

// 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,13 +107,24 @@ 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",
_propOcShareTypes,
},
}
res, err := client.ListContainer(ctx, req)
Expand Down Expand Up @@ -132,7 +152,7 @@ func (s *svc) handlePropfind(w http.ResponseWriter, r *http.Request, ns string)
req := &provider.ListContainerRequest{
Ref: ref,
ArbitraryMetadataKeys: []string{
"http://owncloud.org/ns/share-types",
_propOcShareTypes,
},
}
res, err := client.ListContainer(ctx, req)
Expand Down Expand Up @@ -188,6 +208,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 +290,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,7 +406,7 @@ 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 != "" {
} else if v, ok := amd[_propOcFavorite]; ok && v != "" {
response.Propstat[0].Prop = append(response.Propstat[0].Prop, s.newProp("oc:favorite", "1"))
} else {
response.Propstat[0].Prop = append(response.Propstat[0].Prop, s.newProp("oc:favorite", "0"))
Expand All @@ -387,7 +425,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 +529,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 +549,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 +584,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 +624,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 +652,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 +692,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
5 changes: 3 additions & 2 deletions pkg/storage/fs/ocis/ocis.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,9 @@ const (
// grantPrefix is the prefix for sharing related extended attributes
grantPrefix string = ocisPrefix + "grant."
metadataPrefix string = ocisPrefix + "md."
// TODO implement favorites metadata flag
favPrefix string = ocisPrefix + "fav." // favorite flag, per user

// favorite flag, per user
favPrefix string = ocisPrefix + "fav."

// a temporary etag for a folder that is removed when the mtime propagation happens
tmpEtagAttr string = ocisPrefix + "tmp.etag"
Expand Down

0 comments on commit 2ab71a9

Please sign in to comment.