arnaudq's blog

Thursday May 07, 2009

Group event example using implicit scheduling

The iTIP specification (RFC 2446 or its Calsify successor) contains a group event example showing an initial invitation, a reply and an update to a group event.

The example only shows the messages that are exchanged between the different calendar clients. It does not show what each client would store in the actual calendars of the participants after each step. This is for good reason since iTIP defines only messages between CUAs and also because an iTIP aware client does not necessarily store the actual calendar in iCalendar format (might be a proprietary format).

But in any case, iTIP aware clients have to deal with 2 types of objects:

  • iTIP messages (invitations, responses,...).
  • calendar resources (the event and tasks that users see in their calendar).
The client must handle the complex logic of creating one from another, merging them together,...

Using the new CalDAV Scheduling model, this becomes much simpler since clients do need to worry only about their copy of the event. I'll try to show that by turning the above mentioned iTIP example into a CalDAV Scheduling example.

Organizer A invites B, C, D, and E (See 4.2.1 A Group Event Request in iTIP):

To initiate the group event , the organizer (A) simply creates a regular calendar resource in any of his calendar collections:

>> Request <<

PUT /home/A/calendar/qwue23489.ics HTTP/1.1
If-None-Match: *
Host: cal.example.com
Content-Type: text/calendar
Content-Length: xxxx
   
BEGIN:VCALENDAR
PRODID:-//ACME/DesktopCalendar//EN
VERSION:2.0
BEGIN:VEVENT
ORGANIZER:Mailto:A@example.com
ATTENDEE;ROLE=CHAIR;PARTSTAT=ACCEPTED;CN=BIG A:Mailto:A@example.com
ATTENDEE;RSVP=TRUE;TYPE=INDIVIDUAL;CN=B:Mailto:B@example.com
ATTENDEE;RSVP=TRUE;TYPE=INDIVIDUAL;CN=C:Mailto:C@example.com
ATTENDEE;RSVP=TRUE;TYPE=INDIVIDUAL;CN=Hal:Mailto:D@example.com
ATTENDEE;RSVP=FALSE;TYPE=ROOM:conf_Big@example.com
ATTENDEE;ROLE=NON-PARTICIPANT;RSVP=FALSE:Mailto:E@example.com
DTSTAMP:20090611T190000Z
DTSTART:20090701T200000Z
DTEND:20090701T2000000Z
SUMMARY:Conference
UID:calsrv.example.com-873970198738777@example.com
SEQUENCE:0
STATUS:CONFIRMED
END:VEVENT
END:VCALENDAR

>> Response <<

HTTP/1.1 201 Created
Content-Length: 0
Date: Thu, 11 Jun 2009 09:32:12 GMT

On storing this calendar resource, the server notices that it is a scheduling resource:

  • the organizer is the owner of the calendar collection,
  • the resource has attendees without a SCHEDULE-AGENT=CLIENT parameter.

As a consequence, the server will automatically deliver a copy of the same event in each attendee's calendar (and put a corresponding iTIP message in their scheduling inbox).

The Organizer client can check whether the delivery succeeded by doing a GET on the just created resource and checking the SCHEDULE-STATUS of each attendee:

>> Request <<

GET /home/A/calendar/qwue23489.ics HTTP/1.1
Host: cal.example.com
   
>> Response <<

HTTP/1.1 200 OK
Content-Type: text/calendar
Content-Length: XXX
Date: Thu, 11 Jun 2009 09:32:24 GMT
ETag: "123456789-000-111"
Schedule-Tag: "123456789-000-111"

