arnaudq's blog

Monday Jan 18, 2010

use of email addresses as calendar user addresses

The iCalendar standard defines the notion of organizer and attendees (users, groups, resources,...) for scheduling purpose. Those are identified by "calendar user addresses" which in practice are generally mailto: URIs:

ORGANIZER;CN=Ciy:mailto:ciny@example.com
ATTENDEE;CN=Ciny;PARTSTAT=ACCEPTED:mailto:ciny@example.com
ATTENDEE;CN=Arnaud;PARTSTAT=NEEDS-ACTION;RSVP=TRUE:mailto:arnaudq@example.com


The CalDAV scheduling draft offers a way for a client to discover which calendar user addresses are associated with a particular user through the use of the CALDAV:calendar-user-address-set property.

This property can then be used in different ways, depending on the context:

  • When creating a meeting invitation, it can be used to set the ORGANIZER property value.
  • When fetching events from the server, it can be used to determine whether the owner of the calendar is the organizer or an attendee of the meeting by comparing the addresses returned in the calendar-user-address-set with the ORGANIZER and ATTENDEE values.

Client applications need to take into account a few points when doing this comparison though:

First of all, the calendar-user-address-set property is multivalued: a user may have both  mailto:arnaudq@example.com  and mailto:arnaud.quillaud@example.com  defined as valid calendar addresses. So CUAs need to look for both values when comparing with the ORGANIZER/ATTENDEE values.

Then, when using mailto: URIs, there is no clear definition of how two such URIs should be compared for equality (as far as I know). For example, if one follows the SMTP definition, the local part of an email address is case sensitive. In the context of a calendar client though, I think it makes sense to:

  • try to preserve the original case when storing calendar addresses.
  • but do case insensitive comparison wherever a comparison is required.

Tuesday Dec 15, 2009

Bundling iTIP REQUESTs

When conveying information about a recurring VEVENT or VTODO, iTIP REQUEST messages usually take one of two forms:

Either they contain a full calendar object, i.e. master + exceptions (from RFC2446-bis):

      BEGIN:VCALENDAR
      METHOD:REQUEST
      PRODID:-//Example/ExampleCalendarClient//EN
      VERSION:2.0
      BEGIN:VEVENT
      UID:123456789@example.com
      SEQUENCE:2
      RDATE:19980304T180000Z
      RDATE:19980311T160000Z
      RDATE:19980315T180000Z
      ORGANIZER:mailto:a@example.com
      ATTENDEE;ROLE=CHAIR;PARTSTAT=ACCEPTED:mailto:a@example.com
      ATTENDEE;RSVP=TRUE:mailto:b@example.com
      SUMMARY:Review Accounts
      DTSTART:19980304T180000Z
      DTEND:19980304T200000Z
      DTSTAMP:19980303T193000Z
      LOCATION:Conference Room A
      STATUS:CONFIRMED
      END:VEVENT
      BEGIN:VEVENT
      UID:123456789@example.com
      SEQUENCE:2
      RECURRENCE-ID:19980311T160000Z
      ATTENDEE;ROLE=CHAIR;PARTSTAT=ACCEPTED:mailto:a@example.com
      ATTENDEE;RSVP=TRUE:mailto:b@example.com
      SUMMARY:Review Accounts
      DTSTART:19980311T160000Z
      DTEND:19980304T180000Z
      DTSTAMP:19980306T193000Z
      LOCATION:The Small conference room
      STATUS:CONFIRMED
      END:VEVENT
      END:VCALENDAR

or they contain a single instance:

      BEGIN:VCALENDAR
      METHOD:REQUEST
      PRODID:-//Example/ExampleCalendarClient//EN
      VERSION:2.0
      BEGIN:VEVENT
      UID:123456789@example.com
      SEQUENCE:1
      RECURRENCE-ID:19980311T180000Z
      ORGANIZER:mailto:a@example.com
      ATTENDEE;ROLE=CHAIR;PARTSTAT=ACCEPTED:mailto:a@example.com
      ATTENDEE;RSVP=TRUE:mailto:b@example.com
      SUMMARY:Review Accounts
      DTSTART:19980311T160000Z
      DTEND:19980311T180000Z
      DTSTAMP:19980306T193000Z
      LOCATION:The Small conference room
      STATUS:CONFIRMED
      END:VEVENT
      END:VCALENDAR

