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.

Friday Jul 24, 2009

Email reminders mess (progress on the horizon)

One of my desktops is a Mac Mini and recently, I started to notice that the Apple Mail program would startup by itself at what seemed like random occasions and ask me for very private information like my uid, password etc... (Thunderbird is my main client).

After exonerating my kids I figured out what was happening: I have a family calendar at an ISP who now offers CalDAV access. A few days before I started to notice the ghost Mail, I had setup the Apple iCal calendar client to show that calendar. Most of the events on that calendar have email reminders associated with them. Those reminders are generated by the ISP Server but it looks like my desktop was trying to honor those as well. Would I have configure Apple Mail properly, I would have started to receive 2 copies of each reminders.

The fact that the iCalendar specification remains silent on the subject of who (client or server) should honor VALARM with an ACTION property set to EMAIL is a pretty well known issue but I always thought it was a rather theoritical one (Lightning for example does not honor email alarms). It is the first time that I face it as an end user.

It looks like the new VALARM Extension draft is coming at about the right time for me...

Thursday Jan 29, 2009

VTODO with DUE date in Apple iCal

Following my previous post, I've started to look at how due dates are leveraged by real clients, starting with Apple iCal.

Apple iCal (on Mac OS 10.5) let you create VTODOs ("To Do Items") and store them on a CalDAV Server. By default those todos have no associated dates but one can add a due date (no time):

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

...
DTSTART;TZID=Europe/Paris:20040101T120000
DUE;VALUE=DATE:20090127
... 

The VTODO component has a correct DUE property but it also has a DTSTART property which:

  1. comes out of nowhere (not visible in the UI),
  2. has no meaningful value (value hardcoded to 01/01/2004).

Client that takes the DTSTART into consideration to do some processing (e.g. to build a view) might show inconsistent results when processing such a VTODO.

Worth, the DTSTART property has a DATETIME value when the DUE property has a DATE value, causing the VTODO to be invalid per the new calsify spec.

If one tries to add an alarm (trigger -15 minutes before), the VTODO is stored as:

...
DTSTART;TZID=Europe/Paris:20040101T120000
DUE;VALUE=DATE:20090127
BEGIN:VALARM
X-WR-ALARMUID:5874F585-58CD-4357-88CF-951AEAF8663A
ACTION:AUDIO
TRIGGER:-PT15M
ATTACH;VALUE=URI:Basso
END:VALARM
... 

The TRIGGER property is missing a RELATED=END parameter which would link the alarm time to the DUE date. As a consequence, other clients will consider the TRIGGER to be relative to the DTSTART property. Since its value is meaningless (some time in 2004), the alarm will probably disappear (or be triggered as soon as the client fetches the todo).

Monday Dec 01, 2008

iCalendar to XML conversion using ical4j

The ical4j library is probably the reference java library for manipulating iCalendar data. It offers a full representation of the iCalendar (RFC 2445) data model (calendar components, properties and parameters), a parser, as well a set of helper classes to do date/time calculations.

A nice design point is that there is a good separation between the act of parsing an iCalendar stream and the building of ical4j objects.

This is achieved by using an event driven model where the parser calls back a handler object whenever a new event (beginning/end of a component or property,...) is encountered by the parser. In other words, the parser is the iCalendar equivalent of an XML SAX parser.

Here is the interface that a handler has to implement:

public interface ContentHandler {

    /\*\*
     \* Triggers the start of handling a calendar.
     \*/
    void startCalendar();

    /\*\*
     \* Triggers the end of handling a calendar.
     \*/
    void endCalendar();

    /\*\*
     \* Triggers the start of handling a component.
     \*/
    void startComponent(String name);

    /\*\*
     \* Triggers the end of handling a component.
     \*/
    void endComponent(String name);

    /\*\*
     \* Triggers the start of handling a property.
     \*/
    void startProperty(String name);

    /\*\*
     \* Triggers the handling of a property value.
     \*/
    void propertyValue(String value) throws URISyntaxException, ParseException,
            IOException;

    /\*\*
     \* Triggers the end of handling a property.
     \*/
    void endProperty(String name);

    /\*\*
     \* Triggers the handling of a parameter.
     \*/
    void parameter(String name, String value) throws URISyntaxException;
}

And here is a sample class implementing this interface to produce a very basic iCalendar to XML conversion (using the StAX XML API):

public final class XMLHandler implements ContentHandler {  
    
    private final XMLStreamWriter xmlWriter;
    
    public XMLHandler(XMLStreamWriter xmlWriter) {
        this.xmlWriter = xmlWriter;
    }
    
