CalDAV – Implementierung in conzept 16

Wie in dem Artikel CalDAV – Der Weg zum Kalender angekündigt, haben wir ein Modul zum Austausch von Kalenderdaten zwischen einem externen Kalender und einer conzept 16-Applikation entwickelt. Dieses Modul setzt auf das CalDAV-Protokoll auf und bietet alle gängigen Funktionen eines CalDAV-Clients (Termine abholen, anlegen, bearbeiten und löschen). In diesem Artikel möchte ich Ihnen die Funktionsweise und Implementierung dieser Client-Prozedur vorstellen.


Funktionsweise

Die Kommunikation findet zwischen Client und Server via HTTP statt. Der Client schickt dabei eine Anfrage an den Server, der auf diese wiederum antwortet. Eine typische Anfrage wäre das Abholen von Terminen. Termine liegen als .ics-Datei auf dem Server vor. Eine .ics-Datei enthält Kalenderdaten im iCalendar-Format. Einige Server stellen diese Dateien bereit, unterstützen das CalDAV-Protokoll aber nicht. Dies ist meist bei statischen Kalendern (z.B. einem Feiertagskalender) so. Diese Kalender besitzen meist eine .ics-Datei, die alle Termine enthält. Zum Vergleich: Ein Server, der das CalDAV-Protokoll unterstützt, besitzt für jeden Termin eine eigene .ics-Datei, welche einzeln abgeholt werden kann.

Eine einfache Anfrage um einen Kalender oder einen einzelnen Termin abzuholen sieht wie folgt aus:

GET /calendar/event.ics HTTP/1.1

Sofern die Datei existiert, antwortet der Server mit dem HTTP-Statuscode 200 OK und enthält im Body den Termin im iCalendar-Format:

BEGIN:VCALENDAR
PRODID:-//vectorsoft//conzept 16 CalDAV-Server//DE
VERSION:2.0
BEGIN:VEVENT
SUMMARY:Essen bei Luigi
LOCATION:Pizzeria Luigi
DESCRIPTION:Neue Pizza ausprobieren!
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

Das iCalendar-Format wird im RFC 5545 beschrieben. Eine kurze Einführung gibt es auch auf Wikipedia (englisch).

Das Anlegen und Verändern von Terminen wird über die HTTP-Methode PUT ermöglicht, das Löschen über die HTTP-Methode DELETE.

Das Abholen von Terminen mittels HTTP-Methode GET mag für statische Kalender ausreichen, aber spätestens wenn Termine verändert werden sollen, sind die Methoden des WebDAV-Protokolls geeigneter.

Sofern der Server die Methoden des WebDAV-Protokolls unterstützt, fordert der Client die Termine per REPORT-Methode an. Bei einer REPORT-Anfrage können Filter gesetzt werden, so dass nur bestimmte Termine zurückgeliefert werden. Weiterhin kann festgelegt werden, welche Eigenschaften zu der Ressource, in diesem Fall der Termin, zurückgeliefert werden sollen. Die typische Anfrage des Clients sieht wie folgt aus:

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>

Die Antwort enthält die Adressen (href) aller Termine, die nach dem 01.01.2013 stattfinden, und dessen ETag.

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>

Das ETag einer Ressource verändert sich, wenn sich die Ressource verändert. Dadurch kann der Client feststellen, ob sich ein Termin verändert hat.

Im nächsten Schritt fordert der Client alle Termine an, die er nicht kennt oder die sich verändert haben. Theoretisch könnte jeder Termin mit einer GET-Anfrage abgeholt werden. Praktisch können dadurch aber sehr viele GET-Anfragen entstehen. Daher wird ein sogenannter multiget, eine Variante der REPORT-Methode verwendet:

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>

Dadurch können alle neuen und veränderten Termine in zwei Abfragen abgeholt werden. Dass ein Termin gelöscht wurde, erkennt der Client daran, dass der Termin im ersten REPORT nicht mehr mit aufgeführt wird.

Implementierung

Zum Verwenden der Schnittstelle wird die sogenannte Kalenderadresse benötigt. Dies ist die Adresse (URL) unter der ein oder mehrere Kalender zu finden sind.

Private Kalender, wie beispielsweise der Google Calendar, benötigen zusätzlich zur Kalender-URL eine Authentifizierung, bestehend aus Benutzername und Passwort.

Im folgenden Beispiel wird ein öffentlicher Feiertagskalender von ecoline verwendet. Er benötigt keine Authentifizierung.

Beispiel
// Startzeit: Heute Endzeit: Ende des Jahres
tStartTime->vmServerTime();
tEndTime->vpMonth # 12;
tEndTime->vpDay   # 31;
tEndTime->vpYear  # tStartTime->vpYear;
tEndTime->vpTime  # 23:59:59.99;

// Initialisieren: Kalenderadresse, Benutzername, Passwort
tCalDAV # SysCalDAV:Init('http://www.ecoline-service.de/ics/deutschland.ics');
if (tCalDAV > 0)
{
  // Kalender auswählen
  for   tCal # tCalDAV->SysCalDAV:CalRead(sCal.First);
  loop  tCal # tCalDAV->SysCalDAV:CalRead(sCal.Next, tCal);
  while (tCal > 0)
  {
    // Name des Kalenders merken
    tCalName # tCalDAV->SysCalDAV:CalNameGet(tCal);

    // Termine des Kalenders aktualisieren/abholen
    tCalDAV->SysCalDAV:CalRefresh(tCal);

    // Alle Termine des Kalenders durchlaufen
    for   tEvt # tCalDAV->SysCalDAV:EvtRead(tCal, sCalEvt.First);
    loop  tEvt # tCalDAV->SysCalDAV:EvtRead(tCal, sCalEvt.Next, tEvt);
    while (tEvt > 0)
    {
      // Anfangszeitpunkt des Termins ermitteln
      tEvtTime # tCalDAV->SysCalDAV:EvtDTStartGet(tEvt);

      // Alle Termine, die zwischen heute und Ende des Jahres anfangen, ausgeben
      if (tEvtTime > tStartTime and tEvtTime < tEndTime)
        WinDialogBox(0, tCalName, tCalDAV->SysCalDAV:EvtSummaryGet(tEvt), 0,0,0);
    }
  }

  tCalDAV->SysCalDAV:Term();
}

Die Schnittstelle wurde mit .ics-Dateien und folgenden Servern getestet:

Schreiben Sie einen Kommentar

Ihre E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

Leave the field below empty!

IHRE EVALUIERUNGSLIZENZ - JETZT ANFORDERN!

TESTEN SIE DIE CONZEPT 16 VOLLVERSION - UNVERBINDLICH und KOSTENFREI

Melden Sie sich bei unserem Newsletter an

Anrede*
     
Zustimmung zur Datenverarbeitung gem. DSGVO*



WordPress Cookie-Hinweis von Real Cookie Banner