OSDN Git Service

Hulk did something
[bytom/vapor.git] / vendor / golang.org / x / net / webdav / xml.go
diff --git a/vendor/golang.org/x/net/webdav/xml.go b/vendor/golang.org/x/net/webdav/xml.go
new file mode 100644 (file)
index 0000000..790dc81
--- /dev/null
@@ -0,0 +1,519 @@
+// 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"
+
+       // As of https://go-review.googlesource.com/#/c/12772/ which was submitted
+       // in July 2015, this package uses an internal fork of the standard
+       // library's encoding/xml package, due to changes in the way namespaces
+       // were encoded. Such changes were introduced in the Go 1.5 cycle, but were
+       // rolled back in response to https://github.com/golang/go/issues/11841
+       //
+       // However, this package's exported API, specifically the Property and
+       // DeadPropsHolder types, need to refer to the standard library's version
+       // of the xml.Name type, as code that imports this package cannot refer to
+       // the internal version.
+       //
+       // This file therefore imports both the internal and external versions, as
+       // ixml and xml, and converts between them.
+       //
+       // In the long term, this package should use the standard library's version
+       // only, and the internal fork deleted, once
+       // https://github.com/golang/go/issues/13400 is resolved.
+       ixml "golang.org/x/net/webdav/internal/xml"
+)
+
+// http://www.webdav.org/specs/rfc4918.html#ELEMENT_lockinfo
+type lockInfo struct {
+       XMLName   ixml.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 = ixml.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)
+                       ixml.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 *ixml.Decoder) (ixml.Token, error) {
+       for {
+               t, err := d.Token()
+               if err != nil {
+                       return t, err
+               }
+               switch t.(type) {
+               case ixml.Comment, ixml.Directive, ixml.ProcInst:
+                       continue
+               default:
+                       return t, nil
+               }
+       }
+}
+
+// http://www.webdav.org/specs/rfc4918.html#ELEMENT_prop (for propfind)
+type propfindProps []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 *propfindProps) UnmarshalXML(d *ixml.Decoder, start ixml.StartElement) error {
+       for {
+               t, err := next(d)
+               if err != nil {
+                       return err
+               }
+               switch t.(type) {
+               case ixml.EndElement:
+                       if len(*pn) == 0 {
+                               return fmt.Errorf("%s must not be empty", start.Name.Local)
+                       }
+                       return nil
+               case ixml.StartElement:
+                       name := t.(ixml.StartElement).Name
+                       t, err = next(d)
+                       if err != nil {
+                               return err
+                       }
+                       if _, ok := t.(ixml.EndElement); !ok {
+                               return fmt.Errorf("unexpected token %T", t)
+                       }
+                       *pn = append(*pn, xml.Name(name))
+               }
+       }
+}
+
+// http://www.webdav.org/specs/rfc4918.html#ELEMENT_propfind
+type propfind struct {
+       XMLName  ixml.Name     `xml:"DAV: propfind"`
+       Allprop  *struct{}     `xml:"DAV: allprop"`
+       Propname *struct{}     `xml:"DAV: propname"`
+       Prop     propfindProps `xml:"DAV: prop"`
+       Include  propfindProps `xml:"DAV: include"`
+}
+
+func readPropfind(r io.Reader) (pf propfind, status int, err error) {
+       c := countingReader{r: r}
+       if err = ixml.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"`
+}
+
+// ixmlProperty is the same as the Property type except it holds an ixml.Name
+// instead of an xml.Name.
+type ixmlProperty struct {
+       XMLName  ixml.Name
+       Lang     string `xml:"xml:lang,attr,omitempty"`
+       InnerXML []byte `xml:",innerxml"`
+}
+
+// http://www.webdav.org/specs/rfc4918.html#ELEMENT_error
+// See multistatusWriter for the "D:" namespace prefix.
+type xmlError struct {
+       XMLName  ixml.Name `xml:"D:error"`
+       InnerXML []byte    `xml:",innerxml"`
+}
+
+// http://www.webdav.org/specs/rfc4918.html#ELEMENT_propstat
+// See multistatusWriter for the "D:" namespace prefix.
+type propstat struct {
+       Prop                []Property `xml:"D:prop>_ignored_"`
+       Status              string     `xml:"D:status"`
+       Error               *xmlError  `xml:"D:error"`
+       ResponseDescription string     `xml:"D:responsedescription,omitempty"`
+}
+
+// ixmlPropstat is the same as the propstat type except it holds an ixml.Name
+// instead of an xml.Name.
+type ixmlPropstat struct {
+       Prop                []ixmlProperty `xml:"D:prop>_ignored_"`
+       Status              string         `xml:"D:status"`
+       Error               *xmlError      `xml:"D:error"`
+       ResponseDescription string         `xml:"D:responsedescription,omitempty"`
+}
+
+// MarshalXML prepends the "D:" namespace prefix on properties in the DAV: namespace
+// before encoding. See multistatusWriter.
+func (ps propstat) MarshalXML(e *ixml.Encoder, start ixml.StartElement) error {
+       // Convert from a propstat to an ixmlPropstat.
+       ixmlPs := ixmlPropstat{
+               Prop:                make([]ixmlProperty, len(ps.Prop)),
+               Status:              ps.Status,
+               Error:               ps.Error,
+               ResponseDescription: ps.ResponseDescription,
+       }
+       for k, prop := range ps.Prop {
+               ixmlPs.Prop[k] = ixmlProperty{
+                       XMLName:  ixml.Name(prop.XMLName),
+                       Lang:     prop.Lang,
+                       InnerXML: prop.InnerXML,
+               }
+       }
+
+       for k, prop := range ixmlPs.Prop {
+               if prop.XMLName.Space == "DAV:" {
+                       prop.XMLName = ixml.Name{Space: "", Local: "D:" + prop.XMLName.Local}
+                       ixmlPs.Prop[k] = prop
+               }
+       }
+       // Distinct type to avoid infinite recursion of MarshalXML.
+       type newpropstat ixmlPropstat
+       return e.EncodeElement(newpropstat(ixmlPs), start)
+}
+
+// http://www.webdav.org/specs/rfc4918.html#ELEMENT_response
+// See multistatusWriter for the "D:" namespace prefix.
+type response struct {
+       XMLName             ixml.Name  `xml:"D:response"`
+       Href                []string   `xml:"D:href"`
+       Propstat            []propstat `xml:"D:propstat"`
+       Status              string     `xml:"D:status,omitempty"`
+       Error               *xmlError  `xml:"D:error"`
+       ResponseDescription string     `xml:"D:responsedescription,omitempty"`
+}
+
+// MultistatusWriter marshals one or more Responses into a XML
+// multistatus response.
+// See http://www.webdav.org/specs/rfc4918.html#ELEMENT_multistatus
+// TODO(rsto, mpl): As a workaround, the "D:" namespace prefix, defined as
+// "DAV:" on this element, is prepended on the nested response, as well as on all
+// its nested elements. All property names in the DAV: namespace are prefixed as
+// well. This is because some versions of Mini-Redirector (on windows 7) ignore
+// elements with a default namespace (no prefixed namespace). A less intrusive fix
+// should be possible after golang.org/cl/11074. See https://golang.org/issue/11177
+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 *ixml.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
+               }
+       }
+       err := w.writeHeader()
+       if err != nil {
+               return err
+       }
+       return w.enc.Encode(r)
+}
+
+// writeHeader writes a XML multistatus start element on w's underlying
+// http.ResponseWriter and returns the result of the write operation.
+// After the first write attempt, writeHeader becomes a no-op.
+func (w *multistatusWriter) writeHeader() error {
+       if w.enc != nil {
+               return nil
+       }
+       w.w.Header().Add("Content-Type", "text/xml; charset=utf-8")
+       w.w.WriteHeader(StatusMulti)
+       _, err := fmt.Fprintf(w.w, `<?xml version="1.0" encoding="UTF-8"?>`)
+       if err != nil {
+               return err
+       }
+       w.enc = ixml.NewEncoder(w.w)
+       return w.enc.EncodeToken(ixml.StartElement{
+               Name: ixml.Name{
+                       Space: "DAV:",
+                       Local: "multistatus",
+               },
+               Attr: []ixml.Attr{{
+                       Name:  ixml.Name{Space: "xmlns", Local: "D"},
+                       Value: "DAV:",
+               }},
+       })
+}
+
+// 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
+       }
+       var end []ixml.Token
+       if w.responseDescription != "" {
+               name := ixml.Name{Space: "DAV:", Local: "responsedescription"}
+               end = append(end,
+                       ixml.StartElement{Name: name},
+                       ixml.CharData(w.responseDescription),
+                       ixml.EndElement{Name: name},
+               )
+       }
+       end = append(end, ixml.EndElement{
+               Name: ixml.Name{Space: "DAV:", Local: "multistatus"},
+       })
+       for _, t := range end {
+               err := w.enc.EncodeToken(t)
+               if err != nil {
+                       return err
+               }
+       }
+       return w.enc.Flush()
+}
+
+var xmlLangName = ixml.Name{Space: "http://www.w3.org/XML/1998/namespace", Local: "lang"}
+
+func xmlLang(s ixml.StartElement, d string) string {
+       for _, attr := range s.Attr {
+               if attr.Name == xmlLangName {
+                       return attr.Value
+               }
+       }
+       return d
+}
+
+type xmlValue []byte
+
+func (v *xmlValue) UnmarshalXML(d *ixml.Decoder, start ixml.StartElement) error {
+       // The XML value of a property can be arbitrary, mixed-content XML.
+       // To make sure that the unmarshalled value contains all required
+       // namespaces, we encode all the property value XML tokens into a
+       // buffer. This forces the encoder to redeclare any used namespaces.
+       var b bytes.Buffer
+       e := ixml.NewEncoder(&b)
+       for {
+               t, err := next(d)
+               if err != nil {
+                       return err
+               }
+               if e, ok := t.(ixml.EndElement); ok && e.Name == start.Name {
+                       break
+               }
+               if err = e.EncodeToken(t); err != nil {
+                       return err
+               }
+       }
+       err := e.Flush()
+       if err != nil {
+               return err
+       }
+       *v = b.Bytes()
+       return nil
+}
+
+// http://www.webdav.org/specs/rfc4918.html#ELEMENT_prop (for proppatch)
+type proppatchProps []Property
+
+// UnmarshalXML appends the property names and values enclosed within start
+// to ps.
+//
+// An xml:lang attribute that is defined either on the DAV:prop or property
+// name XML element is propagated to the property's Lang field.
+//
+// UnmarshalXML returns an error if start does not contain any properties or if
+// property values contain syntactically incorrect XML.
+func (ps *proppatchProps) UnmarshalXML(d *ixml.Decoder, start ixml.StartElement) error {
+       lang := xmlLang(start, "")
+       for {
+               t, err := next(d)
+               if err != nil {
+                       return err
+               }
+               switch elem := t.(type) {
+               case ixml.EndElement:
+                       if len(*ps) == 0 {
+                               return fmt.Errorf("%s must not be empty", start.Name.Local)
+                       }
+                       return nil
+               case ixml.StartElement:
+                       p := Property{
+                               XMLName: xml.Name(t.(ixml.StartElement).Name),
+                               Lang:    xmlLang(t.(ixml.StartElement), lang),
+                       }
+                       err = d.DecodeElement(((*xmlValue)(&p.InnerXML)), &elem)
+                       if err != nil {
+                               return err
+                       }
+                       *ps = append(*ps, p)
+               }
+       }
+}
+
+// http://www.webdav.org/specs/rfc4918.html#ELEMENT_set
+// http://www.webdav.org/specs/rfc4918.html#ELEMENT_remove
+type setRemove struct {
+       XMLName ixml.Name
+       Lang    string         `xml:"xml:lang,attr,omitempty"`
+       Prop    proppatchProps `xml:"DAV: prop"`
+}
+
+// http://www.webdav.org/specs/rfc4918.html#ELEMENT_propertyupdate
+type propertyupdate struct {
+       XMLName   ixml.Name   `xml:"DAV: propertyupdate"`
+       Lang      string      `xml:"xml:lang,attr,omitempty"`
+       SetRemove []setRemove `xml:",any"`
+}
+
+func readProppatch(r io.Reader) (patches []Proppatch, status int, err error) {
+       var pu propertyupdate
+       if err = ixml.NewDecoder(r).Decode(&pu); err != nil {
+               return nil, http.StatusBadRequest, err
+       }
+       for _, op := range pu.SetRemove {
+               remove := false
+               switch op.XMLName {
+               case ixml.Name{Space: "DAV:", Local: "set"}:
+                       // No-op.
+               case ixml.Name{Space: "DAV:", Local: "remove"}:
+                       for _, p := range op.Prop {
+                               if len(p.InnerXML) > 0 {
+                                       return nil, http.StatusBadRequest, errInvalidProppatch
+                               }
+                       }
+                       remove = true
+               default:
+                       return nil, http.StatusBadRequest, errInvalidProppatch
+               }
+               patches = append(patches, Proppatch{Remove: remove, Props: op.Prop})
+       }
+       return patches, 0, nil
+}