mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-02-22 05:29:01 -06:00
177 lines
6.1 KiB
Go
177 lines
6.1 KiB
Go
package propfind
|
|
|
|
import (
|
|
"encoding/xml"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
|
|
"github.com/owncloud/ocis/v2/services/webdav/pkg/errors"
|
|
"github.com/owncloud/ocis/v2/services/webdav/pkg/prop"
|
|
)
|
|
|
|
type countingReader struct {
|
|
n int
|
|
r io.Reader
|
|
}
|
|
|
|
func (c *countingReader) Read(p []byte) (int, error) {
|
|
n, err := c.r.Read(p)
|
|
c.n += n
|
|
return n, err
|
|
}
|
|
|
|
// Props represents properties related to a resource
|
|
// http://www.webdav.org/specs/rfc4918.html#ELEMENT_prop (for propfind)
|
|
type Props []xml.Name
|
|
|
|
// XML holds the xml representation of a propfind
|
|
// http://www.webdav.org/specs/rfc4918.html#ELEMENT_propfind
|
|
type XML struct {
|
|
XMLName xml.Name `xml:"DAV: propfind"`
|
|
Allprop *struct{} `xml:"DAV: allprop"`
|
|
Propname *struct{} `xml:"DAV: propname"`
|
|
Prop Props `xml:"DAV: prop"`
|
|
Include Props `xml:"DAV: include"`
|
|
}
|
|
|
|
// PropstatXML holds the xml representation of a propfind response
|
|
// http://www.webdav.org/specs/rfc4918.html#ELEMENT_propstat
|
|
type PropstatXML struct {
|
|
// Prop requires DAV: to be the default namespace in the enclosing
|
|
// XML. This is due to the standard encoding/xml package currently
|
|
// not honoring namespace declarations inside a xmltag with a
|
|
// parent element for anonymous slice elements.
|
|
// Use of multistatusWriter takes care of this.
|
|
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
|
|
type ResponseXML struct {
|
|
XMLName xml.Name `xml:"d:response"`
|
|
Href string `xml:"d:href"`
|
|
Propstat []PropstatXML `xml:"d:propstat"`
|
|
Status string `xml:"d:status,omitempty"`
|
|
Error *errors.ErrorXML `xml:"d:error"`
|
|
ResponseDescription string `xml:"d:responsedescription,omitempty"`
|
|
}
|
|
|
|
// MultiStatusResponseXML holds the xml representation of a multistatus propfind response
|
|
type MultiStatusResponseXML struct {
|
|
XMLName xml.Name `xml:"d:multistatus"`
|
|
XmlnsS string `xml:"xmlns:s,attr,omitempty"`
|
|
XmlnsD string `xml:"xmlns:d,attr,omitempty"`
|
|
XmlnsOC string `xml:"xmlns:oc,attr,omitempty"`
|
|
|
|
Responses []*ResponseXML `xml:"d:response"`
|
|
}
|
|
|
|
// ResponseUnmarshalXML is a workaround for https://github.com/golang/go/issues/13400
|
|
type ResponseUnmarshalXML struct {
|
|
XMLName xml.Name `xml:"response"`
|
|
Href string `xml:"href"`
|
|
Propstat []PropstatUnmarshalXML `xml:"propstat"`
|
|
Status string `xml:"status,omitempty"`
|
|
Error *errors.ErrorXML `xml:"d:error"`
|
|
ResponseDescription string `xml:"responsedescription,omitempty"`
|
|
}
|
|
|
|
// MultiStatusResponseUnmarshalXML is a workaround for https://github.com/golang/go/issues/13400
|
|
type MultiStatusResponseUnmarshalXML struct {
|
|
XMLName xml.Name `xml:"multistatus"`
|
|
XmlnsS string `xml:"xmlns:s,attr,omitempty"`
|
|
XmlnsD string `xml:"xmlns:d,attr,omitempty"`
|
|
XmlnsOC string `xml:"xmlns:oc,attr,omitempty"`
|
|
|
|
Responses []*ResponseUnmarshalXML `xml:"response"`
|
|
}
|
|
|
|
// PropstatUnmarshalXML is a workaround for https://github.com/golang/go/issues/13400
|
|
type PropstatUnmarshalXML struct {
|
|
// Prop requires DAV: to be the default namespace in the enclosing
|
|
// XML. This is due to the standard encoding/xml package currently
|
|
// not honoring namespace declarations inside a xmltag with a
|
|
// parent element for anonymous slice elements.
|
|
// Use of multistatusWriter takes care of this.
|
|
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
|
|
func NewMultiStatusResponseXML() *MultiStatusResponseXML {
|
|
return &MultiStatusResponseXML{
|
|
XmlnsD: "DAV:",
|
|
XmlnsS: "http://sabredav.org/ns",
|
|
XmlnsOC: "http://owncloud.org/ns",
|
|
}
|
|
}
|
|
|
|
// ReadPropfind extracts and parses the propfind XML information from a Reader
|
|
// from https://github.com/golang/net/blob/e514e69ffb8bc3c76a71ae40de0118d794855992/webdav/xml.go#L178-L205
|
|
func ReadPropfind(r io.Reader) (pf XML, status int, err error) {
|
|
c := countingReader{r: r}
|
|
if err = xml.NewDecoder(&c).Decode(&pf); err != nil {
|
|
if err == io.EOF {
|
|
if c.n == 0 {
|
|
// An empty body means to propfind allprop.
|
|
// http://www.webdav.org/specs/rfc4918.html#METHOD_PROPFIND
|
|
return XML{Allprop: new(struct{})}, 0, nil
|
|
}
|
|
err = errors.ErrInvalidPropfind
|
|
}
|
|
return XML{}, http.StatusBadRequest, err
|
|
}
|
|
|
|
if pf.Allprop == nil && pf.Include != nil {
|
|
return XML{}, http.StatusBadRequest, errors.ErrInvalidPropfind
|
|
}
|
|
if pf.Allprop != nil && (pf.Prop != nil || pf.Propname != nil) {
|
|
return XML{}, http.StatusBadRequest, errors.ErrInvalidPropfind
|
|
}
|
|
if pf.Prop != nil && pf.Propname != nil {
|
|
return XML{}, http.StatusBadRequest, errors.ErrInvalidPropfind
|
|
}
|
|
if pf.Propname == nil && pf.Allprop == nil && pf.Prop == nil {
|
|
// jfd: I think <d:prop></d:prop> is perfectly valid ... treat it as allprop
|
|
return XML{Allprop: new(struct{})}, 0, nil
|
|
}
|
|
return pf, 0, nil
|
|
}
|
|
|
|
// UnmarshalXML appends the property names enclosed within start to pn.
|
|
//
|
|
// It returns an error if start does not contain any properties or if
|
|
// properties contain values. Character data between properties is ignored.
|
|
func (pn *Props) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
|
|
for {
|
|
t, err := prop.Next(d)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
switch e := t.(type) {
|
|
case xml.EndElement:
|
|
// jfd: I think <d:prop></d:prop> is perfectly valid ... treat it as allprop
|
|
/*
|
|
if len(*pn) == 0 {
|
|
return fmt.Errorf("%s must not be empty", start.Name.Local)
|
|
}
|
|
*/
|
|
return nil
|
|
case xml.StartElement:
|
|
t, err = prop.Next(d)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if _, ok := t.(xml.EndElement); !ok {
|
|
return fmt.Errorf("unexpected token %T", t)
|
|
}
|
|
*pn = append(*pn, e.Name)
|
|
}
|
|
}
|
|
}
|