Converting ICalendar to XML

I've started working on a CalDAV implementation, which also requires analysis of ICalendar (rfc 2554) objects.

ICalendar objects have properties, components (such as VEVENT, VTODO) and attributes. This is awfully familiar to XML. So instead of trying to come up with a complicated parser and object structure, I decided to just convert it to XML and use PHP's simplexml.

This is my current script:

  1. <?php
  2.  
  3. function iCalendarToXML($icalendarData) {
  4.  
  5.     // Detecting line endings
  6.     if (strpos($icalendarData,"\r\n")) $lb = "\r\n";
  7.     elseif (strpos($icalendarData,"\n")) $lb = "\n";
  8.     else $lb = "\r\n";
  9.  
  10.     // Splitting up items per line
  11.     $lines = explode($lb,$icalendarData);
  12.  
  13.     // Properties can be folded over 2 lines. In this case the second
  14.     // line will be preceeded by a space or tab.
  15.     $lines2 = array();
  16.     foreach($lines as $line) {
  17.  
  18.         if ($line[0]==" " || $line[0]=="\t") {
  19.             $lines2[count($lines2)-1].=substr($line,1);
  20.             continue;
  21.         }
  22.  
  23.         $lines2[]=$line;
  24.  
  25.     }
  26.  
  27.     $xml = '<?xml version="1.0"?>' . "\n";
  28.  
  29.     $spaces = 0;
  30.     foreach($lines2 as $line) {
  31.  
  32.         $matches = array();
  33.         // This matches PROPERTYNAME;ATTRIBUTES:VALUE
  34.         if (preg_match('/^([^:^;]*)(?:;([^:]*))?:(.*)$/',$line,$matches)) {
  35.             $propertyName = strtoupper($matches[1]);
  36.             $attributes = $matches[2];
  37.             $value = $matches[3];
  38.  
  39.             // If the line was in the format BEGIN:COMPONENT or END:COMPONENT, we need to special case it.
  40.             if ($propertyName == 'BEGIN') {
  41.                 $xml.=str_repeat(" ",$spaces);
  42.                 $xml.='<' . strtoupper($value) . ">\n";
  43.                 $spaces+=2;
  44.                 continue;
  45.             } elseif ($propertyName == 'END') {
  46.                 $spaces-=2;
  47.                 $xml.=str_repeat(" ",$spaces);
  48.                 $xml.='</' . strtoupper($value) . ">\n";
  49.                 continue;
  50.             }
  51.  
  52.             $xml.=str_repeat(" ",$spaces);
  53.             $xml.='<' . $propertyName;
  54.             if ($attributes) {
  55.                 // There can be multiple attributes
  56.                 $attributes = explode(';',$attributes);
  57.                 foreach($attributes as $att) {
  58.                    
  59.                     list($attName,$attValue) = explode('=',$att,2);
  60.                     $xml.=' ' . $attName . '="' . htmlspecialchars($attValue) . '"';
  61.  
  62.                 }
  63.             }
  64.  
  65.             $xml.='>'. htmlspecialchars($value) . '</' . $propertyName . ">\n";
  66.          
  67.         }
  68.  
  69.     }
  70.  
  71.     return $xml;
  72.  
  73. }
  74.  
  75. ?>

This will convert:

  1. BEGIN:VCALENDAR
  2. VERSION:2.0
  3. PRODID:-//Example Corp.//CalDAV Client//EN
  4. BEGIN:VTIMEZONE
  5. LAST-MODIFIED:20040110T032845Z
  6. TZID:US/Eastern
  7. BEGIN:DAYLIGHT
  8. DTSTART:20000404T020000
  9. RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4
  10. TZNAME:EDT
  11. TZOFFSETFROM:-0500
  12. TZOFFSETTO:-0400
  13. END:DAYLIGHT
  14. BEGIN:STANDARD
  15. DTSTART:20001026T020000
  16. RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
  17. TZNAME:EST
  18. TZOFFSETFROM:-0400
  19. TZOFFSETTO:-0500
  20. END:STANDARD
  21. END:VTIMEZONE
  22. BEGIN:VEVENT
  23. DESCRIPTION:Hello Im evert
  24.  Next line also
  25.   Blabla
  26. ATTENDEE;PARTSTAT=ACCEPTED;ROLE=CHAIR:mailto:cyrus@example.com
  27. ATTENDEE;PARTSTAT=NEEDS-ACTION:mailto:lisa@example.com
  28. DTSTAMP:20060206T001220Z
  29. DTSTART;TZID=US/Eastern:20060104T100000
  30. DURATION:PT1H
  31. LAST-MODIFIED:20060206T001330Z
  32. ORGANIZER:mailto:cyrus@example.com
  33. SEQUENCE:1
  34. STATUS:TENTATIVE
  35. SUMMARY:Event #3
  36. UID:DC6C50A017428C5216A2F1CD@example.com
  37. X-ABC-GUID:E1CX5Dr-0007ym-Hz@example.com
  38. END:VEVENT
  39. END:VCALENDAR

