From: David Woodhouse Date: Sun, 18 Jul 2010 17:15:22 +0000 (+0100) Subject: Attempt to parse explicit X-Git-Url: https://www.infradead.org/git/?a=commitdiff_plain;h=95f3c5b276fafc993f3c7c9d0a4ca3576a7833e1;p=users%2Fdwmw2%2Fews-sync.git Attempt to parse explicit --- diff --git a/ews2ical.c b/ews2ical.c index 4241728..a944640 100644 --- a/ews2ical.c +++ b/ews2ical.c @@ -9,7 +9,9 @@ #include #include #include + #include +#include #include 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, " has no \n"); + return -1; + } + baseoffset = (const char *)xmlNodeGetContent(xml_node); + if (!baseoffset) { + fprintf(stderr, " is empty\n"); + return -1; + } + ofs = icaldurationtype_from_string(baseoffset); + if (icaldurationtype_is_bad_duration(ofs)) { + fprintf(stderr, "Failed to parse '%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; +}