| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302 |
- // Copyright 2014 The Go Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style
- // license that can be found in the LICENSE file.
- package webdav
- // The XML encoding is covered by Section 14.
- // http://www.webdav.org/specs/rfc4918.html#xml.element.definitions
- import (
- "bytes"
- "encoding/xml"
- "fmt"
- "io"
- "net/http"
- "time"
- )
- // http://www.webdav.org/specs/rfc4918.html#ELEMENT_lockinfo
- type lockInfo struct {
- XMLName xml.Name `xml:"lockinfo"`
- Exclusive *struct{} `xml:"lockscope>exclusive"`
- Shared *struct{} `xml:"lockscope>shared"`
- Write *struct{} `xml:"locktype>write"`
- Owner owner `xml:"owner"`
- }
- // http://www.webdav.org/specs/rfc4918.html#ELEMENT_owner
- type owner struct {
- InnerXML string `xml:",innerxml"`
- }
- func readLockInfo(r io.Reader) (li lockInfo, status int, err error) {
- c := &countingReader{r: r}
- if err = xml.NewDecoder(c).Decode(&li); err != nil {
- if err == io.EOF {
- if c.n == 0 {
- // An empty body means to refresh the lock.
- // http://www.webdav.org/specs/rfc4918.html#refreshing-locks
- return lockInfo{}, 0, nil
- }
- err = errInvalidLockInfo
- }
- return lockInfo{}, http.StatusBadRequest, err
- }
- // We only support exclusive (non-shared) write locks. In practice, these are
- // the only types of locks that seem to matter.
- if li.Exclusive == nil || li.Shared != nil || li.Write == nil {
- return lockInfo{}, http.StatusNotImplemented, errUnsupportedLockInfo
- }
- return li, 0, nil
- }
- 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
- }
- func writeLockInfo(w io.Writer, token string, ld LockDetails) (int, error) {
- depth := "infinity"
- if ld.ZeroDepth {
- depth = "0"
- }
- timeout := ld.Duration / time.Second
- return fmt.Fprintf(w, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"+
- "<D:prop xmlns:D=\"DAV:\"><D:lockdiscovery><D:activelock>\n"+
- " <D:locktype><D:write/></D:locktype>\n"+
- " <D:lockscope><D:exclusive/></D:lockscope>\n"+
- " <D:depth>%s</D:depth>\n"+
- " <D:owner>%s</D:owner>\n"+
- " <D:timeout>Second-%d</D:timeout>\n"+
- " <D:locktoken><D:href>%s</D:href></D:locktoken>\n"+
- " <D:lockroot><D:href>%s</D:href></D:lockroot>\n"+
- "</D:activelock></D:lockdiscovery></D:prop>",
- depth, ld.OwnerXML, timeout, escape(token), escape(ld.Root),
- )
- }
- func escape(s string) string {
- for i := 0; i < len(s); i++ {
- switch s[i] {
- case '"', '&', '\'', '<', '>':
- b := bytes.NewBuffer(nil)
- xml.EscapeText(b, []byte(s))
- return b.String()
- }
- }
- return s
- }
- // 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
- }
- }
- }
- type propnames []xml.Name
- // 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 *propnames) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
- for {
- t, err := next(d)
- if err != nil {
- return err
- }
- switch t.(type) {
- case xml.EndElement:
- if len(*pn) == 0 {
- return fmt.Errorf("%s must not be empty", start.Name.Local)
- }
- return nil
- case xml.StartElement:
- name := t.(xml.StartElement).Name
- t, err = next(d)
- if err != nil {
- return err
- }
- if _, ok := t.(xml.EndElement); !ok {
- return fmt.Errorf("unexpected token %T", t)
- }
- *pn = append(*pn, name)
- }
- }
- }
- // http://www.webdav.org/specs/rfc4918.html#ELEMENT_propfind
- type propfind struct {
- XMLName xml.Name `xml:"DAV: propfind"`
- Allprop *struct{} `xml:"DAV: allprop"`
- Propname *struct{} `xml:"DAV: propname"`
- Prop propnames `xml:"DAV: prop"`
- Include propnames `xml:"DAV: include"`
- }
- func readPropfind(r io.Reader) (pf propfind, 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 propfind{Allprop: new(struct{})}, 0, nil
- }
- err = errInvalidPropfind
- }
- return propfind{}, http.StatusBadRequest, err
- }
- if pf.Allprop == nil && pf.Include != nil {
- return propfind{}, http.StatusBadRequest, errInvalidPropfind
- }
- if pf.Allprop != nil && (pf.Prop != nil || pf.Propname != nil) {
- return propfind{}, http.StatusBadRequest, errInvalidPropfind
- }
- if pf.Prop != nil && pf.Propname != nil {
- return propfind{}, http.StatusBadRequest, errInvalidPropfind
- }
- if pf.Propname == nil && pf.Allprop == nil && pf.Prop == nil {
- return propfind{}, http.StatusBadRequest, errInvalidPropfind
- }
- return pf, 0, nil
- }
- // Property represents a single DAV resource property as defined in RFC 4918.
- // See http://www.webdav.org/specs/rfc4918.html#data.model.for.resource.properties
- type Property 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"`
- }
- // http://www.webdav.org/specs/rfc4918.html#ELEMENT_error
- type xmlError struct {
- XMLName xml.Name `xml:"DAV: error"`
- InnerXML []byte `xml:",innerxml"`
- }
- // http://www.webdav.org/specs/rfc4918.html#ELEMENT_propstat
- type propstat 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 []Property `xml:"prop>_ignored_"`
- Status string `xml:"DAV: status"`
- Error *xmlError `xml:"DAV: error"`
- ResponseDescription string `xml:"DAV: responsedescription,omitempty"`
- }
- // http://www.webdav.org/specs/rfc4918.html#ELEMENT_response
- type response struct {
- XMLName xml.Name `xml:"DAV: response"`
- Href []string `xml:"DAV: href"`
- Propstat []propstat `xml:"DAV: propstat"`
- Status string `xml:"DAV: status,omitempty"`
- Error *xmlError `xml:"DAV: error"`
- ResponseDescription string `xml:"DAV: responsedescription,omitempty"`
- }
- // MultistatusWriter marshals one or more Responses into a XML
- // multistatus response.
- // See http://www.webdav.org/specs/rfc4918.html#ELEMENT_multistatus
- type multistatusWriter struct {
- // ResponseDescription contains the optional responsedescription
- // of the multistatus XML element. Only the latest content before
- // close will be emitted. Empty response descriptions are not
- // written.
- responseDescription string
- w http.ResponseWriter
- enc *xml.Encoder
- }
- // Write validates and emits a DAV response as part of a multistatus response
- // element.
- //
- // It sets the HTTP status code of its underlying http.ResponseWriter to 207
- // (Multi-Status) and populates the Content-Type header. If r is the
- // first, valid response to be written, Write prepends the XML representation
- // of r with a multistatus tag. Callers must call close after the last response
- // has been written.
- func (w *multistatusWriter) write(r *response) error {
- switch len(r.Href) {
- case 0:
- return errInvalidResponse
- case 1:
- if len(r.Propstat) > 0 != (r.Status == "") {
- return errInvalidResponse
- }
- default:
- if len(r.Propstat) > 0 || r.Status == "" {
- return errInvalidResponse
- }
- }
- if w.enc == nil {
- w.w.WriteHeader(StatusMulti)
- w.w.Header().Add("Content-Type", "text/xml; charset=utf-8")
- _, err := fmt.Fprintf(w.w, `<?xml version="1.0" encoding="UTF-8"?>`+
- `<D:multistatus xmlns:D="DAV:">`)
- if err != nil {
- return err
- }
- w.enc = xml.NewEncoder(w.w)
- }
- return w.enc.Encode(r)
- }
- // Close completes the marshalling of the multistatus response. It returns
- // an error if the multistatus response could not be completed. If both the
- // return value and field enc of w are nil, then no multistatus response has
- // been written.
- func (w *multistatusWriter) close() error {
- if w.enc == nil {
- return nil
- }
- if w.responseDescription != "" {
- _, err := fmt.Fprintf(w.w,
- "<D:responsedescription>%s</D:responsedescription>",
- w.responseDescription)
- if err != nil {
- return err
- }
- }
- _, err := fmt.Fprintf(w.w, "</D:multistatus>")
- return err
- }
|