carddav: honor address-data in addressbook-query

This commit is contained in:
Simon Ser 2020-01-27 10:30:19 +01:00
parent 29cccc7ef9
commit 1f509de404
No known key found for this signature in database
GPG Key ID: 0FDE7BE0E88F5E48
3 changed files with 55 additions and 39 deletions

View File

@ -17,8 +17,7 @@ type AddressBook struct {
} }
type AddressBookQuery struct { type AddressBookQuery struct {
Props []string DataRequest AddressDataRequest
AllProp bool
PropFilters []PropFilter PropFilters []PropFilter
FilterTest FilterTest // defaults to FilterAnyOf FilterTest FilterTest // defaults to FilterAnyOf
@ -26,6 +25,11 @@ type AddressBookQuery struct {
Limit int // <= 0 means unlimited Limit int // <= 0 means unlimited
} }
type AddressDataRequest struct {
Props []string
AllProp bool
}
type PropFilter struct { type PropFilter struct {
Name string Name string
Test FilterTest // defaults to FilterAnyOf Test FilterTest // defaults to FilterAnyOf
@ -68,9 +72,7 @@ const (
type AddressBookMultiGet struct { type AddressBookMultiGet struct {
Paths []string Paths []string
DataRequest AddressDataRequest
Props []string
AllProp bool
} }
type AddressObject struct { type AddressObject struct {

View File

@ -142,12 +142,12 @@ func (c *Client) FindAddressBooks(addressBookHomeSet string) ([]AddressBook, err
return l, nil return l, nil
} }
func encodeAddressPropReq(props []string, allProp bool) (*internal.Prop, error) { func encodeAddressPropReq(req *AddressDataRequest) (*internal.Prop, error) {
var addrDataReq addressDataReq var addrDataReq addressDataReq
if allProp { if req.AllProp {
addrDataReq.Allprop = &struct{}{} addrDataReq.Allprop = &struct{}{}
} else { } else {
for _, name := range props { for _, name := range req.Props {
addrDataReq.Props = append(addrDataReq.Props, prop{Name: name}) addrDataReq.Props = append(addrDataReq.Props, prop{Name: name})
} }
} }
@ -245,14 +245,7 @@ func decodeAddressList(ms *internal.Multistatus) ([]AddressObject, error) {
} }
func (c *Client) QueryAddressBook(addressBook string, query *AddressBookQuery) ([]AddressObject, error) { func (c *Client) QueryAddressBook(addressBook string, query *AddressBookQuery) ([]AddressObject, error) {
var props []string propReq, err := encodeAddressPropReq(&query.DataRequest)
var allProp bool
if query != nil {
props = query.Props
allProp = query.AllProp
}
propReq, err := encodeAddressPropReq(props, allProp)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -286,14 +279,7 @@ func (c *Client) QueryAddressBook(addressBook string, query *AddressBookQuery) (
} }
func (c *Client) MultiGetAddressBook(path string, multiGet *AddressBookMultiGet) ([]AddressObject, error) { func (c *Client) MultiGetAddressBook(path string, multiGet *AddressBookMultiGet) ([]AddressObject, error) {
var props []string propReq, err := encodeAddressPropReq(&multiGet.DataRequest)
var allProp bool
if multiGet != nil {
props = multiGet.Props
allProp = multiGet.AllProp
}
propReq, err := encodeAddressPropReq(props, allProp)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -16,8 +16,8 @@ import (
// Backend is a CardDAV server backend. // Backend is a CardDAV server backend.
type Backend interface { type Backend interface {
AddressBook() (*AddressBook, error) AddressBook() (*AddressBook, error)
GetAddressObject(path string) (*AddressObject, error) GetAddressObject(path string, req *AddressDataRequest) (*AddressObject, error)
ListAddressObjects() ([]AddressObject, error) ListAddressObjects(req *AddressDataRequest) ([]AddressObject, error)
QueryAddressObjects(query *AddressBookQuery) ([]AddressObject, error) QueryAddressObjects(query *AddressBookQuery) ([]AddressObject, error)
PutAddressObject(path string, card vcard.Card) error PutAddressObject(path string, card vcard.Card) error
DeleteAddressObject(path string) error DeleteAddressObject(path string) error
@ -108,6 +108,18 @@ func decodeTextMatch(tm *textMatch) *TextMatch {
} }
} }
func decodeAddressDataReq(addressData *addressDataReq) (*AddressDataRequest, error) {
if addressData.Allprop != nil && len(addressData.Props) > 0 {
return nil, internal.HTTPErrorf(http.StatusBadRequest, "carddav: only one of allprop or prop can be specified in address-data")
}
req := &AddressDataRequest{AllProp: addressData.Allprop != nil}
for _, p := range addressData.Props {
req.Props = append(req.Props, p.Name)
}
return req, nil
}
func (h *Handler) handleQuery(w http.ResponseWriter, query *addressbookQuery) error { func (h *Handler) handleQuery(w http.ResponseWriter, query *addressbookQuery) error {
var q AddressBookQuery var q AddressBookQuery
if query.Prop != nil { if query.Prop != nil {
@ -115,13 +127,11 @@ func (h *Handler) handleQuery(w http.ResponseWriter, query *addressbookQuery) er
if err := query.Prop.Decode(&addressData); err != nil && !internal.IsMissingProp(err) { if err := query.Prop.Decode(&addressData); err != nil && !internal.IsMissingProp(err) {
return err return err
} }
if addressData.Allprop != nil && len(addressData.Props) > 0 { req, err := decodeAddressDataReq(&addressData)
return internal.HTTPErrorf(http.StatusBadRequest, "carddav: only one of allprop or prop can be specified in address-data") if err != nil {
} return err
q.AllProp = addressData.Allprop != nil
for _, p := range addressData.Props {
q.Props = append(q.Props, p.Name)
} }
q.DataRequest = *req
} }
q.FilterTest = FilterTest(query.Filter.Test) q.FilterTest = FilterTest(query.Filter.Test)
for _, el := range query.Filter.Props { for _, el := range query.Filter.Props {
@ -163,11 +173,22 @@ func (h *Handler) handleQuery(w http.ResponseWriter, query *addressbookQuery) er
} }
func (h *Handler) handleMultiget(w http.ResponseWriter, multiget *addressbookMultiget) error { func (h *Handler) handleMultiget(w http.ResponseWriter, multiget *addressbookMultiget) error {
var dataReq AddressDataRequest
if multiget.Prop != nil {
var addressData addressDataReq
if err := multiget.Prop.Decode(&addressData); err != nil && !internal.IsMissingProp(err) {
return err
}
decoded, err := decodeAddressDataReq(&addressData)
if err != nil {
return err
}
dataReq = *decoded
}
var resps []internal.Response var resps []internal.Response
for _, href := range multiget.Hrefs { for _, href := range multiget.Hrefs {
// TODO: only get a subset of the vCard fields, depending on the ao, err := h.Backend.GetAddressObject(href.Path, &dataReq)
// multiget query
ao, err := h.Backend.GetAddressObject(href.Path)
if err != nil { if err != nil {
return err // TODO: create internal.Response with error return err // TODO: create internal.Response with error
} }
@ -200,7 +221,8 @@ func (b *backend) Options(r *http.Request) ([]string, error) {
return []string{http.MethodOptions, "PROPFIND"}, nil return []string{http.MethodOptions, "PROPFIND"}, nil
} }
_, err := b.Backend.GetAddressObject(r.URL.Path) var dataReq AddressDataRequest
_, err := b.Backend.GetAddressObject(r.URL.Path, &dataReq)
if httpErr, ok := err.(*internal.HTTPError); ok && httpErr.Code == http.StatusNotFound { if httpErr, ok := err.(*internal.HTTPError); ok && httpErr.Code == http.StatusNotFound {
return []string{http.MethodOptions}, nil return []string{http.MethodOptions}, nil
} else if err != nil { } else if err != nil {
@ -215,7 +237,11 @@ func (b *backend) HeadGet(w http.ResponseWriter, r *http.Request) error {
return &internal.HTTPError{Code: http.StatusMethodNotAllowed} return &internal.HTTPError{Code: http.StatusMethodNotAllowed}
} }
ao, err := b.Backend.GetAddressObject(r.URL.Path) var dataReq AddressDataRequest
if r.Method != http.MethodHead {
dataReq.AllProp = true
}
ao, err := b.Backend.GetAddressObject(r.URL.Path, &dataReq)
if err != nil { if err != nil {
return err return err
} }
@ -230,6 +256,8 @@ func (b *backend) HeadGet(w http.ResponseWriter, r *http.Request) error {
} }
func (b *backend) Propfind(r *http.Request, propfind *internal.Propfind, depth internal.Depth) (*internal.Multistatus, error) { func (b *backend) Propfind(r *http.Request, propfind *internal.Propfind, depth internal.Depth) (*internal.Multistatus, error) {
var dataReq AddressDataRequest
var resps []internal.Response var resps []internal.Response
if r.URL.Path == "/" { if r.URL.Path == "/" {
ab, err := b.Backend.AddressBook() ab, err := b.Backend.AddressBook()
@ -244,7 +272,7 @@ func (b *backend) Propfind(r *http.Request, propfind *internal.Propfind, depth i
resps = append(resps, *resp) resps = append(resps, *resp)
if depth != internal.DepthZero { if depth != internal.DepthZero {
aos, err := b.Backend.ListAddressObjects() aos, err := b.Backend.ListAddressObjects(&dataReq)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -258,7 +286,7 @@ func (b *backend) Propfind(r *http.Request, propfind *internal.Propfind, depth i
} }
} }
} else { } else {
ao, err := b.Backend.GetAddressObject(r.URL.Path) ao, err := b.Backend.GetAddressObject(r.URL.Path, &dataReq)
if err != nil { if err != nil {
return nil, err return nil, err
} }