CalDAV / iCloud
Crate: adapter-caldav · Capabilities: calendars, tasks, contacts
CalDAV/CardDAV is the open standard behind Apple iCloud and many self-hosted servers (Nextcloud, Radicale, …). One adapter serves them all; iCloud is just CalDAV with Apple’s endpoints.
Protocol
Section titled “Protocol”- Discovery + listing:
PROPFINDto enumerate collections (calendars, task lists viaVTODO, address books) and their properties (displayname,calendar-color,getctag,sync-token). - Incremental sync:
REPORTwithsync-collectionreturns the resources changed since async-token, plus per-resource deletions. An invalid token triggers a full re-bootstrap. - Bootstrap / bulk read: a depth-1
PROPFINDlists every resource href, thencalendar-multiget/addressbook-multigetfetches their bodies in chunks (so a large iCloud calendar doesn’t time out on one giant request). - Folder-complete caching: because the bootstrap already enumerated the
whole collection, the event sync multigets all dates (not just the
view window) and marks the change set
complete, so the host caches an unbounded window. Later views are served from cache and only a backgroundsync-collectiondelta touches the network. Servers withoutsync-collectionfall back to a windowed, range-scoped read. - Bodies are iCalendar (
VEVENT/VTODO) and vCard, parsed inmapping.rsintocal-coretypes.RRULE/EXDATEare carried through; occurrences are expanded on the frontend.
Authentication
Section titled “Authentication”HTTP Basic over TLS with a username + password (for iCloud, an app-specific password, not the Apple ID password). There is no OAuth. The server base URL is user-supplied for self-hosted servers; iCloud uses Apple’s well-known endpoints.
Quirks
Section titled “Quirks”- Both homes are best-effort. Discovery probes
calendar-home-setANDaddressbook-home-setindependently and fails only when neither is found. A CalDAV-only server (no address books) and a CardDAV-only server (e.g. Synology Contacts — advertises anaddressbook-home-setbut nocalendar-home-set) both work; the missing side’s listings just come back empty. Well-known resolution tries/.well-known/caldavthen/.well-known/carddav. (Before this, a contacts-only server failed account creation with a “not found” error because the calendar home was mandatory.) - Contacts read via multiget, never inline PROPFIND.
get_contactsdoes a Depth-1 PROPFIND for hrefs then anaddressbook-multigetfor the bodies — it does not ask for inline<CR:address-data/>in a plain PROPFIND. That shortcut is non-standard; iCloud and Synology Contacts silently return resources with no body, so the old inline read yielded zero contacts while persisting a sync token, leaving address books permanently empty (a one-timecache.contactsMultigetHealV2heal clears those poisoned tokens so books re-bootstrap). - Stable ids. A resource is keyed by
{href}|{uid}so renames/moves and per-resource deletions resolve correctly. - Subtasks ride
RELATED-TO. A VTODO’s parameter-lessRELATED-TO(RELTYPE defaults to PARENT; CHILD/SIBLING entries are ignored) carries the parent’s bare UID on the wire; the read path resolves it to the composite{href}|{uid}task id against the fetched set. Because theicalendarcrate keeps only oneRELATED-TOper component when parsing, the link is scanned from the raw iCal text — severalRELATED-TOlines (RFC-legal; e.g. jtx Board’s reciprocalRELTYPE=CHILDentries) would otherwise drop the parent order-dependently. An incremental delta whose parent didn’t itself change falls back to one tolerantuid → idlisting; if that read fails the delta fails too (token not advanced) rather than caching a falsified flat parent. Writes strip the composite id back to the UID; removing the parent just regenerates the VTODO without the property. - Keep recurring masters. The folder-complete sync keeps every event
regardless of date; the legacy windowed fallback still keeps any event
with a recurrence even when its first occurrence is outside the window
(
event_in_windowreturns true forrecurrence.is_some()). - iCloud date sentinels / colours. Colours arrive as
#RRGGBBAA; the alpha is dropped to a plain hex. getctagfast-path. When the collection’s ctag is unchanged, the adapter can skip a full enumeration.
Testing
Section titled “Testing”mockito serves canned PROPFIND/REPORT/multiget XML. Tests assert the
sync-collection token round-trip, per-resource deletions, the
{href}|{uid} id scheme, and the iCalendar → cal-core mapping. For a
live smoke test, an iCloud account with an app-specific password works.