That is at least my observation. I'm having trouble finding out whether:

  • this is just me making assumptions,
  • this is mandated by iTIP/iTIP-bis (but where ?), or by some unwritten convention,
  • this is an artifact of how calendar clients interaction usually works (typically the user can change either all instances or one single instance).

Sending only the master or the master + just a few exceptions could only be interpreted on the attendee side as meaning that the missing exceptions are no longer exceptions (?...). This does not make much sense.

But what about sending multiple instances with different RECURRENCE-ID in one single REQUEST ? For example, one could well change the description of one instance, and reschedule another instance by changing its DTSTART. Bundling those 2 instances into a single REQUEST could make sense. Another scenario is one where an attendee is added to only 3 instances of a recurring event. It seems natural to group those 3 instances into one REQUEST when sending the invitation to this attendee.

But is it legal to do so ? And, more importantly, are clients ready to consume this type of message ?

Tuesday Dec 01, 2009

subcollections under calendar collections

To synchronize a calendar collection, clients typically issue a PROPFIND with Depth = 1 on the calendar collection, asking for the DAV:getetag property.

>> Request <<
 

PROPFIND /dav/home/arnaudq@example.com/calendar/ HTTP/1.1
host: localhost:8080
depth: 1
content-type: text/xml

<?xml version="1.0" encoding="utf-8"?>
<x0:propfind xmlns:x0="DAV:">
 <x0:prop>
  <x0:getetag/>
 </x0:prop>
</x0:propfind>

>> Response <<

HTTP/1.1 207 Multi-Status
Content-Type: application/xml; charset="utf-8"

<?xml version="1.0" encoding="UTF-8"?><D:multistatus xmlns:D="DAV:" xmlns:C="urn:ietf:params:xml:ns:caldav">
<D:response>
<D:href>/dav/home/arnaudq@example.com/calendar/</D:href>
<D:propstat>
<D:prop>
<D:getetag/>
</D:prop>
<D:status>HTTP/1.1 404 Not Found</D:status>
</D:propstat>
</D:response>
<D:response>
<D:href>/dav/home/arnaudq@example.com/calendar/dd5b7d18-837b-2249-995e-765d5fc240e9.ics</D:href>
<D:propstat>
<D:prop>
<D:getetag>"1259672377000.2"</D:getetag>
</D:prop>
<D:status>HTTP/1.1 200 OK</D:status>
</D:propstat>
</D:response>
<D:response>
<D:href>/dav/home/arnaudq@example.com/calendar/c14358f8-9b94-6e44-af3a-d728d336800b.ics</D:href>
<D:propstat>
<D:prop>
<D:getetag>"1259672497000.2"</D:getetag>
</D:prop>
<D:status>HTTP/1.1 200 OK</D:status>
</D:propstat>
</D:response>
</D:multistatus>

All resources directly under the calendar collection will be returned.

Among those, the client should be prepared to see WebDAV collections. While the CalDAV specification forbids calendar collections to contain other calendar collections (at any depth), having a basic WebDAV collection under a calendar collection is legal. To filter them out, a client can either rely on the DAV:href of each returned resource (collection URIs end with a "/" character), or ask for the DAV:resourcetype property:

>> Request <<
 

PROPFIND /dav/home/arnaudq@example.com/calendar/ HTTP/1.1
host: localhost:8080
depth: 1
content-type: text/xml

<?xml version="1.0" encoding="utf-8"?>
<x0:propfind xmlns:x0="DAV:">
 <x0:prop>
  <x0:getetag/>
  <x0:resourcetype/>
 </x0:prop>
</x0:propfind>

>> Response <<

HTTP/1.1 207 Multi-Status
Content-Type: application/xml; charset="utf-8"

