xml.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469
  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. "fmt"
  10. "io"
  11. "net/http"
  12. "time"
  13. "golang.org/x/net/webdav/internal/xml"
  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. // http://www.webdav.org/specs/rfc4918.html#ELEMENT_prop (for propfind)
  106. type propfindProps []xml.Name
  107. // UnmarshalXML appends the property names enclosed within start to pn.
  108. //
  109. // It returns an error if start does not contain any properties or if
  110. // properties contain values. Character data between properties is ignored.
  111. func (pn *propfindProps) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
  112. for {
  113. t, err := next(d)
  114. if err != nil {
  115. return err
  116. }
  117. switch t.(type) {
  118. case xml.EndElement:
  119. if len(*pn) == 0 {
  120. return fmt.Errorf("%s must not be empty", start.Name.Local)
  121. }
  122. return nil
  123. case xml.StartElement:
  124. name := t.(xml.StartElement).Name
  125. t, err = next(d)
  126. if err != nil {
  127. return err
  128. }
  129. if _, ok := t.(xml.EndElement); !ok {
  130. return fmt.Errorf("unexpected token %T", t)
  131. }
  132. *pn = append(*pn, name)
  133. }
  134. }
  135. }
  136. // http://www.webdav.org/specs/rfc4918.html#ELEMENT_propfind
  137. type propfind struct {
  138. XMLName xml.Name `xml:"DAV: propfind"`
  139. Allprop *struct{} `xml:"DAV: allprop"`
  140. Propname *struct{} `xml:"DAV: propname"`
  141. Prop propfindProps `xml:"DAV: prop"`
  142. Include propfindProps `xml:"DAV: include"`
  143. }
  144. func readPropfind(r io.Reader) (pf propfind, status int, err error) {
  145. c := countingReader{r: r}
  146. if err = xml.NewDecoder(&c).Decode(&pf); err != nil {
  147. if err == io.EOF {
  148. if c.n == 0 {
  149. // An empty body means to propfind allprop.
  150. // http://www.webdav.org/specs/rfc4918.html#METHOD_PROPFIND
  151. return propfind{Allprop: new(struct{})}, 0, nil
  152. }
  153. err = errInvalidPropfind
  154. }
  155. return propfind{}, http.StatusBadRequest, err
  156. }
  157. if pf.Allprop == nil && pf.Include != nil {
  158. return propfind{}, http.StatusBadRequest, errInvalidPropfind
  159. }
  160. if pf.Allprop != nil && (pf.Prop != nil || pf.Propname != nil) {
  161. return propfind{}, http.StatusBadRequest, errInvalidPropfind
  162. }
  163. if pf.Prop != nil && pf.Propname != nil {
  164. return propfind{}, http.StatusBadRequest, errInvalidPropfind
  165. }
  166. if pf.Propname == nil && pf.Allprop == nil && pf.Prop == nil {
  167. return propfind{}, http.StatusBadRequest, errInvalidPropfind
  168. }
  169. return pf, 0, nil
  170. }
  171. // Property represents a single DAV resource property as defined in RFC 4918.
  172. // See http://www.webdav.org/specs/rfc4918.html#data.model.for.resource.properties
  173. type Property struct {
  174. // XMLName is the fully qualified name that identifies this property.
  175. XMLName xml.Name
  176. // Lang is an optional xml:lang attribute.
  177. Lang string `xml:"xml:lang,attr,omitempty"`
  178. // InnerXML contains the XML representation of the property value.
  179. // See http://www.webdav.org/specs/rfc4918.html#property_values
  180. //
  181. // Property values of complex type or mixed-content must have fully
  182. // expanded XML namespaces or be self-contained with according
  183. // XML namespace declarations. They must not rely on any XML
  184. // namespace declarations within the scope of the XML document,
  185. // even including the DAV: namespace.
  186. InnerXML []byte `xml:",innerxml"`
  187. }
  188. // http://www.webdav.org/specs/rfc4918.html#ELEMENT_error
  189. // See multistatusWriter for the "D:" namespace prefix.
  190. type xmlError struct {
  191. XMLName xml.Name `xml:"D:error"`
  192. InnerXML []byte `xml:",innerxml"`
  193. }
  194. // http://www.webdav.org/specs/rfc4918.html#ELEMENT_propstat
  195. // See multistatusWriter for the "D:" namespace prefix.
  196. type propstat struct {
  197. Prop []Property `xml:"D:prop>_ignored_"`
  198. Status string `xml:"D:status"`
  199. Error *xmlError `xml:"D:error"`
  200. ResponseDescription string `xml:"D:responsedescription,omitempty"`
  201. }
  202. // MarshalXML prepends the "D:" namespace prefix on properties in the DAV: namespace
  203. // before encoding. See multistatusWriter.
  204. func (ps propstat) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
  205. for k, prop := range ps.Prop {
  206. if prop.XMLName.Space == "DAV:" {
  207. prop.XMLName = xml.Name{Space: "", Local: "D:" + prop.XMLName.Local}
  208. ps.Prop[k] = prop
  209. }
  210. }
  211. // Distinct type to avoid infinite recursion of MarshalXML.
  212. type newpropstat propstat
  213. return e.EncodeElement(newpropstat(ps), start)
  214. }
  215. // http://www.webdav.org/specs/rfc4918.html#ELEMENT_response
  216. // See multistatusWriter for the "D:" namespace prefix.
  217. type response struct {
  218. XMLName xml.Name `xml:"D:response"`
  219. Href []string `xml:"D:href"`
  220. Propstat []propstat `xml:"D:propstat"`
  221. Status string `xml:"D:status,omitempty"`
  222. Error *xmlError `xml:"D:error"`
  223. ResponseDescription string `xml:"D:responsedescription,omitempty"`
  224. }
  225. // MultistatusWriter marshals one or more Responses into a XML
  226. // multistatus response.
  227. // See http://www.webdav.org/specs/rfc4918.html#ELEMENT_multistatus
  228. // TODO(rsto, mpl): As a workaround, the "D:" namespace prefix, defined as
  229. // "DAV:" on this element, is prepended on the nested response, as well as on all
  230. // its nested elements. All property names in the DAV: namespace are prefixed as
  231. // well. This is because some versions of Mini-Redirector (on windows 7) ignore
  232. // elements with a default namespace (no prefixed namespace). A less intrusive fix
  233. // should be possible after golang.org/cl/11074. See https://golang.org/issue/11177
  234. type multistatusWriter struct {
  235. // ResponseDescription contains the optional responsedescription
  236. // of the multistatus XML element. Only the latest content before
  237. // close will be emitted. Empty response descriptions are not
  238. // written.
  239. responseDescription string
  240. w http.ResponseWriter
  241. enc *xml.Encoder
  242. }
  243. // Write validates and emits a DAV response as part of a multistatus response
  244. // element.
  245. //
  246. // It sets the HTTP status code of its underlying http.ResponseWriter to 207
  247. // (Multi-Status) and populates the Content-Type header. If r is the
  248. // first, valid response to be written, Write prepends the XML representation
  249. // of r with a multistatus tag. Callers must call close after the last response
  250. // has been written.
  251. func (w *multistatusWriter) write(r *response) error {
  252. switch len(r.Href) {
  253. case 0:
  254. return errInvalidResponse
  255. case 1:
  256. if len(r.Propstat) > 0 != (r.Status == "") {
  257. return errInvalidResponse
  258. }
  259. default:
  260. if len(r.Propstat) > 0 || r.Status == "" {
  261. return errInvalidResponse
  262. }
  263. }
  264. err := w.writeHeader()
  265. if err != nil {
  266. return err
  267. }
  268. return w.enc.Encode(r)
  269. }
  270. // writeHeader writes a XML multistatus start element on w's underlying
  271. // http.ResponseWriter and returns the result of the write operation.
  272. // After the first write attempt, writeHeader becomes a no-op.
  273. func (w *multistatusWriter) writeHeader() error {
  274. if w.enc != nil {
  275. return nil
  276. }
  277. w.w.Header().Add("Content-Type", "text/xml; charset=utf-8")
  278. w.w.WriteHeader(StatusMulti)
  279. _, err := fmt.Fprintf(w.w, `<?xml version="1.0" encoding="UTF-8"?>`)
  280. if err != nil {
  281. return err
  282. }
  283. w.enc = xml.NewEncoder(w.w)
  284. return w.enc.EncodeToken(xml.StartElement{
  285. Name: xml.Name{
  286. Space: "DAV:",
  287. Local: "multistatus",
  288. },
  289. Attr: []xml.Attr{{
  290. Name: xml.Name{Space: "xmlns", Local: "D"},
  291. Value: "DAV:",
  292. }},
  293. })
  294. }
  295. // Close completes the marshalling of the multistatus response. It returns
  296. // an error if the multistatus response could not be completed. If both the
  297. // return value and field enc of w are nil, then no multistatus response has
  298. // been written.
  299. func (w *multistatusWriter) close() error {
  300. if w.enc == nil {
  301. return nil
  302. }
  303. var end []xml.Token
  304. if w.responseDescription != "" {
  305. name := xml.Name{Space: "DAV:", Local: "responsedescription"}
  306. end = append(end,
  307. xml.StartElement{Name: name},
  308. xml.CharData(w.responseDescription),
  309. xml.EndElement{Name: name},
  310. )
  311. }
  312. end = append(end, xml.EndElement{
  313. Name: xml.Name{Space: "DAV:", Local: "multistatus"},
  314. })
  315. for _, t := range end {
  316. err := w.enc.EncodeToken(t)
  317. if err != nil {
  318. return err
  319. }
  320. }
  321. return w.enc.Flush()
  322. }
  323. // http://www.webdav.org/specs/rfc4918.html#ELEMENT_prop (for proppatch)
  324. type proppatchProps []Property
  325. var xmlLangName = xml.Name{Space: "http://www.w3.org/XML/1998/namespace", Local: "lang"}
  326. func xmlLang(s xml.StartElement, d string) string {
  327. for _, attr := range s.Attr {
  328. if attr.Name == xmlLangName {
  329. return attr.Value
  330. }
  331. }
  332. return d
  333. }
  334. type xmlValue []byte
  335. func (v *xmlValue) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
  336. // The XML value of a property can be arbitrary, mixed-content XML.
  337. // To make sure that the unmarshalled value contains all required
  338. // namespaces, we encode all the property value XML tokens into a
  339. // buffer. This forces the encoder to redeclare any used namespaces.
  340. var b bytes.Buffer
  341. e := xml.NewEncoder(&b)
  342. for {
  343. t, err := next(d)
  344. if err != nil {
  345. return err
  346. }
  347. if e, ok := t.(xml.EndElement); ok && e.Name == start.Name {
  348. break
  349. }
  350. if err = e.EncodeToken(t); err != nil {
  351. return err
  352. }
  353. }
  354. err := e.Flush()
  355. if err != nil {
  356. return err
  357. }
  358. *v = b.Bytes()
  359. return nil
  360. }
  361. // UnmarshalXML appends the property names and values enclosed within start
  362. // to ps.
  363. //
  364. // An xml:lang attribute that is defined either on the DAV:prop or property
  365. // name XML element is propagated to the property's Lang field.
  366. //
  367. // UnmarshalXML returns an error if start does not contain any properties or if
  368. // property values contain syntactically incorrect XML.
  369. func (ps *proppatchProps) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
  370. lang := xmlLang(start, "")
  371. for {
  372. t, err := next(d)
  373. if err != nil {
  374. return err
  375. }
  376. switch elem := t.(type) {
  377. case xml.EndElement:
  378. if len(*ps) == 0 {
  379. return fmt.Errorf("%s must not be empty", start.Name.Local)
  380. }
  381. return nil
  382. case xml.StartElement:
  383. p := Property{
  384. XMLName: t.(xml.StartElement).Name,
  385. Lang: xmlLang(t.(xml.StartElement), lang),
  386. }
  387. err = d.DecodeElement(((*xmlValue)(&p.InnerXML)), &elem)
  388. if err != nil {
  389. return err
  390. }
  391. *ps = append(*ps, p)
  392. }
  393. }
  394. }
  395. // http://www.webdav.org/specs/rfc4918.html#ELEMENT_set
  396. // http://www.webdav.org/specs/rfc4918.html#ELEMENT_remove
  397. type setRemove struct {
  398. XMLName xml.Name
  399. Lang string `xml:"xml:lang,attr,omitempty"`
  400. Prop proppatchProps `xml:"DAV: prop"`
  401. }
  402. // http://www.webdav.org/specs/rfc4918.html#ELEMENT_propertyupdate
  403. type propertyupdate struct {
  404. XMLName xml.Name `xml:"DAV: propertyupdate"`
  405. Lang string `xml:"xml:lang,attr,omitempty"`
  406. SetRemove []setRemove `xml:",any"`
  407. }
  408. func readProppatch(r io.Reader) (patches []Proppatch, status int, err error) {
  409. var pu propertyupdate
  410. if err = xml.NewDecoder(r).Decode(&pu); err != nil {
  411. return nil, http.StatusBadRequest, err
  412. }
  413. for _, op := range pu.SetRemove {
  414. remove := false
  415. switch op.XMLName {
  416. case xml.Name{Space: "DAV:", Local: "set"}:
  417. // No-op.
  418. case xml.Name{Space: "DAV:", Local: "remove"}:
  419. for _, p := range op.Prop {
  420. if len(p.InnerXML) > 0 {
  421. return nil, http.StatusBadRequest, errInvalidProppatch
  422. }
  423. }
  424. remove = true
  425. default:
  426. return nil, http.StatusBadRequest, errInvalidProppatch
  427. }
  428. patches = append(patches, Proppatch{Remove: remove, Props: op.Prop})
  429. }
  430. return patches, 0, nil
  431. }