carddav: add Client.FindAddressBooks

This commit is contained in:
Simon Ser 2020-01-14 23:13:23 +01:00
parent 3d05533a31
commit 9dfabd89c8
No known key found for this signature in database
GPG Key ID: 0FDE7BE0E88F5E48
6 changed files with 114 additions and 14 deletions

View File

@ -1,3 +1,18 @@
package carddav package carddav
import (
"encoding/xml"
)
const namespace = "urn:ietf:params:xml:ns:carddav" const namespace = "urn:ietf:params:xml:ns:carddav"
type AddressBook struct {
Href string
Description string
}
var addressBookName = xml.Name{namespace, "addressbook"}
type AddressBookQuery struct {
AddressDataProps []string
}

View File

@ -26,7 +26,7 @@ func NewClient(c *http.Client, endpoint string) (*Client, error) {
return &Client{wc, ic}, nil return &Client{wc, ic}, nil
} }
func (c *Client) FindAddressbookHomeSet(principal string) (string, error) { func (c *Client) FindAddressBookHomeSet(principal string) (string, error) {
name := xml.Name{namespace, "addressbook-home-set"} name := xml.Name{namespace, "addressbook-home-set"}
propfind := internal.NewPropPropfind(name) propfind := internal.NewPropPropfind(name)
@ -42,3 +42,50 @@ func (c *Client) FindAddressbookHomeSet(principal string) (string, error) {
return prop.Href, nil return prop.Href, nil
} }
func (c *Client) FindAddressBooks(addressBookHomeSet string) ([]AddressBook, error) {
resTypeName := xml.Name{"DAV:", "resourcetype"}
descName := xml.Name{namespace, "addressbook-description"}
propfind := internal.NewPropPropfind(resTypeName, descName)
req, err := c.ic.NewXMLRequest("PROPFIND", addressBookHomeSet, propfind)
if err != nil {
return nil, err
}
req.Header.Add("Depth", "1")
ms, err := c.ic.DoMultiStatus(req)
if err != nil {
return nil, err
}
l := make([]AddressBook, 0, len(ms.Responses))
for i := range ms.Responses {
resp := &ms.Responses[i]
href, err := resp.Href()
if err != nil {
return nil, err
}
var resTypeProp internal.ResourceType
if err := resp.DecodeProp(resTypeName, &resTypeProp); err != nil {
return nil, err
}
if !resTypeProp.Is(addressBookName) {
continue
}
var descProp addressbookDescription
if err := resp.DecodeProp(descName, &descProp); err != nil {
return nil, err
}
l = append(l, AddressBook{
Href: href,
Description: descProp.Data,
})
}
return l, nil
}

View File

@ -5,6 +5,19 @@ import (
) )
type addressbookHomeSet struct { type addressbookHomeSet struct {
Name xml.Name `xml:"urn:ietf:params:xml:ns:carddav addressbook-home-set"` XMLName xml.Name `xml:"urn:ietf:params:xml:ns:carddav addressbook-home-set"`
Href string `xml:"href"` Href string `xml:"href"`
} }
type addressbookDescription struct {
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:carddav addressbook-description"`
Data string `xml:",chardata"`
}
// https://tools.ietf.org/html/rfc6352#section-10.3
type addressbookQuery struct {
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:carddav addressbook-query"`
Prop *internal.Prop `xml:"DAV: prop,omitempty"`
// TODO: DAV:allprop | DAV:propname
// TODO: filter, limit?
}

View File

@ -5,6 +5,6 @@ import (
) )
type currentUserPrincipal struct { type currentUserPrincipal struct {
Name xml.Name `xml:"DAV: current-user-principal"` XMLName xml.Name `xml:"DAV: current-user-principal"`
Href string `xml:"href"` Href string `xml:"href"`
} }

View File

