mirror of
https://github.com/1f349/go-webdav.git
synced 2024-12-22 16:24:14 +00:00
carddav: support multiple address books
This is the equivalent of #127 (and #140) for CardDAV and finally allows backends to serve different address books to different users. While I'm breaking the interface, correct one last instance of "Addressbook" to "AddressBook" (in `AddressBookHomeSetPath`).
This commit is contained in:
parent
e3ba95cd77
commit
eaac65215b
@ -37,22 +37,37 @@ func (*testBackend) CurrentUserPrincipal(ctx context.Context) (string, error) {
|
|||||||
return r, nil
|
return r, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*testBackend) AddressbookHomeSetPath(ctx context.Context) (string, error) {
|
func (*testBackend) AddressBookHomeSetPath(ctx context.Context) (string, error) {
|
||||||
r := ctx.Value(homeSetPathKey).(string)
|
r := ctx.Value(homeSetPathKey).(string)
|
||||||
return r, nil
|
return r, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*testBackend) AddressBook(ctx context.Context) (*AddressBook, error) {
|
func (*testBackend) ListAddressBooks(ctx context.Context) ([]AddressBook, error) {
|
||||||
p := ctx.Value(addressBookPathKey).(string)
|
p := ctx.Value(addressBookPathKey).(string)
|
||||||
return &AddressBook{
|
return []AddressBook{
|
||||||
|
AddressBook{
|
||||||
Path: p,
|
Path: p,
|
||||||
Name: "My contacts",
|
Name: "My contacts",
|
||||||
Description: "Default address book",
|
Description: "Default address book",
|
||||||
MaxResourceSize: 1024,
|
MaxResourceSize: 1024,
|
||||||
SupportedAddressData: nil,
|
SupportedAddressData: nil,
|
||||||
|
},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *testBackend) GetAddressBook(ctx context.Context, path string) (*AddressBook, error) {
|
||||||
|
abs, err := b.ListAddressBooks(ctx)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
for _, ab := range abs {
|
||||||
|
if ab.Path == path {
|
||||||
|
return &ab, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, webdav.NewHTTPError(404, fmt.Errorf("Not found"))
|
||||||
|
}
|
||||||
|
|
||||||
func (*testBackend) GetAddressObject(ctx context.Context, path string, req *AddressDataRequest) (*AddressObject, error) {
|
func (*testBackend) GetAddressObject(ctx context.Context, path string, req *AddressDataRequest) (*AddressObject, error) {
|
||||||
if path == alicePath {
|
if path == alicePath {
|
||||||
card, err := vcard.NewDecoder(strings.NewReader(aliceData)).Decode()
|
card, err := vcard.NewDecoder(strings.NewReader(aliceData)).Decode()
|
||||||
@ -68,7 +83,11 @@ func (*testBackend) GetAddressObject(ctx context.Context, path string, req *Addr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *testBackend) ListAddressObjects(ctx context.Context, req *AddressDataRequest) ([]AddressObject, error) {
|
func (b *testBackend) ListAddressObjects(ctx context.Context, path string, req *AddressDataRequest) ([]AddressObject, error) {
|
||||||
|
p := ctx.Value(addressBookPathKey).(string)
|
||||||
|
if !strings.HasPrefix(path, p) {
|
||||||
|
return nil, webdav.NewHTTPError(404, fmt.Errorf("Not found"))
|
||||||
|
}
|
||||||
alice, err := b.GetAddressObject(ctx, alicePath, req)
|
alice, err := b.GetAddressObject(ctx, alicePath, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -77,7 +96,7 @@ func (b *testBackend) ListAddressObjects(ctx context.Context, req *AddressDataRe
|
|||||||
return []AddressObject{*alice}, nil
|
return []AddressObject{*alice}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*testBackend) QueryAddressObjects(ctx context.Context, query *AddressBookQuery) ([]AddressObject, error) {
|
func (*testBackend) QueryAddressObjects(ctx context.Context, path string, query *AddressBookQuery) ([]AddressObject, error) {
|
||||||
panic("TODO: implement")
|
panic("TODO: implement")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,8 +16,6 @@ import (
|
|||||||
"github.com/emersion/go-webdav/internal"
|
"github.com/emersion/go-webdav/internal"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO: add support for multiple address books
|
|
||||||
|
|
||||||
type PutAddressObjectOptions struct {
|
type PutAddressObjectOptions struct {
|
||||||
// IfNoneMatch indicates that the client does not want to overwrite
|
// IfNoneMatch indicates that the client does not want to overwrite
|
||||||
// an existing resource.
|
// an existing resource.
|
||||||
@ -29,11 +27,12 @@ type PutAddressObjectOptions struct {
|
|||||||
|
|
||||||
// Backend is a CardDAV server backend.
|
// Backend is a CardDAV server backend.
|
||||||
type Backend interface {
|
type Backend interface {
|
||||||
AddressbookHomeSetPath(ctx context.Context) (string, error)
|
AddressBookHomeSetPath(ctx context.Context) (string, error)
|
||||||
AddressBook(ctx context.Context) (*AddressBook, error)
|
ListAddressBooks(ctx context.Context) ([]AddressBook, error)
|
||||||
|
GetAddressBook(ctx context.Context, path string) (*AddressBook, error)
|
||||||
GetAddressObject(ctx context.Context, path string, req *AddressDataRequest) (*AddressObject, error)
|
GetAddressObject(ctx context.Context, path string, req *AddressDataRequest) (*AddressObject, error)
|
||||||
ListAddressObjects(ctx context.Context, req *AddressDataRequest) ([]AddressObject, error)
|
ListAddressObjects(ctx context.Context, path string, req *AddressDataRequest) ([]AddressObject, error)
|
||||||
QueryAddressObjects(ctx context.Context, query *AddressBookQuery) ([]AddressObject, error)
|
QueryAddressObjects(ctx context.Context, path string, query *AddressBookQuery) ([]AddressObject, error)
|
||||||
PutAddressObject(ctx context.Context, path string, card vcard.Card, opts *PutAddressObjectOptions) (loc string, err error)
|
PutAddressObject(ctx context.Context, path string, card vcard.Card, opts *PutAddressObjectOptions) (loc string, err error)
|
||||||
DeleteAddressObject(ctx context.Context, path string) error
|
DeleteAddressObject(ctx context.Context, path string) error
|
||||||
|
|
||||||
@ -90,7 +89,7 @@ func (h *Handler) handleReport(w http.ResponseWriter, r *http.Request) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if report.Query != nil {
|
if report.Query != nil {
|
||||||
return h.handleQuery(r.Context(), w, report.Query)
|
return h.handleQuery(r, w, report.Query)
|
||||||
} else if report.Multiget != nil {
|
} else if report.Multiget != nil {
|
||||||
return h.handleMultiget(r.Context(), w, report.Multiget)
|
return h.handleMultiget(r.Context(), w, report.Multiget)
|
||||||
}
|
}
|
||||||
@ -152,7 +151,7 @@ func decodeAddressDataReq(addressData *addressDataReq) (*AddressDataRequest, err
|
|||||||
return req, nil
|
return req, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handler) handleQuery(ctx context.Context, w http.ResponseWriter, query *addressbookQuery) error {
|
func (h *Handler) handleQuery(r *http.Request, w http.ResponseWriter, query *addressbookQuery) error {
|
||||||
var q AddressBookQuery
|
var q AddressBookQuery
|
||||||
if query.Prop != nil {
|
if query.Prop != nil {
|
||||||
var addressData addressDataReq
|
var addressData addressDataReq
|
||||||
@ -180,7 +179,7 @@ func (h *Handler) handleQuery(ctx context.Context, w http.ResponseWriter, query
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
aos, err := h.Backend.QueryAddressObjects(ctx, &q)
|
aos, err := h.Backend.QueryAddressObjects(r.Context(), r.URL.Path, &q)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -196,7 +195,7 @@ func (h *Handler) handleQuery(ctx context.Context, w http.ResponseWriter, query
|
|||||||
AllProp: query.AllProp,
|
AllProp: query.AllProp,
|
||||||
PropName: query.PropName,
|
PropName: query.PropName,
|
||||||
}
|
}
|
||||||
resp, err := b.propFindAddressObject(ctx, &propfind, &ao)
|
resp, err := b.propFindAddressObject(r.Context(), &propfind, &ao)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -371,7 +370,7 @@ func (b *backend) PropFind(r *http.Request, propfind *internal.PropFind, depth i
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
case resourceTypeAddressBookHomeSet:
|
case resourceTypeAddressBookHomeSet:
|
||||||
homeSetPath, err := b.Backend.AddressbookHomeSetPath(r.Context())
|
homeSetPath, err := b.Backend.AddressBookHomeSetPath(r.Context())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -391,12 +390,10 @@ func (b *backend) PropFind(r *http.Request, propfind *internal.PropFind, depth i
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
case resourceTypeAddressBook:
|
case resourceTypeAddressBook:
|
||||||
// TODO for multiple address books, look through all of them
|
ab, err := b.Backend.GetAddressBook(r.Context(), r.URL.Path)
|
||||||
ab, err := b.Backend.AddressBook(r.Context())
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if r.URL.Path == ab.Path {
|
|
||||||
resp, err := b.propFindAddressBook(r.Context(), propfind, ab)
|
resp, err := b.propFindAddressBook(r.Context(), propfind, ab)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -409,7 +406,6 @@ func (b *backend) PropFind(r *http.Request, propfind *internal.PropFind, depth i
|
|||||||
}
|
}
|
||||||
resps = append(resps, resps_...)
|
resps = append(resps, resps_...)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
case resourceTypeAddressObject:
|
case resourceTypeAddressObject:
|
||||||
ao, err := b.Backend.GetAddressObject(r.Context(), r.URL.Path, &dataReq)
|
ao, err := b.Backend.GetAddressObject(r.Context(), r.URL.Path, &dataReq)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -448,7 +444,7 @@ func (b *backend) propFindUserPrincipal(ctx context.Context, propfind *internal.
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
homeSetPath, err := b.Backend.AddressbookHomeSetPath(ctx)
|
homeSetPath, err := b.Backend.AddressBookHomeSetPath(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -472,7 +468,7 @@ func (b *backend) propFindHomeSet(ctx context.Context, propfind *internal.PropFi
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
homeSetPath, err := b.Backend.AddressbookHomeSetPath(ctx)
|
homeSetPath, err := b.Backend.AddressBookHomeSetPath(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -527,22 +523,20 @@ func (b *backend) propFindAddressBook(ctx context.Context, propfind *internal.Pr
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (b *backend) propFindAllAddressBooks(ctx context.Context, propfind *internal.PropFind, recurse bool) ([]internal.Response, error) {
|
func (b *backend) propFindAllAddressBooks(ctx context.Context, propfind *internal.PropFind, recurse bool) ([]internal.Response, error) {
|
||||||
// TODO iterate over all address books once having multiple is supported
|
abs, err := b.Backend.ListAddressBooks(ctx)
|
||||||
ab, err := b.Backend.AddressBook(ctx)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
abs := []*AddressBook{ab}
|
|
||||||
|
|
||||||
var resps []internal.Response
|
var resps []internal.Response
|
||||||
for _, ab := range abs {
|
for _, ab := range abs {
|
||||||
resp, err := b.propFindAddressBook(ctx, propfind, ab)
|
resp, err := b.propFindAddressBook(ctx, propfind, &ab)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
resps = append(resps, *resp)
|
resps = append(resps, *resp)
|
||||||
if recurse {
|
if recurse {
|
||||||
resps_, err := b.propFindAllAddressObjects(ctx, propfind, ab)
|
resps_, err := b.propFindAllAddressObjects(ctx, propfind, &ab)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -597,7 +591,7 @@ func (b *backend) propFindAddressObject(ctx context.Context, propfind *internal.
|
|||||||
|
|
||||||
func (b *backend) propFindAllAddressObjects(ctx context.Context, propfind *internal.PropFind, ab *AddressBook) ([]internal.Response, error) {
|
func (b *backend) propFindAllAddressObjects(ctx context.Context, propfind *internal.PropFind, ab *AddressBook) ([]internal.Response, error) {
|
||||||
var dataReq AddressDataRequest
|
var dataReq AddressDataRequest
|
||||||
aos, err := b.Backend.ListAddressObjects(ctx, &dataReq)
|
aos, err := b.Backend.ListAddressObjects(ctx, ab.Path, &dataReq)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -614,7 +608,7 @@ func (b *backend) propFindAllAddressObjects(ctx context.Context, propfind *inter
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (b *backend) PropPatch(r *http.Request, update *internal.PropertyUpdate) (*internal.Response, error) {
|
func (b *backend) PropPatch(r *http.Request, update *internal.PropertyUpdate) (*internal.Response, error) {
|
||||||
homeSetPath, err := b.Backend.AddressbookHomeSetPath(r.Context())
|
homeSetPath, err := b.Backend.AddressBookHomeSetPath(r.Context())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -255,7 +255,7 @@ func (b *backend) Move(r *http.Request, dest *internal.Href, overwrite bool) (cr
|
|||||||
|
|
||||||
// BackendSuppliedHomeSet represents either a CalDAV calendar-home-set or a
|
// BackendSuppliedHomeSet represents either a CalDAV calendar-home-set or a
|
||||||
// CardDAV addressbook-home-set. It should only be created via
|
// CardDAV addressbook-home-set. It should only be created via
|
||||||
// caldav.NewCalendarHomeSet or carddav.NewAddressbookHomeSet. Only to
|
// caldav.NewCalendarHomeSet or carddav.NewAddressBookHomeSet. Only to
|
||||||
// be used server-side, for listing a user's home sets as determined by the
|
// be used server-side, for listing a user's home sets as determined by the
|
||||||
// (external) backend.
|
// (external) backend.
|
||||||
type BackendSuppliedHomeSet interface {
|
type BackendSuppliedHomeSet interface {
|
||||||
|
Loading…
Reference in New Issue
Block a user