Home
Personal
Unix
Programming
Pascal
Perl
ICal
Songbook
PHP
C
SourceForge, FreshMeat
Networking
Documents
Reporting
Weblog
CityRail
BOM pictures
Other projects
Contact me
               
   

EXCHANGE COMPATIBLE ICAL INVITATIONS

While processing the call logfile from our PABX, one of the output formats is an email to the caller with information about the call such as length, called number, when it took place and the cost. With the standardisation of the ICal calendaring standard we can also integrate it directly into the calendars and integrated email calendar systems like Exchange.

One click with the mouse (most people manage to be able to do that), and the call is in your calendar. What more do you want!

What we need to do here are three items:

  1. Craft an email which Exchange recognizes as a valid calendar appointment. raft an ICal file which Exchange recognizes as a valid calendar appointment.
  2. See how much it then still works with non-Exchange systems.

Create the email

Since email is the transport layer over which the ICal files are transported, that is our first step: Create an email with a known to be valid ICal file, so that it gets recognized by Exchange as a valid calendar appointment.

Testing, what works and what doesn't work

Test 1

Create an appointment in Outlook and send out to an outside Exchange account. Then bounce it back and see what happens.

This is successful. It gets a calendar icon and has the nice "Accept, Decline, Whatever" buttons at the top. Yay!

Test 2

Create an appointment in Outlook and send out to an outside Exchange account. Save the attachment and send it back with our MUA and see what happens.

Now it starts to get interesting. Instead of having a calendar icon, it has an email icon. It doesn't have the nice "Accept, Decline, Whatever" buttons at the top. But the ICal attachment is there. And when you click on it, it shows up with "This meeting is not in the calendar." and the "Accept, Decline, Whatever" buttons at the top are grayed out. Not good. Very bad actually. Let's see what more options we have.

The attachment is called "meeting.ics".

Test 3

Create an appointment in Outlook, and send out to an outside Exchange account with the "Forward as iCalendar" option. Bounce it back and see what happens.

The email has an email icon again, without the nice buttons. When clicking on the ICal attachment, it shows up as an appointment with a "Save and close" button.

The attachment is called "subject.ics", after the summary which was "subject".

Test 4

To complete the tests, we save the attachment from test 3, send it back with our MUA and see what happens.

The ICal attachment is there again, no "Accept, Decline, Whatever" icons. And when you click on it, it shows up with "This meeting is not in the calendar." and the "Accept, Decline, Whatever" buttons at the top are grayed out. Again. What is different?

Additional data

The messages received from the Exchange server
Test 1 and 2 Test 3 and 4
Message header
Content-type: multipart/alternative multipart/mixed
Content-class: urn:content-classes:calendarmessage urn:content-classes:message
Message body
Content-type of .ics attachment: text/calendar
method=REQUEST
application/octet-stream
(base64 encoded)
The messages sent from our MUA:
Test 1 and 2 Test 3 and 4
Message header
Content-type: multipart/mixed multipart/mixed
Message body
Content-type of .ics attachment: text/calendar text/calendar
Content-Disposition of .ics file: attachment attachment

This sounds like it needs a little bit more manipulation than the normal "let's shoot this attachment and forget about the test".

Bring out the Swiss Army Knife

With the working attachment from test 3, Perl and MIME::Lite should be able to do the job:

	#!/usr/bin/perl -w

	use strict;
	use MIME::Lite;

	my $msg = MIME::Lite->new(
		    From    =>'[email protected]';
		    To      =>'[email protected]',
		    Subject =>'Join me!',
		    Type    =>'multipart/mixed',
		    );
	$msg->attach(
		    Type    => "TEXT",
		    Data    => "foo bar quux",
		    );

	my $part = MIME::Lite->new(
		    Type	=> "application/octet-stream",
		    Filename	=> "subject.ics",
		    Path	=> "subject.ics",
		    Encoding	=> 'base64',
		    Disposition	=> 'attachment',
		    );
	$part->scrub(['date', 'x-mailer', 'mime-version']);
	$part->attr(
		    'content-description' => "subject.ics",
		    );
	$msg->attach($part);

	$msg->send;

