]> www.infradead.org Git - users/dwmw2/ews-sync.git/commitdiff
Attempt to parse explicit <MeetingTimeZone>
authorDavid Woodhouse <dwmw2@infradead.org>
Sun, 18 Jul 2010 17:15:22 +0000 (18:15 +0100)
committerDavid Woodhouse <dwmw2@infradead.org>
Sun, 18 Jul 2010 17:15:22 +0000 (18:15 +0100)
ews2ical.c

index 42417285369fcdf746cf1f175bc330a19da09b5e..a9446404f5c4b58728aacf713ae22e23561fcb2b 100644 (file)
@@ -9,7 +9,9 @@
 #include <string.h>
 #include <ctype.h>
 #include <time.h>
+
 #include <libical/icaltime.h>
+#include <libical/icalduration.h>
 #include <libical/icaltimezone.h>
 FILE *calfile;
 
@@ -24,6 +26,7 @@ int process_subject(xmlNode *xml_node);
 int process_recurrence(xmlNode *xml_node);
 int process_itemid(xmlNode *xmlnode);
 icaltimezone *get_timezone(xmlNode *xmlnode);
+icaltimezone *get_meeting_timezone(xmlNode *xml_node);
 
 int main(int argc, char **argv)
 {
@@ -129,11 +132,13 @@ int main(int argc, char **argv)
        fprintf(calfile, "METHOD:PUBLISH\n");
        fprintf(calfile, "VERSION:2.0\n");
 
-       icaltz = get_timezone(xml_node);
+       icaltz = get_meeting_timezone(xml_node);
+       if (!icaltz)
+               icaltz = get_timezone(xml_node);
        if (icaltz) {
                icalcomponent *comp = icaltimezone_get_component(icaltz);
                char *vtz = icalcomponent_as_ical_string_r(comp);
-               printf("%s", vtz);
+               fprintf(calfile, "%s", vtz);
                free(vtz);
        }
        fprintf(calfile, "BEGIN:VEVENT\n");
@@ -243,36 +248,18 @@ int process_time(xmlNode *xml_node, const char *name, icaltimezone *zone)
 {
        char *ews_time = (char *)xmlNodeGetContent(xml_node);
        struct icaltimetype ical_time;
-       struct tm tm_time;
-       time_t t;
 
        if (!ews_time)
                return -1;
-       if (strlen(ews_time) != 20 || ews_time[4] != '-' ||
-           ews_time[7] != '-' || ews_time[10] != 'T' ||
-           ews_time[13] != ':' || ews_time[16] != ':' ||
-           ews_time[19] != 'Z') {
-               fprintf(stderr, "Failed to parse %s time '%s'\n", name, ews_time);
-               return -1;
-       }
-
-       memset(&tm_time, 0, sizeof(tm_time));
-       tm_time.tm_year = strtol(ews_time, NULL, 10);
-       tm_time.tm_mon  = strtol(ews_time + 5, NULL, 10) - 1;
-       tm_time.tm_mday = strtol(ews_time + 8, NULL, 10);
-       tm_time.tm_hour = strtol(ews_time + 11, NULL, 10);
-       tm_time.tm_min = strtol(ews_time + 14, NULL, 10);
-       tm_time.tm_sec = strtol(ews_time + 17, NULL, 10);
-
-       t = timegm(&tm_time);
-       ical_time = icaltime_from_timet_with_zone(t, 0, zone?:icaltimezone_get_utc_timezone());
-       ical_time.year -= 1900;
+       ical_time = icaltime_from_string(ews_time);
+       if (zone)
+               ical_time = icaltime_convert_to_zone(ical_time, zone);
 
        fprintf(calfile, "%s", name);
 
        if (zone)
-               fprintf(calfile, ";TZID=%s", icaltimezone_get_tzid(zone));
-       fprintf(calfile, ":%04d%02d%02d%02d%02d%02d%s\n",
+               fprintf(calfile, ";TZID=\"%s\"", icaltimezone_get_tzid(zone));
+       fprintf(calfile, ":%04d%02d%02dT%02d%02d%02d%s\n",
                ical_time.year, ical_time.month, ical_time.day,
                ical_time.hour, ical_time.minute, ical_time.second,
                zone?"":"Z");
@@ -356,6 +343,21 @@ static int month_to_number(const char *month)
        fprintf(stderr, "Unrecognised month name '%s'\n", month);
        return 0;
 }
+static int weekday_to_number(const char *day)
+{
+       static char *days[] = {
+               "Sunday", "Monday", "Tuesday", "Wednesday",
+               "Thursday", "Friday", "Saturday"
+       };
+       int daynr;
+       for (daynr = 0; daynr < 7; daynr++) {
+               if (!strcmp(day, days[daynr]))
+                       return daynr + 1;
+       }
+
+       fprintf(stderr, "Unrecognised day name '%s'\n", day);
+       return 0;
+}
 #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
 static const char *weekday_to_ical(const char *weekday)
 {
@@ -363,12 +365,13 @@ static const char *weekday_to_ical(const char *weekday)
                const char *exch;
                const char *ical;
        } table[] = {
-               { "Sunday", "SO" },
+               { "Sunday", "SU" },
                { "Monday", "MO" },
                { "Tuesday", "TU" },
                { "Wednesday", "WE" },
                { "Thursday", "TH" },
                { "Friday", "FR" },
+               { "Saturday", "SA" },
                { "Day", "SU,MO,TU,WE,TH,FR,SA" },
                { "Weekday", "MO,TU,WE,TH,FR" },
                { "WeekendDay", "SA,SU" },
@@ -456,8 +459,8 @@ int process_recurrence(xmlNode *xml_node)
                                        continue;
                                if (!strcmp((char *)xml_node2->name, "DaysOfWeek"))
                                        weekday = (char *)xmlNodeGetContent(xml_node2);
-                               else if (!strcmp((char *)xml_node2->name, "Interval"))
-                                       monthly_interval = (char *)xmlNodeGetContent(xml_node2);
+                               else if (!strcmp((char *)xml_node2->name, "Month"))
+                                       month = (char *)xmlNodeGetContent(xml_node2);
                                else if (!strcmp((char *)xml_node2->name, "DayOfWeekIndex"))
                                        week_index = (char *)xmlNodeGetContent(xml_node2);
                        }
@@ -721,3 +724,207 @@ icaltimezone *get_timezone(xmlNode *xml_node)
                ews_tzname);
        return NULL;
 }
