diff options
Diffstat (limited to 'pim/cmd/caldavfs/principal.go')
| -rw-r--r-- | pim/cmd/caldavfs/principal.go | 66 |
1 files changed, 66 insertions, 0 deletions
diff --git a/pim/cmd/caldavfs/principal.go b/pim/cmd/caldavfs/principal.go new file mode 100644 index 0000000..b5d6558 --- /dev/null +++ b/pim/cmd/caldavfs/principal.go @@ -0,0 +1,66 @@ +package main + +import ( + "encoding/xml" + "fmt" + "net/http" + "strings" +) + +// findPrincipal asks the endpoint who we are. +// +// go-webdav does this too, but it resolves the request path with +// path.Join, which drops a trailing slash: a client pointed at /dav/ +// ends up asking about /dav, and Cyrus answers 405 for that exact +// spelling while answering 207 for /dav/. Deeper paths tolerate either, +// so only this first request needs doing by hand. +func findPrincipal(endpoint, user, pass string) (string, error) { + body := `<?xml version="1.0" encoding="utf-8"?>` + + `<d:propfind xmlns:d="DAV:"><d:prop><d:current-user-principal/></d:prop></d:propfind>` + + req, err := http.NewRequest("PROPFIND", endpoint, strings.NewReader(body)) + if err != nil { + return "", err + } + req.Header.Set("Depth", "0") + req.Header.Set("Content-Type", "application/xml; charset=utf-8") + req.SetBasicAuth(user, pass) + + resp, err := client.Do(req) + if err != nil { + return "", err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusMultiStatus { + return "", fmt.Errorf("%s: %s", endpoint, resp.Status) + } + + // Spelled out rather than using encoding/xml's "a>b" path + // shorthand, which does not carry namespaces through. + type href struct { + Href string `xml:"DAV: href"` + } + type prop struct { + Principal href `xml:"DAV: current-user-principal"` + } + type propstat struct { + Prop prop `xml:"DAV: prop"` + } + type response struct { + Propstats []propstat `xml:"DAV: propstat"` + } + var ms struct { + Responses []response `xml:"DAV: response"` + } + if err := xml.NewDecoder(resp.Body).Decode(&ms); err != nil { + return "", err + } + for _, r := range ms.Responses { + for _, ps := range r.Propstats { + if h := strings.TrimSpace(ps.Prop.Principal.Href); h != "" { + return h, nil + } + } + } + return "", fmt.Errorf("%s: no current-user-principal", endpoint) +} |
