OSDN Git Service

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