As announced in article CalDAV – Der Weg zum Kalender, we have developed a module for exchanging calendar data between an external calendar and a conzept 16 application. This module is based on the CalDAV protocol and offers all the usual functions of a CalDAV client (retrieve, create, edit and delete appointments). In this article, I would like to introduce you to the functionality and implementation of this client procedure.
How it works
Communication takes place between the client and server via HTTP. The client sends a request to the server, which in turn responds to it. A typical request would be to retrieve appointments. Appointments are available as .ics files on the server. An .ics file contains calendar data in iCalendar format. Some servers provide these files, but do not support the CalDAV protocol. This is usually the case with static calendars (e.g. a public holiday calendar). These calendars usually have an .ics file that contains all appointments. For comparison: A server that supports the CalDAV protocol has a separate .ics file for each appointment, which can be retrieved individually.
A simple request to pick up a calendar or a single appointment looks like this:
GET /calendar/event.ics HTTP/1.1
If the file exists, the server responds with the HTTP status code 200 OK and contains the appointment in iCalendar format in the body:
BEGIN:VCALENDAR
PRODID:-//vectorsoft//CONZEPT 16 CalDAV-Server//DE
VERSION:2.0
BEGIN:VEVENT
SUMMARY:Eat at Luigis
LOCATION:Pizzeria Luigi
DESCRIPTION:Try out new Pizza!
DTSTART;TZID=Europe/Berlin:20130503T180000
DTEND;TZID=Europe/Berlin:20130503T210000
DTSTAMP:20130425T144817Z
UID:bjj3pbehp5ovjf74vent9a3820
CREATED:20130503T140000Z
LAST-MODIFIED:20130503T140000Z
SEQUENCE:0
END:VEVENT
END:VCALENDAR
The iCalendar format is described in RFC 5545. There is also a short introduction on Wikipedia (englisch).
Appointments can be created and changed using the HTTP PUT method and deleted using the HTTP DELETE method.
Retrieving appointments using the HTTP method GET may be sufficient for static calendars, but if appointments are possibly changed, the methods of the WebDAV protocol are more suitable.
If the server supports the WebDAV protocol methods, the client requests the appointments using the REPORT method. Filters can be set for a REPORT request so that only certain appointments are returned. It is also possible to specify which properties are supposed to be returned for the resource, in this case the appointment. The typical request from the client looks like this:
REPORT /calendar/ HTTP/1.1
Depth: 1
Content-Type: text/xml; charset=utf-8
Content-Length: xxx
<?xml version="1.0"?>
<B:calendar-query xmlns:B="urn:ietf:params:xml:ns:caldav">
<A:prop xmlns:A="DAV:">
<A:getetag/>
</A:prop>
<B:filter>
<B:comp-filter name="VCALENDAR">
<B:comp-filter name="VEVENT">
<B:time-range start="20130101T000000Z"/>
</B:comp-filter>
</B:comp-filter>
</B:filter>
</B:calendar-query>
The answer contains the addresses (href) of all dates that take place after 01.01.2013 and their E-day.
HTTP/1.1 207 Multi-Status
Content-Type: text/xml; charset=utf-8
Content-Length: xxx
<?xml version="1.0"?>
<A:multistatus xmlns:A="DAV:">
<A:response>
<A:href>/calendar/event-1.ics</A:href>
<A:propstat>
<A:status>HTTP/1.1 200 OK</A:status>
<A:prop>
<d:getetag>"1-5"</A:getetag>
</A:prop>
</A:propstat>
</A:response>
<A:response>
<A:href>/calendar/event-2.ics</A:href>
<A:propstat>
<A:status>HTTP/1.1 200 OK</A:status>
<A:prop>
<A:getetag>"2-5"</A:getetag>
</A:prop>
</A:propstat>
</A:response>
</A:multistatus>
The E-day of a resource changes when the resource changes. This allows the client to determine whether an appointment has changed.
In the next step, the client requests all appointments that it does not know or that have changed. Theoretically, every appointment could be retrieved with a GET request. In practice, however, this can result in a large number of GET requests. For this reason, a so-called multiget, a variant of the REPORT method, is used:
REPORT /calendar/ HTTP/1.1
Depth: 1
Content-Type: text/xml; charset=utf-8
Content-Length: xxx
<?xml version="1.0"?>
<B:calendar-multiget xmlns:B="urn:ietf:params:xml:ns:caldav">
<A:prop xmlns:A="DAV:">
<B:calendar-data/>
</A:prop>
<A:href xmlns:A="DAV:">/calendar/event-1.ics</A:href>
<A:href xmlns:A="DAV:">/calendar/event-2.ics</A:href>
</B:calendar-multiget>
This means that all new and changed appointments can be retrieved in two queries. The client recognizes that an appointment has been deleted by the fact that the appointment is no longer listed in the first REPORT.
Implementation
The so-called calendar address is required to use the interface. This is the address (URL) under which one or more calendars can be found.
Private calendars, such as Google Calendar, require authentication consisting of a user name and password in addition to the calendar URL.
In the following example, a public holiday calendar from ecoline is used. It does not require authentication.
Example
// Starting time: End time today: End of the year
tStartTime->vmServerTime();
tEndTime->vpMonth # 12;
tEndTime->vpDay # 31;
tEndTime->vpYear # tStartTime->vpYear;
tEndTime->vpTime # 23:59:59.99;
// Initialise: Calendar address, user name, password
tCalDAV # SysCalDAV:Init('http://www.ecoline-service.de/ics/deutschland.ics');
if (tCalDAV > 0)
{
// Choose calender
for tCal # tCalDAV->SysCalDAV:CalRead(sCal.First);
loop tCal # tCalDAV->SysCalDAV:CalRead(sCal.Next, tCal);
while (tCal > 0)
{
// Remember calender name
tCalName # tCalDAV->SysCalDAV:CalNameGet(tCal);
// Update/retrieve calendar appointments
tCalDAV->SysCalDAV:CalRefresh(tCal);
// Go through all the dates in the calendar
for tEvt # tCalDAV->SysCalDAV:EvtRead(tCal, sCalEvt.First);
loop tEvt # tCalDAV->SysCalDAV:EvtRead(tCal, sCalEvt.Next, tEvt);
while (tEvt > 0)
{
// Determine the start time of the appointment
tEvtTime # tCalDAV->SysCalDAV:EvtDTStartGet(tEvt);
// Output all dates that start between today and the end of the year
if (tEvtTime > tStartTime and tEvtTime < tEndTime)
WinDialogBox(0, tCalName, tCalDAV->SysCalDAV:EvtSummaryGet(tEvt), 0,0,0);
}
}
tCalDAV->SysCalDAV:Term();
}
The interface was tested with .ics files and the following servers:
- Google Calendar
- fruux
- Memotoo
- CONZEPT 16 CalDAV-Server