
Command Line Introduction to CalDAV and CardDAV
If you manage a consumer or SMB email platform, and your email hosting solution offers calendars and address books, you might like to learn more about CalDAV and CardDAV.
For this reason, we wanted to cover today the basics of how to access CalDAV and CardDAV services, using nothing but the command line. Our test user in this post is [email protected]. The password has been redacted and replaced with the string ‘ThePassword’.
What is CalDAV and CardDAV
These are both open protocols that used to access calendars and address books stored on a server. They allow multiple devices to stay in sync, and allow some advanced feature utilisation (such as scheduling, free-busy lookup, and delegation).
CalDAV and CardDAV are some of the protocols that atmail exposes for users of our products to access their information.
Service Discovery
There are two common methods used to discover CalDAV and CardDAV services. The first and preferred way is via DNS SRV records. The second is by using an “expected” .well-known URL to discover services.
Using DNS
Using DNS SRV records defined in RFC 6764 defines the following records for discover purposes:
- _caldav._tcp: Identifies a CalDAV server that uses HTTP without Transport Layer Security (TLS)
- _caldavs._tcp: Identifies a CalDAV server that uses HTTP with TLS
- _carddav._tcp: Identifies a CalDAV server that uses HTTP without Transport Layer Security (TLS)
- _carddavs._tcp: Identifies a CalDAV server that uses HTTP with TLS
The formats of these records are defined in RFC 2782, but are roughly:
SRV priority weight port target
Where:
- Priority: The priority of this target host. A client MUST attempt to contact the target host with the lowest-numbered priority it can reach; target hosts with the same priority SHOULD be tried in an order defined by the weight field. The range is 0-65535.
- Weight: The weight field specifies a relative weight for entries with the same priority. Larger weights SHOULD be given a proportionately higher probability of being selected. The range of this number is 0-65535.
- Port: The port on this target host of this service. The range is 0-65535.
- Target: The domain name of the target host. There MUST be one or more address records for this name, the name MUST NOT be an alias. A target of “.” means that the service is decidedly not available at this domain.
Of course, we champion only ever using TLS protected services, and you can see by the lookup below that we have deliberately disabled the plaintext protocols:
$ host -t SRV _caldav._tcp.demo.atmail.com _caldav._tcp.demo.atmail.com has SRV record 0 0 0 . $ host -t SRV _carddav._tcp.demo.atmail.com _carddav._tcp.demo.atmail.com has SRV record 0 0 0 . $ host -t SRV _caldavs._tcp.demo.atmail.com _caldavs._tcp.demo.atmail.com has SRV record 1 5 8443 demo-server.atmail.com. $ host -t SRV _carddavs._tcp.demo.atmail.com _carddavs._tcp.demo.atmail.com has SRV record 1 5 8443 demo-server.atmail.com.
Here, you can see that I look up the domain part of the user address (in this case ‘demo.atmail.com’) in DNS, and we find the service for that domain is hosted on the server called ‘demo-server.atmail.com’.
Now, because you can’t specify a full URL or even URL paths in SRV records, RFC 6764 goes on to define that the next step in the process is to gather the path information from a DNS TXT record published for the same service address. In this snippet, you can see the path for our CalDAV and CardDAV service being returned in the DNS TXT response:
$ host -t TXT _caldavs._tcp.demo.atmail.com _caldavs._tcp.demo.atmail.com descriptive text "path=/calendars" $ host -t TXT _carddavs._tcp.demo.atmail.com _carddavs._tcp.demo.atmail.com descriptive text "path=/addressbooks"
The start of the session should now be directed at the server and path as specified in the DNS records, where further discovery can take place. If the combined SRV + TXT lookup results in an error, a client should fall back to the .well-known method described in the next section.
.well-known Discovery
RFC 6764 further defines some pre-determined URL’s that can be tried in case the SRV + TXT lookups, as described above, do not work. The .well-known address are in accordance with RFC 8615.
If the SRV lookup does work, but the TXT lookup does not, the client should use the address and port returned in the SRV record. However, if the SRV lookup did not work, then in many cases, though not specified in the RFC, a client will attempt to access the .well-known URL’s at the user’s domain – which makes the big assumption that the server hosting the domain of the user is actually known by, or aliased by, that same domain and is serving the URL on the standard port number (HTTP/80) or HTTPS/443)) – which isn’t always the case.
The pre-defined URL’s are:
- /.well-known/caldav for CalDAV; and
- /.well-known/carddav for CardDAV.
Retrieving these URL’s should result in a redirection response from the server to where the service is located, and further discoveries can be made. Here is an example from our demo account where you can see the URL of our services being returned:
$ curl -sD - -o /dev/null https://demo-server.atmail.com:8443/.well-known/caldav HTTP/1.1 302 Moved Temporarily Server: nginx/1.16.1 Date: Sat, 09 May 2020 02:27:13 GMT Content-Type: text/html Content-Length: 145 Location: https://demo-server.atmail.com:8443/calendars Connection: keep-alive X-Content-Type-Options: nosniff $ curl -sD - -o /dev/null https://demo-server.atmail.com:8443/.well-known/carddav HTTP/1.1 302 Moved Temporarily Server: nginx/1.16.1 Date: Sat, 09 May 2020 02:27:17 GMT Content-Type: text/html Content-Length: 145 Location: https://demo-server.atmail.com:8443/addressbooks Connection: keep-alive X-Content-Type-Options: nosniff
If the client is unable to determine the server name and path of the service by either the SRV+TXT or the .well-known method, then it will have no choice but to prompt the user for the server address and path information.
Using the services
Hopefully by this point, you know where the CalDAV and CardDAV services are provided, so let’s start interacting with them. Both CalDAV and CardDAV are extensions of the WebDAV protocol as described in RFC 4918, which loosely translates to having web-based folders (Collections), files (Resources), and meta-data for each (Properties).
All calls from here will be authenticated as the user trying to access the service.
CalDAV
Now, we can interact with the service as described in RFC 4791, and subsequent errata that extends the functionality.
Ensuring service
First, we should actually validate there is a CalDAV service on offer at the URL to which we have been directed. To do this, we look for some properties on the URL by using the PROPFIND method, like so:
$ curl --user "[email protected]:ThePassword" -s -X PROPFIND -H "Content-Type: application/xml" -sD /dev/stderr https://demo-server.atmail.com:8443/calendars | xmllint -format - HTTP/1.1 207 Multi-Status Server: nginx/1.16.1 Date: Sat, 09 May 2020 05:49:47 GMT Content-Type: application/xml; charset=utf-8 Transfer-Encoding: chunked Connection: keep-alive Vary: Brief,Prefer DAV: 1, 3, extended-mkcol, access-control, calendarserver-principal-property-search, 2, resource-sharing, calendar-access, calendar-proxy, calendar-auto-schedule, calendar-availability, calendarserver-subscribed, calendarserver-sharing, addressbook <?xml version="1.0"?> <d:multistatus xmlns:d="DAV:" xmlns:s="https://sabredav.org/ns" xmlns:cal="urn:ietf:params:xml:ns:caldav" xmlns:cs="https://calendarserver.org/ns/" xmlns:card="urn:ietf:params:xml:ns:carddav"> <d:response> <d:href>/calendars/</d:href> <d:propstat> <d:prop> <d:resourcetype> <d:collection/> </d:resourcetype> <d:getcontenttype>application/octet-stream</d:getcontenttype> </d:prop> <d:status>HTTP/1.1 200 OK</d:status> </d:propstat> </d:response> <d:response> <d:href>/calendars/[email protected]/</d:href> <d:propstat> <d:prop> <d:resourcetype> <d:collection/> </d:resourcetype> <d:getcontenttype>application/octet-stream</d:getcontenttype> </d:prop> <d:status>HTTP/1.1 200 OK</d:status> </d:propstat> </d:response> </d:multistatus>
So, while there is a lot of information returned there, here is the important part that tells us that indeed CalDAV services are offered at this location, specifically the ‘calendar-access’ field in the DAV header in the response:
DAV: 1, 3, extended-mkcol, access-control, calendarserver-principal-property-search, 2, resource-sharing, calendar-access, calendar-proxy, calendar-auto-schedule, calendar-availability, calendarserver-subscribed, calendarserver-sharing, addressbook
But, we also had another interesting piece of information returned in the body of the request, namely another collection we can explore: /calendars/[email protected]/
Exploring collections (Calendars)
In the above section, we found a collection that was labelled as a calendar, so let’s see what’s in there.
We will recurse down as far as possible with the depth set to infinity, but pass in some query data to state we are only interested in calendar objects and to only return the URL, resource type and the display name of the collection:
$ curl --user "[email protected]:ThePassword" -sD /dev/stderr -H "Content-Type: application/xml" -X PROPFIND -H "Depth: infinity" --data '<d:propfind xmlns:d="DAV:" xmlns:cs="https://calendarserver.org/ns/"><d:prop><d:resourcetype /><d:displayname /></d:prop></d:propfind>' https://demo-server.atmail.com:8443/calendars/[email protected]/ | xmllint -format - HTTP/1.1 207 Multi-Status Server: nginx/1.16.1 Date: Sat, 09 May 2020 05:48:57 GMT Content-Type: application/xml; charset=utf-8 Transfer-Encoding: chunked Connection: keep-alive Vary: Brief,Prefer DAV: 1, 3, extended-mkcol, access-control, calendarserver-principal-property-search, 2, resource-sharing, calendar-access, calendar-proxy, calendar-auto-schedule, calendar-availability, calendarserver-subscribed, calendarserver-sharing, addressbook <?xml version="1.0"?> <d:multistatus xmlns:d="DAV:" xmlns:s="https://sabredav.org/ns" xmlns:cal="urn:ietf:params:xml:ns:caldav" xmlns:cs="https://calendarserver.org/ns/" xmlns:card="urn:ietf:params:xml:ns:carddav"> <d:response> <d:href>/calendars/[email protected]/</d:href> <d:propstat> <d:prop> <d:resourcetype> <d:collection/> </d:resourcetype> </d:prop> <d:status>HTTP/1.1 200 OK</d:status> </d:propstat> <d:propstat> <d:prop> <d:displayname/> </d:prop> <d:status>HTTP/1.1 404 Not Found</d:status> </d:propstat> </d:response> <d:response> <d:href>/calendars/[email protected]/02f29dd9516f1f0c415527e0a60cb3f7/</d:href> <d:propstat> <d:prop> <d:resourcetype> <d:collection/> <cal:calendar/> <cs:shared-owner/> </d:resourcetype> <d:displayname>Birthdays</d:displayname> </d:prop> <d:status>HTTP/1.1 200 OK</d:status> </d:propstat> </d:response> <d:response> <d:href>/calendars/[email protected]/0f9263536b9fc61ada745644735bfd8f/</d:href> <d:propstat> <d:prop> <d:resourcetype> <d:collection/> <cal:calendar/> <cs:shared-owner/> </d:resourcetype> <d:displayname>Work</d:displayname> </d:prop> <d:status>HTTP/1.1 200 OK</d:status> </d:propstat> </d:response> <d:response> <d:href>/calendars/[email protected]/edfab414adccd8b67f7727e3ae03b85b/</d:href> <d:propstat> <d:prop> <d:resourcetype> <d:collection/> <cal:calendar/> <cs:shared-owner/> </d:resourcetype> <d:displayname>Important Dates</d:displayname> </d:prop> <d:status>HTTP/1.1 200 OK</d:status> </d:propstat> </d:response> <d:response> <d:href>/calendars/[email protected]/default/</d:href> <d:propstat> <d:prop> <d:resourcetype> <d:collection/> <cal:calendar/> <cs:shared-owner/> </d:resourcetype> <d:displayname>Family</d:displayname> </d:prop> <d:status>HTTP/1.1 200 OK</d:status> </d:propstat> </d:response> <d:response> <d:href>/calendars/[email protected]/inbox/</d:href> <d:propstat> <d:prop> <d:resourcetype> <d:collection/> <cal:schedule-inbox/> </d:resourcetype> </d:prop> <d:status>HTTP/1.1 200 OK</d:status> </d:propstat> <d:propstat> <d:prop> <d:displayname/> </d:prop> <d:status>HTTP/1.1 404 Not Found</d:status> </d:propstat> </d:response> <d:response> <d:href>/calendars/[email protected]/outbox/</d:href> <d:propstat> <d:prop> <d:resourcetype> <d:collection/> <cal:schedule-outbox/> </d:resourcetype> </d:prop> <d:status>HTTP/1.1 200 OK</d:status> </d:propstat> <d:propstat> <d:prop> <d:displayname/> </d:prop> <d:status>HTTP/1.1 404 Not Found</d:status> </d:propstat> </d:response> </d:multistatus>
So, here you can see three different calendar resource types returned:
- cal:calendar
- cal:schedule-inbox
- cal:schedule-outbox
Both ‘cal:schedule-inbox’ and ‘cal:schedule-outbox’ are special types of resources defined in RFC 6638, that are used for scheduling. For this post, we are only interested in the ‘calendar’ resources returned, namely:
- /calendars/[email protected]/02f29dd9516f1f0c415527e0a60cb3f7/ (Display Name: Birthdays)
- /calendars/[email protected]/0f9263536b9fc61ada745644735bfd8f/ (Display Name: Work)
- /calendars/[email protected]/edfab414adccd8b67f7727e3ae03b85b/ (Display Name: Important Dates)
- /calendars/[email protected]/default/ (Display Name: Family)
So, let’s have a look inside one of these collections (calendars). Let’s choose the ‘Birthdays’ calendar and see what is there:
$ curl --user "[email protected]:ThePassword" -sD /dev/stderr -H "Content-Type: application/xml" -X PROPFIND -H "Depth: infinity" https://demo-server.atmail.com:8443/calendars/[email protected]/02f29dd9516f1f0c415527e0a60cb3f7/ | xmllint -format - HTTP/1.1 207 Multi-Status Server: nginx/1.16.1 Date: Sat, 09 May 2020 05:47:23 GMT Content-Type: application/xml; charset=utf-8 Transfer-Encoding: chunked Connection: keep-alive Vary: Brief,Prefer DAV: 1, 3, extended-mkcol, access-control, calendarserver-principal-property-search, 2, resource-sharing, calendar-access, calendar-proxy, calendar-auto-schedule, calendar-availability, calendarserver-subscribed, calendarserver-sharing, addressbook <?xml version="1.0"?> <d:multistatus xmlns:d="DAV:" xmlns:s="https://sabredav.org/ns" xmlns:cal="urn:ietf:params:xml:ns:caldav" xmlns:cs="https://calendarserver.org/ns/" xmlns:card="urn:ietf:params:xml:ns:carddav"> <d:response> <d:href>/calendars/[email protected]/02f29dd9516f1f0c415527e0a60cb3f7/</d:href> <d:propstat> <d:prop> <d:resourcetype> <d:collection/> <cal:calendar/> <cs:shared-owner/> </d:resourcetype> <d:getcontenttype>application/octet-stream</d:getcontenttype> <cs:getctag>http://sabre.io/ns/sync/3</cs:getctag> <s:sync-token>3</s:sync-token> <cal:supported-calendar-component-set> <cal:comp name="VEVENT"/> <cal:comp name="VTODO"/> </cal:supported-calendar-component-set> <cal:schedule-calendar-transp> <cal:opaque/> </cal:schedule-calendar-transp> <d:displayname>Birthdays</d:displayname> <x1:calendar-order xmlns:x1="https://apple.com/ns/ical/">0</x1:calendar-order> </d:prop> <d:status>HTTP/1.1 200 OK</d:status> </d:propstat> </d:response> <d:response> <d:href>/calendars/[email protected]/02f29dd9516f1f0c415527e0a60cb3f7/33992bd8-d3fe-4b07-baaf-c43d0042fae8.ics</d:href> <d:propstat> <d:prop> <d:getlastmodified>Sat, 09 May 2020 01:24:06 GMT</d:getlastmodified> <d:getcontentlength>554</d:getcontentlength> <d:resourcetype/> <d:getetag>"d34b216dac0dcf327a3cf2d79f95a226"</d:getetag> <d:getcontenttype>text/calendar; charset=utf-8; component=vevent</d:getcontenttype> </d:prop> <d:status>HTTP/1.1 200 OK</d:status> </d:propstat> </d:response> </d:multistatus>
We can see just a single resource or file:
- /calendars/[email protected]/02f29dd9516f1f0c415527e0a60cb3f7/33992bd8-d3fe-4b07-baaf-c43d0042fae8.ics
In this instance, it is a calendar event, and we can tell this via the property:
- <d:getcontenttype>text/calendar; charset=utf-8; component=vevent</d:getcontenttype>
Finally, let’s retrieve that event with a regular old HTTP GET, and see what we have:
curl --user "[email protected]:ThePassword" -i -X GET https://demo-server.atmail.com:8443/calendars/[email protected]/02f29dd9516f1f0c415527e0a60cb3f7/33992bd8-d3fe-4b07-baaf-c43d0042fae8.ics HTTP/1.1 200 OK Server: nginx/1.16.1 Date: Sat, 09 May 2020 05:28:59 GMT Content-Type: text/calendar; charset=utf-8; component=vevent Content-Length: 554 Connection: keep-alive Last-Modified: Sat, 09 May 2020 01:24:06 GMT ETag: "d34b216dac0dcf327a3cf2d79f95a226" X-Content-Type-Options: nosniff BEGIN:VCALENDAR VERSION:2.0 PRODID:-//apiserver//API Server DAV//EN BEGIN:VEVENT UID:33992bd8-d3fe-4b07-baaf-c43d0042fae8 DTSTART;VALUE=DATE:20200506 DTEND;VALUE=DATE:20200507 CREATED:20190124T104704Z DTSTAMP:20190128T234044Z LAST-MODIFIED;X-VOBJ-FLOATINGTIME-ALLOWED=TRUE:20200503T203005 RRULE:FREQ=YEARLY;INTERVAL=1;WKST=MO SEQUENCE:30 STATUS:CONFIRMED SUMMARY:Dad's Birthday TRANSP:OPAQUE BEGIN:VALARM ACKNOWLEDGED:20190128T234044Z ACTION:DISPLAY DESCRIPTION:Default Description TRIGGER:-P1D END:VALARM END:VEVENT END:VCALENDAR
Which, as you can see, is a single event in iCalendar format. We don’t go into that format in this post, but needless to say there is enough information in that event to describe the event. In this case, it is Dad’s birthday on the 6th of May every year and we are reminded of this one day in advance.
CardDAV
The usage of CardDAV is very consistent with the usage of CalDAV, and if you know one, the other should come to hand easily. Let’s run through the same exercises that we did above with CalDAV.
CalDAV is described in RFC 6352 and subsequent errata that extends the functionality.
Ensuring Service
Here we are looking for the ‘addressbook’ option being returned in the DAV response header:
$ curl --user "[email protected]:ThePassword" -s -X PROPFIND -H "Content-Type: application/xml" -sD /dev/stderr https://demo-server.atmail.com:8443/addressbooks | xmllint -format - HTTP/1.1 207 Multi-Status Server: nginx/1.16.1 Date: Sat, 09 May 2020 05:40:00 GMT Content-Type: application/xml; charset=utf-8 Transfer-Encoding: chunked Connection: keep-alive Vary: Brief,Prefer DAV: 1, 3, extended-mkcol, access-control, calendarserver-principal-property-search, 2, resource-sharing, calendar-access, calendar-proxy, calendar-auto-schedule, calendar-availability, calendarserver-subscribed, calendarserver-sharing, addressbook <?xml version="1.0"?> <d:multistatus xmlns:d="DAV:" xmlns:s="https://sabredav.org/ns" xmlns:cal="urn:ietf:params:xml:ns:caldav" xmlns:cs="https://calendarserver.org/ns/" xmlns:card="urn:ietf:params:xml:ns:carddav"> <d:response> <d:href>/addressbooks/</d:href> <d:propstat> <d:prop> <d:resourcetype> <d:collection/> </d:resourcetype> <d:getcontenttype>application/octet-stream</d:getcontenttype> </d:prop> <d:status>HTTP/1.1 200 OK</d:status> </d:propstat> </d:response> <d:response> <d:href>/addressbooks/[email protected]/</d:href> <d:propstat> <d:prop> <d:resourcetype> <d:collection/> </d:resourcetype> <d:getcontenttype>application/octet-stream</d:getcontenttype> </d:prop> <d:status>HTTP/1.1 200 OK</d:status> </d:propstat> </d:response> </d:multistatus>
We can see a collection under here for this user. Let’s go and explore that:
$ curl --user "[email protected]:ThePassword" -s -X PROPFIND -H "Content-Type: application/xml" -sD /dev/stderr https://demo-server.atmail.com:8443/addressbooks/[email protected]/ | xmllint -format - HTTP/1.1 207 Multi-Status Server: nginx/1.16.1 Date: Sat, 09 May 2020 05:40:17 GMT Content-Type: application/xml; charset=utf-8 Transfer-Encoding: chunked Connection: keep-alive Vary: Brief,Prefer DAV: 1, 3, extended-mkcol, access-control, calendarserver-principal-property-search, 2, resource-sharing, calendar-access, calendar-proxy, calendar-auto-schedule, calendar-availability, calendarserver-subscribed, calendarserver-sharing, addressbook <?xml version="1.0"?> <d:multistatus xmlns:d="DAV:" xmlns:s="https://sabredav.org/ns" xmlns:cal="urn:ietf:params:xml:ns:caldav" xmlns:cs="https://calendarserver.org/ns/" xmlns:card="urn:ietf:params:xml:ns:carddav"> <d:response> <d:href>/addressbooks/[email protected]/</d:href> <d:propstat> <d:prop> <d:resourcetype> <d:collection/> </d:resourcetype> <d:getcontenttype>application/octet-stream</d:getcontenttype> </d:prop> <d:status>HTTP/1.1 200 OK</d:status> </d:propstat> </d:response> <d:response> <d:href>/addressbooks/[email protected]/default/</d:href> <d:propstat> <d:prop> <d:resourcetype> <d:collection/> <card:addressbook/> </d:resourcetype> <d:getcontenttype>application/octet-stream</d:getcontenttype> </d:prop> <d:status>HTTP/1.1 200 OK</d:status> </d:propstat> </d:response> </d:multistatus>
Here, we see a single address book collection called ‘default’, and we can tell it is an address book collection because of the resource type property of ‘<card:addressbook/>’. Let’s see what’s in there:
$ curl --user "[email protected]:ThePassword" -s -X PROPFIND -H "Content-Type: application/xml" -sD /dev/stderr https://demo-server.atmail.com:8443/addressbooks/[email protected]/default/ | xmllint -format - HTTP/1.1 207 Multi-Status Server: nginx/1.16.1 Date: Sat, 09 May 2020 05:53:23 GMT Content-Type: application/xml; charset=utf-8 Transfer-Encoding: chunked Connection: keep-alive Vary: Brief,Prefer DAV: 1, 3, extended-mkcol, access-control, calendarserver-principal-property-search, 2, resource-sharing, calendar-access, calendar-proxy, calendar-auto-schedule, calendar-availability, calendarserver-subscribed, calendarserver-sharing, addressbook <?xml version="1.0"?> <d:multistatus xmlns:d="DAV:" xmlns:s="https://sabredav.org/ns" xmlns:cal="urn:ietf:params:xml:ns:caldav" xmlns:cs="https://calendarserver.org/ns/" xmlns:card="urn:ietf:params:xml:ns:carddav"> <d:response> <d:href>/addressbooks/[email protected]/default/</d:href> <d:propstat> <d:prop> <d:resourcetype> <d:collection/> <card:addressbook/> </d:resourcetype> <d:getcontenttype>application/octet-stream</d:getcontenttype> </d:prop> <d:status>HTTP/1.1 200 OK</d:status> </d:propstat> </d:response> <d:response> <d:href>/addressbooks/[email protected]/default/49e6be92-e235-4372-91cd-145673caed4f.vcf</d:href> <d:propstat> <d:prop> <d:getlastmodified>Tue, 04 Feb 2020 03:17:33 GMT</d:getlastmodified> <d:getcontentlength>207</d:getcontentlength> <d:resourcetype/> <d:getetag>"66098840ac4759f5bf12871c85899b33"</d:getetag> <d:getcontenttype>text/vcard; charset=utf-8</d:getcontenttype> </d:prop> <d:status>HTTP/1.1 200 OK</d:status> </d:propstat> </d:response> </d:multistatus>
Okay, it seems we have a single entry, notably:
- /addressbooks/[email protected]/default/49e6be92-e235-4372-91cd-145673caed4f.vcf
We can tell it is an address book entry, due to this property:
- <d:getcontenttype>text/vcard; charset=utf-8</d:getcontenttype>
When we pull down that file, we can see the VCARD data:
$ curl --user "[email protected]:ThePassword" -s -X GET -i https://demo-server.atmail.com:8443/addressbooks/[email protected]/default/49e6be92-e235-4372-91cd-145673caed4f.vcf HTTP/1.1 200 OK Server: nginx/1.16.1 Date: Sat, 09 May 2020 05:56:19 GMT Content-Type: text/vcard; charset=utf-8 Content-Length: 207 Connection: keep-alive Last-Modified: Tue, 04 Feb 2020 03:17:33 GMT ETag: "66098840ac4759f5bf12871c85899b33" X-Content-Type-Options: nosniff BEGIN:VCARD VERSION:3.0 PRODID:-//apiserver//API Server DAV//EN UID:36baeaba-08df-466b-8f10-84a9b3570613 ORG:atmail;; EMAIL;TYPE=OTHER;PREF=1:[email protected] N:Smith;Stan;;; FN:Stan Smith END:VCARD
Conclusion
In this post we have explored a basic usage of the CalDAV and CardDAV protocols, including how to discover the service endpoints, and perform some basic interaction with those endpoints. We encourage you to read the associated RFC’s as peppered above, to gain a fuller understanding of these protocols.
Did you find this CalDAV/CardDAV post useful?
We’re taking ideas for future how-to posts about the behind-the-scenes workings of email services. We invite suggestions here.
Meanwhile, we invite you to check out some of our previous how-to posts:
- IMAP 101: Manual IMAP Sessions
- IMAP Commands
- Advanced IMAP
- POP 101: Manual POP Sessions
- SMTP 101: Manual SMTP Sessions
- SMTP Reply, Status and Error Codes
New to atmail?