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 }