LH DAV is a data plugin: it owns the DAV protocol and the sync change log, and DAV itself (vCard and iCalendar, readable by any standard client) is the portability mechanism. It registers no MCP abilities; tooling plugins use its plain PHP service API (see Provider API).
URL layout
Everything is under /wp-json/lh-dav/v1/ on each site:
(root): answerscurrent-user-principalprincipals/{user ID}/: the signed-in user only; any other ID is a 403addressbooks/{user ID}/{collection}/{name}.vcfandcalendars/{user ID}/{collection}/{name}.icssetup-profile: the Apple.mobileconfig(normal REST authentication, so a cookie with awp_restnonce works)
/.well-known/carddav and /.well-known/caldav are matched on parse_request (no rewrite rules, so nothing to flush across the network) and 301 to the root, only while a provider serves that component.
Serving DAV through REST
Taken from LH Vcard 1.11.0’s CardDAV handler. The REST server only carries the request: LH_DAV_Server builds the full response, returns a WP_REST_Response whose data is a short summary (what REST logs record), and sends the real XML, vCard or iCalendar body on rest_pre_serve_request. OPTIONS on any DAV path, and every method on the namespace root, are answered on rest_pre_dispatch (priority 5) before core’s own handling.
Authentication
Core’s application password check does the login. On DAV paths rest_authentication_errors (priority 99) skips core’s errors so the server can send its own WWW-Authenticate: Basic challenge, which contacts apps need. The server then refuses any request not authenticated by an application password (rest_get_authenticated_app_password()), so a browser cookie alone gets a 401. Users also need the access capability (read by default).
Making and changing collections
Since 0.3.0, MKCALENDAR and extended MKCOL on a free name in a home make a collection, PROPPATCH on a collection renames, recolours or reorders it, and DELETE on a collection removes it, all through a provider implementing LH_DAV_Collection_Manager (see Provider API). The DAV header then includes extended-mkcol. LH Calendars is the first such provider.
Change log and sync tokens
Table {base_prefix}lh_dav_changes (network-wide): id (the sync sequence), site_id, collection (key {component}/{owner user ID, 0 if shared}/{collection id}), resource, operation (modified or deleted), user_id, changed_gmt. Created and upgraded on schema_version() (stored in site option lh_dav-schema_version), never on plugin_version(). Since 0.3.0 changes are recorded and read against the collection’s site_id, which defaults to the current site.
Sync tokens are {base}sync/{sequence}/{state hash}. A token whose state hash no longer matches the collection’s state gets a full resend; one below the pruned-through marker gets 403 valid-sync-token, telling the client to resync. A daily cron on the main site prunes rows older than the retention (90 days) and records the highest pruned ID in site option lh_dav-pruned_through.
Known limits
addressbook-queryandcalendar-queryignore filters and return the whole collection; large calendars may later want time-range filtering.- MOVE and COPY get 403. Collections whose provider does not manage them cannot be made, renamed or deleted over DAV.
- No CalDAV scheduling (RFC 6638) yet, so invitations and replies are not sent from devices.
- PROPFIND returns every known property rather than only those requested.