Atmail Home
  • Home
  • Email Services
  • UX
  • Why Atmail?
  • Support
    • Customer Portal
    • FAQ
    • Videos
    • Support Packages
    • Training Services
    • Professional Services
    • Contact Support
  • Blog
  • Menu
Log in Contact Sales
Log in Contact Sales

Products

  • Email Services
  • Email Protection
  • Email Help Centre
  • Domain Registration and Management
  • Product Brochures
  • Book Demo
  • UX

Support

  • Customer Portal
  • FAQ
  • Videos
  • Support Packages
  • Training Services
  • Professional Services
  • Contact Support

Resources

  • Blog
  • Webinars
  • White Papers
  • Grow Revenue
  • Zimbra Alternative
  • AWS Partner

Compliance

  • Compliance HQ
  • Bug Bounty
  • Privacy Policy
  • Terms of Service

Company

  • About Us
  • Why Atmail?
  • Testimonials
  • Contact Us
  • Site Map
CalDAV and CardDAV - atmail email experts - email hosting - telco grade email
July 21, 2020

Command Line Introduction to CalDAV and CardDAV

Industry

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.

 

 

CalDAV and CardDAV - atmail email experts - email hosting - telco grade email

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:

  1. cal:calendar
  2. cal:schedule-inbox
  3. 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?

atmail is an email solutions company with more than 20 years of global, white label, email expertise. You can trust us to deliver an email platform that is secure, stable and scalable. We power more than 170 million mailboxes worldwide and offer modern, white-labelled, cloud hosted email with your choice of US or (GDPR compliant) EU data centres. We also offer on-premises webmail and/or mail server options. To find out more, we invite you to enquire here.

 

Share This Post

More Posts

Market View: December

Market View: December

December 19, 2022
Market View: October

Market View: October

October 13, 2022

Products

  • Email Services
  • Email Protection
  • Email Help Centre
  • Domain Registration and Management
  • Product Brochures
  • Book Demo
  • UX

Support

  • Customer Portal
  • FAQ
  • Videos
  • Support Packages
  • Training Services
  • Professional Services
  • Contact Support

Resources

  • Blog
  • Webinars
  • White Papers
  • Grow Revenue
  • Zimbra Alternative
  • AWS Partner

Compliance

  • Compliance HQ
  • Bug Bounty
  • Privacy Policy
  • Terms of Service

Company

  • About Us
  • Why Atmail?
  • Testimonials
  • Contact Us
  • Site Map

© 2022 Atmail | All Rights Reserved.

Atmail is a trusted email hosting company with more than 20 years of global email expertise. Powering 170 million mailboxes worldwide, we can be trusted to keep customer email platforms secure, stable, scalable and private. Our modern, white label, multitenant and monetisable solutions are available both on-premises and in the cloud.