CalDav calendar discovery

I found myself in the unfortunate situation to discover a CalDav uri using different providers. In this short post I guide you through a generic example flow which should work for most calendar providers.

#caldav #scripting #calendar

The discovery process can be described as follows:

  1. Get user principal
  2. Find home set of user principal
  3. Get calendar from home set
  4. Fetch all events from calendar

The individual steps can be tested with curl commands. These curl commands depict the request flow and should work with any provider.

1. Get principal

First, read the principal:

cat <<EOF | curl -s -d @- -XPROPFIND \
  -H "Content-Type: application/xml" \
  -H "Depth: 0" -u '$USER:$PASSWORD' -L $CALDAV_HOST \
  | xmllint --format -
<d:propfind xmlns:d="DAV:">
 <d:prop>
  <d:current-user-principal />
 </d:prop>
</d:propfind>
EOF

The principal path ($PRINCIPAL) is returned in the XML response tag <DAV:current-user-principal>.

2. Find home set

Get calendar home set for the principal:

cat <<EOF | curl -s -d @- -XPROPFIND \
  -H "Content-Type: application/xml" \
  -H "Depth: 0" -u '$USER:$PASSWORD' $CALDAV_HOST/$PRINCIPAL \
  | xmllint --format -
<d:propfind xmlns:d="DAV:" xmlns:c="urn:ietf:params:xml:ns:caldav">
 <d:prop>
  <c:calendar-home-set />
 </d:prop>
</d:propfind>
EOF

The users calendar home set $HOME_SET is returned in the <calendar-home-set> XML tag.

3. Get calendar

This home set contains all calendars of the user. Finally, to list the calendars:

cat <<EOF | curl -s -d @- -XPROPFIND \
  -H "Content-Type: application/xml" \
  -H "Depth: 1" -u '$USER:$PASSWORD' $CALDAV_HOST/$HOME_SET \
  | xmllint --format -
<d:propfind xmlns:d="DAV:" xmlns:cs="http://calendarserver.org/ns/" xmlns:c="urn:ietf:params:xml:ns:caldav">
 <d:prop>
  <d:resourcetype />
  <d:displayname />
  <cs:getctag />
  <c:supported-calendar-component-set />
 </d:prop>
</d:propfind>
EOF

Choose a calendar CalDav URI to sync with a particular calendar. The <DAV:href> path in the response is referenced with $CALENDAR below for illustration purposes.

4. Fetch all events

This part is specific to your application. An example to fetch all events:

cat <<EOF | curl -s -d @- -XREPORT -H "Content-Type: application/xml" \
  -H "Depth: 1" -u '$USER:$PASSWORD' $CALDAV_HOST/$CALENDAR \
  | xmllint --format -
<c:calendar-query xmlns:d="DAV:" xmlns:c="urn:ietf:params:xml:ns:caldav">
 <d:prop>
  <d:getetag />
  <c:calendar-data />
 </d:prop>
 <c:filter>
  <c:comp-filter name="VCALENDAR">
   <c:comp-filter name="VEVENT" />
  </c:comp-filter>
 </c:filter>
</c:calendar-query>
EOF

I hope this post helps you to discover user principals and calendar home sets for your specific calendar use case. If the post was too short and concise for you, read this 🤓. I can also suggest to read Building a CalDAV client on sabre.io to get started with your own client.

🛜 RSS | 🐘 Mastodon | 💬 IRC