check in missing packages

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>
This commit is contained in:
Jörn Friedrich Dreyer
2022-04-14 07:53:53 +00:00
parent 997cfc2121
commit 6573c2adba
6 changed files with 549 additions and 0 deletions

14
search/cmd/search/main.go Normal file
View 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
View 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
View 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
View 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
View 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()
}

View 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)
}
}
}