@ -56,7 +56,7 @@ func (c *Client) NewXMLRequest(method string, href string, v interface{}) (*http
func (c *Client) Do(req *http.Request) (*http.Response, error) { func (c *Client) Do(req *http.Request) (*http.Response, error) {
// TODO: remove this quirk // TODO: remove this quirk
req.SetBasicAuth("simon", "") req.SetBasicAuth("emersion", "")
return c.http.Do(req) return c.http.Do(req)
} }

View File

@ -42,7 +42,7 @@ type Multistatus struct {
func (ms *Multistatus) Get(href string) (*Response, error) { func (ms *Multistatus) Get(href string) (*Response, error) {
for i := range ms.Responses { for i := range ms.Responses {
resp := &ms.Responses[i] resp := &ms.Responses[i]
for _, h := range resp.Href { for _, h := range resp.Hrefs {
if h == href { if h == href {
return resp, nil return resp, nil
} }
@ -55,7 +55,7 @@ func (ms *Multistatus) Get(href string) (*Response, error) {
// https://tools.ietf.org/html/rfc4918#section-14.24 // https://tools.ietf.org/html/rfc4918#section-14.24
type Response struct { type Response struct {
XMLName xml.Name `xml:"DAV: response"` XMLName xml.Name `xml:"DAV: response"`
Href []string `xml:"href"` Hrefs []string `xml:"href"`
Propstats []Propstat `xml:"propstat,omitempty"` Propstats []Propstat `xml:"propstat,omitempty"`
ResponseDescription string `xml:"responsedescription,omitempty"` ResponseDescription string `xml:"responsedescription,omitempty"`
Status Status `xml:"status,omitempty"` Status Status `xml:"status,omitempty"`
@ -63,6 +63,16 @@ type Response struct {
Location *Location `xml:"location,omitempty"` Location *Location `xml:"location,omitempty"`
} }
func (resp *Response) Href() (string, error) {
if err := resp.Status.Err(); err != nil {
return "", err
}
if len(resp.Hrefs) != 1 {
return "", fmt.Errorf("webdav: malformed response: expected exactly one href element, got %v", len(resp.Hrefs))
}
return resp.Hrefs[0], nil
}
func (resp *Response) DecodeProp(name xml.Name, v interface{}) error { func (resp *Response) DecodeProp(name xml.Name, v interface{}) error {
if err := resp.Status.Err(); err != nil { if err := resp.Status.Err(); err != nil {
return err return err
@ -71,8 +81,7 @@ func (resp *Response) DecodeProp(name xml.Name, v interface{}) error {
propstat := &resp.Propstats[i] propstat := &resp.Propstats[i]
for j := range propstat.Prop.Raw { for j := range propstat.Prop.Raw {
raw := &propstat.Prop.Raw[j] raw := &propstat.Prop.Raw[j]
if start, ok := raw.tok.(xml.StartElement); ok { if start, ok := raw.tok.(xml.StartElement); ok && name == start.Name {
if name == start.Name {
if err := propstat.Status.Err(); err != nil { if err := propstat.Status.Err(); err != nil {
return err return err
} }
@ -80,7 +89,6 @@ func (resp *Response) DecodeProp(name xml.Name, v interface{}) error {
} }
} }
} }
}
return fmt.Errorf("webdav: missing prop %v %v in response", name.Space, name.Local) return fmt.Errorf("webdav: missing prop %v %v in response", name.Space, name.Local)
} }
@ -120,3 +128,20 @@ func NewPropPropfind(names ...xml.Name) *Propfind {
} }
return &Propfind{Prop: &Prop{Raw: children}} return &Propfind{Prop: &Prop{Raw: children}}
} }
// https://tools.ietf.org/html/rfc4918#section-15.9
type ResourceType struct {
XMLName xml.Name `xml:"DAV: resourcetype"`
Raw []RawXMLValue `xml:",any"`
}
func (t *ResourceType) Is(name xml.Name) bool {
for _, raw := range t.Raw {
if start, ok := raw.tok.(xml.StartElement); ok && name == start.Name {
return true
}
}
return false
}
var CollectionName = xml.Name{"DAV:", "collection"}