To:

  1. <?xml version="1.0"?>
  2. <VCALENDAR>
  3.   <VERSION>2.0</VERSION>
  4.   <PRODID>-//Example Corp.//CalDAV Client//EN</PRODID>
  5.   <VTIMEZONE>
  6.     <LAST-MODIFIED>20040110T032845Z</LAST-MODIFIED>
  7.     <TZID>US/Eastern</TZID>
  8.     <DAYLIGHT>
  9.       <DTSTART>20000404T020000</DTSTART>
  10.       <RRULE>FREQ=YEARLY;BYDAY=1SU;BYMONTH=4</RRULE>
  11.       <TZNAME>EDT</TZNAME>
  12.       <TZOFFSETFROM>-0500</TZOFFSETFROM>
  13.       <TZOFFSETTO>-0400</TZOFFSETTO>
  14.     </DAYLIGHT>
  15.     <STANDARD>
  16.       <DTSTART>20001026T020000</DTSTART>
  17.       <RRULE>FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10</RRULE>
  18.       <TZNAME>EST</TZNAME>
  19.       <TZOFFSETFROM>-0400</TZOFFSETFROM>
  20.       <TZOFFSETTO>-0500</TZOFFSETTO>
  21.     </STANDARD>
  22.   </VTIMEZONE>
  23.   <VEVENT>
  24.     <DESCRIPTION>Hello Im evertNext line also Blabla</DESCRIPTION>
  25.     <ATTENDEE PARTSTAT="ACCEPTED" ROLE="CHAIR">mailto:cyrus@example.com</ATTENDEE>
  26.     <ATTENDEE PARTSTAT="NEEDS-ACTION">mailto:lisa@example.com</ATTENDEE>
  27.     <DTSTAMP>20060206T001220Z</DTSTAMP>
  28.     <DTSTART TZID="US/Eastern">20060104T100000</DTSTART>
  29.     <DURATION>PT1H</DURATION>
  30.     <LAST-MODIFIED>20060206T001330Z</LAST-MODIFIED>
  31.     <ORGANIZER>mailto:cyrus@example.com</ORGANIZER>
  32.     <SEQUENCE>1</SEQUENCE>
  33.     <STATUS>TENTATIVE</STATUS>
  34.     <SUMMARY>Event #3</SUMMARY>
  35.     <UID>DC6C50A017428C5216A2F1CD@example.com</UID>
  36.     <X-ABC-GUID>E1CX5Dr-0007ym-Hz@example.com</X-ABC-GUID>
  37.   </VEVENT>
  38. </VCALENDAR>

I hope this is useful to anyone else.


2 Responses to Converting ICalendar to XML

  1. 982 Jan Schneider 2009-09-05 9:30 pm

    Did you see http://tools.ietf.org/html/draft-royer-calsch-xcal-03?

  2. 983 Evert 2009-09-10 4:18 pm

    Sorry for the late response. I saw this after I made this. Will post an update soon.

Leave a Reply



About

My name is Evert, and I've been writing semi-regularly on this blog since 2006.

I'm currently available for contract work.

more info.

Subscribe

Dropbox

Dropbox is a simple cross-platform online backup and sync application. The first 2GB of space is free, and both you and me get an extra 250MB extra space if you sign up through this link.