Search code examples
icloudvcf-vcardcarddav

Manipulate groups in iCloud with CardDAV protocol


I am currently trying to manipulate groups of my contact list in iCloud through CardDav protocol. For that, I created a group containing a contact in Contact.app: Screenshot

When I export the vCard of my contact directly from Contact app, I have the groups associated with my contacts in the CATEGORIES property :

BEGIN:VCARD
VERSION:3.0
PRODID:-//Apple Inc.//Mac OS X 10.9.2//EN
N:Lebron;Candide;;;
FN:Candide Lebron
ORG:Podbox;
NOTE:Un petit peu candide.
CATEGORIES:GroupForAutomaticTestDELTA
UID:d4c1baf6-f603-4fb5-8f19-d45eb1e7fb23
X-ABUID:D4C1BAF6-F603-4FB5-8F19-D45EB1E7FB23:ABPerson
END:VCARD

However when I request my iCloud server to retrieve this contact I do not get CATEGORIES property set but I retrieve two vCards, one for the contact and one for the group. The group vCard contains references to its members.

Request:

curl --request REPORT  --user ****@*****:**** --header "Content-Type: text/xml" --data '
    <?xml version="1.0" encoding="utf-8" ?>
        <C:addressbook-query xmlns:D="DAV:"
                 xmlns:C="urn:ietf:params:xml:ns:carddav">
 <D:prop>
   <C:address-data/>
 </D:prop>
 </C:addressbook-query>' https://contacts.icloud.com/802592377/carddavhome/card/

contact VCard:

BEGIN:VCARD&#13;
VERSION:3.0&#13;
PRODID:-//Apple Inc.//Mac OS X 10.9.2//EN&#13;
N:Lebron;Candide;;;&#13;
FN:Candide Lebron&#13;
ORG:Podbox;&#13;
NOTE:Un petit peu candide.&#13;
REV:2014-06-12T16:53:51Z&#13;
UID:d4c1baf6-f603-4fb5-8f19-d45eb1e7fb23&#13;
END:VCARD&#13;

group vCard:

BEGIN:VCARD
VERSION:3.0
PRODID:-//Apple Inc.//AddressBook 8.0//EN
N:GroupToBeAddedTO
FN:GroupToBeAddedTO
X-ADDRESSBOOKSERVER-KIND:group
X-ADDRESSBOOKSERVER-MEMBER:urn:uuid:d4c1baf6-f603-4fb5-8f19-d45eb1e7fb23
REV:2014-06-12T16:43:04Z
UID:d59c9f0c-27aa-47e3-96e7-43717bbc1d7e
END:VCARD

Notice the CATEGORIES property appearing in contact export but not with a CardDAV request. My guess would be that the Contact app constructs the CATEGORIES when exporting but I am not sure.

Do someone has an idea of how iCloud works exactly? Am I missing something preventing me to retrieve CATEGORIES through CardDAV?


Solution

  • OK. Let's start at the beginning. What is a CardDAV collection. A CardDAV collection is like a folder containing vCard files. Some (Many?) servers allow multiple of such folders, some don't. iCloud (currently) is in the latter category - there is only one 'folder' on the server to store all vCards of one particular iCloud user. Other servers sometimes call secondary CardDAV collections 'groups'.

    What is a group. A group is a collection of contacts. Addressbook.app uses a special group vCard to represent this relationship. This 'group vCard' has references to it's members via the X-ADDRESSBOOKSERVER-MEMBER property. It contains the UID of the contact vCard (or other group vCard) which is supposed to be a member of this group. In vCard 3 - which is what most CardDAV infrastructure uses - this is kinda Apple specific (hence X-ADDRESSBOOKSERVER-xyz). In vCard 4 this has been included in the spec.

    Addressbook.app AFAIK does not support real CATEGORIES (commonly known as tags). The vCard CATEGORIES field is originally for stuff like 'VIP' or 'Nice Guy', it's not for 'groups' per se. Now when Addressbook.app exports a vCard, it tries to preseve the group information in the CATEGORIES field. Which is a bit weird but somewhat reasonable. A CardDAV group only really makes sense within a CardDAV collection, the group membership information is not contained within the vCard itself (but in that separate vCard record for the group). Remember that the export is intendet to transfer stuff between systems

    Sidenote: If you are coming from the Exchange/Outlook world: CardDAV collections are like Exchange folders. CardDAV groups are like Exchange Distribution Lists.

    I guess what you really want to know is how to add/remove members to a CardDAV group? Let's assume you have a record like:

    BEGIN:VCARD
    UID:joe
    N:User;Joe;;;
    END:VCARD
    

    And you want to add him to a group, like 'Friends'. Assuming the group already exists, it'll look like this (with two exisiting members):

    BEGIN:VCARD
    N:Friends
    FN:Friends
    X-ADDRESSBOOKSERVER-KIND:group
    X-ADDRESSBOOKSERVER-MEMBER:urn:uuid:AwesomeO
    X-ADDRESSBOOKSERVER-MEMBER:urn:uuid:Xavier
    UID:group-uid
    END:VCARD
    

    What you need to do to add a member is fetch the group and then add the UID, the result would like this:

    BEGIN:VCARD
    N:Friends
    FN:Friends
    X-ADDRESSBOOKSERVER-KIND:group
    X-ADDRESSBOOKSERVER-MEMBER:urn:uuid:AwesomeO
    X-ADDRESSBOOKSERVER-MEMBER:urn:uuid:Xavier
    X-ADDRESSBOOKSERVER-MEMBER:urn:uuid:joe
    UID:group-uid
    END:VCARD
    

    Makes sense?

    Don't be confused by the categories hack of the export. This is not how groups work.