The reconcile package is used for DOM reconcilation in Isomorphic Go web applications.

xml.go 16KB

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