    /\*\*
     \* {@inheritDoc}
     \*/
    public void startCalendar() {
        writeStartElement("vcalendar");
    }

    /\*\*
     \* {@inheritDoc}
     \*/
    public void endCalendar() {
        writeEndElement();
    }

    /\*\*
     \* {@inheritDoc}
     \*/
    public void startComponent(String name) {
        writeStartElement(name);
    }

    /\*\*
     \* {@inheritDoc}
     \*/
    public void endComponent(String name) {
        writeEndElement();
    }

    /\*\*
     \* {@inheritDoc}
     \*/
    public void startProperty(String name) {
        writeStartElement(name);
    }

    /\*\*
     \* {@inheritDoc}
     \*/
    public void propertyValue(String value) throws URISyntaxException, ParseException,
            IOException {
        // would need unwrapping
        writeCharacters(value);
    }

    /\*\*
     \* {@inheritDoc}
     \*/
    public void endProperty(String name) {
        writeEndElement();
    }

    /\*\*
     \* {@inheritDoc}
     \*/
    public void parameter(String name, String value) throws URISyntaxException {
        writeAttribute(name, value);
    }
    
    private void writeStartElement(String name) {
        try {
            xmlWriter.writeStartElement(name.toLowerCase());
        } catch (XMLStreamException xe) {
            throw new IllegalStateException("got xml error while writing", xe);
        }
    }
    
    private void writeCharacters(String value) {
        try {
            xmlWriter.writeCharacters(value);
        } catch (XMLStreamException xe) {
            throw new IllegalStateException("got xml error while writing", xe);
        }
    }
    
    private void writeAttribute(String name, String value) {
        try {
            xmlWriter.writeAttribute(name.toLowerCase(), value);
        } catch (XMLStreamException xe) {
            throw new IllegalStateException("got xml error while writing", xe);
        }
    }
    
    private void writeEndElement() {
        try {
            xmlWriter.writeEndElement();
            xmlWriter.writeCharacters("\\r\\n");
        } catch (XMLStreamException xe) {
            throw new IllegalStateException("got xml error while writing", xe);
        }
    }

Finally, a sample program making use of this handler, along with the program output:

public final class Main {
    
    public static final String ICALSTREAM =
            "BEGIN:VCALENDAR\\r\\nPRODID:-//Sun/Sample//EN\\r\\nVERSION:2.0\\r\\n" + 
            "BEGIN:VEVENT\\r\\nUID:1\\r\\nDTSTAMP:20070313T082041Z\\r\\nDTSTART;VALUE=DATE:20081212\\r\\n" +
            "SUMMARY:wrapped \\r\\n" +
            " summary\\r\\n" +
            "END:VEVENT\\r\\nEND:VCALENDAR";
    /\*\*
     \* @param args the command line arguments
     \*/
    public static void main(String[] args) throws Exception {
        
        Reader reader = new UnfoldingReader(new StringReader(ICALSTREAM));
        
        CalendarParser parser = CalendarParserFactory.getInstance().createParser();
        
        StringWriter writer = new StringWriter();
        XMLStreamWriter xmlWriter = XMLOutputFactory.newInstance().createXMLStreamWriter(writer);
        XMLHandler handler = new XMLHandler(xmlWriter);
        xmlWriter.writeStartDocument();
        parser.parse(reader, handler);
        xmlWriter.writeEndDocument();
        xmlWriter.close();
        
        System.out.println("xml representation:" + writer.toString());
    }
}
 
java ical2xml.Main
xml representation:<?xml version="1.0" ?><vcalendar><prodid>-//Sun/Sample//EN</prodid>
<version>2.0</version>
<vevent><uid>1</uid>
<dtstamp>20070313T082041Z</dtstamp>
<dtstart value="DATE">20081212</dtstart>
<summary>wrapped summary</summary>
</vevent>
</vcalendar>

The generated XML is totally non standard (Calconnect is currently defining such a standard) and quite ugly but it shows how the parser can be used to do lightweight processing of an iCalendar stream without going through the intermediate step of creating a full ical4j Calendar object (which can be quite expensive in terms of CPU and memory).

The same technique could be used to generate a json output or to translate an iCalendar stream into a different object model.

Wednesday Nov 22, 2006

The Aquarium - Google Trends - ical - Google Calendar

 

Spending some time in The Aquarium has allowed me to discover among other things Google Trend and the IceRocket Trend Tool.

A search on "ical":


 

 led me to this interesting article on how to subscribe to a Google Calendar using iCal.

 I now need to try the same against Lightning.

Will Google implement CalDAV any day ?

About

arnaudq

Search

Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today
Bookmarks