xml.go 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  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. package webdav
  5. // The XML encoding is covered by Section 14.
  6. // http://www.webdav.org/specs/rfc4918.html#xml.element.definitions
  7. import (
  8. "bytes"
  9. "encoding/xml"
  10. "fmt"
  11. "io"
  12. "net/http"
  13. "time"
  14. )
  15. // http://www.webdav.org/specs/rfc4918.html#ELEMENT_lockinfo
  16. type lockInfo struct {
  17. XMLName xml.Name `xml:"lockinfo"`
  18. Exclusive *struct{} `xml:"lockscope>exclusive"`
  19. Shared *struct{} `xml:"lockscope>shared"`
  20. Write *struct{} `xml:"locktype>write"`
  21. Owner owner `xml:"owner"`
  22. }
  23. // http://www.webdav.org/specs/rfc4918.html#ELEMENT_owner
  24. type owner struct {
  25. InnerXML string `xml:",innerxml"`
  26. }
  27. func readLockInfo(r io.Reader) (li lockInfo, status int, err error) {
  28. c := &countingReader{r: r}
  29. if err = xml.NewDecoder(c).Decode(&li); err != nil {
  30. if err == io.EOF {
  31. if c.n == 0 {
  32. // An empty body means to refresh the lock.
  33. // http://www.webdav.org/specs/rfc4918.html#refreshing-locks
  34. return lockInfo{}, 0, nil
  35. }
  36. err = errInvalidLockInfo
  37. }
  38. return lockInfo{}, http.StatusBadRequest, err
  39. }
  40. // We only support exclusive (non-shared) write locks. In practice, these are
  41. // the only types of locks that seem to matter.
  42. if li.Exclusive == nil || li.Shared != nil || li.Write == nil {
  43. return lockInfo{}, http.StatusNotImplemented, errUnsupportedLockInfo
  44. }
  45. return li, 0, nil
  46. }
  47. type countingReader struct {
  48. n int
  49. r io.Reader
  50. }
  51. func (c *countingReader) Read(p []byte) (int, error) {
  52. n, err := c.r.Read(p)
  53. c.n += n
  54. return n, err
  55. }
  56. func writeLockInfo(w io.Writer, token string, ld LockDetails) (int, error) {
  57. depth := "infinity"
  58. if ld.ZeroDepth {
  59. depth = "0"
  60. }
  61. timeout := ld.Duration / time.Second
  62. return fmt.Fprintf(w, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"+
  63. "<D:prop xmlns:D=\"DAV:\"><D:lockdiscovery><D:activelock>\n"+
  64. " <D:locktype><D:write/></D:locktype>\n"+
  65. " <D:lockscope><D:exclusive/></D:lockscope>\n"+
  66. " <D:depth>%s</D:depth>\n"+
  67. " <D:owner>%s</D:owner>\n"+
  68. " <D:timeout>Second-%d</D:timeout>\n"+
  69. " <D:locktoken><D:href>%s</D:href></D:locktoken>\n"+
  70. " <D:lockroot><D:href>%s</D:href></D:lockroot>\n"+
  71. "</D:activelock></D:lockdiscovery></D:prop>",
  72. depth, ld.OwnerXML, timeout, escape(token), escape(ld.Root),
  73. )
  74. }
  75. func escape(s string) string {
  76. for i := 0; i < len(s); i++ {
  77. switch s[i] {
  78. case '"', '&', '\'', '<', '>':
  79. b := bytes.NewBuffer(nil)
  80. xml.EscapeText(b, []byte(s))
  81. return b.String()
  82. }
  83. }
  84. return s
  85. }
  86. // Next returns the next token, if any, in the XML stream of d.
  87. // RFC 4918 requires to ignore comments, processing instructions
  88. // and directives.
  89. // http://www.webdav.org/specs/rfc4918.html#property_values
  90. // http://www.webdav.org/specs/rfc4918.html#xml-extensibility
  91. func next(d *xml.Decoder) (xml.Token, error) {
  92. for {
  93. t, err := d.Token()
  94. if err != nil {
  95. return t, err
  96. }
  97. switch t.(type) {
  98. case xml.Comment, xml.Directive, xml.ProcInst:
  99. continue
  100. default:
  101. return t, nil
  102. }
  103. }
  104. }
  105. type propnames []xml.Name
  106. // UnmarshalXML appends the property names enclosed within start to pn.
  107. //
  108. // It returns an error if start does not contain any properties or if
  109. // properties contain values. Character data between properties is ignored.
  110. func (pn *propnames) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
  111. for {
  112. t, err := next(d)
  113. if err != nil {
  114. return err
  115. }
  116. switch t.(type) {
  117. case xml.EndElement:
  118. if len(*pn) == 0 {
  119. return fmt.Errorf("%s must not be empty", start.Name.Local)
  120. }
  121. return nil
  122. case xml.StartElement:
  123. name := t.(xml.StartElement).Name
  124. t, err = next(d)
  125. if err != nil {
  126. return err
  127. }
  128. if _, ok := t.(xml.EndElement); !ok {
  129. return fmt.Errorf("unexpected token %T", t)
  130. }
  131. *pn = append(*pn, name)
  132. }
  133. }
  134. }
  135. // http://www.webdav.org/specs/rfc4918.html#ELEMENT_propfind
  136. type propfind struct {
  137. XMLName xml.Name `xml:"DAV: propfind"`
  138. Allprop *struct{} `xml:"DAV: allprop"`
  139. Propname *struct{} `xml:"DAV: propname"`
  140. Prop propnames `xml:"DAV: prop"`
  141. Include propnames `xml:"DAV: include"`
  142. }
  143. func readPropfind(r io.Reader) (pf propfind, status int, err error) {
  144. c := countingReader{r: r}
  145. if err = xml.NewDecoder(&c).Decode(&pf); err != nil {
  146. if err == io.EOF {
  147. if c.n == 0 {
  148. // An empty body means to propfind allprop.
  149. // http://www.webdav.org/specs/rfc4918.html#METHOD_PROPFIND
  150. return propfind{Allprop: new(struct{})}, 0, nil
  151. }
  152. err = errInvalidPropfind
  153. }
  154. return propfind{}, http.StatusBadRequest, err
  155. }
  156. if pf.Allprop == nil && pf.Include != nil {
  157. return propfind{}, http.StatusBadRequest, errInvalidPropfind
  158. }
  159. if pf.Allprop != nil && (pf.Prop != nil || pf.Propname != nil) {
  160. return propfind{}, http.StatusBadRequest, errInvalidPropfind
  161. }
  162. if pf.Prop != nil && pf.Propname != nil {
  163. return propfind{}, http.StatusBadRequest, errInvalidPropfind
  164. }
  165. if pf.Propname == nil && pf.Allprop == nil && pf.Prop == nil {
  166. return propfind{}, http.StatusBadRequest, errInvalidPropfind
  167. }
  168. return pf, 0, nil
  169. }
  170. // Property represents a single DAV resource property as defined in RFC 4918.
  171. // See http://www.webdav.org/specs/rfc4918.html#data.model.for.resource.properties
  172. type Property struct {
  173. // XMLName is the fully qualified name that identifies this property.
  174. XMLName xml.Name
  175. // Lang is an optional xml:lang attribute.
  176. Lang string `xml:"xml:lang,attr,omitempty"`
  177. // InnerXML contains the XML representation of the property value.
  178. // See http://www.webdav.org/specs/rfc4918.html#property_values
  179. //
  180. // Property values of complex type or mixed-content must have fully
  181. // expanded XML namespaces or be self-contained with according
  182. // XML namespace declarations. They must not rely on any XML
  183. // namespace declarations within the scope of the XML document,
  184. // even including the DAV: namespace.
  185. InnerXML []byte `xml:",innerxml"`
  186. }
  187. // http://www.webdav.org/specs/rfc4918.html#ELEMENT_error
  188. type xmlError struct {
  189. XMLName xml.Name `xml:"DAV: error"`
  190. InnerXML []byte `xml:",innerxml"`
  191. }
  192. // http://www.webdav.org/specs/rfc4918.html#ELEMENT_propstat
  193. type propstat struct {
  194. // Prop requires DAV: to be the default namespace in the enclosing
  195. // XML. This is due to the standard encoding/xml package currently
  196. // not honoring namespace declarations inside a xmltag with a
  197. // parent element for anonymous slice elements.
  198. // Use of multistatusWriter takes care of this.
  199. Prop []Property `xml:"prop>_ignored_"`
  200. Status string `xml:"DAV: status"`
  201. Error *xmlError `xml:"DAV: error"`
  202. ResponseDescription string `xml:"DAV: responsedescription,omitempty"`
  203. }
  204. // http://www.webdav.org/specs/rfc4918.html#ELEMENT_response
  205. type response struct {
  206. XMLName xml.Name `xml:"DAV: response"`
  207. Href []string `xml:"DAV: href"`
  208. Propstat []propstat `xml:"DAV: propstat"`
  209. Status string `xml:"DAV: status,omitempty"`
  210. Error *xmlError `xml:"DAV: error"`
  211. ResponseDescription string `xml:"DAV: responsedescription,omitempty"`
  212. }
  213. // MultistatusWriter marshals one or more Responses into a XML
  214. // multistatus response.
  215. // See http://www.webdav.org/specs/rfc4918.html#ELEMENT_multistatus
  216. type multistatusWriter struct {
  217. // ResponseDescription contains the optional responsedescription
  218. // of the multistatus XML element. Only the latest content before
  219. // close will be emitted. Empty response descriptions are not
  220. // written.
  221. responseDescription string
  222. w http.ResponseWriter
  223. enc *xml.Encoder
  224. }
  225. // Write validates and emits a DAV response as part of a multistatus response
  226. // element.
  227. //
  228. // It sets the HTTP status code of its underlying http.ResponseWriter to 207
  229. // (Multi-Status) and populates the Content-Type header. If r is the
  230. // first, valid response to be written, Write prepends the XML representation
  231. // of r with a multistatus tag. Callers must call close after the last response
  232. // has been written.
  233. func (w *multistatusWriter) write(r *response) error {
  234. switch len(r.Href) {
  235. case 0:
  236. return errInvalidResponse
  237. case 1:
  238. if len(r.Propstat) > 0 != (r.Status == "") {
  239. return errInvalidResponse
  240. }
  241. default:
  242. if len(r.Propstat) > 0 || r.Status == "" {
  243. return errInvalidResponse
  244. }
  245. }
  246. if w.enc == nil {
  247. w.w.WriteHeader(StatusMulti)
  248. w.w.Header().Add("Content-Type", "text/xml; charset=utf-8")
  249. _, err := fmt.Fprintf(w.w, `<?xml version="1.0" encoding="UTF-8"?>`+
  250. `<D:multistatus xmlns:D="DAV:">`)
  251. if err != nil {
  252. return err
  253. }
  254. w.enc = xml.NewEncoder(w.w)
  255. }
  256. return w.enc.Encode(r)
  257. }
  258. // Close completes the marshalling of the multistatus response. It returns
  259. // an error if the multistatus response could not be completed. If both the
  260. // return value and field enc of w are nil, then no multistatus response has
  261. // been written.
  262. func (w *multistatusWriter) close() error {
  263. if w.enc == nil {
  264. return nil
  265. }
  266. if w.responseDescription != "" {
  267. _, err := fmt.Fprintf(w.w,
  268. "<D:responsedescription>%s</D:responsedescription>",
  269. w.responseDescription)
  270. if err != nil {
  271. return err
  272. }
  273. }
  274. _, err := fmt.Fprintf(w.w, "</D:multistatus>")
  275. return err
  276. }