mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-03-09 10:47:35 -04:00
check in missing packages
Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>
This commit is contained in:
14
search/cmd/search/main.go
Normal file
14
search/cmd/search/main.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/owncloud/ocis/search/pkg/command"
|
||||
"github.com/owncloud/ocis/search/pkg/config/defaults"
|
||||
)
|
||||
|
||||
func main() {
|
||||
if err := command.Execute(defaults.DefaultConfig()); err != nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
170
webdav/pkg/errors/error.go
Normal file
170
webdav/pkg/errors/error.go
Normal file
@@ -0,0 +1,170 @@
|
||||
package errors
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/xml"
|
||||
"net/http"
|
||||
|
||||
rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
|
||||
"github.com/cs3org/reva/v2/pkg/rgrpc/status"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rs/zerolog"
|
||||
)
|
||||
|
||||
var sabreException = map[int]string{
|
||||
|
||||
// the commented states have no corresponding exception in sabre/dav,
|
||||
// see https://github.com/sabre-io/dav/tree/master/lib/DAV/Exception
|
||||
|
||||
// http.StatusMultipleChoices: "Multiple Choices",
|
||||
// http.StatusMovedPermanently: "Moved Permanently",
|
||||
// http.StatusFound: "Found",
|
||||
// http.StatusSeeOther: "See Other",
|
||||
// http.StatusNotModified: "Not Modified",
|
||||
// http.StatusUseProxy: "Use Proxy",
|
||||
// http.StatusTemporaryRedirect: "Temporary Redirect",
|
||||
// http.StatusPermanentRedirect: "Permanent Redirect",
|
||||
|
||||
http.StatusBadRequest: "Sabre\\DAV\\Exception\\BadRequest",
|
||||
http.StatusUnauthorized: "Sabre\\DAV\\Exception\\NotAuthenticated",
|
||||
http.StatusPaymentRequired: "Sabre\\DAV\\Exception\\PaymentRequired",
|
||||
http.StatusForbidden: "Sabre\\DAV\\Exception\\Forbidden", // InvalidResourceType, InvalidSyncToken, TooManyMatches
|
||||
http.StatusNotFound: "Sabre\\DAV\\Exception\\NotFound",
|
||||
http.StatusMethodNotAllowed: "Sabre\\DAV\\Exception\\MethodNotAllowed",
|
||||
// http.StatusNotAcceptable: "Not Acceptable",
|
||||
// http.StatusProxyAuthRequired: "Proxy Authentication Required",
|
||||
// http.StatusRequestTimeout: "Request Timeout",
|
||||
http.StatusConflict: "Sabre\\DAV\\Exception\\Conflict", // LockTokenMatchesRequestUri
|
||||
// http.StatusGone: "Gone",
|
||||
http.StatusLengthRequired: "Sabre\\DAV\\Exception\\LengthRequired",
|
||||
http.StatusPreconditionFailed: "Sabre\\DAV\\Exception\\PreconditionFailed",
|
||||
// http.StatusRequestEntityTooLarge: "Request Entity Too Large",
|
||||
// http.StatusRequestURITooLong: "Request URI Too Long",
|
||||
http.StatusUnsupportedMediaType: "Sabre\\DAV\\Exception\\UnsupportedMediaType", // ReportNotSupported
|
||||
http.StatusRequestedRangeNotSatisfiable: "Sabre\\DAV\\Exception\\RequestedRangeNotSatisfiable",
|
||||
// http.StatusExpectationFailed: "Expectation Failed",
|
||||
// http.StatusTeapot: "I'm a teapot",
|
||||
// http.StatusMisdirectedRequest: "Misdirected Request",
|
||||
// http.StatusUnprocessableEntity: "Unprocessable Entity",
|
||||
http.StatusLocked: "Sabre\\DAV\\Exception\\Locked", // ConflictingLock
|
||||
// http.StatusFailedDependency: "Failed Dependency",
|
||||
// http.StatusTooEarly: "Too Early",
|
||||
// http.StatusUpgradeRequired: "Upgrade Required",
|
||||
// http.StatusPreconditionRequired: "Precondition Required",
|
||||
// http.StatusTooManyRequests: "Too Many Requests",
|
||||
// http.StatusRequestHeaderFieldsTooLarge: "Request Header Fields Too Large",
|
||||
// http.StatusUnavailableForLegalReasons: "Unavailable For Legal Reasons",
|
||||
|
||||
// http.StatusInternalServerError: "Internal Server Error",
|
||||
http.StatusNotImplemented: "Sabre\\DAV\\Exception\\NotImplemented",
|
||||
// http.StatusBadGateway: "Bad Gateway",
|
||||
http.StatusServiceUnavailable: "Sabre\\DAV\\Exception\\ServiceUnavailable",
|
||||
// http.StatusGatewayTimeout: "Gateway Timeout",
|
||||
// http.StatusHTTPVersionNotSupported: "HTTP Version Not Supported",
|
||||
// http.StatusVariantAlsoNegotiates: "Variant Also Negotiates",
|
||||
http.StatusInsufficientStorage: "Sabre\\DAV\\Exception\\InsufficientStorage",
|
||||
// http.StatusLoopDetected: "Loop Detected",
|
||||
// http.StatusNotExtended: "Not Extended",
|
||||
// http.StatusNetworkAuthenticationRequired: "Network Authentication Required",
|
||||
}
|
||||
|
||||
// SabreException returns a sabre exception text for the HTTP status code. It returns the empty
|
||||
// string if the code is unknown.
|
||||
func SabreException(code int) string {
|
||||
return sabreException[code]
|
||||
}
|
||||
|
||||
// Exception represents a ocdav exception
|
||||
type Exception struct {
|
||||
Code int
|
||||
Message string
|
||||
Header string
|
||||
}
|
||||
|
||||
// Marshal just calls the xml marshaller for a given exception.
|
||||
func Marshal(code int, message string, header string) ([]byte, error) {
|
||||
xmlstring, err := xml.Marshal(&ErrorXML{
|
||||
Xmlnsd: "DAV",
|
||||
Xmlnss: "http://sabredav.org/ns",
|
||||
Exception: sabreException[code],
|
||||
Message: message,
|
||||
Header: header,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
buf.WriteString(xml.Header)
|
||||
buf.Write(xmlstring)
|
||||
return buf.Bytes(), err
|
||||
}
|
||||
|
||||
// ErrorXML holds the xml representation of an error
|
||||
// http://www.webdav.org/specs/rfc4918.html#ELEMENT_error
|
||||
type ErrorXML struct {
|
||||
XMLName xml.Name `xml:"d:error"`
|
||||
Xmlnsd string `xml:"xmlns:d,attr"`
|
||||
Xmlnss string `xml:"xmlns:s,attr"`
|
||||
Exception string `xml:"s:exception"`
|
||||
Message string `xml:"s:message"`
|
||||
InnerXML []byte `xml:",innerxml"`
|
||||
// Header is used to indicate the conflicting request header
|
||||
Header string `xml:"s:header,omitempty"`
|
||||
}
|
||||
|
||||
var (
|
||||
// ErrInvalidDepth is an invalid depth header error
|
||||
ErrInvalidDepth = errors.New("webdav: invalid depth")
|
||||
// ErrInvalidPropfind is an invalid propfind error
|
||||
ErrInvalidPropfind = errors.New("webdav: invalid propfind")
|
||||
// ErrInvalidProppatch is an invalid proppatch error
|
||||
ErrInvalidProppatch = errors.New("webdav: invalid proppatch")
|
||||
// ErrInvalidLockInfo is an invalid lock error
|
||||
ErrInvalidLockInfo = errors.New("webdav: invalid lock info")
|
||||
// ErrUnsupportedLockInfo is an unsupported lock error
|
||||
ErrUnsupportedLockInfo = errors.New("webdav: unsupported lock info")
|
||||
// ErrInvalidTimeout is an invalid timeout error
|
||||
ErrInvalidTimeout = errors.New("webdav: invalid timeout")
|
||||
// ErrInvalidIfHeader is an invalid if header error
|
||||
ErrInvalidIfHeader = errors.New("webdav: invalid If header")
|
||||
// ErrUnsupportedMethod is an unsupported method error
|
||||
ErrUnsupportedMethod = errors.New("webdav: unsupported method")
|
||||
// ErrInvalidLockToken is an invalid lock token error
|
||||
ErrInvalidLockToken = errors.New("webdav: invalid lock token")
|
||||
// ErrConfirmationFailed is returned by a LockSystem's Confirm method.
|
||||
ErrConfirmationFailed = errors.New("webdav: confirmation failed")
|
||||
// ErrForbidden is returned by a LockSystem's Unlock method.
|
||||
ErrForbidden = errors.New("webdav: forbidden")
|
||||
// ErrLocked is returned by a LockSystem's Create, Refresh and Unlock methods.
|
||||
ErrLocked = errors.New("webdav: locked")
|
||||
// ErrNoSuchLock is returned by a LockSystem's Refresh and Unlock methods.
|
||||
ErrNoSuchLock = errors.New("webdav: no such lock")
|
||||
// ErrNotImplemented is returned when hitting not implemented code paths
|
||||
ErrNotImplemented = errors.New("webdav: not implemented")
|
||||
)
|
||||
|
||||
// HandleErrorStatus checks the status code, logs a Debug or Error level message
|
||||
// and writes an appropriate http status
|
||||
func HandleErrorStatus(log *zerolog.Logger, w http.ResponseWriter, s *rpc.Status) {
|
||||
hsc := status.HTTPStatusFromCode(s.Code)
|
||||
if hsc == http.StatusInternalServerError {
|
||||
log.Error().Interface("status", s).Int("code", hsc).Msg(http.StatusText(hsc))
|
||||
} else {
|
||||
log.Debug().Interface("status", s).Int("code", hsc).Msg(http.StatusText(hsc))
|
||||
}
|
||||
w.WriteHeader(hsc)
|
||||
}
|
||||
|
||||
// HandleWebdavError checks the status code, logs an error and creates a webdav response body
|
||||
// if needed
|
||||
func HandleWebdavError(log *zerolog.Logger, w http.ResponseWriter, b []byte, err error) {
|
||||
if err != nil {
|
||||
log.Error().Msgf("error marshaling xml response: %s", b)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
_, err = w.Write(b)
|
||||
if err != nil {
|
||||
log.Err(err).Msg("error writing response")
|
||||
}
|
||||
}
|
||||
48
webdav/pkg/net/headers.go
Normal file
48
webdav/pkg/net/headers.go
Normal file
@@ -0,0 +1,48 @@
|
||||
package net
|
||||
|
||||
// Common HTTP headers.
|
||||
const (
|
||||
HeaderAcceptRanges = "Accept-Ranges"
|
||||
HeaderAccessControlAllowHeaders = "Access-Control-Allow-Headers"
|
||||
HeaderAccessControlExposeHeaders = "Access-Control-Expose-Headers"
|
||||
HeaderContentDisposistion = "Content-Disposition"
|
||||
HeaderContentLength = "Content-Length"
|
||||
HeaderContentRange = "Content-Range"
|
||||
HeaderContentType = "Content-Type"
|
||||
HeaderETag = "ETag"
|
||||
HeaderLastModified = "Last-Modified"
|
||||
HeaderLocation = "Location"
|
||||
HeaderRange = "Range"
|
||||
HeaderIfMatch = "If-Match"
|
||||
)
|
||||
|
||||
// webdav headers
|
||||
const (
|
||||
HeaderDav = "DAV" // https://datatracker.ietf.org/doc/html/rfc4918#section-10.1
|
||||
HeaderDepth = "Depth" // https://datatracker.ietf.org/doc/html/rfc4918#section-10.2
|
||||
HeaderDestination = "Destination" // https://datatracker.ietf.org/doc/html/rfc4918#section-10.3
|
||||
HeaderIf = "If" // https://datatracker.ietf.org/doc/html/rfc4918#section-10.4
|
||||
HeaderLockToken = "Lock-Token" // https://datatracker.ietf.org/doc/html/rfc4918#section-10.5
|
||||
HeaderOverwrite = "Overwrite" // https://datatracker.ietf.org/doc/html/rfc4918#section-10.6
|
||||
HeaderTimeout = "Timeout" // https://datatracker.ietf.org/doc/html/rfc4918#section-10.7
|
||||
)
|
||||
|
||||
// Non standard HTTP headers.
|
||||
const (
|
||||
HeaderOCFileID = "OC-FileId"
|
||||
HeaderOCETag = "OC-ETag"
|
||||
HeaderOCChecksum = "OC-Checksum"
|
||||
HeaderOCPermissions = "OC-Perm"
|
||||
HeaderTusResumable = "Tus-Resumable"
|
||||
HeaderTusVersion = "Tus-Version"
|
||||
HeaderTusExtension = "Tus-Extension"
|
||||
HeaderTusChecksumAlgorithm = "Tus-Checksum-Algorithm"
|
||||
HeaderTusUploadExpires = "Upload-Expires"
|
||||
HeaderUploadChecksum = "Upload-Checksum"
|
||||
HeaderUploadLength = "Upload-Length"
|
||||
HeaderUploadMetadata = "Upload-Metadata"
|
||||
HeaderUploadOffset = "Upload-Offset"
|
||||
HeaderOCMtime = "X-OC-Mtime"
|
||||
HeaderExpectedEntityLength = "X-Expected-Entity-Length"
|
||||
HeaderLitmus = "X-Litmus"
|
||||
)
|
||||
12
webdav/pkg/net/net.go
Normal file
12
webdav/pkg/net/net.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package net
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
)
|
||||
|
||||
// EncodePath encodes the path of a url.
|
||||
//
|
||||
// slashes (/) are treated as path-separators.
|
||||
func EncodePath(path string) string {
|
||||
return (&url.URL{Path: path}).EscapedPath()
|
||||
}
|
||||
125
webdav/pkg/prop/prop.go
Normal file
125
webdav/pkg/prop/prop.go
Normal file
@@ -0,0 +1,125 @@
|
||||
package prop
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/xml"
|
||||
)
|
||||
|
||||
// PropertyXML represents a single DAV resource property as defined in RFC 4918.
|
||||
// http://www.webdav.org/specs/rfc4918.html#data.model.for.resource.properties
|
||||
type PropertyXML struct {
|
||||
// XMLName is the fully qualified name that identifies this property.
|
||||
XMLName xml.Name
|
||||
|
||||
// Lang is an optional xml:lang attribute.
|
||||
Lang string `xml:"xml:lang,attr,omitempty"`
|
||||
|
||||
// InnerXML contains the XML representation of the property value.
|
||||
// See http://www.webdav.org/specs/rfc4918.html#property_values
|
||||
//
|
||||
// Property values of complex type or mixed-content must have fully
|
||||
// expanded XML namespaces or be self-contained with according
|
||||
// XML namespace declarations. They must not rely on any XML
|
||||
// namespace declarations within the scope of the XML document,
|
||||
// even including the DAV: namespace.
|
||||
InnerXML []byte `xml:",innerxml"`
|
||||
}
|
||||
|
||||
func xmlEscaped(val string) []byte {
|
||||
buf := new(bytes.Buffer)
|
||||
xml.Escape(buf, []byte(val))
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
// 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: "",
|
||||
InnerXML: xmlEscaped(val),
|
||||
}
|
||||
}
|
||||
|
||||
// Escaped returns a new PropertyXML instance while xml-escaping the value
|
||||
// TODO properly use the space
|
||||
func Escaped(key, val string) PropertyXML {
|
||||
return PropertyXML{
|
||||
XMLName: xml.Name{Space: "", Local: key},
|
||||
Lang: "",
|
||||
InnerXML: xmlEscaped(val),
|
||||
}
|
||||
}
|
||||
|
||||
// NotFound returns a new PropertyXML instance with an empty value
|
||||
func NotFound(key string) PropertyXML {
|
||||
return PropertyXML{
|
||||
XMLName: xml.Name{Space: "", Local: key},
|
||||
Lang: "",
|
||||
}
|
||||
}
|
||||
|
||||
// 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: "",
|
||||
}
|
||||
}
|
||||
|
||||
// Raw returns a new PropertyXML instance for the given key/value pair
|
||||
// TODO properly use the space
|
||||
func Raw(key, val string) PropertyXML {
|
||||
return PropertyXML{
|
||||
XMLName: xml.Name{Space: "", Local: key},
|
||||
Lang: "",
|
||||
InnerXML: []byte(val),
|
||||
}
|
||||
}
|
||||
|
||||
// Next returns the next token, if any, in the XML stream of d.
|
||||
// RFC 4918 requires to ignore comments, processing instructions
|
||||
// and directives.
|
||||
// http://www.webdav.org/specs/rfc4918.html#property_values
|
||||
// http://www.webdav.org/specs/rfc4918.html#xml-extensibility
|
||||
func Next(d *xml.Decoder) (xml.Token, error) {
|
||||
for {
|
||||
t, err := d.Token()
|
||||
if err != nil {
|
||||
return t, err
|
||||
}
|
||||
switch t.(type) {
|
||||
case xml.Comment, xml.Directive, xml.ProcInst:
|
||||
continue
|
||||
default:
|
||||
return t, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ActiveLock holds active lock xml data
|
||||
// http://www.webdav.org/specs/rfc4918.html#ELEMENT_activelock
|
||||
// <!ELEMENT activelock (lockscope, locktype, depth, owner?, timeout?,
|
||||
// locktoken?, lockroot)>
|
||||
type ActiveLock struct {
|
||||
XMLName xml.Name `xml:"activelock"`
|
||||
Exclusive *struct{} `xml:"lockscope>exclusive,omitempty"`
|
||||
Shared *struct{} `xml:"lockscope>shared,omitempty"`
|
||||
Write *struct{} `xml:"locktype>write,omitempty"`
|
||||
Depth string `xml:"depth"`
|
||||
Owner Owner `xml:"owner,omitempty"`
|
||||
Timeout string `xml:"timeout,omitempty"`
|
||||
Locktoken string `xml:"locktoken>href"`
|
||||
Lockroot string `xml:"lockroot>href,omitempty"`
|
||||
}
|
||||
|
||||
// Owner captures the inner UML of a lock owner element http://www.webdav.org/specs/rfc4918.html#ELEMENT_owner
|
||||
type Owner struct {
|
||||
InnerXML string `xml:",innerxml"`
|
||||
}
|
||||
|
||||
// Escape repaces ", &, ', < and > with their xml representation
|
||||
func Escape(s string) string {
|
||||
b := bytes.NewBuffer(nil)
|
||||
_ = xml.EscapeText(b, []byte(s))
|
||||
return b.String()
|
||||
}
|
||||
180
webdav/pkg/propfind/propfind.go
Normal file
180
webdav/pkg/propfind/propfind.go
Normal file
@@ -0,0 +1,180 @@
|
||||
package propfind
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/owncloud/ocis/webdav/pkg/errors"
|
||||
"github.com/owncloud/ocis/webdav/pkg/prop"
|
||||
)
|
||||
|
||||
const (
|
||||
_spaceTypeProject = "project"
|
||||
)
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user