<?xml version="1.0" encoding="UTF-8"?><D:multistatus xmlns:D="DAV:" xmlns:C="urn:ietf:params:xml:ns:caldav" xmlns:M="urn:ietf:params:xml:ns:carddav">
<D:response>
<D:href>/dav/home/arnaudq@example.com/calendar/</D:href>
<D:propstat>
<D:prop>
<D:resourcetype><C:calendar/><D:collection/></D:resourcetype>
</D:prop>
<D:status>HTTP/1.1 200 OK</D:status>
</D:propstat>
<D:propstat>
<D:prop>
<D:getetag/>
</D:prop>
<D:status>HTTP/1.1 404 Not Found</D:status>
</D:propstat>
</D:response>
<D:response>
<D:href>/dav/home/arnaudq@example.com/calendar/dd5b7d18-837b-2249-995e-765d5fc240e9.ics</D:href>
<D:propstat>
<D:prop>
<D:getetag>"1259672377000.2"</D:getetag>
<D:resourcetype></D:resourcetype>
</D:prop>
<D:status>HTTP/1.1 200 OK</D:status>
</D:propstat>
</D:response>
<D:response>
<D:href>/dav/home/arnaudq@example.com/calendar/attachments/</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:getetag/>
</D:prop>
<D:status>HTTP/1.1 404 Not Found</D:status>
</D:propstat>
</D:response>
</D:multistatus>
In the example above, the calendar collection contains an "attachments" subcollection. CalDAV clients can simply ignore it, based on the value of its DAV:resourcetype property which is not empty.

Monday Nov 02, 2009

DAV:current-user-privilege-set

The DAV:current-user-privilege-set WebDAV ACL property allows a client application to know what operations the currently authenticated user can issue on a WebDAV resource (read, read-write, etc...).

Until now, I was under the impression that servers should return only the top level privileges (aggregate or not).

For example, given a server with the following supported set (note that DAV:all is not abstract):

 [DAV:, all] (aggregate)
      |
      +-- [DAV:, read] (aggregate)
             |
             +-- [DAV:, read-acl] (abstract)
             +-- [DAV:, read-current-user-privilege-set] (abstract)
      |
      +-- [DAV:, write] (aggregate)
             |
             +-- [DAV:, write-acl] (abstract)
             +-- [DAV:, write-properties]
             +-- [DAV:, write-content]

and a user with all rights on a resource,  I was expecting the following DAV:current-user-privilege-set:

 <D:current-user-privilege-set>
    <D:privilege><D:all/></D:privilege>
 </D:current-user-privilege-set>

But the WebDAV ACL specification clearly states that "Aggregate privileges and their contained privileges are listed". So what the server should return is really the full set:

 <D:current-user-privilege-set>
    <D:privilege><D:all/></D:privilege>
    <D:privilege><D:read/></D:privilege>
    <D:privilege><D:write/></D:privilege>
    <D:privilege><D:write-properties/></D:privilege>
    <D:privilege><D:write-content/></D:privilege>
 </D:current-user-privilege-set>

I guess this makes client implementers life easier.


Tuesday Aug 04, 2009

More CalDAV enabled phones... starting with Symbian ?

The iPhone might be the first CalDAV enabled phone but hopefully, it won't be long before other start to appear.

My colleague Maximilian Odendahl has been working on a CalDAV provider for the native Symbian OS Calendar application for a few months now.

He started with a standalone application that syncs up one or more CalDAV accounts using the native Calendar database API (using WebDAV Sync when appropriate).

This is already working great. Here is a screenshot of the native Calendar application, showing events pulled from our hosted preview server:



Now that Sun Microsystems has joined the Symbian foundation, Max will be able to fully integrate his CalDAV provider into the native application, for example to allow direct configuration of an account (image below is still a mockup):


More info on his work at http://developer.symbian.org/wiki/index.php/CalDav.

(Can't resist to mention that after testing his client against 8 different servers, he found the next generation Sun Calendar Server to be the most standard compliant ...)

Calendar

Feeds

Search

Links

Navigation

Referrers