BEGIN:VCALENDAR
PRODID:-//ACME/DesktopCalendar//EN
VERSION:2.0
BEGIN:VEVENT
ORGANIZER:Mailto:A@example.com
ATTENDEE;ROLE=CHAIR;PARTSTAT=ACCEPTED;CN=BIG A:Mailto:A@example.com
ATTENDEE;SCHEDULE-STATUS="1.2";TYPE=INDIVIDUAL;CN=B:Mailto:B@example.com
ATTENDEE;SCHEDULE-STATUS="1.2";TYPE=INDIVIDUAL;CN=C:Mailto:C@example.com
ATTENDEE;SCHEDULE-STATUS="1.2";TYPE=INDIVIDUAL;CN=Hal:Mailto:D@example.com
ATTENDEE;SCHEDULE-STATUS="1.2";TYPE=ROOM:conf_Big@example.com
ATTENDEE;SCHEDULE-STATUS="1.2";ROLE=NON-PARTICIPANT:Mailto:E@example.com
DTSTAMP:20090611T190000Z
DTSTART:20090701T200000Z
DTEND:20090701T2000000Z
SUMMARY:Conference
UID:calsrv.example.com-873970198738777@example.com
SEQUENCE:0
STATUS:CONFIRMED
END:VEVENT
END:VCALENDAR

Reply from attendee B (See 4.2.2 Reply To A Group Event Request in iTIP. )

The calendar client of user B does a synchronization between its local cache and the default calendar collection of user B and fetches the event.

>> Request <<

GET /home/B/calendar/bzyw23492.ics HTTP/1.1
Host: cal.example.com
   
>> Response <<

HTTP/1.1 200 OK
Content-Type: text/calendar
Content-Length: XXX
Date: Thu, 11 Jun 2009 09:34:24 GMT
ETag: "123456789-000-112"
Schedule-Tag: "123456789-000-112"

BEGIN:VCALENDAR
PRODID:-//ACME/DesktopCalendar//EN
VERSION:2.0
BEGIN:VEVENT
ORGANIZER:Mailto:A@example.com
ATTENDEE;ROLE=CHAIR;PARTSTAT=ACCEPTED;CN=BIG A:Mailto:A@example.com
ATTENDEE;PARTSTAT=NEEDS-ACTION;RSVP=TRUE;TYPE=INDIVIDUAL;CN=B:Mailto:B@example.com
ATTENDEE;TYPE=INDIVIDUAL;CN=C:Mailto:C@example.com
ATTENDEE;TYPE=INDIVIDUAL;CN=Hal:Mailto:D@example.com
ATTENDEE;TYPE=ROOM:conf_Big@example.com
ATTENDEE;ROLE=NON-PARTICIPANT:Mailto:E@example.com
DTSTAMP:20090611T190000Z
DTSTART:20090701T200000Z
DTEND:20090701T2000000Z
SUMMARY:Conference
UID:calsrv.example.com-873970198738777@example.com
SEQUENCE:0
STATUS:CONFIRMED
END:VEVENT
END:VCALENDAR

