Skip to content

Commit

Permalink
initial favorites support
Browse files Browse the repository at this point in the history
Signed-off-by: Jörn Friedrich Dreyer <[email protected]>
  • Loading branch information
butonic committed Nov 13, 2019
1 parent 08634bf commit 46f30fb
Show file tree
Hide file tree
Showing 3 changed files with 274 additions and 34 deletions.
26 changes: 22 additions & 4 deletions internal/http/services/owncloud/ocdav/propfind.go
Original file line number Diff line number Diff line change
Expand Up @@ -251,8 +251,17 @@ func (s *svc) mdToPropResponse(ctx context.Context, pf *propfindXML, md *storage
response.Propstat[0].Prop = append(response.Propstat[0].Prop, s.newProp("oc:checksums", value))
}

// TODO: read favorite via separate call? that would be expensive? I hope it is in the md
response.Propstat[0].Prop = append(response.Propstat[0].Prop, s.newProp("oc:favorite", "0"))
// favorites from arbitrary metadata
if k := md.GetArbitraryMetadata(); k == nil {
response.Propstat[0].Prop = append(response.Propstat[0].Prop, s.newProp("oc:favorite", "0"))
} else if amd := k.GetMetadata(); amd == nil {
response.Propstat[0].Prop = append(response.Propstat[0].Prop, s.newProp("oc:favorite", "0"))
} else if v, ok := amd["http://owncloud.org/ns/favorite"]; ok && v != "" {
response.Propstat[0].Prop = append(response.Propstat[0].Prop, s.newProp("oc:favorite", "1"))
} else {
response.Propstat[0].Prop = append(response.Propstat[0].Prop, s.newProp("oc:favorite", "0"))
}
// TODO return other properties ... but how do we put them in a namespace?
} else {
// otherwise return only the requested properties
propstatOK := propstatXML{
Expand Down Expand Up @@ -300,9 +309,18 @@ func (s *svc) mdToPropResponse(ctx context.Context, pf *propfindXML, md *storage
propstatNotFound.Prop = append(propstatNotFound.Prop, s.newProp("oc:owner-id", ""))
}
case "favorite": // phoenix only
// can be 0 or 1
// 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
propstatOK.Prop = append(propstatOK.Prop, s.newProp("oc:favorite", "0"))
// 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["http://owncloud.org/ns/favorite"]; ok && v != "" {
propstatOK.Prop = append(propstatOK.Prop, s.newProp("oc:favorite", "1"))
} else {
propstatOK.Prop = append(propstatOK.Prop, s.newProp("oc:favorite", "0"))
}
case "checksums": // desktop
if md.Checksum != nil {
// TODO(jfd): the actual value is an abomination like this:
Expand Down
129 changes: 124 additions & 5 deletions internal/http/services/owncloud/ocdav/proppatch.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,32 +24,151 @@ import (
"fmt"
"io"
"net/http"
"path"

"go.opencensus.io/trace"

rpcpb "github.com/cs3org/go-cs3apis/cs3/rpc"
storageproviderv0alphapb "github.com/cs3org/go-cs3apis/cs3/storageprovider/v0alpha"
"github.com/cs3org/reva/pkg/appctx"
"github.com/pkg/errors"
)

func (s *svc) doProppatch(w http.ResponseWriter, r *http.Request, ns string) {
ctx := r.Context()
ctx, span := trace.StartSpan(ctx, "proppatch")
defer span.End()
log := appctx.GetLogger(ctx)
//fn := path.Join(ns, r.URL.Path)

_, status, err := readProppatch(r.Body)
fn := path.Join(ns, r.URL.Path)

pp, status, err := readProppatch(r.Body)
if err != nil {
log.Error().Err(err).Msg("error reading proppatch")
w.WriteHeader(status)
return
}

_, err = s.getClient()
c, err := s.getClient()
if err != nil {
log.Error().Err(err).Msg("error getting grpc client")
w.WriteHeader(http.StatusInternalServerError)
return
}
// TODO(jfd): implement properties

w.WriteHeader(http.StatusNotImplemented)
mkeys := []string{}

pf := &propfindXML{
Prop: propfindProps{},
}
for i := range pp {
if len(pp[i].Props) < 1 {
continue
}
if pp[i].Remove {
rreq := &storageproviderv0alphapb.UnsetArbitraryMetadataRequest{
Ref: &storageproviderv0alphapb.Reference{
Spec: &storageproviderv0alphapb.Reference_Path{Path: fn},
},
ArbitraryMetadataKeys: make([]string, len(pp[i].Props)),
}
for j := range pp[i].Props {
pf.Prop = append(pf.Prop, pp[i].Props[j].XMLName)
// don't use path.Join. It removes the double slash! concatenate with a /
key := fmt.Sprintf("%s/%s", pp[i].Props[j].XMLName.Space, pp[i].Props[j].XMLName.Local)
rreq.ArbitraryMetadataKeys = append(rreq.ArbitraryMetadataKeys, key)
}
mkeys = append(mkeys, rreq.ArbitraryMetadataKeys...)
res, err := c.UnsetArbitraryMetadata(ctx, rreq)
if err != nil {
log.Error().Err(err).Msg("error sending a grpc UnsetArbitraryMetadata request")
w.WriteHeader(http.StatusInternalServerError)
return
}

if res.Status.Code != rpcpb.Code_CODE_OK {
if res.Status.Code == rpcpb.Code_CODE_NOT_FOUND {
log.Warn().Str("path", fn).Msg("resource not found")
w.WriteHeader(http.StatusNotFound)
return
}
w.WriteHeader(http.StatusInternalServerError)
return
}
} else {
sreq := &storageproviderv0alphapb.SetArbitraryMetadataRequest{
Ref: &storageproviderv0alphapb.Reference{
Spec: &storageproviderv0alphapb.Reference_Path{Path: fn},
},
ArbitraryMetadata: &storageproviderv0alphapb.ArbitraryMetadata{
Metadata: map[string]string{},
},
}
for j := range pp[i].Props {
pf.Prop = append(pf.Prop, pp[i].Props[j].XMLName)
// don't use path.Join. It removes the double slash! concatenate with a /
key := fmt.Sprintf("%s/%s", pp[i].Props[j].XMLName.Space, pp[i].Props[j].XMLName.Local)
mkeys = append(mkeys, key)
sreq.ArbitraryMetadata.Metadata[key] = string(pp[i].Props[j].InnerXML)
}
res, err := c.SetArbitraryMetadata(ctx, sreq)
if err != nil {
log.Error().Err(err).Msg("error sending a grpc SetArbitraryMetadata request")
w.WriteHeader(http.StatusInternalServerError)
return
}

if res.Status.Code != rpcpb.Code_CODE_OK {
if res.Status.Code == rpcpb.Code_CODE_NOT_FOUND {
log.Warn().Str("path", fn).Msg("resource not found")
w.WriteHeader(http.StatusNotFound)
return
}
w.WriteHeader(http.StatusInternalServerError)
return
}
}

}

req := &storageproviderv0alphapb.StatRequest{
Ref: &storageproviderv0alphapb.Reference{
Spec: &storageproviderv0alphapb.Reference_Path{Path: fn},
},
ArbitraryMetadataKeys: mkeys,
}
res, err := c.Stat(ctx, req)
if err != nil {
log.Error().Err(err).Msg("error sending a grpc stat request")
w.WriteHeader(http.StatusInternalServerError)
return
}

if res.Status.Code != rpcpb.Code_CODE_OK {
if res.Status.Code == rpcpb.Code_CODE_NOT_FOUND {
log.Warn().Str("path", fn).Msg("resource not found")
w.WriteHeader(http.StatusNotFound)
return
}
w.WriteHeader(http.StatusInternalServerError)
return
}

info := res.Info
infos := []*storageproviderv0alphapb.ResourceInfo{info}

propRes, err := s.formatPropfind(ctx, pf, infos, ns)
if err != nil {
log.Error().Err(err).Msg("error formatting propfind")
w.WriteHeader(http.StatusInternalServerError)
return
}
w.Header().Set("DAV", "1, 3, extended-mkcol")
w.Header().Set("Content-Type", "application/xml; charset=utf-8")
w.WriteHeader(http.StatusMultiStatus)
if _, err := w.Write([]byte(propRes)); err != nil {
log.Err(err).Msg("error writing response")
}
}

// Proppatch describes a property update instruction as defined in RFC 4918.
Expand Down
Loading

0 comments on commit 46f30fb

Please sign in to comment.