From 9c8dd928711f32a879369e233e693c6f1f57bb62 Mon Sep 17 00:00:00 2001 From: David Christofas Date: Mon, 21 Mar 2022 18:27:18 +0100 Subject: [PATCH 1/6] use different props method for static values When putting static values in propstat properties we can skip the xml encoding when we know that there are no reserved characters. Same for 'not found' properties. --- .../owncloud/ocdav/propfind/propfind.go | 88 +++++++++---------- .../services/owncloud/ocdav/props/props.go | 16 ++++ .../http/services/owncloud/ocdav/trashbin.go | 24 ++--- .../owncloud/ocs/conversions/permissions.go | 5 ++ 4 files changed, 77 insertions(+), 56 deletions(-) diff --git a/internal/http/services/owncloud/ocdav/propfind/propfind.go b/internal/http/services/owncloud/ocdav/propfind/propfind.go index e109c08a83..0dd78cb02f 100644 --- a/internal/http/services/owncloud/ocdav/propfind/propfind.go +++ b/internal/http/services/owncloud/ocdav/propfind/propfind.go @@ -819,13 +819,13 @@ func mdToPropResponse(ctx context.Context, pf *XML, md *provider.ResourceInfo, p if ls == nil { // favorites from arbitrary metadata if k := md.GetArbitraryMetadata(); k == nil { - propstatOK.Prop = append(propstatOK.Prop, props.NewProp("oc:favorite", "0")) + propstatOK.Prop = append(propstatOK.Prop, props.NewPropRaw("oc:favorite", "0")) } else if amd := k.GetMetadata(); amd == nil { - propstatOK.Prop = append(propstatOK.Prop, props.NewProp("oc:favorite", "0")) + propstatOK.Prop = append(propstatOK.Prop, props.NewPropRaw("oc:favorite", "0")) } else if v, ok := amd[net.PropOcFavorite]; ok && v != "" { propstatOK.Prop = append(propstatOK.Prop, props.NewProp("oc:favorite", v)) } else { - propstatOK.Prop = append(propstatOK.Prop, props.NewProp("oc:favorite", "0")) + propstatOK.Prop = append(propstatOK.Prop, props.NewPropRaw("oc:favorite", "0")) } } @@ -845,13 +845,13 @@ func mdToPropResponse(ctx context.Context, pf *XML, md *provider.ResourceInfo, p if md.Id != nil { propstatOK.Prop = append(propstatOK.Prop, props.NewProp("oc:fileid", resourceid.OwnCloudResourceIDWrap(md.Id))) } else { - propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewProp("oc:fileid", "")) + propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewNotFound("oc:fileid")) } case "id": // desktop client only if md.Id != nil { propstatOK.Prop = append(propstatOK.Prop, props.NewProp("oc:id", resourceid.OwnCloudResourceIDWrap(md.Id))) } else { - propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewProp("oc:id", "")) + propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewNotFound("oc:id")) } case "spaceid": if md.Id != nil { @@ -874,20 +874,20 @@ func mdToPropResponse(ctx context.Context, pf *XML, md *provider.ResourceInfo, p if ls != nil && md.PermissionSet != nil { propstatOK.Prop = append( propstatOK.Prop, - props.NewProp("oc:public-link-permission", strconv.FormatUint(uint64(role.OCSPermissions()), 10))) + props.NewProp("oc:public-link-permission", role.OCSPermissions().String())) } else { - propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewProp("oc:public-link-permission", "")) + propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewNotFound("oc:public-link-permission")) } case "public-link-item-type": // only on a share root node if ls != nil { if md.Type == provider.ResourceType_RESOURCE_TYPE_CONTAINER { - propstatOK.Prop = append(propstatOK.Prop, props.NewProp("oc:public-link-item-type", "folder")) + propstatOK.Prop = append(propstatOK.Prop, props.NewPropRaw("oc:public-link-item-type", "folder")) } else { - propstatOK.Prop = append(propstatOK.Prop, props.NewProp("oc:public-link-item-type", "file")) + propstatOK.Prop = append(propstatOK.Prop, props.NewPropRaw("oc:public-link-item-type", "file")) // redirectref is another option } } else { - propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewProp("oc:public-link-item-type", "")) + propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewNotFound("oc:public-link-item-type")) } case "public-link-share-datetime": if ls != nil && ls.Mtime != nil { @@ -895,7 +895,7 @@ func mdToPropResponse(ctx context.Context, pf *XML, md *provider.ResourceInfo, p shareTimeString := t.Format(net.RFC1123) propstatOK.Prop = append(propstatOK.Prop, props.NewProp("oc:public-link-share-datetime", shareTimeString)) } else { - propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewProp("oc:public-link-share-datetime", "")) + propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewNotFound("oc:public-link-share-datetime")) } case "public-link-share-owner": if ls != nil && ls.Owner != nil { @@ -905,10 +905,10 @@ func mdToPropResponse(ctx context.Context, pf *XML, md *provider.ResourceInfo, p } else { u, _ := ctxpkg.ContextGetUser(ctx) sublog.Error().Interface("share", ls).Interface("user", u).Msg("the current user in the context should be the owner of a public link share") - propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewProp("oc:public-link-share-owner", "")) + propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewNotFound("oc:public-link-share-owner")) } } else { - propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewProp("oc:public-link-share-owner", "")) + propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewNotFound("oc:public-link-share-owner")) } case "public-link-expiration": if ls != nil && ls.Expiration != nil { @@ -916,9 +916,9 @@ func mdToPropResponse(ctx context.Context, pf *XML, md *provider.ResourceInfo, p expireTimeString := t.Format(net.RFC1123) propstatOK.Prop = append(propstatOK.Prop, props.NewProp("oc:public-link-expiration", expireTimeString)) } else { - propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewProp("oc:public-link-expiration", "")) + propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewNotFound("oc:public-link-expiration")) } - propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewProp("oc:public-link-expiration", "")) + propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewNotFound("oc:public-link-expiration")) case "size": // phoenix only // TODO we cannot find out if md.Size is set or not because ints in go default to 0 // TODO what is the difference to d:quota-used-bytes (which only exists for collections)? @@ -928,7 +928,7 @@ func mdToPropResponse(ctx context.Context, pf *XML, md *provider.ResourceInfo, p propstatOK.Prop = append(propstatOK.Prop, props.NewProp("oc:size", size)) } else { // link share root collection has no size - propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewProp("oc:size", "")) + propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewNotFound("oc:size")) } case "owner-id": // phoenix only if md.Owner != nil { @@ -937,10 +937,10 @@ func mdToPropResponse(ctx context.Context, pf *XML, md *provider.ResourceInfo, p propstatOK.Prop = append(propstatOK.Prop, props.NewProp("oc:owner-id", u.Username)) } else { sublog.Debug().Msg("TODO fetch user username") - propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewProp("oc:owner-id", "")) + propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewNotFound("oc:owner-id")) } } else { - propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewProp("oc:owner-id", "")) + propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewNotFound("oc:owner-id")) } case "favorite": // phoenix only // TODO: can be 0 or 1?, in oc10 it is present or not @@ -948,17 +948,17 @@ func mdToPropResponse(ctx context.Context, pf *XML, md *provider.ResourceInfo, p // TODO: this boolean favorite property is so horribly wrong ... either it is presont, or it is not ... unless ... it is possible to have a non binary value ... we need to double check if ls == nil { if k := md.GetArbitraryMetadata(); k == nil { - propstatOK.Prop = append(propstatOK.Prop, props.NewProp("oc:favorite", "0")) + propstatOK.Prop = append(propstatOK.Prop, props.NewPropRaw("oc:favorite", "0")) } else if amd := k.GetMetadata(); amd == nil { - propstatOK.Prop = append(propstatOK.Prop, props.NewProp("oc:favorite", "0")) + propstatOK.Prop = append(propstatOK.Prop, props.NewPropRaw("oc:favorite", "0")) } else if v, ok := amd[net.PropOcFavorite]; ok && v != "" { - propstatOK.Prop = append(propstatOK.Prop, props.NewProp("oc:favorite", "1")) + propstatOK.Prop = append(propstatOK.Prop, props.NewPropRaw("oc:favorite", "1")) } else { - propstatOK.Prop = append(propstatOK.Prop, props.NewProp("oc:favorite", "0")) + propstatOK.Prop = append(propstatOK.Prop, props.NewPropRaw("oc:favorite", "0")) } } else { // link share root collection has no favorite - propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewProp("oc:favorite", "")) + propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewNotFound("oc:favorite")) } case "checksums": // desktop ... not really ... the desktop sends the OC-Checksum header @@ -992,7 +992,7 @@ func mdToPropResponse(ctx context.Context, pf *XML, md *provider.ResourceInfo, p checksums.WriteString("") propstatOK.Prop = append(propstatOK.Prop, props.NewPropRaw("oc:checksums", checksums.String())) } else { - propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewProp("oc:checksums", "")) + propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewNotFound("oc:checksums")) } case "share-types": // desktop var types strings.Builder @@ -1013,7 +1013,7 @@ func mdToPropResponse(ctx context.Context, pf *XML, md *provider.ResourceInfo, p if types.Len() != 0 { propstatOK.Prop = append(propstatOK.Prop, props.NewPropRaw("oc:share-types", types.String())) } else { - propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewProp("oc:"+pf.Prop[i].Local, "")) + propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewNotFound("oc:"+pf.Prop[i].Local)) } case "owner-display-name": // phoenix only if md.Owner != nil { @@ -1022,10 +1022,10 @@ func mdToPropResponse(ctx context.Context, pf *XML, md *provider.ResourceInfo, p propstatOK.Prop = append(propstatOK.Prop, props.NewProp("oc:owner-display-name", u.DisplayName)) } else { sublog.Debug().Msg("TODO fetch user displayname") - propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewProp("oc:owner-display-name", "")) + propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewNotFound("oc:owner-display-name")) } } else { - propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewProp("oc:owner-display-name", "")) + propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewNotFound("oc:owner-display-name")) } case "downloadURL": // desktop if isPublic && md.Type == provider.ResourceType_RESOURCE_TYPE_FILE { @@ -1046,7 +1046,7 @@ func mdToPropResponse(ctx context.Context, pf *XML, md *provider.ResourceInfo, p } propstatOK.Prop = append(propstatOK.Prop, props.NewProp("oc:downloadURL", publicURL+baseURI+path)) } else { - propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewProp("oc:"+pf.Prop[i].Local, "")) + propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewNotFound("oc:"+pf.Prop[i].Local)) } case "signature-auth": if isPublic { @@ -1063,7 +1063,7 @@ func mdToPropResponse(ctx context.Context, pf *XML, md *provider.ResourceInfo, p propstatOK.Prop = append(propstatOK.Prop, props.NewPropRaw("oc:signature-auth", sb.String())) } else { - propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewProp("oc:signature-auth", "")) + propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewNotFound("oc:signature-auth")) } } case "privatelink": // phoenix only @@ -1079,7 +1079,7 @@ func mdToPropResponse(ctx context.Context, pf *XML, md *provider.ResourceInfo, p // TODO(jfd): double check the client behavior with reva on backup restore fallthrough default: - propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewProp("oc:"+pf.Prop[i].Local, "")) + propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewNotFound("oc:"+pf.Prop[i].Local)) } case net.NsDav: switch pf.Prop[i].Local { @@ -1087,7 +1087,7 @@ func mdToPropResponse(ctx context.Context, pf *XML, md *provider.ResourceInfo, p if md.Etag != "" { propstatOK.Prop = append(propstatOK.Prop, props.NewProp("d:getetag", quoteEtag(md.Etag))) } else { - propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewProp("d:getetag", "")) + propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewNotFound("d:getetag")) } case "getcontentlength": // both // see everts stance on this https://stackoverflow.com/a/31621912, he points to http://tools.ietf.org/html/rfc4918#section-15.3 @@ -1096,7 +1096,7 @@ func mdToPropResponse(ctx context.Context, pf *XML, md *provider.ResourceInfo, p // which is not the case ... so we don't return it on collections. owncloud has oc:size for that // TODO we cannot find out if md.Size is set or not because ints in go default to 0 if md.Type == provider.ResourceType_RESOURCE_TYPE_CONTAINER { - propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewProp("d:getcontentlength", "")) + propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewNotFound("d:getcontentlength")) } else { propstatOK.Prop = append(propstatOK.Prop, props.NewProp("d:getcontentlength", size)) } @@ -1104,13 +1104,13 @@ func mdToPropResponse(ctx context.Context, pf *XML, md *provider.ResourceInfo, p if md.Type == provider.ResourceType_RESOURCE_TYPE_CONTAINER { propstatOK.Prop = append(propstatOK.Prop, props.NewPropRaw("d:resourcetype", "")) } else { - propstatOK.Prop = append(propstatOK.Prop, props.NewProp("d:resourcetype", "")) + propstatOK.Prop = append(propstatOK.Prop, props.NewPropRaw("d:resourcetype", "")) // redirectref is another option } case "getcontenttype": // phoenix if md.Type == provider.ResourceType_RESOURCE_TYPE_CONTAINER { // directories have no contenttype - propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewProp("d:getcontenttype", "")) + propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewNotFound("d:getcontenttype")) } else if md.MimeType != "" { propstatOK.Prop = append(propstatOK.Prop, props.NewProp("d:getcontenttype", md.MimeType)) } @@ -1121,7 +1121,7 @@ func mdToPropResponse(ctx context.Context, pf *XML, md *provider.ResourceInfo, p lastModifiedString := t.Format(net.RFC1123) propstatOK.Prop = append(propstatOK.Prop, props.NewProp("d:getlastmodified", lastModifiedString)) } else { - propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewProp("d:getlastmodified", "")) + propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewNotFound("d:getlastmodified")) } case "quota-used-bytes": // RFC 4331 if md.Type == provider.ResourceType_RESOURCE_TYPE_CONTAINER { @@ -1130,23 +1130,23 @@ func mdToPropResponse(ctx context.Context, pf *XML, md *provider.ResourceInfo, p // in ocis we plan to always mak the quota a property of the storage space propstatOK.Prop = append(propstatOK.Prop, props.NewProp("d:quota-used-bytes", size)) } else { - propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewProp("d:quota-used-bytes", "")) + propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewNotFound("d:quota-used-bytes")) } case "quota-available-bytes": // RFC 4331 if md.Type == provider.ResourceType_RESOURCE_TYPE_CONTAINER { // oc10 returns -3 for unlimited, -2 for unknown, -1 for uncalculated propstatOK.Prop = append(propstatOK.Prop, props.NewProp("d:quota-available-bytes", quota)) } else { - propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewProp("d:quota-available-bytes", "")) + propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewNotFound("d:quota-available-bytes")) } case "lockdiscovery": // http://www.webdav.org/specs/rfc2518.html#PROPERTY_lockdiscovery if lock == nil { - propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewProp("d:lockdiscovery", "")) + propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewNotFound("d:lockdiscovery")) } else { propstatOK.Prop = append(propstatOK.Prop, props.NewPropRaw("d:lockdiscovery", activeLocks(&sublog, lock))) } default: - propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewProp("d:"+pf.Prop[i].Local, "")) + propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewNotFound("d:"+pf.Prop[i].Local)) } case net.NsOCS: switch pf.Prop[i].Local { @@ -1165,21 +1165,21 @@ func mdToPropResponse(ctx context.Context, pf *XML, md *provider.ResourceInfo, p perms &^= conversions.PermissionCreate perms &^= conversions.PermissionDelete } - propstatOK.Prop = append(propstatOK.Prop, props.NewPropNS(pf.Prop[i].Space, pf.Prop[i].Local, strconv.FormatUint(uint64(perms), 10))) + propstatOK.Prop = append(propstatOK.Prop, props.NewPropNS(pf.Prop[i].Space, pf.Prop[i].Local, perms.String())) } default: - propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewProp("d:"+pf.Prop[i].Local, "")) + propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewNotFound("d:"+pf.Prop[i].Local)) } default: // handle custom properties if k := md.GetArbitraryMetadata(); k == nil { - propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewPropNS(pf.Prop[i].Space, pf.Prop[i].Local, "")) + propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewNotFoundNS(pf.Prop[i].Space, pf.Prop[i].Local)) } else if amd := k.GetMetadata(); amd == nil { - propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewPropNS(pf.Prop[i].Space, pf.Prop[i].Local, "")) + propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewNotFoundNS(pf.Prop[i].Space, pf.Prop[i].Local)) } else if v, ok := amd[metadataKeyOf(&pf.Prop[i])]; ok && v != "" { propstatOK.Prop = append(propstatOK.Prop, props.NewPropNS(pf.Prop[i].Space, pf.Prop[i].Local, v)) } else { - propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewPropNS(pf.Prop[i].Space, pf.Prop[i].Local, "")) + propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewNotFoundNS(pf.Prop[i].Space, pf.Prop[i].Local)) } } } diff --git a/internal/http/services/owncloud/ocdav/props/props.go b/internal/http/services/owncloud/ocdav/props/props.go index f3065e357c..822ae58ec3 100644 --- a/internal/http/services/owncloud/ocdav/props/props.go +++ b/internal/http/services/owncloud/ocdav/props/props.go @@ -68,6 +68,22 @@ func NewProp(key, val string) *PropertyXML { } } +// NewNotFound returns a new PropertyXML instance with an empty value +func NewNotFound(key string) *PropertyXML { + return &PropertyXML{ + XMLName: xml.Name{Space: "", Local: key}, + Lang: "", + } +} + +// NewNotFound returns a new PropertyXML instance with the given namespace and an empty value +func NewNotFoundNS(namespace, key string) *PropertyXML { + return &PropertyXML{ + XMLName: xml.Name{Space: namespace, Local: key}, + Lang: "", + } +} + // NewPropRaw returns a new PropertyXML instance for the given key/value pair // TODO properly use the space func NewPropRaw(key, val string) *PropertyXML { diff --git a/internal/http/services/owncloud/ocdav/trashbin.go b/internal/http/services/owncloud/ocdav/trashbin.go index b94ee5f0d7..0f1640a014 100644 --- a/internal/http/services/owncloud/ocdav/trashbin.go +++ b/internal/http/services/owncloud/ocdav/trashbin.go @@ -306,10 +306,10 @@ func (h *TrashbinHandler) formatTrashPropfind(ctx context.Context, s *svc, space { Status: "HTTP/1.1 404 Not Found", Prop: []*props.PropertyXML{ - props.NewProp("oc:trashbin-original-filename", ""), - props.NewProp("oc:trashbin-original-location", ""), - props.NewProp("oc:trashbin-delete-datetime", ""), - props.NewProp("d:getcontentlength", ""), + props.NewNotFound("oc:trashbin-original-filename"), + props.NewNotFound("oc:trashbin-original-location"), + props.NewNotFound("oc:trashbin-delete-datetime"), + props.NewNotFound("d:getcontentlength"), }, }, }, @@ -398,7 +398,7 @@ func (h *TrashbinHandler) itemToPropResponse(ctx context.Context, s *svc, spaceI if item.Type == provider.ResourceType_RESOURCE_TYPE_CONTAINER { propstatOK.Prop = append(propstatOK.Prop, props.NewProp("d:getcontentlength", size)) } else { - propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewProp("oc:size", "")) + propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewNotFound("oc:size")) } case "trashbin-original-filename": // yes this is redundant, can be derived from oc:trashbin-original-location which contains the full path, clients should not fetch it @@ -413,13 +413,13 @@ func (h *TrashbinHandler) itemToPropResponse(ctx context.Context, s *svc, spaceI case "spaceid": propstatOK.Prop = append(propstatOK.Prop, props.NewProp("oc:spaceid", spaceID)) default: - propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewProp("oc:"+pf.Prop[i].Local, "")) + propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewNotFound("oc:"+pf.Prop[i].Local)) } case net.NsDav: switch pf.Prop[i].Local { case "getcontentlength": if item.Type == provider.ResourceType_RESOURCE_TYPE_CONTAINER { - propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewProp("d:getcontentlength", "")) + propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewNotFound("d:getcontentlength")) } else { propstatOK.Prop = append(propstatOK.Prop, props.NewProp("d:getcontentlength", size)) } @@ -427,21 +427,21 @@ func (h *TrashbinHandler) itemToPropResponse(ctx context.Context, s *svc, spaceI if item.Type == provider.ResourceType_RESOURCE_TYPE_CONTAINER { propstatOK.Prop = append(propstatOK.Prop, props.NewPropRaw("d:resourcetype", "")) } else { - propstatOK.Prop = append(propstatOK.Prop, props.NewProp("d:resourcetype", "")) + propstatOK.Prop = append(propstatOK.Prop, props.NewPropRaw("d:resourcetype", "")) // redirectref is another option } case "getcontenttype": if item.Type == provider.ResourceType_RESOURCE_TYPE_CONTAINER { - propstatOK.Prop = append(propstatOK.Prop, props.NewProp("d:getcontenttype", "httpd/unix-directory")) + propstatOK.Prop = append(propstatOK.Prop, props.NewPropRaw("d:getcontenttype", "httpd/unix-directory")) } else { - propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewProp("d:getcontenttype", "")) + propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewNotFound("d:getcontenttype")) } default: - propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewProp("d:"+pf.Prop[i].Local, "")) + propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewNotFound("d:"+pf.Prop[i].Local)) } default: // TODO (jfd) lookup shortname for unknown namespaces? - propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewProp(pf.Prop[i].Space+":"+pf.Prop[i].Local, "")) + propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewNotFound(pf.Prop[i].Space+":"+pf.Prop[i].Local)) } } response.Propstat = append(response.Propstat, propstatOK, propstatNotFound) diff --git a/internal/http/services/owncloud/ocs/conversions/permissions.go b/internal/http/services/owncloud/ocs/conversions/permissions.go index b3cc3a2eb4..bc2f86e0e5 100644 --- a/internal/http/services/owncloud/ocs/conversions/permissions.go +++ b/internal/http/services/owncloud/ocs/conversions/permissions.go @@ -20,6 +20,7 @@ package conversions import ( "fmt" + "strconv" ) // Permissions reflects the CRUD permissions used in the OCS sharing API @@ -62,3 +63,7 @@ func NewPermissions(val int) (Permissions, error) { func (p Permissions) Contain(other Permissions) bool { return p&other == other } + +func (p Permissions) String() string { + return strconv.FormatUint(uint64(p), 10) +} From 9f90ce19c0e1e1bb2ab8c74c5a3ceda4dac57906 Mon Sep 17 00:00:00 2001 From: David Christofas Date: Mon, 21 Mar 2022 18:37:08 +0100 Subject: [PATCH 2/6] reduce unnecessary use of pointers --- .../owncloud/ocdav/propfind/propfind.go | 12 +++++------ .../http/services/owncloud/ocdav/proppatch.go | 4 ++-- .../services/owncloud/ocdav/props/props.go | 20 +++++++++---------- .../http/services/owncloud/ocdav/trashbin.go | 10 +++++----- 4 files changed, 23 insertions(+), 23 deletions(-) diff --git a/internal/http/services/owncloud/ocdav/propfind/propfind.go b/internal/http/services/owncloud/ocdav/propfind/propfind.go index 0dd78cb02f..d9b4a483b1 100644 --- a/internal/http/services/owncloud/ocdav/propfind/propfind.go +++ b/internal/http/services/owncloud/ocdav/propfind/propfind.go @@ -83,10 +83,10 @@ type PropstatXML struct { // not honoring namespace declarations inside a xmltag with a // parent element for anonymous slice elements. // Use of multistatusWriter takes care of this. - Prop []*props.PropertyXML `xml:"d:prop>_ignored_"` - Status string `xml:"d:status"` - Error *errors.ErrorXML `xml:"d:error"` - ResponseDescription string `xml:"d:responsedescription,omitempty"` + Prop []props.PropertyXML `xml:"d:prop>_ignored_"` + Status string `xml:"d:status"` + Error *errors.ErrorXML `xml:"d:error"` + ResponseDescription string `xml:"d:responsedescription,omitempty"` } // ResponseXML holds the xml representation of a propfind response @@ -728,11 +728,11 @@ func mdToPropResponse(ctx context.Context, pf *XML, md *provider.ResourceInfo, p propstatOK := PropstatXML{ Status: "HTTP/1.1 200 OK", - Prop: []*props.PropertyXML{}, + Prop: []props.PropertyXML{}, } propstatNotFound := PropstatXML{ Status: "HTTP/1.1 404 Not Found", - Prop: []*props.PropertyXML{}, + Prop: []props.PropertyXML{}, } // when allprops has been requested if pf.Allprop != nil { diff --git a/internal/http/services/owncloud/ocdav/proppatch.go b/internal/http/services/owncloud/ocdav/proppatch.go index 8d28bb2e13..ebe95fb52b 100644 --- a/internal/http/services/owncloud/ocdav/proppatch.go +++ b/internal/http/services/owncloud/ocdav/proppatch.go @@ -323,7 +323,7 @@ func (s *svc) formatProppatchResponse(ctx context.Context, acceptedProps []xml.N } if len(acceptedProps) > 0 { - propstatBody := []*props.PropertyXML{} + propstatBody := []props.PropertyXML{} for i := range acceptedProps { propstatBody = append(propstatBody, props.NewPropNS(acceptedProps[i].Space, acceptedProps[i].Local, "")) } @@ -334,7 +334,7 @@ func (s *svc) formatProppatchResponse(ctx context.Context, acceptedProps []xml.N } if len(removedProps) > 0 { - propstatBody := []*props.PropertyXML{} + propstatBody := []props.PropertyXML{} for i := range removedProps { propstatBody = append(propstatBody, props.NewPropNS(removedProps[i].Space, removedProps[i].Local, "")) } diff --git a/internal/http/services/owncloud/ocdav/props/props.go b/internal/http/services/owncloud/ocdav/props/props.go index 822ae58ec3..eabb986f73 100644 --- a/internal/http/services/owncloud/ocdav/props/props.go +++ b/internal/http/services/owncloud/ocdav/props/props.go @@ -50,8 +50,8 @@ func xmlEscaped(val string) []byte { } // NewPropNS returns a new PropertyXML instance -func NewPropNS(namespace string, local string, val string) *PropertyXML { - return &PropertyXML{ +func NewPropNS(namespace string, local string, val string) PropertyXML { + return PropertyXML{ XMLName: xml.Name{Space: namespace, Local: local}, Lang: "", InnerXML: xmlEscaped(val), @@ -60,8 +60,8 @@ func NewPropNS(namespace string, local string, val string) *PropertyXML { // NewProp returns a new PropertyXML instance while xml-escaping the value // TODO properly use the space -func NewProp(key, val string) *PropertyXML { - return &PropertyXML{ +func NewProp(key, val string) PropertyXML { + return PropertyXML{ XMLName: xml.Name{Space: "", Local: key}, Lang: "", InnerXML: xmlEscaped(val), @@ -69,16 +69,16 @@ func NewProp(key, val string) *PropertyXML { } // NewNotFound returns a new PropertyXML instance with an empty value -func NewNotFound(key string) *PropertyXML { - return &PropertyXML{ +func NewNotFound(key string) PropertyXML { + return PropertyXML{ XMLName: xml.Name{Space: "", Local: key}, Lang: "", } } // NewNotFound returns a new PropertyXML instance with the given namespace and an empty value -func NewNotFoundNS(namespace, key string) *PropertyXML { - return &PropertyXML{ +func NewNotFoundNS(namespace, key string) PropertyXML { + return PropertyXML{ XMLName: xml.Name{Space: namespace, Local: key}, Lang: "", } @@ -86,8 +86,8 @@ func NewNotFoundNS(namespace, key string) *PropertyXML { // NewPropRaw returns a new PropertyXML instance for the given key/value pair // TODO properly use the space -func NewPropRaw(key, val string) *PropertyXML { - return &PropertyXML{ +func NewPropRaw(key, val string) PropertyXML { + return PropertyXML{ XMLName: xml.Name{Space: "", Local: key}, Lang: "", InnerXML: []byte(val), diff --git a/internal/http/services/owncloud/ocdav/trashbin.go b/internal/http/services/owncloud/ocdav/trashbin.go index 0f1640a014..20249be62c 100644 --- a/internal/http/services/owncloud/ocdav/trashbin.go +++ b/internal/http/services/owncloud/ocdav/trashbin.go @@ -299,13 +299,13 @@ func (h *TrashbinHandler) formatTrashPropfind(ctx context.Context, s *svc, space Propstat: []propfind.PropstatXML{ { Status: "HTTP/1.1 200 OK", - Prop: []*props.PropertyXML{ + Prop: []props.PropertyXML{ props.NewPropRaw("d:resourcetype", ""), }, }, { Status: "HTTP/1.1 404 Not Found", - Prop: []*props.PropertyXML{ + Prop: []props.PropertyXML{ props.NewNotFound("oc:trashbin-original-filename"), props.NewNotFound("oc:trashbin-original-location"), props.NewNotFound("oc:trashbin-delete-datetime"), @@ -362,7 +362,7 @@ func (h *TrashbinHandler) itemToPropResponse(ctx context.Context, s *svc, spaceI // return all known properties propstatOK := propfind.PropstatXML{ Status: "HTTP/1.1 200 OK", - Prop: []*props.PropertyXML{}, + Prop: []props.PropertyXML{}, } // yes this is redundant, can be derived from oc:trashbin-original-location which contains the full path, clients should not fetch it propstatOK.Prop = append(propstatOK.Prop, props.NewProp("oc:trashbin-original-filename", path.Base(item.Ref.Path))) @@ -384,11 +384,11 @@ func (h *TrashbinHandler) itemToPropResponse(ctx context.Context, s *svc, spaceI // otherwise return only the requested properties propstatOK := propfind.PropstatXML{ Status: "HTTP/1.1 200 OK", - Prop: []*props.PropertyXML{}, + Prop: []props.PropertyXML{}, } propstatNotFound := propfind.PropstatXML{ Status: "HTTP/1.1 404 Not Found", - Prop: []*props.PropertyXML{}, + Prop: []props.PropertyXML{}, } for i := range pf.Prop { switch pf.Prop[i].Space { From 7763278103a5ccc38b983b745f0227d6a7ca5782 Mon Sep 17 00:00:00 2001 From: David Christofas Date: Mon, 21 Mar 2022 18:42:44 +0100 Subject: [PATCH 3/6] simplify the EncodePath method The results of the old code were a bit different but this version is still compliant to the webdav rfc: https://datatracker.ietf.org/doc/html/rfc4918#section-8.3.1. It is also a whole lot faster. --- .../http/services/owncloud/ocdav/net/net.go | 30 +------------------ .../services/owncloud/ocdav/net/net_test.go | 2 +- 2 files changed, 2 insertions(+), 30 deletions(-) diff --git a/internal/http/services/owncloud/ocdav/net/net.go b/internal/http/services/owncloud/ocdav/net/net.go index 586c8c7cc4..9fab05c963 100644 --- a/internal/http/services/owncloud/ocdav/net/net.go +++ b/internal/http/services/owncloud/ocdav/net/net.go @@ -19,9 +19,7 @@ package net import ( - "fmt" "net/url" - "regexp" "strings" "github.com/pkg/errors" @@ -69,37 +67,11 @@ func (d Depth) String() string { return string(d) } -// replaceAllStringSubmatchFunc is taken from 'Go: Replace String with Regular Expression Callback' -// see: https://elliotchance.medium.com/go-replace-string-with-regular-expression-callback-f89948bad0bb -func replaceAllStringSubmatchFunc(re *regexp.Regexp, str string, repl func([]string) string) string { - result := "" - lastIndex := 0 - for _, v := range re.FindAllSubmatchIndex([]byte(str), -1) { - groups := []string{} - for i := 0; i < len(v); i += 2 { - groups = append(groups, str[v[i]:v[i+1]]) - } - result += str[lastIndex:v[0]] + repl(groups) - lastIndex = v[1] - } - return result + str[lastIndex:] -} - -var hrefre = regexp.MustCompile(`([^A-Za-z0-9_\-.~()/:@!$])`) - // EncodePath encodes the path of a url. // // slashes (/) are treated as path-separators. -// ported from https://github.com/sabre-io/http/blob/bb27d1a8c92217b34e778ee09dcf79d9a2936e84/lib/functions.php#L369-L379 func EncodePath(path string) string { - return replaceAllStringSubmatchFunc(hrefre, path, func(groups []string) string { - b := groups[1] - var sb strings.Builder - for i := 0; i < len(b); i++ { - sb.WriteString(fmt.Sprintf("%%%x", b[i])) - } - return sb.String() - }) + return (&url.URL{Path: path}).EscapedPath() } // ParseDepth parses the depth header value defined in https://tools.ietf.org/html/rfc4918#section-9.1 diff --git a/internal/http/services/owncloud/ocdav/net/net_test.go b/internal/http/services/owncloud/ocdav/net/net_test.go index 24ee8da430..3b254067f3 100644 --- a/internal/http/services/owncloud/ocdav/net/net_test.go +++ b/internal/http/services/owncloud/ocdav/net/net_test.go @@ -64,7 +64,7 @@ var _ = Describe("Net", func() { Describe("EncodePath", func() { It("encodes paths", func() { Expect(net.EncodePath("foo")).To(Equal("foo")) - Expect(net.EncodePath("/some/path/Folder %^*(#1)")).To(Equal("/some/path/Folder%20%25%5e%2a(%231)")) + Expect(net.EncodePath("/some/path/Folder %^*(#1)")).To(Equal("/some/path/Folder%20%25%5E%2A%28%231%29")) }) /* From 72cd2e80e624047c7ee54abc416c93402384259f Mon Sep 17 00:00:00 2001 From: David Christofas Date: Mon, 21 Mar 2022 18:47:25 +0100 Subject: [PATCH 4/6] add changelog --- changelog/unreleased/ocdav-cleanup.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 changelog/unreleased/ocdav-cleanup.md diff --git a/changelog/unreleased/ocdav-cleanup.md b/changelog/unreleased/ocdav-cleanup.md new file mode 100644 index 0000000000..ecbe7e69d3 --- /dev/null +++ b/changelog/unreleased/ocdav-cleanup.md @@ -0,0 +1,5 @@ +Change: small clean up of the ocdav code + +Cleaned up the ocdav code to make it more readable and in one case a bit faster. + +https://github.com/cs3org/reva/pull/2658 From 2b44d0fe77bc9fd2ed70307079d2bceb21580485 Mon Sep 17 00:00:00 2001 From: David Christofas Date: Wed, 23 Mar 2022 12:17:05 +0100 Subject: [PATCH 5/6] improve naming of prop package --- .../http/services/owncloud/ocdav/locks.go | 6 +- .../ocdav/{props/props.go => prop/prop.go} | 22 +- .../owncloud/ocdav/propfind/propfind.go | 200 +++++++++--------- .../http/services/owncloud/ocdav/proppatch.go | 18 +- .../http/services/owncloud/ocdav/trashbin.go | 70 +++--- 5 files changed, 158 insertions(+), 158 deletions(-) rename internal/http/services/owncloud/ocdav/{props/props.go => prop/prop.go} (86%) diff --git a/internal/http/services/owncloud/ocdav/locks.go b/internal/http/services/owncloud/ocdav/locks.go index e4bfb00446..822d23dc94 100644 --- a/internal/http/services/owncloud/ocdav/locks.go +++ b/internal/http/services/owncloud/ocdav/locks.go @@ -36,7 +36,7 @@ import ( types "github.com/cs3org/go-cs3apis/cs3/types/v1beta1" "github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/errors" "github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/net" - "github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/props" + "github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/prop" "github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/spacelookup" "github.com/cs3org/reva/v2/pkg/appctx" ctxpkg "github.com/cs3org/reva/v2/pkg/ctx" @@ -557,10 +557,10 @@ func writeLockInfo(w io.Writer, token string, ld LockDetails) (int, error) { lockdiscovery.WriteString(" Infinite\n") } if token != "" { - lockdiscovery.WriteString(fmt.Sprintf(" %s\n", props.Escape(token))) + lockdiscovery.WriteString(fmt.Sprintf(" %s\n", prop.Escape(token))) } if href != "" { - lockdiscovery.WriteString(fmt.Sprintf(" %s\n", props.Escape(href))) + lockdiscovery.WriteString(fmt.Sprintf(" %s\n", prop.Escape(href))) } lockdiscovery.WriteString("") diff --git a/internal/http/services/owncloud/ocdav/props/props.go b/internal/http/services/owncloud/ocdav/prop/prop.go similarity index 86% rename from internal/http/services/owncloud/ocdav/props/props.go rename to internal/http/services/owncloud/ocdav/prop/prop.go index eabb986f73..607679520c 100644 --- a/internal/http/services/owncloud/ocdav/props/props.go +++ b/internal/http/services/owncloud/ocdav/prop/prop.go @@ -16,7 +16,7 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -package props +package prop import ( "bytes" @@ -49,8 +49,8 @@ func xmlEscaped(val string) []byte { return buf.Bytes() } -// NewPropNS returns a new PropertyXML instance -func NewPropNS(namespace string, local string, val string) PropertyXML { +// EscapedNS returns a new PropertyXML instance while xml-escaping the value +func EscapedNS(namespace string, local string, val string) PropertyXML { return PropertyXML{ XMLName: xml.Name{Space: namespace, Local: local}, Lang: "", @@ -58,9 +58,9 @@ func NewPropNS(namespace string, local string, val string) PropertyXML { } } -// NewProp returns a new PropertyXML instance while xml-escaping the value +// Escaped returns a new PropertyXML instance while xml-escaping the value // TODO properly use the space -func NewProp(key, val string) PropertyXML { +func Escaped(key, val string) PropertyXML { return PropertyXML{ XMLName: xml.Name{Space: "", Local: key}, Lang: "", @@ -68,25 +68,25 @@ func NewProp(key, val string) PropertyXML { } } -// NewNotFound returns a new PropertyXML instance with an empty value -func NewNotFound(key string) PropertyXML { +// NotFound returns a new PropertyXML instance with an empty value +func NotFound(key string) PropertyXML { return PropertyXML{ XMLName: xml.Name{Space: "", Local: key}, Lang: "", } } -// NewNotFound returns a new PropertyXML instance with the given namespace and an empty value -func NewNotFoundNS(namespace, key string) PropertyXML { +// NotFoundNS returns a new PropertyXML instance with the given namespace and an empty value +func NotFoundNS(namespace, key string) PropertyXML { return PropertyXML{ XMLName: xml.Name{Space: namespace, Local: key}, Lang: "", } } -// NewPropRaw returns a new PropertyXML instance for the given key/value pair +// Raw returns a new PropertyXML instance for the given key/value pair // TODO properly use the space -func NewPropRaw(key, val string) PropertyXML { +func Raw(key, val string) PropertyXML { return PropertyXML{ XMLName: xml.Name{Space: "", Local: key}, Lang: "", diff --git a/internal/http/services/owncloud/ocdav/propfind/propfind.go b/internal/http/services/owncloud/ocdav/propfind/propfind.go index d9b4a483b1..1295f37ede 100644 --- a/internal/http/services/owncloud/ocdav/propfind/propfind.go +++ b/internal/http/services/owncloud/ocdav/propfind/propfind.go @@ -39,7 +39,7 @@ import ( "github.com/cs3org/reva/v2/internal/grpc/services/storageprovider" "github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/errors" "github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/net" - "github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/props" + "github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/prop" "github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/spacelookup" "github.com/cs3org/reva/v2/internal/http/services/owncloud/ocs/conversions" "github.com/cs3org/reva/v2/pkg/appctx" @@ -83,10 +83,10 @@ type PropstatXML struct { // not honoring namespace declarations inside a xmltag with a // parent element for anonymous slice elements. // Use of multistatusWriter takes care of this. - Prop []props.PropertyXML `xml:"d:prop>_ignored_"` - Status string `xml:"d:status"` - Error *errors.ErrorXML `xml:"d:error"` - ResponseDescription string `xml:"d:responsedescription,omitempty"` + Prop []prop.PropertyXML `xml:"d:prop>_ignored_"` + Status string `xml:"d:status"` + Error *errors.ErrorXML `xml:"d:error"` + ResponseDescription string `xml:"d:responsedescription,omitempty"` } // ResponseXML holds the xml representation of a propfind response @@ -136,10 +136,10 @@ type PropstatUnmarshalXML struct { // not honoring namespace declarations inside a xmltag with a // parent element for anonymous slice elements. // Use of multistatusWriter takes care of this. - Prop []*props.PropertyXML `xml:"prop"` - Status string `xml:"status"` - Error *errors.ErrorXML `xml:"d:error"` - ResponseDescription string `xml:"responsedescription,omitempty"` + Prop []*prop.PropertyXML `xml:"prop"` + Status string `xml:"status"` + Error *errors.ErrorXML `xml:"d:error"` + ResponseDescription string `xml:"responsedescription,omitempty"` } // NewMultiStatusResponseXML returns a preconfigured instance of MultiStatusResponseXML @@ -728,11 +728,11 @@ func mdToPropResponse(ctx context.Context, pf *XML, md *provider.ResourceInfo, p propstatOK := PropstatXML{ Status: "HTTP/1.1 200 OK", - Prop: []props.PropertyXML{}, + Prop: []prop.PropertyXML{}, } propstatNotFound := PropstatXML{ Status: "HTTP/1.1 404 Not Found", - Prop: []props.PropertyXML{}, + Prop: []prop.PropertyXML{}, } // when allprops has been requested if pf.Allprop != nil { @@ -741,9 +741,9 @@ func mdToPropResponse(ctx context.Context, pf *XML, md *provider.ResourceInfo, p if md.Id != nil { id := resourceid.OwnCloudResourceIDWrap(md.Id) propstatOK.Prop = append(propstatOK.Prop, - props.NewProp("oc:id", id), - props.NewProp("oc:fileid", id), - props.NewProp("oc:spaceid", md.Id.StorageId), + prop.Escaped("oc:id", id), + prop.Escaped("oc:fileid", id), + prop.Escaped("oc:spaceid", md.Id.StorageId), ) } @@ -751,37 +751,37 @@ func mdToPropResponse(ctx context.Context, pf *XML, md *provider.ResourceInfo, p // etags must be enclosed in double quotes and cannot contain them. // See https://tools.ietf.org/html/rfc7232#section-2.3 for details // TODO(jfd) handle weak tags that start with 'W/' - propstatOK.Prop = append(propstatOK.Prop, props.NewProp("d:getetag", quoteEtag(md.Etag))) + propstatOK.Prop = append(propstatOK.Prop, prop.Escaped("d:getetag", quoteEtag(md.Etag))) } if md.PermissionSet != nil { - propstatOK.Prop = append(propstatOK.Prop, props.NewProp("oc:permissions", wdp)) + propstatOK.Prop = append(propstatOK.Prop, prop.Escaped("oc:permissions", wdp)) } // always return size, well nearly always ... public link shares are a little weird if md.Type == provider.ResourceType_RESOURCE_TYPE_CONTAINER { - propstatOK.Prop = append(propstatOK.Prop, props.NewPropRaw("d:resourcetype", "")) + propstatOK.Prop = append(propstatOK.Prop, prop.Raw("d:resourcetype", "")) if ls == nil { - propstatOK.Prop = append(propstatOK.Prop, props.NewProp("oc:size", size)) + propstatOK.Prop = append(propstatOK.Prop, prop.Escaped("oc:size", size)) } // A PROPFIND request SHOULD NOT return DAV:quota-available-bytes and DAV:quota-used-bytes // from https://www.rfc-editor.org/rfc/rfc4331.html#section-2 - // propstatOK.Prop = append(propstatOK.Prop, props.NewProp("d:quota-used-bytes", size)) - // propstatOK.Prop = append(propstatOK.Prop, props.NewProp("d:quota-available-bytes", quota)) + // propstatOK.Prop = append(propstatOK.Prop, prop.NewProp("d:quota-used-bytes", size)) + // propstatOK.Prop = append(propstatOK.Prop, prop.NewProp("d:quota-available-bytes", quota)) } else { propstatOK.Prop = append(propstatOK.Prop, - props.NewProp("d:resourcetype", ""), - props.NewProp("d:getcontentlength", size), + prop.Escaped("d:resourcetype", ""), + prop.Escaped("d:getcontentlength", size), ) if md.MimeType != "" { - propstatOK.Prop = append(propstatOK.Prop, props.NewProp("d:getcontenttype", md.MimeType)) + propstatOK.Prop = append(propstatOK.Prop, prop.Escaped("d:getcontenttype", md.MimeType)) } } // Finder needs the getLastModified property to work. if md.Mtime != nil { t := utils.TSToTime(md.Mtime).UTC() lastModifiedString := t.Format(net.RFC1123) - propstatOK.Prop = append(propstatOK.Prop, props.NewProp("d:getlastmodified", lastModifiedString)) + propstatOK.Prop = append(propstatOK.Prop, prop.Escaped("d:getlastmodified", lastModifiedString)) } // stay bug compatible with oc10, see https://github.com/owncloud/core/pull/38304#issuecomment-762185241 @@ -812,25 +812,25 @@ func mdToPropResponse(ctx context.Context, pf *XML, md *provider.ResourceInfo, p } if checksums.Len() > 0 { checksums.WriteString("") - propstatOK.Prop = append(propstatOK.Prop, props.NewPropRaw("oc:checksums", checksums.String())) + propstatOK.Prop = append(propstatOK.Prop, prop.Raw("oc:checksums", checksums.String())) } // ls do not report any properties as missing by default if ls == nil { // favorites from arbitrary metadata if k := md.GetArbitraryMetadata(); k == nil { - propstatOK.Prop = append(propstatOK.Prop, props.NewPropRaw("oc:favorite", "0")) + propstatOK.Prop = append(propstatOK.Prop, prop.Raw("oc:favorite", "0")) } else if amd := k.GetMetadata(); amd == nil { - propstatOK.Prop = append(propstatOK.Prop, props.NewPropRaw("oc:favorite", "0")) + propstatOK.Prop = append(propstatOK.Prop, prop.Raw("oc:favorite", "0")) } else if v, ok := amd[net.PropOcFavorite]; ok && v != "" { - propstatOK.Prop = append(propstatOK.Prop, props.NewProp("oc:favorite", v)) + propstatOK.Prop = append(propstatOK.Prop, prop.Escaped("oc:favorite", v)) } else { - propstatOK.Prop = append(propstatOK.Prop, props.NewPropRaw("oc:favorite", "0")) + propstatOK.Prop = append(propstatOK.Prop, prop.Raw("oc:favorite", "0")) } } if lock != nil { - propstatOK.Prop = append(propstatOK.Prop, props.NewPropRaw("d:lockdiscovery", activeLocks(&sublog, lock))) + propstatOK.Prop = append(propstatOK.Prop, prop.Raw("d:lockdiscovery", activeLocks(&sublog, lock))) } // TODO return other properties ... but how do we put them in a namespace? } else { @@ -843,21 +843,21 @@ func mdToPropResponse(ctx context.Context, pf *XML, md *provider.ResourceInfo, p // I tested the desktop client and phoenix to annotate which properties are requestted, see below cases case "fileid": // phoenix only if md.Id != nil { - propstatOK.Prop = append(propstatOK.Prop, props.NewProp("oc:fileid", resourceid.OwnCloudResourceIDWrap(md.Id))) + propstatOK.Prop = append(propstatOK.Prop, prop.Escaped("oc:fileid", resourceid.OwnCloudResourceIDWrap(md.Id))) } else { - propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewNotFound("oc:fileid")) + propstatNotFound.Prop = append(propstatNotFound.Prop, prop.NotFound("oc:fileid")) } case "id": // desktop client only if md.Id != nil { - propstatOK.Prop = append(propstatOK.Prop, props.NewProp("oc:id", resourceid.OwnCloudResourceIDWrap(md.Id))) + propstatOK.Prop = append(propstatOK.Prop, prop.Escaped("oc:id", resourceid.OwnCloudResourceIDWrap(md.Id))) } else { - propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewNotFound("oc:id")) + propstatNotFound.Prop = append(propstatNotFound.Prop, prop.NotFound("oc:id")) } case "spaceid": if md.Id != nil { - propstatOK.Prop = append(propstatOK.Prop, props.NewProp("oc:spaceid", md.Id.StorageId)) + propstatOK.Prop = append(propstatOK.Prop, prop.Escaped("oc:spaceid", md.Id.StorageId)) } else { - propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewProp("oc:spaceid", "")) + propstatNotFound.Prop = append(propstatNotFound.Prop, prop.Escaped("oc:spaceid", "")) } case "permissions": // both // oc:permissions take several char flags to indicate the permissions the user has on this node: @@ -869,78 +869,78 @@ func mdToPropResponse(ctx context.Context, pf *XML, md *provider.ResourceInfo, p // R = Shareable (Reshare) // M = Mounted // in contrast, the ocs:share-permissions further down below indicate clients the maximum permissions that can be granted - propstatOK.Prop = append(propstatOK.Prop, props.NewProp("oc:permissions", wdp)) + propstatOK.Prop = append(propstatOK.Prop, prop.Escaped("oc:permissions", wdp)) case "public-link-permission": // only on a share root node if ls != nil && md.PermissionSet != nil { propstatOK.Prop = append( propstatOK.Prop, - props.NewProp("oc:public-link-permission", role.OCSPermissions().String())) + prop.Escaped("oc:public-link-permission", role.OCSPermissions().String())) } else { - propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewNotFound("oc:public-link-permission")) + propstatNotFound.Prop = append(propstatNotFound.Prop, prop.NotFound("oc:public-link-permission")) } case "public-link-item-type": // only on a share root node if ls != nil { if md.Type == provider.ResourceType_RESOURCE_TYPE_CONTAINER { - propstatOK.Prop = append(propstatOK.Prop, props.NewPropRaw("oc:public-link-item-type", "folder")) + propstatOK.Prop = append(propstatOK.Prop, prop.Raw("oc:public-link-item-type", "folder")) } else { - propstatOK.Prop = append(propstatOK.Prop, props.NewPropRaw("oc:public-link-item-type", "file")) + propstatOK.Prop = append(propstatOK.Prop, prop.Raw("oc:public-link-item-type", "file")) // redirectref is another option } } else { - propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewNotFound("oc:public-link-item-type")) + propstatNotFound.Prop = append(propstatNotFound.Prop, prop.NotFound("oc:public-link-item-type")) } case "public-link-share-datetime": if ls != nil && ls.Mtime != nil { t := utils.TSToTime(ls.Mtime).UTC() // TODO or ctime? shareTimeString := t.Format(net.RFC1123) - propstatOK.Prop = append(propstatOK.Prop, props.NewProp("oc:public-link-share-datetime", shareTimeString)) + propstatOK.Prop = append(propstatOK.Prop, prop.Escaped("oc:public-link-share-datetime", shareTimeString)) } else { - propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewNotFound("oc:public-link-share-datetime")) + propstatNotFound.Prop = append(propstatNotFound.Prop, prop.NotFound("oc:public-link-share-datetime")) } case "public-link-share-owner": if ls != nil && ls.Owner != nil { if net.IsCurrentUserOwner(ctx, ls.Owner) { u := ctxpkg.ContextMustGetUser(ctx) - propstatOK.Prop = append(propstatOK.Prop, props.NewProp("oc:public-link-share-owner", u.Username)) + propstatOK.Prop = append(propstatOK.Prop, prop.Escaped("oc:public-link-share-owner", u.Username)) } else { u, _ := ctxpkg.ContextGetUser(ctx) sublog.Error().Interface("share", ls).Interface("user", u).Msg("the current user in the context should be the owner of a public link share") - propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewNotFound("oc:public-link-share-owner")) + propstatNotFound.Prop = append(propstatNotFound.Prop, prop.NotFound("oc:public-link-share-owner")) } } else { - propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewNotFound("oc:public-link-share-owner")) + propstatNotFound.Prop = append(propstatNotFound.Prop, prop.NotFound("oc:public-link-share-owner")) } case "public-link-expiration": if ls != nil && ls.Expiration != nil { t := utils.TSToTime(ls.Expiration).UTC() expireTimeString := t.Format(net.RFC1123) - propstatOK.Prop = append(propstatOK.Prop, props.NewProp("oc:public-link-expiration", expireTimeString)) + propstatOK.Prop = append(propstatOK.Prop, prop.Escaped("oc:public-link-expiration", expireTimeString)) } else { - propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewNotFound("oc:public-link-expiration")) + propstatNotFound.Prop = append(propstatNotFound.Prop, prop.NotFound("oc:public-link-expiration")) } - propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewNotFound("oc:public-link-expiration")) + propstatNotFound.Prop = append(propstatNotFound.Prop, prop.NotFound("oc:public-link-expiration")) case "size": // phoenix only // TODO we cannot find out if md.Size is set or not because ints in go default to 0 // TODO what is the difference to d:quota-used-bytes (which only exists for collections)? // oc:size is available on files and folders and behaves like d:getcontentlength or d:quota-used-bytes respectively // The hasPrefix is a workaround to make children of the link root show a size if they have 0 bytes if ls == nil || strings.HasPrefix(md.Path, "/"+ls.Token+"/") { - propstatOK.Prop = append(propstatOK.Prop, props.NewProp("oc:size", size)) + propstatOK.Prop = append(propstatOK.Prop, prop.Escaped("oc:size", size)) } else { // link share root collection has no size - propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewNotFound("oc:size")) + propstatNotFound.Prop = append(propstatNotFound.Prop, prop.NotFound("oc:size")) } case "owner-id": // phoenix only if md.Owner != nil { if net.IsCurrentUserOwner(ctx, md.Owner) { u := ctxpkg.ContextMustGetUser(ctx) - propstatOK.Prop = append(propstatOK.Prop, props.NewProp("oc:owner-id", u.Username)) + propstatOK.Prop = append(propstatOK.Prop, prop.Escaped("oc:owner-id", u.Username)) } else { sublog.Debug().Msg("TODO fetch user username") - propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewNotFound("oc:owner-id")) + propstatNotFound.Prop = append(propstatNotFound.Prop, prop.NotFound("oc:owner-id")) } } else { - propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewNotFound("oc:owner-id")) + propstatNotFound.Prop = append(propstatNotFound.Prop, prop.NotFound("oc:owner-id")) } case "favorite": // phoenix only // TODO: can be 0 or 1?, in oc10 it is present or not @@ -948,17 +948,17 @@ func mdToPropResponse(ctx context.Context, pf *XML, md *provider.ResourceInfo, p // TODO: this boolean favorite property is so horribly wrong ... either it is presont, or it is not ... unless ... it is possible to have a non binary value ... we need to double check if ls == nil { if k := md.GetArbitraryMetadata(); k == nil { - propstatOK.Prop = append(propstatOK.Prop, props.NewPropRaw("oc:favorite", "0")) + propstatOK.Prop = append(propstatOK.Prop, prop.Raw("oc:favorite", "0")) } else if amd := k.GetMetadata(); amd == nil { - propstatOK.Prop = append(propstatOK.Prop, props.NewPropRaw("oc:favorite", "0")) + propstatOK.Prop = append(propstatOK.Prop, prop.Raw("oc:favorite", "0")) } else if v, ok := amd[net.PropOcFavorite]; ok && v != "" { - propstatOK.Prop = append(propstatOK.Prop, props.NewPropRaw("oc:favorite", "1")) + propstatOK.Prop = append(propstatOK.Prop, prop.Raw("oc:favorite", "1")) } else { - propstatOK.Prop = append(propstatOK.Prop, props.NewPropRaw("oc:favorite", "0")) + propstatOK.Prop = append(propstatOK.Prop, prop.Raw("oc:favorite", "0")) } } else { // link share root collection has no favorite - propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewNotFound("oc:favorite")) + propstatNotFound.Prop = append(propstatNotFound.Prop, prop.NotFound("oc:favorite")) } case "checksums": // desktop ... not really ... the desktop sends the OC-Checksum header @@ -990,9 +990,9 @@ func mdToPropResponse(ctx context.Context, pf *XML, md *provider.ResourceInfo, p } if checksums.Len() > 13 { checksums.WriteString("") - propstatOK.Prop = append(propstatOK.Prop, props.NewPropRaw("oc:checksums", checksums.String())) + propstatOK.Prop = append(propstatOK.Prop, prop.Raw("oc:checksums", checksums.String())) } else { - propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewNotFound("oc:checksums")) + propstatNotFound.Prop = append(propstatNotFound.Prop, prop.NotFound("oc:checksums")) } case "share-types": // desktop var types strings.Builder @@ -1011,21 +1011,21 @@ func mdToPropResponse(ctx context.Context, pf *XML, md *provider.ResourceInfo, p } if types.Len() != 0 { - propstatOK.Prop = append(propstatOK.Prop, props.NewPropRaw("oc:share-types", types.String())) + propstatOK.Prop = append(propstatOK.Prop, prop.Raw("oc:share-types", types.String())) } else { - propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewNotFound("oc:"+pf.Prop[i].Local)) + propstatNotFound.Prop = append(propstatNotFound.Prop, prop.NotFound("oc:"+pf.Prop[i].Local)) } case "owner-display-name": // phoenix only if md.Owner != nil { if net.IsCurrentUserOwner(ctx, md.Owner) { u := ctxpkg.ContextMustGetUser(ctx) - propstatOK.Prop = append(propstatOK.Prop, props.NewProp("oc:owner-display-name", u.DisplayName)) + propstatOK.Prop = append(propstatOK.Prop, prop.Escaped("oc:owner-display-name", u.DisplayName)) } else { sublog.Debug().Msg("TODO fetch user displayname") - propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewNotFound("oc:owner-display-name")) + propstatNotFound.Prop = append(propstatNotFound.Prop, prop.NotFound("oc:owner-display-name")) } } else { - propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewNotFound("oc:owner-display-name")) + propstatNotFound.Prop = append(propstatNotFound.Prop, prop.NotFound("oc:owner-display-name")) } case "downloadURL": // desktop if isPublic && md.Type == provider.ResourceType_RESOURCE_TYPE_FILE { @@ -1044,9 +1044,9 @@ func mdToPropResponse(ctx context.Context, pf *XML, md *provider.ResourceInfo, p path = sb.String() } - propstatOK.Prop = append(propstatOK.Prop, props.NewProp("oc:downloadURL", publicURL+baseURI+path)) + propstatOK.Prop = append(propstatOK.Prop, prop.Escaped("oc:downloadURL", publicURL+baseURI+path)) } else { - propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewNotFound("oc:"+pf.Prop[i].Local)) + propstatNotFound.Prop = append(propstatNotFound.Prop, prop.NotFound("oc:"+pf.Prop[i].Local)) } case "signature-auth": if isPublic { @@ -1061,9 +1061,9 @@ func mdToPropResponse(ctx context.Context, pf *XML, md *provider.ResourceInfo, p sb.WriteString(expiration.Format(time.RFC3339)) sb.WriteString("") - propstatOK.Prop = append(propstatOK.Prop, props.NewPropRaw("oc:signature-auth", sb.String())) + propstatOK.Prop = append(propstatOK.Prop, prop.Raw("oc:signature-auth", sb.String())) } else { - propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewNotFound("oc:signature-auth")) + propstatNotFound.Prop = append(propstatNotFound.Prop, prop.NotFound("oc:signature-auth")) } } case "privatelink": // phoenix only @@ -1079,15 +1079,15 @@ func mdToPropResponse(ctx context.Context, pf *XML, md *provider.ResourceInfo, p // TODO(jfd): double check the client behavior with reva on backup restore fallthrough default: - propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewNotFound("oc:"+pf.Prop[i].Local)) + propstatNotFound.Prop = append(propstatNotFound.Prop, prop.NotFound("oc:"+pf.Prop[i].Local)) } case net.NsDav: switch pf.Prop[i].Local { case "getetag": // both if md.Etag != "" { - propstatOK.Prop = append(propstatOK.Prop, props.NewProp("d:getetag", quoteEtag(md.Etag))) + propstatOK.Prop = append(propstatOK.Prop, prop.Escaped("d:getetag", quoteEtag(md.Etag))) } else { - propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewNotFound("d:getetag")) + propstatNotFound.Prop = append(propstatNotFound.Prop, prop.NotFound("d:getetag")) } case "getcontentlength": // both // see everts stance on this https://stackoverflow.com/a/31621912, he points to http://tools.ietf.org/html/rfc4918#section-15.3 @@ -1096,57 +1096,57 @@ func mdToPropResponse(ctx context.Context, pf *XML, md *provider.ResourceInfo, p // which is not the case ... so we don't return it on collections. owncloud has oc:size for that // TODO we cannot find out if md.Size is set or not because ints in go default to 0 if md.Type == provider.ResourceType_RESOURCE_TYPE_CONTAINER { - propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewNotFound("d:getcontentlength")) + propstatNotFound.Prop = append(propstatNotFound.Prop, prop.NotFound("d:getcontentlength")) } else { - propstatOK.Prop = append(propstatOK.Prop, props.NewProp("d:getcontentlength", size)) + propstatOK.Prop = append(propstatOK.Prop, prop.Escaped("d:getcontentlength", size)) } case "resourcetype": // both if md.Type == provider.ResourceType_RESOURCE_TYPE_CONTAINER { - propstatOK.Prop = append(propstatOK.Prop, props.NewPropRaw("d:resourcetype", "")) + propstatOK.Prop = append(propstatOK.Prop, prop.Raw("d:resourcetype", "")) } else { - propstatOK.Prop = append(propstatOK.Prop, props.NewPropRaw("d:resourcetype", "")) + propstatOK.Prop = append(propstatOK.Prop, prop.Raw("d:resourcetype", "")) // redirectref is another option } case "getcontenttype": // phoenix if md.Type == provider.ResourceType_RESOURCE_TYPE_CONTAINER { // directories have no contenttype - propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewNotFound("d:getcontenttype")) + propstatNotFound.Prop = append(propstatNotFound.Prop, prop.NotFound("d:getcontenttype")) } else if md.MimeType != "" { - propstatOK.Prop = append(propstatOK.Prop, props.NewProp("d:getcontenttype", md.MimeType)) + propstatOK.Prop = append(propstatOK.Prop, prop.Escaped("d:getcontenttype", md.MimeType)) } case "getlastmodified": // both // TODO we cannot find out if md.Mtime is set or not because ints in go default to 0 if md.Mtime != nil { t := utils.TSToTime(md.Mtime).UTC() lastModifiedString := t.Format(net.RFC1123) - propstatOK.Prop = append(propstatOK.Prop, props.NewProp("d:getlastmodified", lastModifiedString)) + propstatOK.Prop = append(propstatOK.Prop, prop.Escaped("d:getlastmodified", lastModifiedString)) } else { - propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewNotFound("d:getlastmodified")) + propstatNotFound.Prop = append(propstatNotFound.Prop, prop.NotFound("d:getlastmodified")) } case "quota-used-bytes": // RFC 4331 if md.Type == provider.ResourceType_RESOURCE_TYPE_CONTAINER { // always returns the current usage, // in oc10 there seems to be a bug that makes the size in webdav differ from the one in the user properties, not taking shares into account // in ocis we plan to always mak the quota a property of the storage space - propstatOK.Prop = append(propstatOK.Prop, props.NewProp("d:quota-used-bytes", size)) + propstatOK.Prop = append(propstatOK.Prop, prop.Escaped("d:quota-used-bytes", size)) } else { - propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewNotFound("d:quota-used-bytes")) + propstatNotFound.Prop = append(propstatNotFound.Prop, prop.NotFound("d:quota-used-bytes")) } case "quota-available-bytes": // RFC 4331 if md.Type == provider.ResourceType_RESOURCE_TYPE_CONTAINER { // oc10 returns -3 for unlimited, -2 for unknown, -1 for uncalculated - propstatOK.Prop = append(propstatOK.Prop, props.NewProp("d:quota-available-bytes", quota)) + propstatOK.Prop = append(propstatOK.Prop, prop.Escaped("d:quota-available-bytes", quota)) } else { - propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewNotFound("d:quota-available-bytes")) + propstatNotFound.Prop = append(propstatNotFound.Prop, prop.NotFound("d:quota-available-bytes")) } case "lockdiscovery": // http://www.webdav.org/specs/rfc2518.html#PROPERTY_lockdiscovery if lock == nil { - propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewNotFound("d:lockdiscovery")) + propstatNotFound.Prop = append(propstatNotFound.Prop, prop.NotFound("d:lockdiscovery")) } else { - propstatOK.Prop = append(propstatOK.Prop, props.NewPropRaw("d:lockdiscovery", activeLocks(&sublog, lock))) + propstatOK.Prop = append(propstatOK.Prop, prop.Raw("d:lockdiscovery", activeLocks(&sublog, lock))) } default: - propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewNotFound("d:"+pf.Prop[i].Local)) + propstatNotFound.Prop = append(propstatNotFound.Prop, prop.NotFound("d:"+pf.Prop[i].Local)) } case net.NsOCS: switch pf.Prop[i].Local { @@ -1165,21 +1165,21 @@ func mdToPropResponse(ctx context.Context, pf *XML, md *provider.ResourceInfo, p perms &^= conversions.PermissionCreate perms &^= conversions.PermissionDelete } - propstatOK.Prop = append(propstatOK.Prop, props.NewPropNS(pf.Prop[i].Space, pf.Prop[i].Local, perms.String())) + propstatOK.Prop = append(propstatOK.Prop, prop.EscapedNS(pf.Prop[i].Space, pf.Prop[i].Local, perms.String())) } default: - propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewNotFound("d:"+pf.Prop[i].Local)) + propstatNotFound.Prop = append(propstatNotFound.Prop, prop.NotFound("d:"+pf.Prop[i].Local)) } default: // handle custom properties if k := md.GetArbitraryMetadata(); k == nil { - propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewNotFoundNS(pf.Prop[i].Space, pf.Prop[i].Local)) + propstatNotFound.Prop = append(propstatNotFound.Prop, prop.NotFoundNS(pf.Prop[i].Space, pf.Prop[i].Local)) } else if amd := k.GetMetadata(); amd == nil { - propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewNotFoundNS(pf.Prop[i].Space, pf.Prop[i].Local)) + propstatNotFound.Prop = append(propstatNotFound.Prop, prop.NotFoundNS(pf.Prop[i].Space, pf.Prop[i].Local)) } else if v, ok := amd[metadataKeyOf(&pf.Prop[i])]; ok && v != "" { - propstatOK.Prop = append(propstatOK.Prop, props.NewPropNS(pf.Prop[i].Space, pf.Prop[i].Local, v)) + propstatOK.Prop = append(propstatOK.Prop, prop.EscapedNS(pf.Prop[i].Space, pf.Prop[i].Local, v)) } else { - propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewNotFoundNS(pf.Prop[i].Space, pf.Prop[i].Local)) + propstatNotFound.Prop = append(propstatNotFound.Prop, prop.NotFoundNS(pf.Prop[i].Space, pf.Prop[i].Local)) } } } @@ -1244,13 +1244,13 @@ func activeLocks(log *zerolog.Logger, lock *provider.Lock) string { if lock.User != nil { // TODO oc10 uses displayname and email, needs a user lookup - activelocks.WriteString(props.Escape(lock.User.OpaqueId + "@" + lock.User.Idp)) + activelocks.WriteString(prop.Escape(lock.User.OpaqueId + "@" + lock.User.Idp)) } if lock.AppName != "" { if lock.User != nil { activelocks.WriteString(" via ") } - activelocks.WriteString(props.Escape(lock.AppName)) + activelocks.WriteString(prop.Escape(lock.AppName)) } activelocks.WriteString("") } @@ -1259,7 +1259,7 @@ func activeLocks(log *zerolog.Logger, lock *provider.Lock) string { activelocks.WriteString("") if lock.LockId != "" { activelocks.WriteString("") - activelocks.WriteString(props.Escape(lock.LockId)) + activelocks.WriteString(prop.Escape(lock.LockId)) activelocks.WriteString("") } // lockroot is only used when setting the lock @@ -1296,7 +1296,7 @@ func metadataKeyOf(n *xml.Name) string { // properties contain values. Character data between properties is ignored. func (pn *Props) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { for { - t, err := props.Next(d) + t, err := prop.Next(d) if err != nil { return err } @@ -1310,7 +1310,7 @@ func (pn *Props) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { */ return nil case xml.StartElement: - t, err = props.Next(d) + t, err = prop.Next(d) if err != nil { return err } diff --git a/internal/http/services/owncloud/ocdav/proppatch.go b/internal/http/services/owncloud/ocdav/proppatch.go index ebe95fb52b..950ea76abd 100644 --- a/internal/http/services/owncloud/ocdav/proppatch.go +++ b/internal/http/services/owncloud/ocdav/proppatch.go @@ -32,8 +32,8 @@ import ( provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" "github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/errors" "github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/net" + "github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/prop" "github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/propfind" - "github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/props" "github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/spacelookup" "github.com/cs3org/reva/v2/pkg/appctx" ctxpkg "github.com/cs3org/reva/v2/pkg/ctx" @@ -323,9 +323,9 @@ func (s *svc) formatProppatchResponse(ctx context.Context, acceptedProps []xml.N } if len(acceptedProps) > 0 { - propstatBody := []props.PropertyXML{} + propstatBody := []prop.PropertyXML{} for i := range acceptedProps { - propstatBody = append(propstatBody, props.NewPropNS(acceptedProps[i].Space, acceptedProps[i].Local, "")) + propstatBody = append(propstatBody, prop.EscapedNS(acceptedProps[i].Space, acceptedProps[i].Local, "")) } response.Propstat = append(response.Propstat, propfind.PropstatXML{ Status: "HTTP/1.1 200 OK", @@ -334,9 +334,9 @@ func (s *svc) formatProppatchResponse(ctx context.Context, acceptedProps []xml.N } if len(removedProps) > 0 { - propstatBody := []props.PropertyXML{} + propstatBody := []prop.PropertyXML{} for i := range removedProps { - propstatBody = append(propstatBody, props.NewPropNS(removedProps[i].Space, removedProps[i].Local, "")) + propstatBody = append(propstatBody, prop.EscapedNS(removedProps[i].Space, removedProps[i].Local, "")) } response.Propstat = append(response.Propstat, propfind.PropstatXML{ Status: "HTTP/1.1 204 No Content", @@ -386,11 +386,11 @@ type Proppatch struct { // remove them, it sets them. Remove bool // Props contains the properties to be set or removed. - Props []props.PropertyXML + Props []prop.PropertyXML } // http://www.webdav.org/specs/rfc4918.html#ELEMENT_prop (for proppatch) -type proppatchProps []props.PropertyXML +type proppatchProps []prop.PropertyXML // UnmarshalXML appends the property names and values enclosed within start // to ps. @@ -403,7 +403,7 @@ type proppatchProps []props.PropertyXML func (ps *proppatchProps) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { lang := xmlLang(start, "") for { - t, err := props.Next(d) + t, err := prop.Next(d) if err != nil { return err } @@ -414,7 +414,7 @@ func (ps *proppatchProps) UnmarshalXML(d *xml.Decoder, start xml.StartElement) e } return nil case xml.StartElement: - p := props.PropertyXML{} + p := prop.PropertyXML{} err = d.DecodeElement(&p, &elem) if err != nil { return err diff --git a/internal/http/services/owncloud/ocdav/trashbin.go b/internal/http/services/owncloud/ocdav/trashbin.go index 20249be62c..158acfe6ec 100644 --- a/internal/http/services/owncloud/ocdav/trashbin.go +++ b/internal/http/services/owncloud/ocdav/trashbin.go @@ -31,8 +31,8 @@ import ( "github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/errors" "github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/net" + "github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/prop" "github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/propfind" - "github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/props" "github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/spacelookup" rtrace "github.com/cs3org/reva/v2/pkg/trace" "github.com/cs3org/reva/v2/pkg/utils/resourceid" @@ -299,17 +299,17 @@ func (h *TrashbinHandler) formatTrashPropfind(ctx context.Context, s *svc, space Propstat: []propfind.PropstatXML{ { Status: "HTTP/1.1 200 OK", - Prop: []props.PropertyXML{ - props.NewPropRaw("d:resourcetype", ""), + Prop: []prop.PropertyXML{ + prop.Raw("d:resourcetype", ""), }, }, { Status: "HTTP/1.1 404 Not Found", - Prop: []props.PropertyXML{ - props.NewNotFound("oc:trashbin-original-filename"), - props.NewNotFound("oc:trashbin-original-location"), - props.NewNotFound("oc:trashbin-delete-datetime"), - props.NewNotFound("d:getcontentlength"), + Prop: []prop.PropertyXML{ + prop.NotFound("oc:trashbin-original-filename"), + prop.NotFound("oc:trashbin-original-location"), + prop.NotFound("oc:trashbin-delete-datetime"), + prop.NotFound("d:getcontentlength"), }, }, }, @@ -362,33 +362,33 @@ func (h *TrashbinHandler) itemToPropResponse(ctx context.Context, s *svc, spaceI // return all known properties propstatOK := propfind.PropstatXML{ Status: "HTTP/1.1 200 OK", - Prop: []props.PropertyXML{}, + Prop: []prop.PropertyXML{}, } // yes this is redundant, can be derived from oc:trashbin-original-location which contains the full path, clients should not fetch it - propstatOK.Prop = append(propstatOK.Prop, props.NewProp("oc:trashbin-original-filename", path.Base(item.Ref.Path))) - propstatOK.Prop = append(propstatOK.Prop, props.NewProp("oc:trashbin-original-location", strings.TrimPrefix(item.Ref.Path, "/"))) - propstatOK.Prop = append(propstatOK.Prop, props.NewProp("oc:trashbin-delete-timestamp", strconv.FormatUint(item.DeletionTime.Seconds, 10))) - propstatOK.Prop = append(propstatOK.Prop, props.NewProp("oc:trashbin-delete-datetime", dTime)) + propstatOK.Prop = append(propstatOK.Prop, prop.Escaped("oc:trashbin-original-filename", path.Base(item.Ref.Path))) + propstatOK.Prop = append(propstatOK.Prop, prop.Escaped("oc:trashbin-original-location", strings.TrimPrefix(item.Ref.Path, "/"))) + propstatOK.Prop = append(propstatOK.Prop, prop.Escaped("oc:trashbin-delete-timestamp", strconv.FormatUint(item.DeletionTime.Seconds, 10))) + propstatOK.Prop = append(propstatOK.Prop, prop.Escaped("oc:trashbin-delete-datetime", dTime)) if item.Type == provider.ResourceType_RESOURCE_TYPE_CONTAINER { - propstatOK.Prop = append(propstatOK.Prop, props.NewPropRaw("d:resourcetype", "")) + propstatOK.Prop = append(propstatOK.Prop, prop.Raw("d:resourcetype", "")) // TODO(jfd): decide if we can and want to list oc:size for folders } else { propstatOK.Prop = append(propstatOK.Prop, - props.NewProp("d:resourcetype", ""), - props.NewProp("d:getcontentlength", size), + prop.Escaped("d:resourcetype", ""), + prop.Escaped("d:getcontentlength", size), ) } - propstatOK.Prop = append(propstatOK.Prop, props.NewProp("oc:spaceid", spaceID)) + propstatOK.Prop = append(propstatOK.Prop, prop.Escaped("oc:spaceid", spaceID)) response.Propstat = append(response.Propstat, propstatOK) } else { // otherwise return only the requested properties propstatOK := propfind.PropstatXML{ Status: "HTTP/1.1 200 OK", - Prop: []props.PropertyXML{}, + Prop: []prop.PropertyXML{}, } propstatNotFound := propfind.PropstatXML{ Status: "HTTP/1.1 404 Not Found", - Prop: []props.PropertyXML{}, + Prop: []prop.PropertyXML{}, } for i := range pf.Prop { switch pf.Prop[i].Space { @@ -396,52 +396,52 @@ func (h *TrashbinHandler) itemToPropResponse(ctx context.Context, s *svc, spaceI switch pf.Prop[i].Local { case "oc:size": if item.Type == provider.ResourceType_RESOURCE_TYPE_CONTAINER { - propstatOK.Prop = append(propstatOK.Prop, props.NewProp("d:getcontentlength", size)) + propstatOK.Prop = append(propstatOK.Prop, prop.Escaped("d:getcontentlength", size)) } else { - propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewNotFound("oc:size")) + propstatNotFound.Prop = append(propstatNotFound.Prop, prop.NotFound("oc:size")) } case "trashbin-original-filename": // yes this is redundant, can be derived from oc:trashbin-original-location which contains the full path, clients should not fetch it - propstatOK.Prop = append(propstatOK.Prop, props.NewProp("oc:trashbin-original-filename", path.Base(item.Ref.Path))) + propstatOK.Prop = append(propstatOK.Prop, prop.Escaped("oc:trashbin-original-filename", path.Base(item.Ref.Path))) case "trashbin-original-location": // TODO (jfd) double check and clarify the cs3 spec what the Key is about and if Path is only the folder that contains the file or if it includes the filename - propstatOK.Prop = append(propstatOK.Prop, props.NewProp("oc:trashbin-original-location", strings.TrimPrefix(item.Ref.Path, "/"))) + propstatOK.Prop = append(propstatOK.Prop, prop.Escaped("oc:trashbin-original-location", strings.TrimPrefix(item.Ref.Path, "/"))) case "trashbin-delete-datetime": - propstatOK.Prop = append(propstatOK.Prop, props.NewProp("oc:trashbin-delete-datetime", dTime)) + propstatOK.Prop = append(propstatOK.Prop, prop.Escaped("oc:trashbin-delete-datetime", dTime)) case "trashbin-delete-timestamp": - propstatOK.Prop = append(propstatOK.Prop, props.NewProp("oc:trashbin-delete-timestamp", strconv.FormatUint(item.DeletionTime.Seconds, 10))) + propstatOK.Prop = append(propstatOK.Prop, prop.Escaped("oc:trashbin-delete-timestamp", strconv.FormatUint(item.DeletionTime.Seconds, 10))) case "spaceid": - propstatOK.Prop = append(propstatOK.Prop, props.NewProp("oc:spaceid", spaceID)) + propstatOK.Prop = append(propstatOK.Prop, prop.Escaped("oc:spaceid", spaceID)) default: - propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewNotFound("oc:"+pf.Prop[i].Local)) + propstatNotFound.Prop = append(propstatNotFound.Prop, prop.NotFound("oc:"+pf.Prop[i].Local)) } case net.NsDav: switch pf.Prop[i].Local { case "getcontentlength": if item.Type == provider.ResourceType_RESOURCE_TYPE_CONTAINER { - propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewNotFound("d:getcontentlength")) + propstatNotFound.Prop = append(propstatNotFound.Prop, prop.NotFound("d:getcontentlength")) } else { - propstatOK.Prop = append(propstatOK.Prop, props.NewProp("d:getcontentlength", size)) + propstatOK.Prop = append(propstatOK.Prop, prop.Escaped("d:getcontentlength", size)) } case "resourcetype": if item.Type == provider.ResourceType_RESOURCE_TYPE_CONTAINER { - propstatOK.Prop = append(propstatOK.Prop, props.NewPropRaw("d:resourcetype", "")) + propstatOK.Prop = append(propstatOK.Prop, prop.Raw("d:resourcetype", "")) } else { - propstatOK.Prop = append(propstatOK.Prop, props.NewPropRaw("d:resourcetype", "")) + propstatOK.Prop = append(propstatOK.Prop, prop.Raw("d:resourcetype", "")) // redirectref is another option } case "getcontenttype": if item.Type == provider.ResourceType_RESOURCE_TYPE_CONTAINER { - propstatOK.Prop = append(propstatOK.Prop, props.NewPropRaw("d:getcontenttype", "httpd/unix-directory")) + propstatOK.Prop = append(propstatOK.Prop, prop.Raw("d:getcontenttype", "httpd/unix-directory")) } else { - propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewNotFound("d:getcontenttype")) + propstatNotFound.Prop = append(propstatNotFound.Prop, prop.NotFound("d:getcontenttype")) } default: - propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewNotFound("d:"+pf.Prop[i].Local)) + propstatNotFound.Prop = append(propstatNotFound.Prop, prop.NotFound("d:"+pf.Prop[i].Local)) } default: // TODO (jfd) lookup shortname for unknown namespaces? - propstatNotFound.Prop = append(propstatNotFound.Prop, props.NewNotFound(pf.Prop[i].Space+":"+pf.Prop[i].Local)) + propstatNotFound.Prop = append(propstatNotFound.Prop, prop.NotFound(pf.Prop[i].Space+":"+pf.Prop[i].Local)) } } response.Propstat = append(response.Propstat, propstatOK, propstatNotFound) From e8882b57c25cbcdae3bdff53fd971c1032390cea Mon Sep 17 00:00:00 2001 From: David Christofas Date: Tue, 5 Apr 2022 11:34:12 +0200 Subject: [PATCH 6/6] update core commit id --- .drone.env | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.drone.env b/.drone.env index fd04cf6687..15c8d1ed6b 100644 --- a/.drone.env +++ b/.drone.env @@ -1,3 +1,3 @@ # The test runner source for API tests -CORE_COMMITID=e285879a8a79e692497937ebf340bc6b9c925b4f +CORE_COMMITID=cb558615d4a1850629be1c463454dabac71a4905 CORE_BRANCH=master