Attendee B accepts the invitation. The CUA simply does so by issuing a PUT on the same (attendee's) calendar resource, with an updated PARTSTAT for Attendee B. It makes use of the conditional If-Schedule-Tag-Match header to avoid minor update by other attendees conflicting with its own change:

>> Request <<

PUT /home/B/calendar/bzyw23492.ics HTTP/1.1
If-Schedule-Tag-Match: "123456789-000-112"
Host: cal.example.com
Content-Type: text/calendar
Content-Length: xxxx   

BEGIN:VCALENDAR
PRODID:-//ACME/DesktopCalendar//EN
VERSION:2.0
BEGIN:VEVENT
ORGANIZER:Mailto:A@example.com
ATTENDEE;ROLE=CHAIR;PARTSTAT=ACCEPTED;CN=BIG A:Mailto:A@example.com
ATTENDEE;PARTSTAT=ACCEPTED;TYPE=INDIVIDUAL;CN=B:Mailto:B@example.com
ATTENDEE;TYPE=INDIVIDUAL;CN=C:Mailto:C@example.com
ATTENDEE;TYPE=INDIVIDUAL;CN=Hal:Mailto:D@example.com
ATTENDEE;TYPE=ROOM:conf_Big@example.com
ATTENDEE;ROLE=NON-PARTICIPANT:Mailto:E@example.com
DTSTAMP:20090611T190000Z
DTSTART:20090701T200000Z
DTEND:20090701T2000000Z
SUMMARY:Conference
UID:calsrv.example.com-873970198738777@example.com
SEQUENCE:0
STATUS:CONFIRMED
END:VEVENT
END:VCALENDAR
>> Response <<

HTTP/1.1 204 No Content
Content-Length: 0
Date: Thu, 11 Jun 2009 09:34:30 GMT

The server will then automatically process the reply and update the Organizer's copy accordingly.

Organizer receives the reply from Attendee B

The Organizer's CUA fetches the new version of the group meeting:

>> Request <<

GET /home/A/calendar/qwue23489.ics HTTP/1.1
Host: cal.example.com
   
>> Response <<

HTTP/1.1 200 OK
Content-Type: text/calendar
Content-Length: XXX
Date: Thu, 11 Jun 2009 09:40:24 GMT
ETag: "123456789-000-333"
Schedule-Tag: "123456789-000-333"

BEGIN:VCALENDAR
PRODID:-//ACME/DesktopCalendar//EN
VERSION:2.0
BEGIN:VEVENT
ORGANIZER:Mailto:A@example.com
ATTENDEE;ROLE=CHAIR;PARTSTAT=ACCEPTED;CN=BIG A:Mailto:A@example.com
ATTENDEE;SCHEDULE-STATUS="2.0";PARTSTAT=ACCEPTED;TYPE=INDIVIDUAL;CN=B:Mailto:B@example.com
ATTENDEE;SCHEDULE-STATUS="1.2";TYPE=INDIVIDUAL;CN=C:Mailto:C@example.com
ATTENDEE;SCHEDULE-STATUS="1.2";TYPE=INDIVIDUAL;CN=Hal:Mailto:D@example.com
ATTENDEE;SCHEDULE-STATUS="1.2";TYPE=ROOM:conf_Big@example.com
ATTENDEE;SCHEDULE-STATUS="1.2";ROLE=NON-PARTICIPANT:Mailto:E@example.com
DTSTAMP:20090611T190000Z
DTSTART:20090701T200000Z
DTEND:20090701T2000000Z
SUMMARY:Conference
UID:calsrv.example.com-873970198738777@example.com
SEQUENCE:0
STATUS:CONFIRMED
END:VEVENT
END:VCALENDAR

Of course, this example is only scratching the surface of what it takes to create a good scheduling aware client but it shows that minimal scheduling processing can be achieved at a very low cost.


Friday Mar 13, 2009

URI encoding in DAV:href element

The DAV:href xml element, defined in the WebDAV specification is used in many request/response payloads and DAV properties. It is important to note that the element value is a URI as defined in RFC3986.

As such, this value must be percent encoded if it contains characters outside the allowed range. When clients and server misinterpret this requirement, things can get messy. Here is some illustration:

If a calendar client knows about the principal collection of a user, it can retrieve the CalDAV calendar-home-set property of the user:

PROPFIND /dav/principals/johnd/ HTTP/1.1
host: dav.example.com
content-type: text/xml
content-lengh: xxx
depth: 0

<?xml version="1.0" encoding="utf-8"?>
<D:propfind xmlns:C="urn:ietf:params:xml:ns:caldav" xmlns:D="DAV:">
 <D:prop>
  <C:calendar-home-set/>
 </D:prop>
</D:propfind>

In this case, the calendar home was identified internally as '/dav/home/John.Doe@example.com/'. Our server was sending the following response:

HTTP/1.1 207 Multistatus
Date: xxx
Content-Type: application/xml; charset="utf-8"
Content-Length: xxxx

<?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/principals/johnd/</D:href>
    <D:propstat>
      <D:prop>
        <C:calendar-home-set><D:href>/dav/home/John.Doe%40example.com/</D:href></C:calendar-home-set>
      </D:prop>
      <D:status>HTTP/1.1 200 OK</D:status>
    </D:propstat>
  </D:response>
</D:multistatus>

(Once percent encoded, the '@' character becomes '%40').

Using the returned calendar-home-set, the client would then get the list of calendars under this calendar home by issuing a Depth=1 PROPFIND query. But it would re encode the returned URI, resulting in a:

PROPFIND /dav/home/John.Doe%2540example.com/ HTTP/1.1

The client encoded '%' as '%25' and the server was interpreting the request as targeting a collection identified internally as /dav/home/John.Doe%40example.com/ which of course did not exist.

As it turned out, the '@' character is a reserved character but it is one of the few reserved characters allowed in the path component of a URI. So our server probably should not have encoded it in the first place.

But given that the URI was already encoded, the client should have either send it as is (i.e. without the need to either decode or reencode it):

PROPFIND /dav/home/John.Doe%40example.com/ HTTP/1.1

or with the '@' character decoded:

PROPFIND /dav/home/John.Doe@example.com/ HTTP/1.1

It looks like there are other cases of client/server misinterpreting those URIs.

Thursday Mar 12, 2009

VTODO with DUE date in other clients

To finish on the VTODO with DUE date topic, I've looked at some "not yet CalDAV enabled" clients.

Microsoft Outlook

In Outlook (more precisely, in Outlook 2003), you can create a task with a start and/or due date (no time). This task can have an associated alarm, which is specified as a date and time. This removes the ambiguity of using relative alarms, found in both Apple iCal and Lightning. It also allows to have a task with no start or due date and just an alarm.

eM Client

Using the eM Client , you can also create tasks. Both start and due date/time must be specified. This task can have an associated alarm, specified as a date and time.

Note: the eM client is CalDAV enabled for VEVENT but not yet for VTODOs.

Evolution

Evolution offers all combinations of start/due, using either date or date and time. Nevertheless, I could not find a way to set an alarm which is quite strange.

Note: Evolution is CalDAV enabled for VEVENT but not for VTODOs.

Sun Convergence

The Sun Convergence web client exposes only an optional due date (or date time). The associated email or sms alarm can be either relative or absolute ("On Date" in the pulldown below):


Thursday Mar 05, 2009

Convergence web client

A nice video showing the calendar capabilities of our new web client.

Friday Jan 30, 2009

VTODO with DUE date in Lightning

Lightning 0.9 also let you create VTODOs (Tasks) and store them on a CalDAV Server. By default those tasks have no associated dates but one can add a due date + time:

Here is how such a task is stored on the CalDAV server:

...
DUE;TZID=Europe/Paris:20090129T160000
... 

The VTODO component has a correct DUE property and no DTSTART.

If one tries to add an alarm to this task (trigger -15 minutes before), the start time of the task which was unset becomes auto selected and can not be removed (although it can be changed):

Here is how the VTODO is stored on the CalDAV Server:

...
DTSTART;TZID=Europe/Paris:20090129T160000
DUE;TZID=Europe/Paris:20090129T160000
BEGIN:VALARM
TRIGGER;VALUE=DURATION:-PT15M
DESCRIPTION:Mozilla Alarm: New Task
ACTION:DISPLAY
END:VALARM
...

Here again, the TRIGGER property is missing a RELATED=END parameter to indicate that the alarm is relative to the due date. Instead, a DTSTART was added, "without the user's consent".

In addition, given that the DTSTART value is equal to the DUE value, this component is valid per RFC2445 but invalid per the new calsify spec where the DUE value must be later in time than the value of the DTSTART.

I should have started by stating that I'm not trying to put any judgment on the quality of one implementation over another but rather to see how todos with due date are used by clients, how they can interoperate, and maybe help future client implementations.


Calendar

Feeds

Search

Links

Navigation

Referrers