| // Copyright 2014 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package webdav |
| |
| // The XML encoding is covered by Section 14. |
| // http://www.webdav.org/specs/rfc4918.html#xml.element.definitions |
| |
| import ( |
| "bytes" |
| "encoding/xml" |
| "fmt" |
| "io" |
| "net/http" |
| "time" |
| ) |
| |
| // http://www.webdav.org/specs/rfc4918.html#ELEMENT_lockinfo |
| type lockInfo struct { |
| XMLName xml.Name `xml:"lockinfo"` |
| Exclusive *struct{} `xml:"lockscope>exclusive"` |
| Shared *struct{} `xml:"lockscope>shared"` |
| Write *struct{} `xml:"locktype>write"` |
| Owner owner `xml:"owner"` |
| } |
| |
| // http://www.webdav.org/specs/rfc4918.html#ELEMENT_owner |
| type owner struct { |
| InnerXML string `xml:",innerxml"` |
| } |
| |
| func readLockInfo(r io.Reader) (li lockInfo, status int, err error) { |
| c := &countingReader{r: r} |
| if err = xml.NewDecoder(c).Decode(&li); err != nil { |
| if err == io.EOF { |
| if c.n == 0 { |
| // An empty body means to refresh the lock. |
| // http://www.webdav.org/specs/rfc4918.html#refreshing-locks |
| return lockInfo{}, 0, nil |
| } |
| err = errInvalidLockInfo |
| } |
| return lockInfo{}, http.StatusBadRequest, err |
| } |
| // We only support exclusive (non-shared) write locks. In practice, these are |
| // the only types of locks that seem to matter. |
| if li.Exclusive == nil || li.Shared != nil || li.Write == nil { |
| return lockInfo{}, http.StatusNotImplemented, errUnsupportedLockInfo |
| } |
| return li, 0, nil |
| } |
| |
| type countingReader struct { |
| n int |
| r io.Reader |
| } |
| |
| func (c *countingReader) Read(p []byte) (int, error) { |
| n, err := c.r.Read(p) |
| c.n += n |
| return n, err |
| } |
| |
| func writeLockInfo(w io.Writer, token string, ld LockDetails) (int, error) { |
| depth := "infinity" |
| if ld.ZeroDepth { |
| depth = "0" |
| } |
| timeout := ld.Duration / time.Second |
| return fmt.Fprintf(w, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"+ |
| "<D:prop xmlns:D=\"DAV:\"><D:lockdiscovery><D:activelock>\n"+ |
| " <D:locktype><D:write/></D:locktype>\n"+ |
| " <D:lockscope><D:exclusive/></D:lockscope>\n"+ |
| " <D:depth>%s</D:depth>\n"+ |
| " <D:owner>%s</D:owner>\n"+ |
| " <D:timeout>Second-%d</D:timeout>\n"+ |
| " <D:locktoken><D:href>%s</D:href></D:locktoken>\n"+ |
| " <D:lockroot><D:href>%s</D:href></D:lockroot>\n"+ |
| "</D:activelock></D:lockdiscovery></D:prop>", |
| depth, ld.OwnerXML, timeout, escape(token), escape(ld.Root), |
| ) |
| } |
| |
| func escape(s string) string { |
| for i := 0; i < len(s); i++ { |
| switch s[i] { |
| case '"', '&', '\'', '<', '>': |
| b := bytes.NewBuffer(nil) |
| xml.EscapeText(b, []byte(s)) |
| return b.String() |
| } |
| } |
| return s |
| } |