Commit 34ff4cd5 authored by mpl's avatar mpl Committed by Nigel Tao

webdav: make it work for Mini-Redirector, emit namespace-prefixed XML.

The default webDAV client for windows explorer, Mini-Redirector,
apparently can not handle XML elements with a default namespace.

This change modifies the emitted XML so that elements are prefixed with
the D: namespace, defined as as "DAV:" in the multistatus tag.

Fixes golang/go#11177

Change-Id: Ib323ca312fa10bd5aa6e6c61d90812d066543bac
Reviewed-on: https://go-review.googlesource.com/10942Reviewed-by: 's avatarNigel Tao <nigeltao@golang.org>
parent 38f3db38
......@@ -278,7 +278,7 @@ loop:
if conflict {
pstatForbidden := Propstat{
Status: http.StatusForbidden,
XMLError: `<error xmlns="DAV:"><cannot-modify-protected-property/></error>`,
XMLError: `<D:cannot-modify-protected-property xmlns:D="DAV:"/>`,
}
pstatFailedDep := Propstat{
Status: StatusFailedDependency,
......@@ -328,7 +328,7 @@ loop:
func findResourceType(fs FileSystem, ls LockSystem, name string, fi os.FileInfo) (string, error) {
if fi.IsDir() {
return `<collection xmlns="DAV:"/>`, nil
return `<D:collection xmlns:D="DAV:"/>`, nil
}
return "", nil
}
......@@ -377,8 +377,8 @@ func findETag(fs FileSystem, ls LockSystem, name string, fi os.FileInfo) (string
func findSupportedLock(fs FileSystem, ls LockSystem, name string, fi os.FileInfo) (string, error) {
return `` +
`<lockentry xmlns="DAV:">` +
`<lockscope><exclusive/></lockscope>` +
`<locktype><write/></locktype>` +
`</lockentry>`, nil
`<D:lockentry xmlns:D="DAV:">` +
`<D:lockscope><D:exclusive/></D:lockscope>` +
`<D:locktype><D:write/></D:locktype>` +
`</D:lockentry>`, nil
}
......@@ -44,6 +44,15 @@ func TestMemPS(t *testing.T) {
return nil
}
const (
lockEntry = `` +
`<D:lockentry xmlns:D="DAV:">` +
`<D:lockscope><D:exclusive/></D:lockscope>` +
`<D:locktype><D:write/></D:locktype>` +
`</D:lockentry>`
statForbiddenError = `<D:cannot-modify-protected-property xmlns:D="DAV:"/>`
)
type propOp struct {
op string
name string
......@@ -95,7 +104,7 @@ func TestMemPS(t *testing.T) {
Status: http.StatusOK,
Props: []Property{{
XMLName: xml.Name{Space: "DAV:", Local: "resourcetype"},
InnerXML: []byte(`<collection xmlns="DAV:"/>`),
InnerXML: []byte(`<D:collection xmlns:D="DAV:"/>`),
}, {
XMLName: xml.Name{Space: "DAV:", Local: "displayname"},
InnerXML: []byte("dir"),
......@@ -109,13 +118,8 @@ func TestMemPS(t *testing.T) {
XMLName: xml.Name{Space: "DAV:", Local: "getcontenttype"},
InnerXML: []byte("text/plain; charset=utf-8"),
}, {
XMLName: xml.Name{Space: "DAV:", Local: "supportedlock"},
InnerXML: []byte(`` +
`<lockentry xmlns="DAV:">` +
`<lockscope><exclusive/></lockscope>` +
`<locktype><write/></locktype>` +
`</lockentry>`,
),
XMLName: xml.Name{Space: "DAV:", Local: "supportedlock"},
InnerXML: []byte(lockEntry),
}},
}},
}, {
......@@ -142,13 +146,8 @@ func TestMemPS(t *testing.T) {
XMLName: xml.Name{Space: "DAV:", Local: "getetag"},
InnerXML: nil, // Calculated during test.
}, {
XMLName: xml.Name{Space: "DAV:", Local: "supportedlock"},
InnerXML: []byte(`` +
`<lockentry xmlns="DAV:">` +
`<lockscope><exclusive/></lockscope>` +
`<locktype><write/></locktype>` +
`</lockentry>`,
),
XMLName: xml.Name{Space: "DAV:", Local: "supportedlock"},
InnerXML: []byte(lockEntry),
}},
}},
}, {
......@@ -179,13 +178,8 @@ func TestMemPS(t *testing.T) {
XMLName: xml.Name{Space: "DAV:", Local: "getetag"},
InnerXML: nil, // Calculated during test.
}, {
XMLName: xml.Name{Space: "DAV:", Local: "supportedlock"},
InnerXML: []byte(`` +
`<lockentry xmlns="DAV:">` +
`<lockscope><exclusive/></lockscope>` +
`<locktype><write/></locktype>` +
`</lockentry>`,
),
XMLName: xml.Name{Space: "DAV:", Local: "supportedlock"},
InnerXML: []byte(lockEntry),
}}}, {
Status: http.StatusNotFound,
Props: []Property{{
......@@ -204,7 +198,7 @@ func TestMemPS(t *testing.T) {
Status: http.StatusOK,
Props: []Property{{
XMLName: xml.Name{Space: "DAV:", Local: "resourcetype"},
InnerXML: []byte(`<collection xmlns="DAV:"/>`),
InnerXML: []byte(`<D:collection xmlns:D="DAV:"/>`),
}},
}},
}, {
......@@ -296,7 +290,7 @@ func TestMemPS(t *testing.T) {
}},
wantPropstats: []Propstat{{
Status: http.StatusForbidden,
XMLError: `<error xmlns="DAV:"><cannot-modify-protected-property/></error>`,
XMLError: statForbiddenError,
Props: []Property{{
XMLName: xml.Name{Space: "DAV:", Local: "getetag"},
}},
......@@ -351,7 +345,7 @@ func TestMemPS(t *testing.T) {
}},
wantPropstats: []Propstat{{
Status: http.StatusForbidden,
XMLError: `<error xmlns="DAV:"><cannot-modify-protected-property/></error>`,
XMLError: statForbiddenError,
Props: []Property{{
XMLName: xml.Name{Space: "DAV:", Local: "displayname"},
}},
......
......@@ -206,32 +206,55 @@ type Property struct {
}
// http://www.webdav.org/specs/rfc4918.html#ELEMENT_error
// See multistatusWriter for the "D:" namespace prefix.
type xmlError struct {
XMLName xml.Name `xml:"DAV: error"`
XMLName xml.Name `xml:"D:error"`
InnerXML []byte `xml:",innerxml"`
}
// http://www.webdav.org/specs/rfc4918.html#ELEMENT_propstat
// See multistatusWriter for the "D:" namespace prefix.
type propstat struct {
Prop []Property `xml:"DAV: prop>_ignored_"`
Status string `xml:"DAV: status"`
Error *xmlError `xml:"DAV: error"`
ResponseDescription string `xml:"DAV: responsedescription,omitempty"`
Prop []Property `xml:"D:prop>_ignored_"`
Status string `xml:"D:status"`
Error *xmlError `xml:"D:error"`
ResponseDescription string `xml:"D:responsedescription,omitempty"`
}
// MarshalXML prepends the "D:" namespace prefix on properties in the DAV: namespace
// before encoding. See multistatusWriter.
func (ps propstat) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
for k, prop := range ps.Prop {
if prop.XMLName.Space == "DAV:" {
prop.XMLName = xml.Name{Space: "", Local: "D:" + prop.XMLName.Local}
ps.Prop[k] = prop
}
}
// Distinct type to avoid infinite recursion of MarshalXML.
type newpropstat propstat
return e.EncodeElement(newpropstat(ps), start)
}
// http://www.webdav.org/specs/rfc4918.html#ELEMENT_response
// See multistatusWriter for the "D:" namespace prefix.
type response struct {
XMLName xml.Name `xml:"DAV: response"`
Href []string `xml:"DAV: href"`
Propstat []propstat `xml:"DAV: propstat"`
Status string `xml:"DAV: status,omitempty"`
Error *xmlError `xml:"DAV: error"`
ResponseDescription string `xml:"DAV: responsedescription,omitempty"`
XMLName xml.Name `xml:"D:response"`
Href []string `xml:"D:href"`
Propstat []propstat `xml:"D:propstat"`
Status string `xml:"D:status,omitempty"`
Error *xmlError `xml:"D:error"`
ResponseDescription string `xml:"D:responsedescription,omitempty"`
}
// MultistatusWriter marshals one or more Responses into a XML
// multistatus response.
// See http://www.webdav.org/specs/rfc4918.html#ELEMENT_multistatus
// TODO(rsto, mpl): As a workaround, the "D:" namespace prefix, defined as
// "DAV:" on this element, is prepended on the nested response, as well as on all
// its nested elements. All property names in the DAV: namespace are prefixed as
// well. This is because some versions of Mini-Redirector (on windows 7) ignore
// elements with a default namespace (no prefixed namespace). A less intrusive fix
// should be possible after golang.org/cl/11074. See https://golang.org/issue/11177
type multistatusWriter struct {
// ResponseDescription contains the optional responsedescription
// of the multistatus XML element. Only the latest content before
......@@ -291,7 +314,7 @@ func (w *multistatusWriter) writeHeader() error {
Local: "multistatus",
},
Attr: []xml.Attr{{
Name: xml.Name{Local: "xmlns"},
Name: xml.Name{Space: "xmlns", Local: "D"},
Value: "DAV:",
}},
})
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment