github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/net/webdav/xml.go (about)

     1  // Copyright 2014 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package webdav
     6  
     7  // The XML encoding is covered by Section 14.
     8  // http://www.webdav.org/specs/rfc4918.html#xml.element.definitions
     9  
    10  import (
    11  	"bytes"
    12  	"fmt"
    13  	"io"
    14  	"net/http"
    15  	"time"
    16  
    17  	"golang.org/x/net/webdav/internal/xml"
    18  )
    19  
    20  // http://www.webdav.org/specs/rfc4918.html#ELEMENT_lockinfo
    21  type lockInfo struct {
    22  	XMLName   xml.Name  `xml:"lockinfo"`
    23  	Exclusive *struct{} `xml:"lockscope>exclusive"`
    24  	Shared    *struct{} `xml:"lockscope>shared"`
    25  	Write     *struct{} `xml:"locktype>write"`
    26  	Owner     owner     `xml:"owner"`
    27  }
    28  
    29  // http://www.webdav.org/specs/rfc4918.html#ELEMENT_owner
    30  type owner struct {
    31  	InnerXML string `xml:",innerxml"`
    32  }
    33  
    34  func readLockInfo(r io.Reader) (li lockInfo, status int, err error) {
    35  	c := &countingReader{r: r}
    36  	if err = xml.NewDecoder(c).Decode(&li); err != nil {
    37  		if err == io.EOF {
    38  			if c.n == 0 {
    39  				// An empty body means to refresh the lock.
    40  				// http://www.webdav.org/specs/rfc4918.html#refreshing-locks
    41  				return lockInfo{}, 0, nil
    42  			}
    43  			err = errInvalidLockInfo
    44  		}
    45  		return lockInfo{}, http.StatusBadRequest, err
    46  	}
    47  	// We only support exclusive (non-shared) write locks. In practice, these are
    48  	// the only types of locks that seem to matter.
    49  	if li.Exclusive == nil || li.Shared != nil || li.Write == nil {
    50  		return lockInfo{}, http.StatusNotImplemented, errUnsupportedLockInfo
    51  	}
    52  	return li, 0, nil
    53  }
    54  
    55  type countingReader struct {
    56  	n int
    57  	r io.Reader
    58  }
    59  
    60  func (c *countingReader) Read(p []byte) (int, error) {
    61  	n, err := c.r.Read(p)
    62  	c.n += n
    63  	return n, err
    64  }
    65  
    66  func writeLockInfo(w io.Writer, token string, ld LockDetails) (int, error) {
    67  	depth := "infinity"
    68  	if ld.ZeroDepth {
    69  		depth = "0"
    70  	}
    71  	timeout := ld.Duration / time.Second
    72  	return fmt.Fprintf(w, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"+
    73  		"<D:prop xmlns:D=\"DAV:\"><D:lockdiscovery><D:activelock>\n"+
    74  		"	<D:locktype><D:write/></D:locktype>\n"+
    75  		"	<D:lockscope><D:exclusive/></D:lockscope>\n"+
    76  		"	<D:depth>%s</D:depth>\n"+
    77  		"	<D:owner>%s</D:owner>\n"+
    78  		"	<D:timeout>Second-%d</D:timeout>\n"+
    79  		"	<D:locktoken><D:href>%s</D:href></D:locktoken>\n"+
    80  		"	<D:lockroot><D:href>%s</D:href></D:lockroot>\n"+
    81  		"</D:activelock></D:lockdiscovery></D:prop>",
    82  		depth, ld.OwnerXML, timeout, escape(token), escape(ld.Root),
    83  	)
    84  }
    85  
    86  func escape(s string) string {
    87  	for i := 0; i < len(s); i++ {
    88  		switch s[i] {
    89  		case '"', '&', '\'', '<', '>':
    90  			b := bytes.NewBuffer(nil)
    91  			xml.EscapeText(b, []byte(s))
    92  			return b.String()
    93  		}
    94  	}
    95  	return s
    96  }
    97  
    98  // Next returns the next token, if any, in the XML stream of d.
    99  // RFC 4918 requires to ignore comments, processing instructions
   100  // and directives.
   101  // http://www.webdav.org/specs/rfc4918.html#property_values
   102  // http://www.webdav.org/specs/rfc4918.html#xml-extensibility
   103  func next(d *xml.Decoder) (xml.Token, error) {
   104  	for {
   105  		t, err := d.Token()
   106  		if err != nil {
   107  			return t, err
   108  		}
   109  		switch t.(type) {
   110  		case xml.Comment, xml.Directive, xml.ProcInst:
   111  			continue
   112  		default:
   113  			return t, nil
   114  		}
   115  	}
   116  }
   117  
   118  // http://www.webdav.org/specs/rfc4918.html#ELEMENT_prop (for propfind)
   119  type propfindProps []xml.Name
   120  
   121  // UnmarshalXML appends the property names enclosed within start to pn.
   122  //
   123  // It returns an error if start does not contain any properties or if
   124  // properties contain values. Character data between properties is ignored.
   125  func (pn *propfindProps) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
   126  	for {
   127  		t, err := next(d)
   128  		if err != nil {
   129  			return err
   130  		}
   131  		switch t.(type) {
   132  		case xml.EndElement:
   133  			if len(*pn) == 0 {
   134  				return fmt.Errorf("%s must not be empty", start.Name.Local)
   135  			}
   136  			return nil
   137  		case xml.StartElement:
   138  			name := t.(xml.StartElement).Name
   139  			t, err = next(d)
   140  			if err != nil {
   141  				return err
   142  			}
   143  			if _, ok := t.(xml.EndElement); !ok {
   144  				return fmt.Errorf("unexpected token %T", t)
   145  			}
   146  			*pn = append(*pn, name)
   147  		}
   148  	}
   149  }
   150  
   151  // http://www.webdav.org/specs/rfc4918.html#ELEMENT_propfind
   152  type propfind struct {
   153  	XMLName  xml.Name      `xml:"DAV: propfind"`
   154  	Allprop  *struct{}     `xml:"DAV: allprop"`
   155  	Propname *struct{}     `xml:"DAV: propname"`
   156  	Prop     propfindProps `xml:"DAV: prop"`
   157  	Include  propfindProps `xml:"DAV: include"`
   158  }
   159  
   160  func readPropfind(r io.Reader) (pf propfind, status int, err error) {
   161  	c := countingReader{r: r}
   162  	if err = xml.NewDecoder(&c).Decode(&pf); err != nil {
   163  		if err == io.EOF {
   164  			if c.n == 0 {
   165  				// An empty body means to propfind allprop.
   166  				// http://www.webdav.org/specs/rfc4918.html#METHOD_PROPFIND
   167  				return propfind{Allprop: new(struct{})}, 0, nil
   168  			}
   169  			err = errInvalidPropfind
   170  		}
   171  		return propfind{}, http.StatusBadRequest, err
   172  	}
   173  
   174  	if pf.Allprop == nil && pf.Include != nil {
   175  		return propfind{}, http.StatusBadRequest, errInvalidPropfind
   176  	}
   177  	if pf.Allprop != nil && (pf.Prop != nil || pf.Propname != nil) {
   178  		return propfind{}, http.StatusBadRequest, errInvalidPropfind
   179  	}
   180  	if pf.Prop != nil && pf.Propname != nil {
   181  		return propfind{}, http.StatusBadRequest, errInvalidPropfind
   182  	}
   183  	if pf.Propname == nil && pf.Allprop == nil && pf.Prop == nil {
   184  		return propfind{}, http.StatusBadRequest, errInvalidPropfind
   185  	}
   186  	return pf, 0, nil
   187  }
   188  
   189  // Property represents a single DAV resource property as defined in RFC 4918.
   190  // See http://www.webdav.org/specs/rfc4918.html#data.model.for.resource.properties
   191  type Property struct {
   192  	// XMLName is the fully qualified name that identifies this property.
   193  	XMLName xml.Name
   194  
   195  	// Lang is an optional xml:lang attribute.
   196  	Lang string `xml:"xml:lang,attr,omitempty"`
   197  
   198  	// InnerXML contains the XML representation of the property value.
   199  	// See http://www.webdav.org/specs/rfc4918.html#property_values
   200  	//
   201  	// Property values of complex type or mixed-content must have fully
   202  	// expanded XML namespaces or be self-contained with according
   203  	// XML namespace declarations. They must not rely on any XML
   204  	// namespace declarations within the scope of the XML document,
   205  	// even including the DAV: namespace.
   206  	InnerXML []byte `xml:",innerxml"`
   207  }
   208  
   209  // http://www.webdav.org/specs/rfc4918.html#ELEMENT_error
   210  // See multistatusWriter for the "D:" namespace prefix.
   211  type xmlError struct {
   212  	XMLName  xml.Name `xml:"D:error"`
   213  	InnerXML []byte   `xml:",innerxml"`
   214  }
   215  
   216  // http://www.webdav.org/specs/rfc4918.html#ELEMENT_propstat
   217  // See multistatusWriter for the "D:" namespace prefix.
   218  type propstat struct {
   219  	Prop                []Property `xml:"D:prop>_ignored_"`
   220  	Status              string     `xml:"D:status"`
   221  	Error               *xmlError  `xml:"D:error"`
   222  	ResponseDescription string     `xml:"D:responsedescription,omitempty"`
   223  }
   224  
   225  // MarshalXML prepends the "D:" namespace prefix on properties in the DAV: namespace
   226  // before encoding. See multistatusWriter.
   227  func (ps propstat) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
   228  	for k, prop := range ps.Prop {
   229  		if prop.XMLName.Space == "DAV:" {
   230  			prop.XMLName = xml.Name{Space: "", Local: "D:" + prop.XMLName.Local}
   231  			ps.Prop[k] = prop
   232  		}
   233  	}
   234  	// Distinct type to avoid infinite recursion of MarshalXML.
   235  	type newpropstat propstat
   236  	return e.EncodeElement(newpropstat(ps), start)
   237  }
   238  
   239  // http://www.webdav.org/specs/rfc4918.html#ELEMENT_response
   240  // See multistatusWriter for the "D:" namespace prefix.
   241  type response struct {
   242  	XMLName             xml.Name   `xml:"D:response"`
   243  	Href                []string   `xml:"D:href"`
   244  	Propstat            []propstat `xml:"D:propstat"`
   245  	Status              string     `xml:"D:status,omitempty"`
   246  	Error               *xmlError  `xml:"D:error"`
   247  	ResponseDescription string     `xml:"D:responsedescription,omitempty"`
   248  }
   249  
   250  // MultistatusWriter marshals one or more Responses into a XML
   251  // multistatus response.
   252  // See http://www.webdav.org/specs/rfc4918.html#ELEMENT_multistatus
   253  // TODO(rsto, mpl): As a workaround, the "D:" namespace prefix, defined as
   254  // "DAV:" on this element, is prepended on the nested response, as well as on all
   255  // its nested elements. All property names in the DAV: namespace are prefixed as
   256  // well. This is because some versions of Mini-Redirector (on windows 7) ignore
   257  // elements with a default namespace (no prefixed namespace). A less intrusive fix
   258  // should be possible after golang.org/cl/11074. See https://golang.org/issue/11177
   259  type multistatusWriter struct {
   260  	// ResponseDescription contains the optional responsedescription
   261  	// of the multistatus XML element. Only the latest content before
   262  	// close will be emitted. Empty response descriptions are not
   263  	// written.
   264  	responseDescription string
   265  
   266  	w   http.ResponseWriter
   267  	enc *xml.Encoder
   268  }
   269  
   270  // Write validates and emits a DAV response as part of a multistatus response
   271  // element.
   272  //
   273  // It sets the HTTP status code of its underlying http.ResponseWriter to 207
   274  // (Multi-Status) and populates the Content-Type header. If r is the
   275  // first, valid response to be written, Write prepends the XML representation
   276  // of r with a multistatus tag. Callers must call close after the last response
   277  // has been written.
   278  func (w *multistatusWriter) write(r *response) error {
   279  	switch len(r.Href) {
   280  	case 0:
   281  		return errInvalidResponse
   282  	case 1:
   283  		if len(r.Propstat) > 0 != (r.Status == "") {
   284  			return errInvalidResponse
   285  		}
   286  	default:
   287  		if len(r.Propstat) > 0 || r.Status == "" {
   288  			return errInvalidResponse
   289  		}
   290  	}
   291  	err := w.writeHeader()
   292  	if err != nil {
   293  		return err
   294  	}
   295  	return w.enc.Encode(r)
   296  }
   297  
   298  // writeHeader writes a XML multistatus start element on w's underlying
   299  // http.ResponseWriter and returns the result of the write operation.
   300  // After the first write attempt, writeHeader becomes a no-op.
   301  func (w *multistatusWriter) writeHeader() error {
   302  	if w.enc != nil {
   303  		return nil
   304  	}
   305  	w.w.Header().Add("Content-Type", "text/xml; charset=utf-8")
   306  	w.w.WriteHeader(StatusMulti)
   307  	_, err := fmt.Fprintf(w.w, `<?xml version="1.0" encoding="UTF-8"?>`)
   308  	if err != nil {
   309  		return err
   310  	}
   311  	w.enc = xml.NewEncoder(w.w)
   312  	return w.enc.EncodeToken(xml.StartElement{
   313  		Name: xml.Name{
   314  			Space: "DAV:",
   315  			Local: "multistatus",
   316  		},
   317  		Attr: []xml.Attr{{
   318  			Name:  xml.Name{Space: "xmlns", Local: "D"},
   319  			Value: "DAV:",
   320  		}},
   321  	})
   322  }
   323  
   324  // Close completes the marshalling of the multistatus response. It returns
   325  // an error if the multistatus response could not be completed. If both the
   326  // return value and field enc of w are nil, then no multistatus response has
   327  // been written.
   328  func (w *multistatusWriter) close() error {
   329  	if w.enc == nil {
   330  		return nil
   331  	}
   332  	var end []xml.Token
   333  	if w.responseDescription != "" {
   334  		name := xml.Name{Space: "DAV:", Local: "responsedescription"}
   335  		end = append(end,
   336  			xml.StartElement{Name: name},
   337  			xml.CharData(w.responseDescription),
   338  			xml.EndElement{Name: name},
   339  		)
   340  	}
   341  	end = append(end, xml.EndElement{
   342  		Name: xml.Name{Space: "DAV:", Local: "multistatus"},
   343  	})
   344  	for _, t := range end {
   345  		err := w.enc.EncodeToken(t)
   346  		if err != nil {
   347  			return err
   348  		}
   349  	}
   350  	return w.enc.Flush()
   351  }
   352  
   353  // http://www.webdav.org/specs/rfc4918.html#ELEMENT_prop (for proppatch)
   354  type proppatchProps []Property
   355  
   356  var xmlLangName = xml.Name{Space: "http://www.w3.org/XML/1998/namespace", Local: "lang"}
   357  
   358  func xmlLang(s xml.StartElement, d string) string {
   359  	for _, attr := range s.Attr {
   360  		if attr.Name == xmlLangName {
   361  			return attr.Value
   362  		}
   363  	}
   364  	return d
   365  }
   366  
   367  type xmlValue []byte
   368  
   369  func (v *xmlValue) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
   370  	// The XML value of a property can be arbitrary, mixed-content XML.
   371  	// To make sure that the unmarshalled value contains all required
   372  	// namespaces, we encode all the property value XML tokens into a
   373  	// buffer. This forces the encoder to redeclare any used namespaces.
   374  	var b bytes.Buffer
   375  	e := xml.NewEncoder(&b)
   376  	for {
   377  		t, err := next(d)
   378  		if err != nil {
   379  			return err
   380  		}
   381  		if e, ok := t.(xml.EndElement); ok && e.Name == start.Name {
   382  			break
   383  		}
   384  		if err = e.EncodeToken(t); err != nil {
   385  			return err
   386  		}
   387  	}
   388  	err := e.Flush()
   389  	if err != nil {
   390  		return err
   391  	}
   392  	*v = b.Bytes()
   393  	return nil
   394  }
   395  
   396  // UnmarshalXML appends the property names and values enclosed within start
   397  // to ps.
   398  //
   399  // An xml:lang attribute that is defined either on the DAV:prop or property
   400  // name XML element is propagated to the property's Lang field.
   401  //
   402  // UnmarshalXML returns an error if start does not contain any properties or if
   403  // property values contain syntactically incorrect XML.
   404  func (ps *proppatchProps) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
   405  	lang := xmlLang(start, "")
   406  	for {
   407  		t, err := next(d)
   408  		if err != nil {
   409  			return err
   410  		}
   411  		switch elem := t.(type) {
   412  		case xml.EndElement:
   413  			if len(*ps) == 0 {
   414  				return fmt.Errorf("%s must not be empty", start.Name.Local)
   415  			}
   416  			return nil
   417  		case xml.StartElement:
   418  			p := Property{
   419  				XMLName: t.(xml.StartElement).Name,
   420  				Lang:    xmlLang(t.(xml.StartElement), lang),
   421  			}
   422  			err = d.DecodeElement(((*xmlValue)(&p.InnerXML)), &elem)
   423  			if err != nil {
   424  				return err
   425  			}
   426  			*ps = append(*ps, p)
   427  		}
   428  	}
   429  }
   430  
   431  // http://www.webdav.org/specs/rfc4918.html#ELEMENT_set
   432  // http://www.webdav.org/specs/rfc4918.html#ELEMENT_remove
   433  type setRemove struct {
   434  	XMLName xml.Name
   435  	Lang    string         `xml:"xml:lang,attr,omitempty"`
   436  	Prop    proppatchProps `xml:"DAV: prop"`
   437  }
   438  
   439  // http://www.webdav.org/specs/rfc4918.html#ELEMENT_propertyupdate
   440  type propertyupdate struct {
   441  	XMLName   xml.Name    `xml:"DAV: propertyupdate"`
   442  	Lang      string      `xml:"xml:lang,attr,omitempty"`
   443  	SetRemove []setRemove `xml:",any"`
   444  }
   445  
   446  func readProppatch(r io.Reader) (patches []Proppatch, status int, err error) {
   447  	var pu propertyupdate
   448  	if err = xml.NewDecoder(r).Decode(&pu); err != nil {
   449  		return nil, http.StatusBadRequest, err
   450  	}
   451  	for _, op := range pu.SetRemove {
   452  		remove := false
   453  		switch op.XMLName {
   454  		case xml.Name{Space: "DAV:", Local: "set"}:
   455  			// No-op.
   456  		case xml.Name{Space: "DAV:", Local: "remove"}:
   457  			for _, p := range op.Prop {
   458  				if len(p.InnerXML) > 0 {
   459  					return nil, http.StatusBadRequest, errInvalidProppatch
   460  				}
   461  			}
   462  			remove = true
   463  		default:
   464  			return nil, http.StatusBadRequest, errInvalidProppatch
   465  		}
   466  		patches = append(patches, Proppatch{Remove: remove, Props: op.Prop})
   467  	}
   468  	return patches, 0, nil
   469  }