That was easy, this was how test 3 behaved. Now see if we can get it as in test 1.

	#!/usr/bin/perl -w

	use strict;
	use MIME::Lite;

	my $msg = MIME::Lite->new(
		    From    =>'[email protected]',
		    To      =>'[email protected]',
		    Subject =>'Join me!',
		    Type    =>'multipart/alternative'
		    );
	$msg->attr(
		    'content-class' => 'urn:content-classes:calendarmessage'
		   );
	$msg->attach(
		    Type => "TEXT",
		    Data => "foo bar quux"
		    );

	my $part = MIME::Lite->new(
		    Type	=> "text/calendar; method=REQUEST; name=\"subject.ics\"",
		    Filename    => "subject.ics",
		    Path	=> "subject.ics",
		    Encoding    => "8bit",
		    );
	$part->scrub(['date', 'x-mailer', 'mime-version']);
	$part->attr(
		    'content-class' => 'urn:content-classes:calendarmessage',
		    'content-description' => "subject.ics",
		    );
	$part->replace(
		    'content-disposition' => "",
		    );
	$msg->attach($part);

	$msg->send;

Et voila! A beautiful "Accept, Decline, Whatever" button bar appears above the email. It doesn't get the first price in the beauty-contest, but it works.

Create the ICal files

ICal files are normal text files (so no XML or binary stuff) being key:value pairs. Timestamps are in YYYMMDDxHHMMSSz format (x being a T, z being Z). As for everything else, there is a Perl module available for creating this format: Data::ICal.

A Calendar consist of Events and Alarms. Alarms shouldn't be necessary for this. Looking at the ICal file the Exchange server sends (and accepts in the previous test), it doesn't look like there is much Exchange specific magic in it:

	BEGIN:VCALENDAR
	PRODID:-//Microsoft Corporation//Outlook 11.0 MIMEDIR//EN
	VERSION:2.0
	METHOD:PUBLISH
	BEGIN:VEVENT
	ORGANIZER:MAILTO:[email protected]
	DTSTART:20060306T050000Z
	DTEND:20060306T053000Z
	LOCATION:location
	TRANSP:OPAQUE
	SEQUENCE:0
	UID:040000008200E00074C5B7101A82E0080000000020DC2A139243C601000000000000000
	 00000DEF81CB498E1594B90378312C3D6551D
	DTSTAMP:20060309T045649Z
	DESCRIPTION:FreeFormText.\nMore FreeFormText.\n\n
	SUMMARY:subject
	PRIORITY:5
	X-MICROSOFT-CDO-IMPORTANCE:1
	CLASS:PUBLIC
	END:VEVENT
	END:VCALENDAR

No idea what the UID is for, but the rest is very easy to make.

The first attempt to create this resulted in a email which showed up in Exchange as a MIME encoding file, but in Outlook the icon was a normal email icon (so no Calendar or attachment icon).

The second attempt had all fields except for UID and X-MICROSOFT-CDO-IMPORTANCE. Still no luck.

The third attempt had all fields except for the X-MICROSOFT-CDO-IMPORTANCE. Yay! The "Accept, Decline, Whatever" button bar was there again.

	#!/usr/bin/perl -w

	use strict;

	use Date::ICal;
	use Data::ICal;
	use Data::ICal::Entry::Event;


	my $calendar = Data::ICal->new();
	$calendar->add_properties(
            method      => "PUBLISH",
	);
	my $event = Data::ICal::Entry::Event->new();
	$event->add_properties(
		summary     => "Subject",
		description => "FreeFormText.\\nMore FreeFormText.\\n\\n",
		dtstart     => Date::ICal->new( epoch => time )->ical,
		dtend       => Date::ICal->new( epoch => time+3 )->ical,
		dtstamp     => Date::ICal->new( epoch => time )->ical,
		class       => "PUBLIC",
		organizer   => "MAILTO:foo\@bar",
		location    => "Phone call",
		priority    => 5,
		transp      => "OPAQUE",
		sequence    => 0,
		uid         => "123",
	);

	$calendar->add_entry($event);
	print $calendar->as_string;

Other Mail / Calendar applications

Now the final test: How does it interact with other Mail and Calendar applications?

Apples iCalendar

I've only tried Apples iCalendar. And of course it didn't recognize it. The difference between iCalenbdar and Exchange, besides some timezone information, was that iCalendar used the method PUBLISH instead of REQUEST. Using REQUEST, it just brings up the calendar, while using PUBLISH, it imported it. Using PUBLISH with Exchange works fine.

       
               
               

$Id: pascal.php,v 1.5 2002/01/05 06:41:36 mavetju Exp $