Figure out how Google's CardDAV server works
Closed, ResolvedPublic

Description

Related to D15001

Google works in mysterious ways:

  • Sink's CardDAV client implementation works with KolabNow
  • Sink's CardDAV client implementation works with my test server (Radicale)
  • Doesn't work with Google

After some debugging I found:

On my test server if I do:

curl -i -X PROPFIND "http://localhost:5232/test1/8543fd29-88bf-5e45-2cf2-36f8c4657fbc" --upload-file propfind.xml -H "Depth: 1" --user 'test1:'

with propfind.xml being (exactly the same as Sink's request body):

<?xml version="1.0"?>
<propfind xmlns="DAV:">
    <prop xmlns="DAV:">
        <displayname xmlns="DAV:"/>
        <resourcetype xmlns="DAV:"/>
        <getctag xmlns="http://calendarserver.org/ns/"/>
    </prop>
</propfind>

I get:

<?xml version='1.0' encoding='utf-8'?><?xml version="1.0" encoding="utf-8"?>
<multistatus xmlns="DAV:" xmlns:CR="urn:ietf:params:xml:ns:carddav" xmlns:CS="http://calendarserver.org/ns/">
  <response>
    <href>/test1/8543fd29-88bf-5e45-2cf2-36f8c4657fbc/</href>
    <propstat>
      <prop>
        <displayname>AddressBook</displayname>
        <resourcetype>
          <CR:addressbook/>
          <collection/>
        </resourcetype>
        <CS:getctag>"5a772e0534b073083c00789d1ddd206c"</CS:getctag>
      </prop>
      <status>HTTP/1.1 200 OK</status>
    </propstat>
  </response>
</multistatus>

With Google, I get:

<?xml version="1.0" encoding="UTF-8"?>
<d:multistatus xmlns:cal="urn:ietf:params:xml:ns:caldav" xmlns:card="urn:ietf:params:xml:ns:carddav" xmlns:cs="http://calendarserver.org/ns/" xmlns:d="DAV:" xmlns:ical="http://apple.com/ns/ical/">
 <d:response>
  <d:href>/carddav/v1/principals/<my-email-address>/lists/default/</d:href>
  <d:propstat>
   <d:status>HTTP/1.1 200 OK</d:status>
  </d:propstat>
 </d:response>
</d:multistatus>

It doesn't change if change the Depth to 0, or if I use XML's better namespacing mechanism in propfind.xml (specify the namespaces in the top-level element with prefixes, and use the prefixes inside)

rnicole updated the task description. (Show Details)Aug 24 2018, 8:57 AM

Well, it seems that Google is completely ignoring the request if the Content-Type: application/xml is not present so adding -H "Content-Type: application/xml" to Curl's command line does the trick, and Google returns a valid CTag

looks like this is what the client is supposed to do anyways (http://sabre.io/dav/building-a-carddav-client/), so let's make sure that we have something like Content-Type: application/xml; charset=utf-8 in the request.
Is this perhaps something that we generally have missing in all requests in kdav2?

No, we do have the Content-Type: text/xml; charset=utf-8 in KDav2 (Google apparently don't care if it's text/xml or application/xml, and from the XML RFC, the only difference between the two is human readability). The issue was more in the way I was testing from the command-line. The issue with Sink seems to be that Google can't find the getctag property (whereas it works from the command-line). This is weird since the propfind.xml is basically a copy paste from Sink's logs.

I think I've found it. From the Google Contacts API, we find that the Home Set (/lists) contains all the other address books (/lists/default being one of them, but there can be more), but no contacts, so from my understanding, this is why the Home Set does not have any CTag. For some reason, it is also not possible to get the CTag of child collections from the /lists URL. I think the best solution is to refresh every collections individually in KDAV2 in the DavCollectionsFetchJob.