diff --git a/internal/http/services/owncloud/ocdav/propfind.go b/internal/http/services/owncloud/ocdav/propfind.go
index e9bf990319d..700fc0360ea 100644
--- a/internal/http/services/owncloud/ocdav/propfind.go
+++ b/internal/http/services/owncloud/ocdav/propfind.go
@@ -29,7 +29,6 @@ import (
"path"
"strconv"
"strings"
- "time"
"go.opencensus.io/trace"
@@ -50,6 +49,9 @@ const (
_nsOCS = "http://open-collaboration-services.org/ns"
_propOcFavorite = "http://owncloud.org/ns/favorite"
+
+ // RFC1123 time that mimics oc10. time.RFC1123 would end in "UTC", see https://github.com/golang/go/issues/13781
+ RFC1123 = "Mon, 02 Jan 2006 15:04:05 GMT"
)
// ns is the namespace that is prefixed to the path in the cs3 namespace
@@ -344,7 +346,11 @@ func (s *svc) mdToPropResponse(ctx context.Context, pf *propfindXML, md *provide
isShared := !isCurrentUserOwner(ctx, md.Owner)
var wdp string
- if md.PermissionSet != nil {
+ switch {
+ case ls != nil:
+ // link share only appears on root collection
+ wdp = ""
+ case md.PermissionSet != nil:
wdp = role.WebDAVPermissions(
md.Type == provider.ResourceType_RESOURCE_TYPE_CONTAINER,
isShared,
@@ -385,32 +391,30 @@ func (s *svc) mdToPropResponse(ctx context.Context, pf *propfindXML, md *provide
propstatOK.Prop = append(propstatOK.Prop, s.newProp("oc:permissions", wdp))
}
- // always return size
+ // always return size, well nearly always ... public link shares are a little weird
size := fmt.Sprintf("%d", md.Size)
if md.Type == provider.ResourceType_RESOURCE_TYPE_CONTAINER {
- propstatOK.Prop = append(propstatOK.Prop,
- s.newPropRaw("d:resourcetype", ""),
- s.newProp("oc:size", size),
- )
- propstatNotFound.Prop = append(propstatNotFound.Prop,
- s.newProp("d:getcontenttype", ""),
- s.newProp("d:getcontentlength", ""),
- )
+ propstatOK.Prop = append(propstatOK.Prop, s.newPropRaw("d:resourcetype", ""))
+ if ls == nil {
+ propstatOK.Prop = append(propstatOK.Prop, s.newProp("oc:size", size))
+ } else {
+ //no size, but also no 404
+ }
} else {
propstatOK.Prop = append(propstatOK.Prop,
s.newProp("d:resourcetype", ""),
s.newProp("d:getcontentlength", size),
)
if md.MimeType != "" {
- propstatOK.Prop = append(propstatOK.Prop,
- s.newProp("d:getcontenttype", md.MimeType),
- )
+ propstatOK.Prop = append(propstatOK.Prop, s.newProp("d:getcontenttype", md.MimeType))
}
}
// Finder needs the getLastModified property to work.
- t := utils.TSToTime(md.Mtime).UTC()
- lastModifiedString := t.Format(time.RFC1123Z)
- propstatOK.Prop = append(propstatOK.Prop, s.newProp("d:getlastmodified", lastModifiedString))
+ if md.Mtime != nil {
+ t := utils.TSToTime(md.Mtime).UTC()
+ lastModifiedString := t.Format(RFC1123)
+ propstatOK.Prop = append(propstatOK.Prop, s.newProp("d:getlastmodified", lastModifiedString))
+ }
// stay bug compatible with oc10, see https://github.com/owncloud/core/pull/38304#issuecomment-762185241
var checksums strings.Builder
@@ -443,15 +447,18 @@ func (s *svc) mdToPropResponse(ctx context.Context, pf *propfindXML, md *provide
propstatOK.Prop = append(propstatOK.Prop, s.newPropRaw("oc:checksums", checksums.String()))
}
- // favorites from arbitrary metadata
- if k := md.GetArbitraryMetadata(); k == nil {
- 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[_propOcFavorite]; ok && v != "" {
- propstatOK.Prop = append(propstatOK.Prop, s.newProp("oc:favorite", v))
- } else {
- propstatOK.Prop = append(propstatOK.Prop, s.newProp("oc:favorite", "0"))
+ // 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, 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[_propOcFavorite]; ok && v != "" {
+ propstatOK.Prop = append(propstatOK.Prop, s.newProp("oc:favorite", v))
+ } else {
+ propstatOK.Prop = append(propstatOK.Prop, s.newProp("oc:favorite", "0"))
+ }
}
// TODO return other properties ... but how do we put them in a namespace?
} else {
@@ -512,7 +519,7 @@ func (s *svc) mdToPropResponse(ctx context.Context, pf *propfindXML, md *provide
case "public-link-share-datetime":
if ls != nil && ls.Mtime != nil {
t := utils.TSToTime(ls.Mtime).UTC() // TODO or ctime?
- shareTimeString := t.Format(time.RFC1123Z)
+ shareTimeString := t.Format(RFC1123)
propstatOK.Prop = append(propstatOK.Prop, s.newProp("oc:public-link-share-datetime", shareTimeString))
} else {
propstatNotFound.Prop = append(propstatNotFound.Prop, s.newProp("oc:public-link-share-datetime", ""))
@@ -533,7 +540,7 @@ func (s *svc) mdToPropResponse(ctx context.Context, pf *propfindXML, md *provide
case "public-link-expiration":
if ls != nil && ls.Expiration != nil {
t := utils.TSToTime(ls.Expiration).UTC()
- expireTimeString := t.Format(time.RFC1123Z)
+ expireTimeString := t.Format(RFC1123)
propstatOK.Prop = append(propstatOK.Prop, s.newProp("oc:public-link-expiration", expireTimeString))
} else {
propstatNotFound.Prop = append(propstatNotFound.Prop, s.newProp("oc:public-link-expiration", ""))
@@ -542,7 +549,12 @@ func (s *svc) mdToPropResponse(ctx context.Context, pf *propfindXML, md *provide
case "size": // phoenix only
// TODO we cannot find out if md.Size is set or not because ints in go default to 0
// oc:size is also available on folders
- propstatOK.Prop = append(propstatOK.Prop, s.newProp("oc:size", size))
+ if ls == nil {
+ propstatOK.Prop = append(propstatOK.Prop, s.newProp("oc:size", size))
+ } else {
+ // link share root collection has no size
+ propstatNotFound.Prop = append(propstatNotFound.Prop, s.newProp("oc:size", ""))
+ }
case "owner-id": // phoenix only
if md.Owner != nil {
if isCurrentUserOwner(ctx, md.Owner) {
@@ -559,14 +571,19 @@ func (s *svc) mdToPropResponse(ctx context.Context, pf *propfindXML, md *provide
// TODO: can be 0 or 1?, in oc10 it is present or not
// TODO: read favorite via separate call? that would be expensive? I hope it is in the md
// 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 k := md.GetArbitraryMetadata(); k == nil {
- 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[_propOcFavorite]; ok && v != "" {
- propstatOK.Prop = append(propstatOK.Prop, s.newProp("oc:favorite", "1"))
+ if ls == nil {
+ if k := md.GetArbitraryMetadata(); k == nil {
+ 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[_propOcFavorite]; ok && v != "" {
+ propstatOK.Prop = append(propstatOK.Prop, s.newProp("oc:favorite", "1"))
+ } else {
+ propstatOK.Prop = append(propstatOK.Prop, s.newProp("oc:favorite", "0"))
+ }
} else {
- propstatOK.Prop = append(propstatOK.Prop, s.newProp("oc:favorite", "0"))
+ // link share root collection has no favorite
+ propstatNotFound.Prop = append(propstatNotFound.Prop, s.newProp("oc:favorite", ""))
}
case "checksums": // desktop ... not really ... the desktop sends the OC-Checksum header
@@ -675,9 +692,13 @@ func (s *svc) mdToPropResponse(ctx context.Context, pf *propfindXML, md *provide
}
case "getlastmodified": // both
// TODO we cannot find out if md.Mtime is set or not because ints in go default to 0
- t := utils.TSToTime(md.Mtime).UTC()
- lastModifiedString := t.Format(time.RFC1123Z)
- propstatOK.Prop = append(propstatOK.Prop, s.newProp("d:getlastmodified", lastModifiedString))
+ if md.Mtime != nil {
+ t := utils.TSToTime(md.Mtime).UTC()
+ lastModifiedString := t.Format(RFC1123)
+ propstatOK.Prop = append(propstatOK.Prop, s.newProp("d:getlastmodified", lastModifiedString))
+ } else {
+ propstatNotFound.Prop = append(propstatNotFound.Prop, s.newProp("d:getlastmodified", ""))
+ }
default:
propstatNotFound.Prop = append(propstatNotFound.Prop, s.newProp("d:"+pf.Prop[i].Local, ""))
}
diff --git a/internal/http/services/owncloud/ocdav/publicfile.go b/internal/http/services/owncloud/ocdav/publicfile.go
index cbb3bc944b7..a87e84c8dad 100644
--- a/internal/http/services/owncloud/ocdav/publicfile.go
+++ b/internal/http/services/owncloud/ocdav/publicfile.go
@@ -24,12 +24,13 @@ import (
rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
+ typesv1beta1 "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
"github.com/cs3org/reva/pkg/appctx"
"github.com/cs3org/reva/pkg/rhttp/router"
"go.opencensus.io/trace"
)
-// PublicFileHandler handles trashbin requests
+// PublicFileHandler handles requests on a shared file. it needs to be wrapped in a collection
type PublicFileHandler struct {
namespace string
}
@@ -177,39 +178,15 @@ func (s *svc) handlePropfindOnToken(w http.ResponseWriter, r *http.Request, ns s
return
}
- infos := []*provider.ResourceInfo{}
-
- if onContainer {
- // TODO: filter out metadata like favorite and arbitrary metadata
- if depth != "0" {
- // if the request is to a public link, we need to add yet another value for the file entry.
- infos = append(infos, &provider.ResourceInfo{
- // append the shared as a container. Annex to OC10 standards.
- Id: tokenStatInfo.Id,
- Path: tokenStatInfo.Path,
- Type: provider.ResourceType_RESOURCE_TYPE_CONTAINER,
- Mtime: tokenStatInfo.Mtime,
- Size: tokenStatInfo.Size,
- Etag: tokenStatInfo.Etag,
- PermissionSet: tokenStatInfo.PermissionSet,
- })
- }
- } else if path.Base(r.URL.Path) != path.Base(pathRes.Path) {
+ if !onContainer && path.Base(r.URL.Path) != path.Base(pathRes.Path) {
// if queried on the wrong path, return not found
w.WriteHeader(http.StatusNotFound)
return
}
+ // adjust path
+ tokenStatInfo.Path = path.Join("/", tokenStatInfo.Path, path.Base(pathRes.Path))
- infos = append(infos, &provider.ResourceInfo{
- Id: tokenStatInfo.Id,
- Path: path.Join("/", tokenStatInfo.Path, path.Base(pathRes.Path)),
- Type: tokenStatInfo.Type,
- Size: tokenStatInfo.Size,
- MimeType: tokenStatInfo.MimeType,
- Mtime: tokenStatInfo.Mtime,
- Etag: tokenStatInfo.Etag,
- PermissionSet: tokenStatInfo.PermissionSet,
- })
+ infos := s.getPublicFileInfos(onContainer, depth == "0", tokenStatInfo)
propRes, err := s.formatPropfind(ctx, &pf, infos, ns)
if err != nil {
@@ -225,3 +202,40 @@ func (s *svc) handlePropfindOnToken(w http.ResponseWriter, r *http.Request, ns s
sublog.Err(err).Msg("error writing response")
}
}
+
+// there are only two possible entries
+// 1. the non existing collection
+// 2. the shared file
+func (s *svc) getPublicFileInfos(onContainer, onlyRoot bool, i *provider.ResourceInfo) []*provider.ResourceInfo {
+ infos := []*provider.ResourceInfo{}
+ if onContainer {
+ // copy link-share data if present
+ // we don't copy everything because the checksum should not be present
+ var o *typesv1beta1.Opaque
+ if i.Opaque != nil && i.Opaque.Map != nil && i.Opaque.Map["link-share"] != nil {
+ o = &typesv1beta1.Opaque{
+ Map: map[string]*typesv1beta1.OpaqueEntry{
+ "link-share": i.Opaque.Map["link-share"],
+ },
+ }
+ }
+ // always add collection
+ infos = append(infos, &provider.ResourceInfo{
+ // Opaque carries the link-share data we need when rendering the collection root href
+ Opaque: o,
+ Path: path.Dir(i.Path),
+ Type: provider.ResourceType_RESOURCE_TYPE_CONTAINER,
+ })
+ if onlyRoot {
+ return infos
+ }
+ }
+
+ // link share only appears on root collection
+ delete(i.Opaque.Map, "link-share")
+
+ // add the file info
+ infos = append(infos, i)
+
+ return infos
+}