|
|
@ -9,7 +9,6 @@ package dav |
|
|
|
|
|
|
|
|
import ( |
|
|
import ( |
|
|
"bytes" |
|
|
"bytes" |
|
|
"encoding/xml" |
|
|
|
|
|
"errors" |
|
|
"errors" |
|
|
"fmt" |
|
|
"fmt" |
|
|
"io" |
|
|
"io" |
|
|
@ -32,7 +31,7 @@ import ( |
|
|
// In the long term, this package should use the standard library's version
|
|
|
// In the long term, this package should use the standard library's version
|
|
|
// only, and the internal fork deleted, once
|
|
|
// only, and the internal fork deleted, once
|
|
|
// https://github.com/golang/go/issues/13400 is resolved.
|
|
|
// https://github.com/golang/go/issues/13400 is resolved.
|
|
|
ixml "tank/rest/dav/internal/xml" |
|
|
"tank/rest/dav/xml" |
|
|
) |
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -71,7 +70,7 @@ var ( |
|
|
|
|
|
|
|
|
// http://www.webdav.org/specs/rfc4918.html#ELEMENT_lockinfo
|
|
|
// http://www.webdav.org/specs/rfc4918.html#ELEMENT_lockinfo
|
|
|
type LockInfo struct { |
|
|
type LockInfo struct { |
|
|
XMLName ixml.Name `xml:"lockinfo"` |
|
|
XMLName xml.Name `xml:"lockinfo"` |
|
|
Exclusive *struct{} `xml:"lockscope>exclusive"` |
|
|
Exclusive *struct{} `xml:"lockscope>exclusive"` |
|
|
Shared *struct{} `xml:"lockscope>shared"` |
|
|
Shared *struct{} `xml:"lockscope>shared"` |
|
|
Write *struct{} `xml:"locktype>write"` |
|
|
Write *struct{} `xml:"locktype>write"` |
|
|
@ -101,7 +100,7 @@ func escape(s string) string { |
|
|
switch s[i] { |
|
|
switch s[i] { |
|
|
case '"', '&', '\'', '<', '>': |
|
|
case '"', '&', '\'', '<', '>': |
|
|
b := bytes.NewBuffer(nil) |
|
|
b := bytes.NewBuffer(nil) |
|
|
ixml.EscapeText(b, []byte(s)) |
|
|
xml.EscapeText(b, []byte(s)) |
|
|
return b.String() |
|
|
return b.String() |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
@ -113,14 +112,14 @@ func escape(s string) string { |
|
|
// and directives.
|
|
|
// and directives.
|
|
|
// http://www.webdav.org/specs/rfc4918.html#property_values
|
|
|
// http://www.webdav.org/specs/rfc4918.html#property_values
|
|
|
// http://www.webdav.org/specs/rfc4918.html#xml-extensibility
|
|
|
// http://www.webdav.org/specs/rfc4918.html#xml-extensibility
|
|
|
func next(d *ixml.Decoder) (ixml.Token, error) { |
|
|
func next(d *xml.Decoder) (xml.Token, error) { |
|
|
for { |
|
|
for { |
|
|
t, err := d.Token() |
|
|
t, err := d.Token() |
|
|
if err != nil { |
|
|
if err != nil { |
|
|
return t, err |
|
|
return t, err |
|
|
} |
|
|
} |
|
|
switch t.(type) { |
|
|
switch t.(type) { |
|
|
case ixml.Comment, ixml.Directive, ixml.ProcInst: |
|
|
case xml.Comment, xml.Directive, xml.ProcInst: |
|
|
continue |
|
|
continue |
|
|
default: |
|
|
default: |
|
|
return t, nil |
|
|
return t, nil |
|
|
@ -135,25 +134,25 @@ type PropfindProps []xml.Name |
|
|
//
|
|
|
//
|
|
|
// It returns an error if start does not contain any properties or if
|
|
|
// It returns an error if start does not contain any properties or if
|
|
|
// properties contain values. Character data between properties is ignored.
|
|
|
// properties contain values. Character data between properties is ignored.
|
|
|
func (pn *PropfindProps) UnmarshalXML(d *ixml.Decoder, start ixml.StartElement) error { |
|
|
func (pn *PropfindProps) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { |
|
|
for { |
|
|
for { |
|
|
t, err := next(d) |
|
|
t, err := next(d) |
|
|
if err != nil { |
|
|
if err != nil { |
|
|
return err |
|
|
return err |
|
|
} |
|
|
} |
|
|
switch t.(type) { |
|
|
switch t.(type) { |
|
|
case ixml.EndElement: |
|
|
case xml.EndElement: |
|
|
if len(*pn) == 0 { |
|
|
if len(*pn) == 0 { |
|
|
return fmt.Errorf("%s must not be empty", start.Name.Local) |
|
|
return fmt.Errorf("%s must not be empty", start.Name.Local) |
|
|
} |
|
|
} |
|
|
return nil |
|
|
return nil |
|
|
case ixml.StartElement: |
|
|
case xml.StartElement: |
|
|
name := t.(ixml.StartElement).Name |
|
|
name := t.(xml.StartElement).Name |
|
|
t, err = next(d) |
|
|
t, err = next(d) |
|
|
if err != nil { |
|
|
if err != nil { |
|
|
return err |
|
|
return err |
|
|
} |
|
|
} |
|
|
if _, ok := t.(ixml.EndElement); !ok { |
|
|
if _, ok := t.(xml.EndElement); !ok { |
|
|
return fmt.Errorf("unexpected token %T", t) |
|
|
return fmt.Errorf("unexpected token %T", t) |
|
|
} |
|
|
} |
|
|
*pn = append(*pn, xml.Name(name)) |
|
|
*pn = append(*pn, xml.Name(name)) |
|
|
@ -164,7 +163,7 @@ func (pn *PropfindProps) UnmarshalXML(d *ixml.Decoder, start ixml.StartElement) |
|
|
// http://www.webdav.org/specs/rfc4918.html#ELEMENT_propfind
|
|
|
// http://www.webdav.org/specs/rfc4918.html#ELEMENT_propfind
|
|
|
// <!ELEMENT propfind ( propname | (allprop, include?) | prop ) >
|
|
|
// <!ELEMENT propfind ( propname | (allprop, include?) | prop ) >
|
|
|
type Propfind struct { |
|
|
type Propfind struct { |
|
|
XMLName ixml.Name `xml:"DAV: propfind"` |
|
|
XMLName xml.Name `xml:"DAV: propfind"` |
|
|
Allprop *struct{} `xml:"DAV: allprop"` |
|
|
Allprop *struct{} `xml:"DAV: allprop"` |
|
|
Propname *struct{} `xml:"DAV: propname"` |
|
|
Propname *struct{} `xml:"DAV: propname"` |
|
|
Prop PropfindProps `xml:"DAV: prop"` |
|
|
Prop PropfindProps `xml:"DAV: prop"` |
|
|
@ -174,7 +173,7 @@ type Propfind struct { |
|
|
//从request中读出需要的属性。比如:getcontentlength 大小 creationdate 创建时间
|
|
|
//从request中读出需要的属性。比如:getcontentlength 大小 creationdate 创建时间
|
|
|
func ReadPropfind(reader io.Reader) (propfind Propfind, status int, err error) { |
|
|
func ReadPropfind(reader io.Reader) (propfind Propfind, status int, err error) { |
|
|
c := CountingReader{reader: reader} |
|
|
c := CountingReader{reader: reader} |
|
|
if err = ixml.NewDecoder(&c).Decode(&propfind); err != nil { |
|
|
if err = xml.NewDecoder(&c).Decode(&propfind); err != nil { |
|
|
if err == io.EOF { |
|
|
if err == io.EOF { |
|
|
if c.n == 0 { |
|
|
if c.n == 0 { |
|
|
// An empty body means to propfind allprop.
|
|
|
// An empty body means to propfind allprop.
|
|
|
@ -221,10 +220,10 @@ type Property struct { |
|
|
InnerXML []byte `xml:",innerxml"` |
|
|
InnerXML []byte `xml:",innerxml"` |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// ixmlProperty is the same as the Property type except it holds an ixml.Name
|
|
|
// ixmlProperty is the same as the Property type except it holds an xml.Name
|
|
|
// instead of an xml.Name.
|
|
|
// instead of an xml.Name.
|
|
|
type IxmlProperty struct { |
|
|
type IxmlProperty struct { |
|
|
XMLName ixml.Name |
|
|
XMLName xml.Name |
|
|
Lang string `xml:"xml:lang,attr,omitempty"` |
|
|
Lang string `xml:"xml:lang,attr,omitempty"` |
|
|
InnerXML []byte `xml:",innerxml"` |
|
|
InnerXML []byte `xml:",innerxml"` |
|
|
} |
|
|
} |
|
|
@ -232,7 +231,7 @@ type IxmlProperty struct { |
|
|
// http://www.webdav.org/specs/rfc4918.html#ELEMENT_error
|
|
|
// http://www.webdav.org/specs/rfc4918.html#ELEMENT_error
|
|
|
// See MultiStatusWriter for the "D:" namespace prefix.
|
|
|
// See MultiStatusWriter for the "D:" namespace prefix.
|
|
|
type XmlError struct { |
|
|
type XmlError struct { |
|
|
XMLName ixml.Name `xml:"D:error"` |
|
|
XMLName xml.Name `xml:"D:error"` |
|
|
InnerXML []byte `xml:",innerxml"` |
|
|
InnerXML []byte `xml:",innerxml"` |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
@ -245,7 +244,7 @@ type SubPropstat struct { |
|
|
ResponseDescription string `xml:"D:responsedescription,omitempty"` |
|
|
ResponseDescription string `xml:"D:responsedescription,omitempty"` |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// ixmlPropstat is the same as the propstat type except it holds an ixml.Name
|
|
|
// ixmlPropstat is the same as the propstat type except it holds an xml.Name
|
|
|
// instead of an xml.Name.
|
|
|
// instead of an xml.Name.
|
|
|
type IxmlPropstat struct { |
|
|
type IxmlPropstat struct { |
|
|
Prop []IxmlProperty `xml:"D:prop>_ignored_"` |
|
|
Prop []IxmlProperty `xml:"D:prop>_ignored_"` |
|
|
@ -256,7 +255,7 @@ type IxmlPropstat struct { |
|
|
|
|
|
|
|
|
// MarshalXML prepends the "D:" namespace prefix on properties in the DAV: namespace
|
|
|
// MarshalXML prepends the "D:" namespace prefix on properties in the DAV: namespace
|
|
|
// before encoding. See MultiStatusWriter.
|
|
|
// before encoding. See MultiStatusWriter.
|
|
|
func (ps SubPropstat) MarshalXML(e *ixml.Encoder, start ixml.StartElement) error { |
|
|
func (ps SubPropstat) MarshalXML(e *xml.Encoder, start xml.StartElement) error { |
|
|
// Convert from a propstat to an ixmlPropstat.
|
|
|
// Convert from a propstat to an ixmlPropstat.
|
|
|
ixmlPs := IxmlPropstat{ |
|
|
ixmlPs := IxmlPropstat{ |
|
|
Prop: make([]IxmlProperty, len(ps.Prop)), |
|
|
Prop: make([]IxmlProperty, len(ps.Prop)), |
|
|
@ -266,7 +265,7 @@ func (ps SubPropstat) MarshalXML(e *ixml.Encoder, start ixml.StartElement) error |
|
|
} |
|
|
} |
|
|
for k, prop := range ps.Prop { |
|
|
for k, prop := range ps.Prop { |
|
|
ixmlPs.Prop[k] = IxmlProperty{ |
|
|
ixmlPs.Prop[k] = IxmlProperty{ |
|
|
XMLName: ixml.Name(prop.XMLName), |
|
|
XMLName: xml.Name(prop.XMLName), |
|
|
Lang: prop.Lang, |
|
|
Lang: prop.Lang, |
|
|
InnerXML: prop.InnerXML, |
|
|
InnerXML: prop.InnerXML, |
|
|
} |
|
|
} |
|
|
@ -274,7 +273,7 @@ func (ps SubPropstat) MarshalXML(e *ixml.Encoder, start ixml.StartElement) error |
|
|
|
|
|
|
|
|
for k, prop := range ixmlPs.Prop { |
|
|
for k, prop := range ixmlPs.Prop { |
|
|
if prop.XMLName.Space == "DAV:" { |
|
|
if prop.XMLName.Space == "DAV:" { |
|
|
prop.XMLName = ixml.Name{Space: "", Local: "D:" + prop.XMLName.Local} |
|
|
prop.XMLName = xml.Name{Space: "", Local: "D:" + prop.XMLName.Local} |
|
|
ixmlPs.Prop[k] = prop |
|
|
ixmlPs.Prop[k] = prop |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
@ -286,7 +285,7 @@ func (ps SubPropstat) MarshalXML(e *ixml.Encoder, start ixml.StartElement) error |
|
|
// http://www.webdav.org/specs/rfc4918.html#ELEMENT_response
|
|
|
// http://www.webdav.org/specs/rfc4918.html#ELEMENT_response
|
|
|
// See MultiStatusWriter for the "D:" namespace prefix.
|
|
|
// See MultiStatusWriter for the "D:" namespace prefix.
|
|
|
type Response struct { |
|
|
type Response struct { |
|
|
XMLName ixml.Name `xml:"D:response"` |
|
|
XMLName xml.Name `xml:"D:response"` |
|
|
Href []string `xml:"D:href"` |
|
|
Href []string `xml:"D:href"` |
|
|
Propstat []SubPropstat `xml:"D:propstat"` |
|
|
Propstat []SubPropstat `xml:"D:propstat"` |
|
|
Status string `xml:"D:status,omitempty"` |
|
|
Status string `xml:"D:status,omitempty"` |
|
|
@ -311,7 +310,7 @@ type MultiStatusWriter struct { |
|
|
ResponseDescription string |
|
|
ResponseDescription string |
|
|
|
|
|
|
|
|
Writer http.ResponseWriter |
|
|
Writer http.ResponseWriter |
|
|
Encoder *ixml.Encoder |
|
|
Encoder *xml.Encoder |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// Write validates and emits a DAV response as part of a multistatus response
|
|
|
// Write validates and emits a DAV response as part of a multistatus response
|
|
|
@ -355,14 +354,14 @@ func (this *MultiStatusWriter) writeHeader() error { |
|
|
if err != nil { |
|
|
if err != nil { |
|
|
return err |
|
|
return err |
|
|
} |
|
|
} |
|
|
this.Encoder = ixml.NewEncoder(this.Writer) |
|
|
this.Encoder = xml.NewEncoder(this.Writer) |
|
|
return this.Encoder.EncodeToken(ixml.StartElement{ |
|
|
return this.Encoder.EncodeToken(xml.StartElement{ |
|
|
Name: ixml.Name{ |
|
|
Name: xml.Name{ |
|
|
Space: "DAV:", |
|
|
Space: "DAV:", |
|
|
Local: "multistatus", |
|
|
Local: "multistatus", |
|
|
}, |
|
|
}, |
|
|
Attr: []ixml.Attr{{ |
|
|
Attr: []xml.Attr{{ |
|
|
Name: ixml.Name{Space: "xmlns", Local: "D"}, |
|
|
Name: xml.Name{Space: "xmlns", Local: "D"}, |
|
|
Value: "DAV:", |
|
|
Value: "DAV:", |
|
|
}}, |
|
|
}}, |
|
|
}) |
|
|
}) |
|
|
@ -376,17 +375,17 @@ func (this *MultiStatusWriter) Close() error { |
|
|
if this.Encoder == nil { |
|
|
if this.Encoder == nil { |
|
|
return nil |
|
|
return nil |
|
|
} |
|
|
} |
|
|
var end []ixml.Token |
|
|
var end []xml.Token |
|
|
if this.ResponseDescription != "" { |
|
|
if this.ResponseDescription != "" { |
|
|
name := ixml.Name{Space: "DAV:", Local: "responsedescription"} |
|
|
name := xml.Name{Space: "DAV:", Local: "responsedescription"} |
|
|
end = append(end, |
|
|
end = append(end, |
|
|
ixml.StartElement{Name: name}, |
|
|
xml.StartElement{Name: name}, |
|
|
ixml.CharData(this.ResponseDescription), |
|
|
xml.CharData(this.ResponseDescription), |
|
|
ixml.EndElement{Name: name}, |
|
|
xml.EndElement{Name: name}, |
|
|
) |
|
|
) |
|
|
} |
|
|
} |
|
|
end = append(end, ixml.EndElement{ |
|
|
end = append(end, xml.EndElement{ |
|
|
Name: ixml.Name{Space: "DAV:", Local: "multistatus"}, |
|
|
Name: xml.Name{Space: "DAV:", Local: "multistatus"}, |
|
|
}) |
|
|
}) |
|
|
for _, t := range end { |
|
|
for _, t := range end { |
|
|
err := this.Encoder.EncodeToken(t) |
|
|
err := this.Encoder.EncodeToken(t) |
|
|
@ -397,9 +396,9 @@ func (this *MultiStatusWriter) Close() error { |
|
|
return this.Encoder.Flush() |
|
|
return this.Encoder.Flush() |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
var xmlLangName = ixml.Name{Space: "http://www.w3.org/XML/1998/namespace", Local: "lang"} |
|
|
var xmlLangName = xml.Name{Space: "http://www.w3.org/XML/1998/namespace", Local: "lang"} |
|
|
|
|
|
|
|
|
func xmlLang(s ixml.StartElement, d string) string { |
|
|
func xmlLang(s xml.StartElement, d string) string { |
|
|
for _, attr := range s.Attr { |
|
|
for _, attr := range s.Attr { |
|
|
if attr.Name == xmlLangName { |
|
|
if attr.Name == xmlLangName { |
|
|
return attr.Value |
|
|
return attr.Value |
|
|
@ -410,19 +409,19 @@ func xmlLang(s ixml.StartElement, d string) string { |
|
|
|
|
|
|
|
|
type XmlValue []byte |
|
|
type XmlValue []byte |
|
|
|
|
|
|
|
|
func (v *XmlValue) UnmarshalXML(d *ixml.Decoder, start ixml.StartElement) error { |
|
|
func (v *XmlValue) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { |
|
|
// The XML value of a property can be arbitrary, mixed-content XML.
|
|
|
// The XML value of a property can be arbitrary, mixed-content XML.
|
|
|
// To make sure that the unmarshalled value contains all required
|
|
|
// To make sure that the unmarshalled value contains all required
|
|
|
// namespaces, we encode all the property value XML tokens into a
|
|
|
// namespaces, we encode all the property value XML tokens into a
|
|
|
// buffer. This forces the encoder to redeclare any used namespaces.
|
|
|
// buffer. This forces the encoder to redeclare any used namespaces.
|
|
|
var b bytes.Buffer |
|
|
var b bytes.Buffer |
|
|
e := ixml.NewEncoder(&b) |
|
|
e := xml.NewEncoder(&b) |
|
|
for { |
|
|
for { |
|
|
t, err := next(d) |
|
|
t, err := next(d) |
|
|
if err != nil { |
|
|
if err != nil { |
|
|
return err |
|
|
return err |
|
|
} |
|
|
} |
|
|
if e, ok := t.(ixml.EndElement); ok && e.Name == start.Name { |
|
|
if e, ok := t.(xml.EndElement); ok && e.Name == start.Name { |
|
|
break |
|
|
break |
|
|
} |
|
|
} |
|
|
if err = e.EncodeToken(t); err != nil { |
|
|
if err = e.EncodeToken(t); err != nil { |
|
|
@ -448,7 +447,7 @@ type ProppatchProps []Property |
|
|
//
|
|
|
//
|
|
|
// UnmarshalXML returns an error if start does not contain any properties or if
|
|
|
// UnmarshalXML returns an error if start does not contain any properties or if
|
|
|
// property values contain syntactically incorrect XML.
|
|
|
// property values contain syntactically incorrect XML.
|
|
|
func (ps *ProppatchProps) UnmarshalXML(d *ixml.Decoder, start ixml.StartElement) error { |
|
|
func (ps *ProppatchProps) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { |
|
|
lang := xmlLang(start, "") |
|
|
lang := xmlLang(start, "") |
|
|
for { |
|
|
for { |
|
|
t, err := next(d) |
|
|
t, err := next(d) |
|
|
@ -456,15 +455,15 @@ func (ps *ProppatchProps) UnmarshalXML(d *ixml.Decoder, start ixml.StartElement) |
|
|
return err |
|
|
return err |
|
|
} |
|
|
} |
|
|
switch elem := t.(type) { |
|
|
switch elem := t.(type) { |
|
|
case ixml.EndElement: |
|
|
case xml.EndElement: |
|
|
if len(*ps) == 0 { |
|
|
if len(*ps) == 0 { |
|
|
return fmt.Errorf("%s must not be empty", start.Name.Local) |
|
|
return fmt.Errorf("%s must not be empty", start.Name.Local) |
|
|
} |
|
|
} |
|
|
return nil |
|
|
return nil |
|
|
case ixml.StartElement: |
|
|
case xml.StartElement: |
|
|
p := Property{ |
|
|
p := Property{ |
|
|
XMLName: xml.Name(t.(ixml.StartElement).Name), |
|
|
XMLName: xml.Name(t.(xml.StartElement).Name), |
|
|
Lang: xmlLang(t.(ixml.StartElement), lang), |
|
|
Lang: xmlLang(t.(xml.StartElement), lang), |
|
|
} |
|
|
} |
|
|
err = d.DecodeElement(((*XmlValue)(&p.InnerXML)), &elem) |
|
|
err = d.DecodeElement(((*XmlValue)(&p.InnerXML)), &elem) |
|
|
if err != nil { |
|
|
if err != nil { |
|
|
@ -478,14 +477,14 @@ func (ps *ProppatchProps) UnmarshalXML(d *ixml.Decoder, start ixml.StartElement) |
|
|
// http://www.webdav.org/specs/rfc4918.html#ELEMENT_set
|
|
|
// http://www.webdav.org/specs/rfc4918.html#ELEMENT_set
|
|
|
// http://www.webdav.org/specs/rfc4918.html#ELEMENT_remove
|
|
|
// http://www.webdav.org/specs/rfc4918.html#ELEMENT_remove
|
|
|
type SetRemove struct { |
|
|
type SetRemove struct { |
|
|
XMLName ixml.Name |
|
|
XMLName xml.Name |
|
|
Lang string `xml:"xml:lang,attr,omitempty"` |
|
|
Lang string `xml:"xml:lang,attr,omitempty"` |
|
|
Prop ProppatchProps `xml:"DAV: prop"` |
|
|
Prop ProppatchProps `xml:"DAV: prop"` |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// http://www.webdav.org/specs/rfc4918.html#ELEMENT_propertyupdate
|
|
|
// http://www.webdav.org/specs/rfc4918.html#ELEMENT_propertyupdate
|
|
|
type PropertyUpdate struct { |
|
|
type PropertyUpdate struct { |
|
|
XMLName ixml.Name `xml:"DAV: propertyupdate"` |
|
|
XMLName xml.Name `xml:"DAV: propertyupdate"` |
|
|
Lang string `xml:"xml:lang,attr,omitempty"` |
|
|
Lang string `xml:"xml:lang,attr,omitempty"` |
|
|
SetRemove []SetRemove `xml:",any"` |
|
|
SetRemove []SetRemove `xml:",any"` |
|
|
} |
|
|
} |
|
|
|