Skip to content

Commit 4de6415

Browse files
committed
carddav: PROPPATCH support for address books
The groundwork for address objects is also there, but it's not fully implemented.
1 parent 25f1014 commit 4de6415

File tree

2 files changed

+145
-23
lines changed

2 files changed

+145
-23
lines changed

carddav/carddav_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,10 @@ func (*testBackend) CreateAddressBook(ctx context.Context, ab *AddressBook) erro
7272
panic("TODO: implement")
7373
}
7474

75+
func (*testBackend) UpdateAddressBook(ctx context.Context, ab *AddressBook) error {
76+
panic("TODO: implement")
77+
}
78+
7579
func (*testBackend) DeleteAddressBook(ctx context.Context, path string) error {
7680
panic("TODO: implement")
7781
}

carddav/server.go

Lines changed: 141 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ type Backend interface {
3131
ListAddressBooks(ctx context.Context) ([]AddressBook, error)
3232
GetAddressBook(ctx context.Context, path string) (*AddressBook, error)
3333
CreateAddressBook(ctx context.Context, addressBook *AddressBook) error
34+
UpdateAddressBook(ctx context.Context, addressBook *AddressBook) error
3435
DeleteAddressBook(ctx context.Context, path string) error
3536
GetAddressObject(ctx context.Context, path string, req *AddressDataRequest) (*AddressObject, error)
3637
ListAddressObjects(ctx context.Context, path string, req *AddressDataRequest) ([]AddressObject, error)
@@ -614,43 +615,160 @@ func (b *backend) propFindAllAddressObjects(ctx context.Context, propfind *inter
614615
}
615616

616617
func (b *backend) PropPatch(r *http.Request, update *internal.PropertyUpdate) (*internal.Response, error) {
617-
homeSetPath, err := b.Backend.AddressBookHomeSetPath(r.Context())
618-
if err != nil {
619-
return nil, err
620-
}
621-
618+
resType := b.resourceTypeAtPath(r.URL.Path)
622619
resp := internal.NewOKResponse(r.URL.Path)
623620

624-
if r.URL.Path == homeSetPath {
625-
// TODO: support PROPPATCH for address books
621+
switch resType {
622+
case resourceTypeAddressBook:
623+
ab, err := b.Backend.GetAddressBook(r.Context(), r.URL.Path)
624+
if err != nil {
625+
return nil, err
626+
}
627+
err = b.propPatchAddressBook(r.Context(), update, ab, resp)
628+
if err != nil {
629+
return nil, err
630+
}
631+
err = b.Backend.UpdateAddressBook(r.Context(), ab)
632+
if err != nil {
633+
return nil, err
634+
}
635+
case resourceTypeAddressObject:
636+
dataReq := AddressDataRequest{AllProp: true}
637+
ao, err := b.Backend.GetAddressObject(r.Context(), r.URL.Path, &dataReq)
638+
if err != nil {
639+
return nil, err
640+
}
641+
// TODO: support PROPPATCH for address objects
642+
err = b.propPatchAddressObject(r.Context(), update, ao, resp)
643+
if err != nil {
644+
return nil, err
645+
}
646+
// TODO: interface for updating contacts?
647+
//err = b.Backend.UpdateAddressObject(r.Context(), ab)
648+
//if err != nil {
649+
// return nil, err
650+
//}
651+
default:
626652
for _, prop := range update.Remove {
627-
emptyVal := internal.NewRawXMLElement(prop.Prop.XMLName, nil, nil)
628-
if err := resp.EncodeProp(http.StatusNotImplemented, emptyVal); err != nil {
629-
return nil, err
653+
for _, raw := range prop.Prop.Raw {
654+
rxn, ok := raw.XMLName()
655+
if !ok {
656+
return nil, fmt.Errorf("failed to parse properties")
657+
}
658+
emptyVal := internal.NewRawXMLElement(rxn, nil, nil)
659+
if err := resp.EncodeProp(http.StatusMethodNotAllowed, emptyVal); err != nil {
660+
return nil, err
661+
}
630662
}
631663
}
632664
for _, prop := range update.Set {
633-
emptyVal := internal.NewRawXMLElement(prop.Prop.XMLName, nil, nil)
634-
if err := resp.EncodeProp(http.StatusNotImplemented, emptyVal); err != nil {
635-
return nil, err
665+
for _, raw := range prop.Prop.Raw {
666+
rxn, ok := raw.XMLName()
667+
if !ok {
668+
return nil, fmt.Errorf("failed to parse properties")
669+
}
670+
emptyVal := internal.NewRawXMLElement(rxn, nil, nil)
671+
if err := resp.EncodeProp(http.StatusMethodNotAllowed, emptyVal); err != nil {
672+
return nil, err
673+
}
636674
}
637675
}
638-
} else {
639-
for _, prop := range update.Remove {
640-
emptyVal := internal.NewRawXMLElement(prop.Prop.XMLName, nil, nil)
641-
if err := resp.EncodeProp(http.StatusMethodNotAllowed, emptyVal); err != nil {
642-
return nil, err
676+
}
677+
return resp, nil
678+
}
679+
680+
func (b *backend) propPatchAddressBook(ctx context.Context, update *internal.PropertyUpdate, ab *AddressBook, resp *internal.Response) error {
681+
// TODO handle all properties
682+
var (
683+
name internal.DisplayName
684+
desc addressbookDescription
685+
)
686+
for _, prop := range update.Remove {
687+
for _, raw := range prop.Prop.Raw {
688+
rxn, ok := raw.XMLName()
689+
if !ok {
690+
return fmt.Errorf("failed to parse properties")
691+
}
692+
switch rxn {
693+
case internal.DisplayNameName:
694+
ab.Name = ""
695+
if err := resp.EncodeProp(http.StatusOK, internal.DisplayName{}); err != nil {
696+
return err
697+
}
698+
case addressBookDescriptionName:
699+
ab.Description = ""
700+
if err := resp.EncodeProp(http.StatusOK, desc); err != nil {
701+
return err
702+
}
703+
default:
704+
emptyVal := internal.NewRawXMLElement(rxn, nil, nil)
705+
if err := resp.EncodeProp(http.StatusNotImplemented, emptyVal); err != nil {
706+
return err
707+
}
643708
}
644709
}
645-
for _, prop := range update.Set {
646-
emptyVal := internal.NewRawXMLElement(prop.Prop.XMLName, nil, nil)
647-
if err := resp.EncodeProp(http.StatusMethodNotAllowed, emptyVal); err != nil {
648-
return nil, err
710+
}
711+
for _, prop := range update.Set {
712+
for _, raw := range prop.Prop.Raw {
713+
rxn, ok := raw.XMLName()
714+
if !ok {
715+
return fmt.Errorf("failed to parse properties")
716+
}
717+
switch rxn {
718+
case internal.DisplayNameName:
719+
if err := raw.Decode(&name); err != nil {
720+
return err
721+
}
722+
ab.Name = name.Name
723+
if err := resp.EncodeProp(http.StatusOK, internal.DisplayName{}); err != nil {
724+
return err
725+
}
726+
case addressBookDescriptionName:
727+
if err := raw.Decode(&desc); err != nil {
728+
return err
729+
}
730+
ab.Description = desc.Description
731+
if err := resp.EncodeProp(http.StatusOK, desc); err != nil {
732+
return err
733+
}
734+
default:
735+
emptyVal := internal.NewRawXMLElement(rxn, nil, nil)
736+
if err := resp.EncodeProp(http.StatusNotImplemented, emptyVal); err != nil {
737+
return err
738+
}
649739
}
650740
}
651741
}
742+
return nil
743+
}
652744

653-
return resp, nil
745+
func (b *backend) propPatchAddressObject(ctx context.Context, update *internal.PropertyUpdate, ao *AddressObject, resp *internal.Response) error {
746+
// TODO: support PROPPATCH for address objects
747+
for _, prop := range update.Remove {
748+
for _, raw := range prop.Prop.Raw {
749+
rxn, ok := raw.XMLName()
750+
if !ok {
751+
return fmt.Errorf("failed to parse properties")
752+
}
753+
emptyVal := internal.NewRawXMLElement(rxn, nil, nil)
754+
if err := resp.EncodeProp(http.StatusNotImplemented, emptyVal); err != nil {
755+
return err
756+
}
757+
}
758+
}
759+
for _, prop := range update.Set {
760+
for _, raw := range prop.Prop.Raw {
761+
rxn, ok := raw.XMLName()
762+
if !ok {
763+
return fmt.Errorf("failed to parse properties")
764+
}
765+
emptyVal := internal.NewRawXMLElement(rxn, nil, nil)
766+
if err := resp.EncodeProp(http.StatusNotImplemented, emptyVal); err != nil {
767+
return err
768+
}
769+
}
770+
}
771+
return nil
654772
}
655773

656774
func (b *backend) Put(r *http.Request) (*internal.Href, error) {

0 commit comments

Comments
 (0)