+
+int get_baseoffset(xmlNode *xml_node, int *retval)
+{
+       const char *baseoffset;
+       struct icaldurationtype ofs;
+
+       for (xml_node = xml_node->children; xml_node; xml_node = xml_node->next) {
+               if (xml_node->type != XML_ELEMENT_NODE)
+                       continue;
+               else if (!strcmp((char *)xml_node->name, "BaseOffset"))
+                       break;
+       }
+       if (!xml_node) {
+               fprintf(stderr, "<MeetingTimeZone> has no <BaseOffset>\n");
+               return -1;
+       }
+       baseoffset = (const char *)xmlNodeGetContent(xml_node);
+       if (!baseoffset) {
+               fprintf(stderr, "<BaseOffset> is empty\n");
+               return -1;
+       }
+       ofs = icaldurationtype_from_string(baseoffset);
+       if (icaldurationtype_is_bad_duration(ofs)) {
+               fprintf(stderr, "Failed to parse <BaseOffset> '%s'\n", baseoffset);
+               return -1;
+       }
+       if (ofs.is_neg)
+               *retval = ofs.minutes * 60;
+       else
+               *retval = -ofs.minutes * 60;
+       return 0;
+}
+
+int process_relativeyearlyrecurrence(xmlNode *xml_node, struct icalrecurrencetype *ical_recur)
+{
+       const char *week = NULL, *month = NULL, *weekday = NULL;
+       int weeknr, monthnr, daynr;
+
+       for (xml_node = xml_node->children; xml_node; xml_node = xml_node->next) {
+               if (xml_node->type != XML_ELEMENT_NODE)
+                       continue;
+               if (!strcmp((char *)xml_node->name, "DaysOfWeek")) {
+                       weekday = (char *)xmlNodeGetContent(xml_node);
+               } else if (!strcmp((char *)xml_node->name, "Month")) {
+                       month = (char *)xmlNodeGetContent(xml_node);
+               } else if (!strcmp((char *)xml_node->name, "DayOfWeekIndex")) {
+                       week = (char *)xmlNodeGetContent(xml_node);
+               }
+       }
+       if (!week || !month || !weekday) {
+               fprintf(stderr, "RelativeWeeklyRecurrence missing essential fields (%s,%s,%s)\n",
+                       week, month, weekday);
+               return -1;
+       }
+       monthnr = month_to_number(month);
+       if (!month)
+               return -1;
+
+       weeknr = weekindex_to_ical(week);
+       if (!weeknr)
+               return -1;
+
+       daynr = weekday_to_number(weekday);
+       if (!daynr)
+               return -1;
+
+       icalrecurrencetype_clear(ical_recur);
+       ical_recur->freq = ICAL_YEARLY_RECURRENCE;
+       ical_recur->by_month[0] = monthnr;
+       if (weeknr > 0)
+               ical_recur->by_day[0] = daynr + (weeknr * 8);
+       else
+               ical_recur->by_day[0] = -8 - daynr;
+
+       return 0;
+}
+
+icalcomponent *process_timezone_rule(xmlNode *xml_node,
+                         icalcomponent_kind kind, int *offset)
+{
+       icalcomponent *comp = icalcomponent_new(kind);
+       char *tzname;
+       icalproperty *prop;
+
+       tzname = (char *)xmlGetProp(xml_node, (xmlChar *)"TimeZoneName");
+       if (tzname) {
+               prop = icalproperty_new_tzname(tzname);
+               icalcomponent_add_property(comp, prop);
+       }
+
+       for (xml_node = xml_node->children; xml_node; xml_node = xml_node->next) {
+               if (xml_node->type != XML_ELEMENT_NODE)
+                       continue;
+               else if (!strcmp((char *)xml_node->name, "Offset")) {
+                       char *ofs_string = (char *)xmlNodeGetContent(xml_node);
+                       struct icaldurationtype ofs = icaldurationtype_from_string(ofs_string);
+                       if (ofs.is_neg)
+                               *offset += ofs.minutes * 60;
+                       else
+                               *offset -= ofs.minutes * 60;
+
+                       prop = icalproperty_new_tzoffsetto(*offset);
+                       icalcomponent_add_property(comp, prop);
+               } else if (!strcmp((char *)xml_node->name, "RelativeYearlyRecurrence")) {
+                       struct icalrecurrencetype ical_recur;
+
+                       if (process_relativeyearlyrecurrence(xml_node, &ical_recur))
+                               return NULL;
+                       prop = icalproperty_new_rrule(ical_recur);
+                       icalcomponent_add_property(comp, prop);
+               } else if (!strcmp((char *)xml_node->name, "AbsoluteDate")) {
+                       /* Are there really timezones which change on the same date
+                          every year? */
+                       fprintf(stderr, "Don't know how to handle AbsoluteDate for timezone change: '%s'\n",
+                               xmlNodeGetContent(xml_node));
+               } else if (!strcmp((char *)xml_node->name, "Time")) {
+                       struct icaltimetype dtstart;
+                       char *time_string = (char *)xmlNodeGetContent(xml_node);
+
+                       if (strlen(time_string) != 8 || time_string[2] != ':' ||
+                           time_string[5] != ':') {
+                               fprintf(stderr, "Cannot parse dst change time '%s'\n",
+                                       time_string);
+                               return NULL;
+                       }
+                       memset(&dtstart, 0, sizeof(dtstart));
+                       dtstart.year = 1900;
+                       dtstart.month = 1;
+                       dtstart.day = 1;
+                       dtstart.hour = strtol(time_string, NULL, 10);
+                       dtstart.minute = strtol(time_string + 3, NULL, 10);
+                       dtstart.second = strtol(time_string + 6, NULL, 10);
+
+                       prop = icalproperty_new_dtstart(dtstart);
+                       icalcomponent_add_property(comp, prop);
+               }
+       }
+       return comp;
+}
+
+icaltimezone *get_meeting_timezone(xmlNode *xml_node)
+{
+       icalcomponent *comp = NULL, *dst_zone = NULL, *std_zone = NULL;
+       icalproperty *prop;
+       const char *tzname = NULL;
+
+       int std_offset, dst_offset;
+
+       for (xml_node = xml_node->children; xml_node; xml_node = xml_node->next) {
+               if (xml_node->type != XML_ELEMENT_NODE)
+                       continue;
+               else if (!strcmp((char *)xml_node->name, "MeetingTimeZone"))
+                       break;
+       }
+       if (!xml_node)
+               return NULL;
+       comp = icalcomponent_new(ICAL_VTIMEZONE_COMPONENT);
+       
+       tzname = (const char *)xmlGetProp(xml_node, (xmlChar *)"TimeZoneName");
+
+       prop = icalproperty_new_tzid(tzname);
+       icalcomponent_add_property(comp, prop);
+
+       if (get_baseoffset(xml_node, &std_offset))
+               return NULL;
+       dst_offset = std_offset;
+
+       for (xml_node = xml_node->children; xml_node; xml_node = xml_node->next) {
+               if (xml_node->type != XML_ELEMENT_NODE)
+                       continue;
+               if (!strcmp((char *)xml_node->name, "BaseOffset"))
+                       continue;
+               else if (!strcmp((char *)xml_node->name, "Standard")) {
+                       std_zone = process_timezone_rule(xml_node,
+                                                        ICAL_XSTANDARD_COMPONENT,
+                                                        &std_offset);
+               } else if (!strcmp((char *)xml_node->name, "Daylight")) {
+                       dst_zone = process_timezone_rule(xml_node,
+                                                        ICAL_XDAYLIGHT_COMPONENT,
+                                                        &dst_offset);
+               } else {
+                       fprintf(stderr, "Unknown element in MeetingTimeZone: %s\n",
+                               xml_node->name);
+               }
+       }
+
+       if (std_zone && dst_zone) {
+               icaltimezone *z;
+
+               prop = icalproperty_new_tzoffsetfrom(dst_offset);
+               icalcomponent_add_property(std_zone, prop);
+
+               prop = icalproperty_new_tzoffsetfrom(std_offset);
+               icalcomponent_add_property(dst_zone, prop);
+
+               icalcomponent_add_component(comp, std_zone);
+               icalcomponent_add_component(comp, dst_zone);
+
+               z = icaltimezone_new();
+               icaltimezone_set_component(z, comp);
+               return z;
+       }
+       return NULL;
+}