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