CFLAGS_ews_syncfolder.o := $(SOUP_CFLAGS)
CFLAGS_ews_autodiscover.o := $(SOUP_CFLAGS)
CFLAGS_ews2ical.o := $(ICAL_CFLAGS)
+CFLAGS_calitem_to_ical.o := $(ICAL_CFLAGS)
%.o: %.c
$(CC) -c -o $@ $(CFLAGS) $(CFLAGS_$@) $< -MD -MF .$@.dep
clean:
rm -f $(PROGS) *.o .*.o.dep
-ews2ical: ews2ical.o
- $(CC) -o $@ $< $(LDFLAGS) $(XML2_LDFLAGS) $(ICAL_LDFLAGS) $(GLIB_LDFLAGS)
+ews2ical: ews2ical.o calitem_to_ical.o
+ $(CC) -o $@ $^ $(LDFLAGS) $(XML2_LDFLAGS) $(ICAL_LDFLAGS) $(GLIB_LDFLAGS)
ews_autodiscover: ews_autodiscover.o
- $(CC) -o $@ $< $(LDFLAGS) $(XML2_LDFLAGS) $(SOUP_LDFLAGS) $(GLIB_LDFLAGS)
+ $(CC) -o $@ $^ $(LDFLAGS) $(XML2_LDFLAGS) $(SOUP_LDFLAGS) $(GLIB_LDFLAGS)
-ews_syncfolder: ews_syncfolder.o
- $(CC) -o $@ $< $(LDFLAGS) $(XML2_LDFLAGS) $(SOUP_LDFLAGS) $(GLIB_LDFLAGS)
+ews_syncfolder: ews_syncfolder.o calitem_to_ical.o
+ $(CC) -o $@ $^ $(LDFLAGS) $(XML2_LDFLAGS) $(SOUP_LDFLAGS) $(GLIB_LDFLAGS) $(ICAL_LDFLAGS)
--- /dev/null
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+#include <time.h>
+
+#include <libical/icaltime.h>
+#include <libical/icalduration.h>
+#include <libical/icaltimezone.h>
+
+#include <glib.h>
+#include <glib/gprintf.h>
+
+FILE *calfile;
+
+int process_relativeyearlyrecurrence(xmlNode *xml_node, struct icalrecurrencetype *ical_recur);
+int process_absoluteyearlyrecurrence(xmlNode *xml_node, struct icalrecurrencetype *ical_recur);
+int process_relativemonthlyrecurrence(xmlNode *xml_node, struct icalrecurrencetype *ical_recur);
+int process_absolutemonthlyrecurrence(xmlNode *xml_node, struct icalrecurrencetype *ical_recur);
+int process_weeklyrecurrence(xmlNode *xml_node, struct icalrecurrencetype *ical_recur);
+int process_dailyrecurrence(xmlNode *xml_node, struct icalrecurrencetype *ical_recur);
+
+int process_organizer(icalcomponent *comp, xmlNode *xml_node);
+int process_required_attendees(icalcomponent *comp, xmlNode *xml_node);
+int process_optional_attendees(icalcomponent *comp, xmlNode *xml_node);
+int process_time(icalcomponent *comp, xmlNode *xml_node, icaltimetype *ical_time);
+int process_truefalse(icalcomponent *comp, xmlNode *xml_node, gboolean *val);
+int process_location(icalcomponent *comp, xmlNode *xml_node);
+int process_body(icalcomponent *comp, xmlNode *xml_node);
+int process_subject(icalcomponent *comp, xmlNode *xml_node);
+int process_recurrence(icalcomponent *comp, xmlNode *xml_node, icaltimezone *zone);
+int process_itemid(icalcomponent *comp, xmlNode *xmlnode);
+int process_reminder_mins(icalcomponent *comp, xmlNode *xmlnode);
+icaltimezone *get_timezone(xmlNode *xmlnode);
+icaltimezone *get_meeting_timezone(xmlNode *xml_node);
+
+icalcomponent *ews_calitem_to_ical(xmlNode *xml_node);
+
+icalcomponent *ews_calitem_to_ical(xmlNode *xml_node)
+{
+ icaltimetype dtstart, dtend;
+ icaltimezone *icaltz;
+ icalcomponent *comp, *calcomp;
+ icalproperty *prop;
+ gboolean allday = FALSE;
+
+ dtstart = dtend = icaltime_null_time();
+
+ calcomp = icalcomponent_new_vcalendar();
+ icalcomponent_set_method(calcomp, ICAL_METHOD_PUBLISH);
+ prop = icalproperty_new_version("2.0");
+ icalcomponent_add_property(calcomp, prop);
+
+ icaltz = get_meeting_timezone(xml_node);
+ if (!icaltz)
+ icaltz = get_timezone(xml_node);
+ if (icaltz) {
+ icalcomponent *comp = icaltimezone_get_component(icaltz);
+ icalcomponent_add_component(calcomp, comp);
+ }
+ comp = icalcomponent_new(ICAL_VEVENT_COMPONENT);
+
+ 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, "Organizer"))
+ process_organizer(comp, xml_node);
+ else if (!strcmp((char *)xml_node->name, "RequiredAttendees"))
+ process_required_attendees(comp, xml_node);
+ else if (!strcmp((char *)xml_node->name, "OptionalAttendees"))
+ process_optional_attendees(comp, xml_node);
+ else if (!strcmp((char *)xml_node->name, "Start"))
+ process_time(comp, xml_node, &dtstart);
+ else if (!strcmp((char *)xml_node->name, "End"))
+ process_time(comp, xml_node, &dtend);
+ else if (!strcmp((char *)xml_node->name, "Body"))
+ process_body(comp, xml_node);
+ else if (!strcmp((char *)xml_node->name, "Location"))
+ process_location(comp, xml_node);
+ else if (!strcmp((char *)xml_node->name, "Subject"))
+ process_subject(comp, xml_node);
+ else if (!strcmp((char *)xml_node->name, "Recurrence"))
+ process_recurrence(comp, xml_node, icaltz);
+ else if (!strcmp((char *)xml_node->name, "ItemId"))
+ process_itemid(comp, xml_node);
+ else if (!strcmp((char *)xml_node->name, "IsAllDayEvent"))
+ process_truefalse(comp, xml_node, &allday);
+ else if (!strcmp((char *)xml_node->name, "ReminderMinutesBeforeStart"))
+ process_reminder_mins(comp, xml_node);
+ else if (!strcmp((char *)xml_node->name, "ParentFolderId") ||
+ !strcmp((char *)xml_node->name, "DateTimeReceived") ||
+ !strcmp((char *)xml_node->name, "Size") ||
+ !strcmp((char *)xml_node->name, "IsSubmitted") ||
+ !strcmp((char *)xml_node->name, "IsDraft") ||
+ !strcmp((char *)xml_node->name, "IsFromMe") ||
+ !strcmp((char *)xml_node->name, "IsResend") ||
+ !strcmp((char *)xml_node->name, "IsUnmodified") ||
+ !strcmp((char *)xml_node->name, "DateTimeSent") ||
+ !strcmp((char *)xml_node->name, "DateTimeCreated") ||
+ !strcmp((char *)xml_node->name, "ResponseObjects") ||
+ !strcmp((char *)xml_node->name, "DisplayCc") ||
+ !strcmp((char *)xml_node->name, "DisplayTo") ||
+ !strcmp((char *)xml_node->name, "Culture") ||
+ !strcmp((char *)xml_node->name, "IsRecurring") ||
+ !strcmp((char *)xml_node->name, "MeetingRequestWasSent") ||
+ !strcmp((char *)xml_node->name, "IsResponseRequested") ||
+ !strcmp((char *)xml_node->name, "MyResponseType") ||
+ !strcmp((char *)xml_node->name, "ConflictingMeetingCount") ||
+ !strcmp((char *)xml_node->name, "AdjacentMeetingCount") ||
+ !strcmp((char *)xml_node->name, "TimeZone") ||
+ !strcmp((char *)xml_node->name, "AppointmentSequenceNumber") ||
+ !strcmp((char *)xml_node->name, "AppointmentState")) {
+ /* Ignore these */
+ }
+#if 0
+ else
+ fprintf(stderr, "Unhandled node type '%s'\n", xml_node->name);
+#endif
+ }
+
+ /* We don't handle really floating events -- which change their time
+ according to the time zone of the observer (like lunch at noon
+ under the sundial wherever you are in the world). But that's OK;
+ Exchange doesn't seem to either:
+ - AFAICT, you can't create them with Outlook.
+ - If you send Exchange an invitation with floating times, which
+ happens to have a VTIMEZONE, it'll assume that timezone (in
+ violation of RFC2445).
+ - If you send Exchange an invitation with floating times with
+ *no* stray VTIMEZONE in the file (which was a mistake), then
+ it creates an item with no timezone but does weird things --
+ this invite:
+ DTSTART:20100720T120000
+ DTEND:20100720T120010
+ ... leads to an Exchange object saying...
+ <t:Start>2010-07-20T11:00:00Z</t:Start>
+ <t:End>2010-07-20T11:00:00Z</t:End>
+
+ For any recurring object without time zones (including the last
+ test above, as well as all day events such as birthdays created
+ with Outlook/Exchange 2003, Exchange will refuse to return the
+ object if the <Recurrence> field is requested, reporting
+ 'Corrupt Data'.
+
+ Tested with Exchange 2007.
+ */
+ if (icaltz && !allday) {
+ dtstart = icaltime_convert_to_zone(dtstart, icaltz);
+ dtend = icaltime_convert_to_zone(dtend, icaltz);
+ }
+ if (allday) {
+ dtstart.is_date = 1;
+ dtend.is_date = 1;
+ }
+ if (!icaltime_is_null_time(dtstart))
+ icalcomponent_set_dtstart(comp, dtstart);
+
+ if (!icaltime_is_null_time(dtend))
+ icalcomponent_set_dtend(comp, dtend);
+
+ icalcomponent_add_component(calcomp, comp);
+ return calcomp;
+}
+
+int process_mailbox(xmlNode *xml_node, const char **r_name, const char **r_email)
+{
+ const char *type = NULL, *name = NULL, *email = NULL;
+
+ 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, "Name"))
+ name = (char *)xmlNodeGetContent(xml_node);
+ if (!strcmp((char *)xml_node->name, "EmailAddress"))
+ email = (char *)xmlNodeGetContent(xml_node);
+ if (!strcmp((char *)xml_node->name, "RoutingType")) {
+ type = (char *)xmlNodeGetContent(xml_node);
+ }
+ }
+
+ /* We seem to get EX routing for people who don't exist any more */
+ if (type && strcmp(type, "SMTP")) {
+ if (strcmp(type, "EX"))
+ fprintf(stderr, "Unknown RoutingType '%s' ('%s' '%s')\n",
+ type, name, email);
+ return -1;
+ }
+ *r_name = name;
+ *r_email = email;
+ return 0;
+}
+
+int process_organizer(icalcomponent *comp, xmlNode *xml_node)
+{
+ icalproperty *prop;
+ icalparameter *param;
+
+ 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, "Mailbox")) {
+ const char *name = NULL, *email = NULL;
+ char *mailtoname;
+ if (process_mailbox(xml_node, &name, &email))
+ return -1;
+
+ mailtoname = g_strdup_printf("mailto:%s", email);
+
+ prop = icalproperty_new_organizer(mailtoname);
+ free(mailtoname);
+ param = icalparameter_new_cn(name);
+ icalproperty_add_parameter(prop, param);
+ icalcomponent_add_property(comp, prop);
+ }
+ }
+ return 0;
+}
+
+int process_attendee(icalcomponent *comp, xmlNode *xml_node, icalparameter_role role)
+{
+ icalproperty *prop;
+ icalparameter *param;
+
+ 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, "Mailbox")) {
+ const char *name = NULL, *email = NULL;
+ char *mailtoname;
+ if (process_mailbox(xml_node, &name, &email))
+ return -1;
+
+ mailtoname = g_strdup_printf("mailto:%s", email);
+
+ prop = icalproperty_new_attendee(mailtoname);
+ free(mailtoname);
+ param = icalparameter_new_cn(name);
+ icalproperty_add_parameter(prop, param);
+ param = icalparameter_new_role(role);
+ icalproperty_add_parameter(prop, param);
+ icalcomponent_add_property(comp, prop);
+ }
+ }
+ return 0;
+}
+
+int process_required_attendees(icalcomponent *comp, xmlNode *xml_node)
+{
+ 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, "Attendee")) {
+ if (process_attendee(comp, xml_node,
+ ICAL_ROLE_REQPARTICIPANT))
+ { }
+ }
+ }
+ return 0;
+}
+
+int process_optional_attendees(icalcomponent *comp, xmlNode *xml_node)
+{
+ 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, "Attendee")) {
+ if (process_attendee(comp, xml_node,
+ ICAL_ROLE_OPTPARTICIPANT))
+ { }
+ }
+ }
+ return 0;
+}
+
+int process_time(icalcomponent *comp, xmlNode *xml_node, icaltimetype *ical_time)
+{
+ char *ews_time = (char *)xmlNodeGetContent(xml_node);
+
+ if (!ews_time)
+ return -1;
+ *ical_time = icaltime_from_string(ews_time);
+ return 0;
+}
+
+int process_truefalse(icalcomponent *comp, xmlNode *xml_node, gboolean *val)
+{
+ char *truth = (char *)xmlNodeGetContent(xml_node);
+
+ if (!truth)
+ return -1;
+ if (!strcmp(truth, "true"))
+ *val = TRUE;
+ else if (!strcmp(truth, "false"))
+ *val = FALSE;
+ else {
+ fprintf(stderr, "Unrecognised truth value '%s' in %s node\n",
+ truth, xml_node->name);
+ return -1;
+ }
+ return 0;
+}
+
+int process_location (icalcomponent *comp, xmlNode *xml_node)
+{
+ const char *loc = (char *)xmlNodeGetContent(xml_node);
+
+ if (!loc)
+ return -1;
+ icalcomponent_set_location(comp, loc);
+ return 0;
+}
+
+int process_body(icalcomponent *comp, xmlNode *xml_node)
+{
+ const char *body = (char *)xmlNodeGetContent(xml_node);
+
+ if (!body)
+ return -1;
+
+ if (!strncasecmp(body, "<html", 5)) {
+#if 0 /* XX: libical doesn't seem to escape this properly */
+ icalproperty *prop;
+ icalparameter *param;
+
+ prop = icalproperty_new_x(body);
+ icalproperty_set_x_name(prop, "X-ALT-DESC");
+
+ param = icalparameter_new_fmttype("text/html");
+ icalproperty_add_parameter(prop, param);
+ icalcomponent_add_property(comp, prop);
+#endif
+ /* FIXME: html2text */
+ icalcomponent_set_description(comp, body);
+ } else {
+ icalcomponent_set_description(comp, body);
+ }
+
+
+ return 0;
+}
+
+int process_subject(icalcomponent *comp, xmlNode *xml_node)
+{
+ const char *subject = (char *)xmlNodeGetContent(xml_node);
+
+ if (!subject)
+ return -1;
+
+ icalcomponent_set_summary(comp, subject);
+ return 0;
+}
+
+
+static int month_to_number(const char *month)
+{
+ static char *months[] = {
+ "January", "February", "March", "April", "May", "June", "July",
+ "August", "September", "October", "November", "December"
+ };
+ int monthnr;
+ for (monthnr = 0; monthnr < 12; monthnr++) {
+ if (!strcmp(month, months[monthnr]))
+ return monthnr + 1;
+ }
+
+ fprintf(stderr, "Unrecognised month name '%s'\n", month);
+ return 0;
+}
+static int weekday_to_number(const char *day, int accept)
+{
+ static char *days[] = {
+ "Sunday", "Monday", "Tuesday", "Wednesday",
+ "Thursday", "Friday", "Saturday",
+ "Day", "Weekday", "WeekendDay"
+ };
+ int daynr;
+ for (daynr = 0; daynr < accept; 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 int weekindex_to_ical(const char *week)
+{
+ static struct {
+ char *exch;
+ int week;
+ } table[] = {
+ { "First", 1 },
+ { "Second", 2 },
+ { "Third", 3 },
+ { "Fourth", 4 },
+ { "Last", -1 }
+ };
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(table); i++) {
+ if (!strcmp(week, table[i].exch))
+ return table[i].week;
+ }
+ fprintf(stderr, "Unrecognised DayOfWeekIndex '%s'\n", week);
+ return 0;
+}
+
+int process_recurrence(icalcomponent *comp, xmlNode *xml_node, icaltimezone *zone)
+{
+ struct icalrecurrencetype ical_recur;
+ char *end_date = NULL, *nr_occurrences = NULL;
+ icalproperty *prop;
+ xmlNode *xml_node2;
+
+ ical_recur.freq = ICAL_NO_RECURRENCE;
+ if (!zone)
+ fprintf(stderr, "Recurrence with no recognised TimeZone. Hope this is an all-day event\n");
+
+ 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, "WeeklyRecurrence")) {
+ if (process_weeklyrecurrence(xml_node, &ical_recur))
+ return -1;
+ } else if (!strcmp((char *)xml_node->name, "DailyRecurrence")) {
+ if (process_dailyrecurrence(xml_node, &ical_recur))
+ return -1;
+ } else if (!strcmp((char *)xml_node->name, "AbsoluteYearlyRecurrence")) {
+ if (process_absoluteyearlyrecurrence(xml_node, &ical_recur))
+ return -1;
+ } else if (!strcmp((char *)xml_node->name, "RelativeYearlyRecurrence")) {
+ if (process_relativeyearlyrecurrence(xml_node, &ical_recur))
+ return -1;
+ } else if (!strcmp((char *)xml_node->name, "AbsoluteMonthlyRecurrence")) {
+ if (process_absolutemonthlyrecurrence(xml_node, &ical_recur))
+ return -1;
+ } else if (!strcmp((char *)xml_node->name, "RelativeMonthlyRecurrence")) {
+ if (process_relativemonthlyrecurrence(xml_node, &ical_recur))
+ return -1;
+ } else if (!strcmp((char *)xml_node->name, "EndDateRecurrence")) {
+ for (xml_node2 = xml_node->children; xml_node2;
+ xml_node2 = xml_node2->next) {
+ if (xml_node2->type != XML_ELEMENT_NODE)
+ continue;
+ if (!strcmp((char *)xml_node2->name, "EndDate"))
+ end_date = (char *)xmlNodeGetContent(xml_node2);
+ }
+ } else if (!strcmp((char *)xml_node->name, "NumberedRecurrence")) {
+ for (xml_node2 = xml_node->children; xml_node2;
+ xml_node2 = xml_node2->next) {
+ if (xml_node2->type != XML_ELEMENT_NODE)
+ continue;
+ if (!strcmp((char *)xml_node2->name, "NumberOfOccurrences"))
+ nr_occurrences = (char *)xmlNodeGetContent(xml_node2);
+ }
+ }
+ }
+ if (ical_recur.freq == ICAL_NO_RECURRENCE) {
+ fprintf(stderr, "No recognised Recurrence type\n");
+ return -1;
+ }
+
+ if (end_date) {
+ if (strlen(end_date) != 11 || end_date[4] != '-' ||
+ end_date[7] != '-' || end_date[10] != 'Z') {
+ fprintf(stderr, "Failed to parse Recurrence EndDate '%s'\n",
+ end_date);
+ return -1;
+ }
+ end_date = strdup(end_date);
+ end_date[10] = 0;
+ ical_recur.until = icaltime_from_string(end_date);
+ } else if (nr_occurrences) {
+ ical_recur.count = strtol(nr_occurrences, NULL, 10);
+ }
+ prop = icalproperty_new_rrule(ical_recur);
+ icalcomponent_add_property(comp, prop);
+ return 0;
+}
+
+int process_itemid(icalcomponent *comp, xmlNode *xml_node)
+{
+ const char *id = (char *)xmlGetProp(xml_node, (unsigned char *)"Id");
+ if (!id)
+ return -1;
+
+ icalcomponent_set_uid(comp, id);
+ return 0;
+}
+int process_reminder_mins(icalcomponent *calcomp, xmlNode *xml_node)
+{
+ const char *minutes;
+ int minutesnr;
+ icalcomponent *comp;
+ icalproperty *prop;
+ struct icaltriggertype trig;
+
+ minutes = (char *)xmlNodeGetContent(xml_node);
+ if (!minutes)
+ return -1;
+
+ minutesnr = strtol(minutes, NULL, 10);
+
+ comp = icalcomponent_new_valarm();
+ prop = icalproperty_new_action(ICAL_ACTION_DISPLAY);
+ icalcomponent_add_property(comp, prop);
+ prop = icalproperty_new_description("REMINDER");
+ icalcomponent_add_property(comp, prop);
+ trig = icaltriggertype_from_int(-minutesnr * 60);
+ prop = icalproperty_new_trigger(trig);
+ icalcomponent_add_property(comp, prop);
+
+ icalcomponent_add_component(calcomp, comp);
+ return 0;
+}
+
+static const char *ews_tz_to_ical(const char *ewstz)
+{
+ static struct {
+ const char *exch;
+ const char *ical;
+ } table[] = {
+ /* List found at http://forums.asp.net/p/1518462/3641104.aspx */
+ { "(UTC) Casablanca", "Africa/Casablanca" },
+ { "(UTC) Coordinated Universal Time", "UTC" },
+ { "(UTC) Dublin, Edinburgh, Lisbon, London", "Europe/London" },
+ { "(UTC) Monrovia, Reykjavik", "Atlantic/Reykjavik" },
+ { "(UTC+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna", "Europe/Amsterdam" },
+ { "(UTC+01:00) Belgrade, Bratislava, Budapest, Ljubljana, Prague", "Europe/Belgrade" },
+ { "(UTC+01:00) Brussels, Copenhagen, Madrid, Paris", "Europe/Brussels" },
+ { "(UTC+01:00) Sarajevo, Skopje, Warsaw, Zagreb", "Europe/Sarajevo" },
+ { "(UTC+01:00) West Central Africa", "Africa/Douala" },
+ { "(UTC+02:00) Amman", "Asia/Amman" },
+ { "(UTC+02:00) Athens, Bucharest, Istanbul", "Europe/Athens" },
+ { "(UTC+02:00) Beirut", "Asia/Beirut" },
+ { "(UTC+02:00) Cairo", "Africa/Cairo" },
+ { "(UTC+02:00) Harare, Pretoria", "Africa/Harare" },
+ { "(UTC+02:00) Helsinki, Kyiv, Riga, Sofia, Tallinn, Vilnius", "Europe/Helsinki" },
+ { "(UTC+02:00) Jerusalem", "Asia/Jerusalem" },
+ { "(UTC+02:00) Minsk", "Europe/Minsk" },
+ { "(UTC+02:00) Windhoek", "Africa/Windhoek" },
+ { "(UTC+03:00) Baghdad", "Asia/Baghdad" },
+ { "(UTC+03:00) Kuwait, Riyadh", "Asia/Kuwait" },
+ { "(UTC+03:00) Moscow, St. Petersburg, Volgograd", "Europe/Moscow" },
+ { "(UTC+03:00) Nairobi", "Africa/Nairobi" },
+ { "(UTC+03:30) Tehran", "Asia/Tehran" },
+ { "(UTC+04:00) Abu Dhabi, Muscat", "Asia/Muscat" },
+ { "(UTC+04:00) Baku", "Asia/Baku" },
+ { "(UTC+04:00) Port Louis", "Indian/Mauritius" },
+ { "(UTC+04:00) Tbilisi", "Asia/Tbilisi" },
+ { "(UTC+04:00) Yerevan", "Asia/Yerevan" },
+ { "(UTC+04:30) Kabul", "Asia/Kabul" },
+ { "(UTC+05:00) Ekaterinburg", "Asia/Yekaterinburg" },
+ { "(UTC+05:00) Islamabad, Karachi", "Asia/Karachi" },
+ { "(UTC+05:00) Tashkent", "Asia/Tashkent" },
+ { "(UTC+05:30) Chennai, Kolkata, Mumbai, New Delhi", "Asia/Kolkata" },
+ { "(UTC+05:30) Sri Jayawardenepura", "Asia/Colombo" },
+ { "(UTC+05:45) Kathmandu", "Asia/Katmandu" },
+ { "(UTC+06:00) Astana, Dhaka", "Asia/Dhaka" },
+ { "(UTC+06:00) Novosibirsk", "Asia/Novosibirsk" },
+ { "(UTC+06:30) Yangon (Rangoon)", "Asia/Rangoon" },
+ { "(UTC+07:00) Bangkok, Hanoi, Jakarta", "Asia/Bangkok" },
+ { "(UTC+07:00) Krasnoyarsk", "Asia/Krasnoyarsk" },
+ { "(UTC+08:00) Beijing, Chongqing, Hong Kong, Urumqi", "Asia/Shanghai" },
+ { "(UTC+08:00) Irkutsk", "Asia/Irkutsk" },
+ { "(UTC+08:00) Kuala Lumpur, Singapore", "Asia/Kuala_Lumpur" },
+ { "(UTC+08:00) Perth", "Australia/Perth" },
+ { "(UTC+08:00) Taipei", "Asia/Taipei" },
+ { "(UTC+08:00) Ulaanbaatar", "Asia/Ulaanbaatar" },
+ { "(UTC+09:00) Osaka, Sapporo, Tokyo", "Asia/Tokyo" },
+ { "(UTC+09:00) Seoul", "Asia/Seoul" },
+ { "(UTC+09:00) Yakutsk", "Asia/Yakutsk" },
+ { "(UTC+09:30) Adelaide", "Australia/Adelaide" },
+ { "(UTC+09:30) Darwin", "Australia/Darwin" },
+ { "(UTC+10:00) Brisbane", "Australia/Brisbane" },
+ { "(UTC+10:00) Canberra, Melbourne, Sydney", "Australia/Melbourne" },
+ { "(UTC+10:00) Guam, Port Moresby", "Pacific/Guam" },
+ { "(UTC+10:00) Hobart", "Australia/Hobart" },
+ { "(UTC+10:00) Vladivostok", "Asia/Vladivostok" },
+ { "(UTC+11:00) Magadan, Solomon Is., New Caledonia", "Asia/Magadan" },
+ { "(UTC+12:00) Auckland, Wellington", "Pacific/Auckland" },
+ { "(UTC+12:00) Fiji, Marshall Is.", "Pacific/Fiji" },
+ { "(UTC+12:00) Petropavlovsk-Kamchatsky", "Asia/Kamchatka" },
+ { "(UTC+13:00) Nuku'alofa", "Pacific/Tongatapu" },
+ { "(UTC-01:00) Azores", "Atlantic/Azores" },
+ { "(UTC-01:00) Cape Verde Is.", "Atlantic/Cape_Verde" },
+ { "(UTC-02:00) Mid-Atlantic", "Atlantic/South_Georgia" },
+ { "(UTC-03:00) Brasilia", "America/Sao_Paulo" },
+ { "(UTC-03:00) Buenos Aires", "America/Argentina/Buenos_Aires" },
+ { "(UTC-03:00) Cayenne", "America/Cayenne" },
+ { "(UTC-03:00) Greenland", "America/Godthab" },
+ { "(UTC-03:00) Montevideo", "America/Montevideo" },
+ { "(UTC-03:30) Newfoundland", "America/St_Johns" },
+ { "(UTC-04:00) Asuncion", "America/Asuncion" },
+ { "(UTC-04:00) Atlantic Time (Canada)", "America/Halifax" },
+ { "(UTC-04:00) Georgetown, La Paz, San Juan", "America/Argentina/San_Juan" },
+ { "(UTC-04:00) Manaus", "America/Manaus" },
+ { "(UTC-04:00) Santiago", "America/Santiago" },
+ { "(UTC-04:30) Caracas", "America/Caracas" },
+ { "(UTC-05:00) Bogota, Lima, Quito", "America/Bogota" },
+ { "(UTC-05:00) Eastern Time (US & Canada)", "America/New_York" },
+ { "(UTC-05:00) Indiana (East)", "America/Indiana/Indianapolis" },
+ { "(UTC-06:00) Central America", "America/Costa_Rica" },
+ { "(UTC-06:00) Central Time (US & Canada)", "America/Chicago" },
+ /* ? */ { "(UTC-06:00) Guadalajara, Mexico City, Monterrey", "America/Mexico_City" },
+ { "(UTC-06:00) Saskatchewan", "Canada/Saskatchewan" },
+ { "(UTC-07:00) Arizona", "America/Phoenix" },
+ { "(UTC-07:00) Chihuahua, La Paz, Mazatlan", "America/Chihuahua" },
+ { "(UTC-07:00) Mountain Time (US & Canada)", "America/Denver" },
+ { "(UTC-08:00) Pacific Time (US & Canada)", "America/Los_Angeles" },
+ { "(UTC-08:00) Tijuana, Baja California", "America/Tijuana" },
+ { "(UTC-09:00) Alaska", "America/Anchorage" },
+ { "(UTC-10:00) Hawaii", "Pacific/Honolulu" },
+ { "(UTC-11:00) Midway Island, Samoa", "Pacific/Midway" },
+ /* ? */ { "(UTC-12:00) International Date Line West", "Pacific/Apia" },
+
+ /* Extra zones I've seen in testing. Is there *no* sanity in
+ *any* part of what Microsoft does with time zones? */
+ { "(GMT) Greenwich Mean Time : Dublin, Edinburgh, Lisbon, London", "Europe/London" },
+ { "Pacific Standard Time", "America/Los_Angeles" },
+ };
+ int i;
+ int offset = 0;
+
+ if (!ewstz)
+ return NULL;
+
+ /* Sometimes it says 'UTC'; sometimes 'GMT' */
+ if (!strncmp(ewstz, "(GMT", 4))
+ offset = 4;
+
+ for (i = 0; i < ARRAY_SIZE(table); i++) {
+ if (!strcmp(ewstz + offset, table[i].exch + offset))
+ return table[i].ical;
+ }
+
+ fprintf(stderr, "Unrecognised TimeZone '%s'\n", ewstz);
+ return NULL;
+
+}
+
+icaltimezone *get_timezone(xmlNode *xml_node)
+{
+ icaltimezone *zone = NULL;
+ const char *ews_tzname = NULL;
+ const char *tzname = NULL;
+
+ 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, "TimeZone"))
+ break;
+ }
+ if (!xml_node) {
+ fprintf(stderr, "Failed to find TimeZone element; falling back to UTC\n");
+ return NULL;
+ }
+
+ ews_tzname = (char *)xmlNodeGetContent(xml_node);
+
+ /* FIXME: Look for manual timezone definitions in the XML and compare against
+ those first, before using the standard Windows ones */
+
+ tzname = ews_tz_to_ical(ews_tzname);
+ if (!tzname)
+ return NULL;
+
+ zone = icaltimezone_get_builtin_timezone(tzname);
+ if (zone)
+ return zone;
+
+ fprintf(stderr, "Failed to load ical timezone for '%s' (%s)\n", tzname,
+ 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, "RelativeYearlyRecurrence 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, 10);
+ if (!daynr)
+ return -1;
+
+ icalrecurrencetype_clear(ical_recur);
+ ical_recur->freq = ICAL_YEARLY_RECURRENCE;
+ ical_recur->by_month[0] = monthnr;
+
+
+ if (daynr < 8) {
+ if (weeknr > 0)
+ ical_recur->by_day[0] = daynr + (weeknr * 8);
+ else
+ ical_recur->by_day[0] = -8 - daynr;
+ } else if (daynr == 8) { /* Day */
+ ical_recur->by_month_day[0] = weeknr;
+ } else if (daynr == 9) { /* Weekday */
+ ical_recur->by_day[0] = 2;
+ ical_recur->by_day[1] = 3;
+ ical_recur->by_day[2] = 4;
+ ical_recur->by_day[3] = 5;
+ ical_recur->by_day[4] = 6;
+ ical_recur->by_set_pos[0] = weeknr;
+ } else if (daynr == 10) { /* WeekendDay */
+ ical_recur->by_day[0] = 1;
+ ical_recur->by_day[1] = 7;
+ ical_recur->by_set_pos[0] = weeknr;
+ }
+
+ return 0;
+}
+int process_absoluteyearlyrecurrence(xmlNode *xml_node, struct icalrecurrencetype *ical_recur)
+{
+ const char *day_of_month = NULL;
+ const char *month = NULL;
+ int daynr, monthnr;
+
+ 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, "DayOfMonth"))
+ day_of_month = (char *)xmlNodeGetContent(xml_node);
+ else if (!strcmp((char *)xml_node->name, "Month"))
+ month = (char *)xmlNodeGetContent(xml_node);
+ }
+ if (!day_of_month || !month) {
+ fprintf(stderr, "AbsoluteYearlyRecurrence missing essential fields (%s,%s)\n",
+ day_of_month, month);
+ return -1;
+ }
+ daynr = strtol(day_of_month, NULL, 10);
+ if (!daynr) {
+ fprintf(stderr, "Failed to parse DayOfMonth '%s'\n", day_of_month);
+ return -1;
+ }
+ monthnr = month_to_number(month);
+ if (!month)
+ return -1;
+
+ icalrecurrencetype_clear(ical_recur);
+ ical_recur->freq = ICAL_YEARLY_RECURRENCE;
+ ical_recur->by_month[0] = monthnr;
+ ical_recur->by_month_day[0] = daynr;
+ return 0;
+}
+
+int process_relativemonthlyrecurrence(xmlNode *xml_node, struct icalrecurrencetype *ical_recur)
+{
+ const char *week = NULL, *interval = NULL, *weekday = NULL;
+ int weeknr, intervalnr, 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, "Interval")) {
+ interval = (char *)xmlNodeGetContent(xml_node);
+ } else if (!strcmp((char *)xml_node->name, "DayOfWeekIndex")) {
+ week = (char *)xmlNodeGetContent(xml_node);
+ }
+ }
+ if (!week || !interval || !weekday) {
+ fprintf(stderr, "RelativeMonthlyRecurrence missing essential fields (%s,%s,%s)\n",
+ week, interval, weekday);
+ return -1;
+ }
+ intervalnr = strtol(interval, NULL, 10);
+ if (!intervalnr) {
+ fprintf(stderr, "Failed to parse Interval '%s'\n", interval);
+ return -1;
+ }
+ weeknr = weekindex_to_ical(week);
+ if (!weeknr)
+ return -1;
+
+ daynr = weekday_to_number(weekday, 10);
+ if (!daynr)
+ return -1;
+
+ icalrecurrencetype_clear(ical_recur);
+ ical_recur->freq = ICAL_MONTHLY_RECURRENCE;
+ ical_recur->interval = intervalnr;
+ if (daynr < 8) {
+ if (weeknr > 0)
+ ical_recur->by_day[0] = daynr + (weeknr * 8);
+ else
+ ical_recur->by_day[0] = -8 - daynr;
+ } else if (daynr == 8) { /* Day */
+ ical_recur->by_month_day[0] = weeknr;
+ } else if (daynr == 9) { /* Weekday */
+ ical_recur->by_day[0] = 2;
+ ical_recur->by_day[1] = 3;
+ ical_recur->by_day[2] = 4;
+ ical_recur->by_day[3] = 5;
+ ical_recur->by_day[4] = 6;
+ ical_recur->by_set_pos[0] = weeknr;
+ } else if (daynr == 10) { /* WeekendDay */
+ ical_recur->by_day[0] = 1;
+ ical_recur->by_day[1] = 7;
+ ical_recur->by_set_pos[0] = weeknr;
+ }
+
+ return 0;
+}
+int process_absolutemonthlyrecurrence(xmlNode *xml_node, struct icalrecurrencetype *ical_recur)
+{
+ const char *interval = NULL, *monthday = NULL;
+ int intervalnr, monthdaynr;
+
+ 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, "DayOfMonth")) {
+ monthday = (char *)xmlNodeGetContent(xml_node);
+ } else if (!strcmp((char *)xml_node->name, "Interval")) {
+ interval = (char *)xmlNodeGetContent(xml_node);
+ }
+ }
+ if (!interval || !monthday) {
+ fprintf(stderr, "AbsoluteMonthlyRecurrence missing essential fields (%s,%s)\n",
+ interval, monthday);
+ return -1;
+ }
+ intervalnr = strtol(interval, NULL, 10);
+ if (!intervalnr) {
+ fprintf(stderr, "Failed to parse Interval '%s'\n", interval);
+ return -1;
+ }
+ monthdaynr = strtol(monthday, NULL, 10);
+ if (!monthday) {
+ fprintf(stderr, "Failed to parse DayOfMonth '%s'\n", monthday);
+ return -1;
+ }
+
+ icalrecurrencetype_clear(ical_recur);
+ ical_recur->freq = ICAL_MONTHLY_RECURRENCE;
+ ical_recur->interval = intervalnr;
+ ical_recur->by_month_day[0] = monthdaynr;
+ return 0;
+}
+
+static int weekdays_to_recur_byday(const char *days, struct icalrecurrencetype *ical_recur)
+{
+ const char *space;
+ const char *day;
+ int count = 0;
+ int daynr;
+
+ do {
+ space = strchr(days, ' ');
+ if (space)
+ day = g_strndup(days, space - days);
+ else
+ day = days;
+
+ daynr = weekday_to_number(day, 7);
+ if (!daynr)
+ return -1;
+
+ if (count == ICAL_BY_DAY_SIZE) {
+ fprintf(stderr, "Too many days in DaysOfWeek list\n");
+ return -1;
+ }
+
+ ical_recur->by_day[count++] = daynr;
+ if (space) {
+ free((char *)day);
+ days = space + 1;
+ }
+ } while (space);
+ return 0;
+}
+
+
+
+int process_weeklyrecurrence(xmlNode *xml_node, struct icalrecurrencetype *ical_recur)
+{
+ const char *interval = NULL, *weekday = NULL, *firstday = NULL;
+ int intervalnr, firstdaynr;
+
+ 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, "Interval")) {
+ interval = (char *)xmlNodeGetContent(xml_node);
+ } else if (!strcmp((char *)xml_node->name, "FirstDayOfWeek")) {
+ firstday = (char *)xmlNodeGetContent(xml_node);
+ }
+ }
+ if (!interval || !weekday) {
+ fprintf(stderr, "WeeklyRecurrence missing essential fields (%s,%s)\n",
+ interval, weekday);
+ return -1;
+ }
+ intervalnr = strtol(interval, NULL, 10);
+ if (!intervalnr) {
+ fprintf(stderr, "Failed to parse Interval '%s'\n", interval);
+ return -1;
+ }
+ if (firstday)
+ firstdaynr = weekday_to_number(firstday, 7);
+ else
+ firstdaynr = 0;
+
+ icalrecurrencetype_clear(ical_recur);
+ ical_recur->interval = intervalnr;
+ ical_recur->week_start = firstdaynr;
+
+ if (weekdays_to_recur_byday(weekday, ical_recur))
+ return -1;
+
+ ical_recur->freq = ICAL_WEEKLY_RECURRENCE;
+ return 0;
+}
+int process_dailyrecurrence(xmlNode *xml_node, struct icalrecurrencetype *ical_recur)
+{
+ const char *interval = NULL;
+ int intervalnr;
+
+ 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, "Interval")) {
+ interval = (char *)xmlNodeGetContent(xml_node);
+ }
+ }
+ if (!interval) {
+ fprintf(stderr, "DailyRecurrence missing essential fields (%s)\n",
+ interval);
+ return -1;
+ }
+ intervalnr = strtol(interval, NULL, 10);
+ if (!intervalnr) {
+ fprintf(stderr, "Failed to parse Interval '%s'\n", interval);
+ return -1;
+ }
+
+ icalrecurrencetype_clear(ical_recur);
+ ical_recur->freq = ICAL_DAILY_RECURRENCE;
+ ical_recur->interval = intervalnr;
+
+ 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;
+ icaltimezone *z;
+ 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) {
+ 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);
+ } else {
+ if (!std_zone) {
+ std_zone = icalcomponent_new(ICAL_XSTANDARD_COMPONENT);
+
+ prop = icalproperty_new_tzoffsetto(std_offset);
+ icalcomponent_add_property(std_zone, prop);
+
+ prop = icalproperty_new_tzoffsetfrom(std_offset);
+ icalcomponent_add_property(std_zone, prop);
+ }
+ icalcomponent_add_component(comp, std_zone);
+ }
+
+ z = icaltimezone_new();
+ icaltimezone_set_component(z, comp);
+ return z;
+}
FILE *calfile;
-int process_relativeyearlyrecurrence(xmlNode *xml_node, struct icalrecurrencetype *ical_recur);
-int process_absoluteyearlyrecurrence(xmlNode *xml_node, struct icalrecurrencetype *ical_recur);
-int process_relativemonthlyrecurrence(xmlNode *xml_node, struct icalrecurrencetype *ical_recur);
-int process_absolutemonthlyrecurrence(xmlNode *xml_node, struct icalrecurrencetype *ical_recur);
-int process_weeklyrecurrence(xmlNode *xml_node, struct icalrecurrencetype *ical_recur);
-int process_dailyrecurrence(xmlNode *xml_node, struct icalrecurrencetype *ical_recur);
-
-int process_organizer(icalcomponent *comp, xmlNode *xml_node);
-int process_required_attendees(icalcomponent *comp, xmlNode *xml_node);
-int process_optional_attendees(icalcomponent *comp, xmlNode *xml_node);
-int process_time(icalcomponent *comp, xmlNode *xml_node, icaltimetype *ical_time);
-int process_truefalse(icalcomponent *comp, xmlNode *xml_node, gboolean *val);
-int process_location(icalcomponent *comp, xmlNode *xml_node);
-int process_body(icalcomponent *comp, xmlNode *xml_node);
-int process_subject(icalcomponent *comp, xmlNode *xml_node);
-int process_recurrence(icalcomponent *comp, xmlNode *xml_node, icaltimezone *zone);
-int process_itemid(icalcomponent *comp, xmlNode *xmlnode);
-int process_reminder_mins(icalcomponent *comp, xmlNode *xmlnode);
-icaltimezone *get_timezone(xmlNode *xmlnode);
-icaltimezone *get_meeting_timezone(xmlNode *xml_node);
-
-icalcomponent *ews_calitem_to_ical(xmlNode *xml_node);
+extern icalcomponent *ews_calitem_to_ical(xmlNode *xml_node);
int main(int argc, char **argv)
{
return 0;
}
-icalcomponent *ews_calitem_to_ical(xmlNode *xml_node)
-{
- icaltimetype dtstart, dtend;
- icaltimezone *icaltz;
- icalcomponent *comp, *calcomp;
- icalproperty *prop;
- gboolean allday = FALSE;
-
- dtstart = dtend = icaltime_null_time();
-
- calcomp = icalcomponent_new_vcalendar();
- icalcomponent_set_method(calcomp, ICAL_METHOD_PUBLISH);
- prop = icalproperty_new_version("2.0");
- icalcomponent_add_property(calcomp, prop);
-
- icaltz = get_meeting_timezone(xml_node);
- if (!icaltz)
- icaltz = get_timezone(xml_node);
- if (icaltz) {
- icalcomponent *comp = icaltimezone_get_component(icaltz);
- icalcomponent_add_component(calcomp, comp);
- }
- comp = icalcomponent_new(ICAL_VEVENT_COMPONENT);
-
- 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, "Organizer"))
- process_organizer(comp, xml_node);
- else if (!strcmp((char *)xml_node->name, "RequiredAttendees"))
- process_required_attendees(comp, xml_node);
- else if (!strcmp((char *)xml_node->name, "OptionalAttendees"))
- process_optional_attendees(comp, xml_node);
- else if (!strcmp((char *)xml_node->name, "Start"))
- process_time(comp, xml_node, &dtstart);
- else if (!strcmp((char *)xml_node->name, "End"))
- process_time(comp, xml_node, &dtend);
- else if (!strcmp((char *)xml_node->name, "Body"))
- process_body(comp, xml_node);
- else if (!strcmp((char *)xml_node->name, "Location"))
- process_location(comp, xml_node);
- else if (!strcmp((char *)xml_node->name, "Subject"))
- process_subject(comp, xml_node);
- else if (!strcmp((char *)xml_node->name, "Recurrence"))
- process_recurrence(comp, xml_node, icaltz);
- else if (!strcmp((char *)xml_node->name, "ItemId"))
- process_itemid(comp, xml_node);
- else if (!strcmp((char *)xml_node->name, "IsAllDayEvent"))
- process_truefalse(comp, xml_node, &allday);
- else if (!strcmp((char *)xml_node->name, "ReminderMinutesBeforeStart"))
- process_reminder_mins(comp, xml_node);
- else if (!strcmp((char *)xml_node->name, "ParentFolderId") ||
- !strcmp((char *)xml_node->name, "DateTimeReceived") ||
- !strcmp((char *)xml_node->name, "Size") ||
- !strcmp((char *)xml_node->name, "IsSubmitted") ||
- !strcmp((char *)xml_node->name, "IsDraft") ||
- !strcmp((char *)xml_node->name, "IsFromMe") ||
- !strcmp((char *)xml_node->name, "IsResend") ||
- !strcmp((char *)xml_node->name, "IsUnmodified") ||
- !strcmp((char *)xml_node->name, "DateTimeSent") ||
- !strcmp((char *)xml_node->name, "DateTimeCreated") ||
- !strcmp((char *)xml_node->name, "ResponseObjects") ||
- !strcmp((char *)xml_node->name, "DisplayCc") ||
- !strcmp((char *)xml_node->name, "DisplayTo") ||
- !strcmp((char *)xml_node->name, "Culture") ||
- !strcmp((char *)xml_node->name, "IsRecurring") ||
- !strcmp((char *)xml_node->name, "MeetingRequestWasSent") ||
- !strcmp((char *)xml_node->name, "IsResponseRequested") ||
- !strcmp((char *)xml_node->name, "MyResponseType") ||
- !strcmp((char *)xml_node->name, "ConflictingMeetingCount") ||
- !strcmp((char *)xml_node->name, "AdjacentMeetingCount") ||
- !strcmp((char *)xml_node->name, "TimeZone") ||
- !strcmp((char *)xml_node->name, "AppointmentSequenceNumber") ||
- !strcmp((char *)xml_node->name, "AppointmentState")) {
- /* Ignore these */
- }
-#if 0
- else
- fprintf(stderr, "Unhandled node type '%s'\n", xml_node->name);
-#endif
- }
-
- /* We don't handle really floating events -- which change their time
- according to the time zone of the observer (like lunch at noon
- under the sundial wherever you are in the world). But that's OK;
- Exchange doesn't seem to either:
- - AFAICT, you can't create them with Outlook.
- - If you send Exchange an invitation with floating times, which
- happens to have a VTIMEZONE, it'll assume that timezone (in
- violation of RFC2445).
- - If you send Exchange an invitation with floating times with
- *no* stray VTIMEZONE in the file (which was a mistake), then
- it creates an item with no timezone but does weird things --
- this invite:
- DTSTART:20100720T120000
- DTEND:20100720T120010
- ... leads to an Exchange object saying...
- <t:Start>2010-07-20T11:00:00Z</t:Start>
- <t:End>2010-07-20T11:00:00Z</t:End>
-
- For any recurring object without time zones (including the last
- test above, as well as all day events such as birthdays created
- with Outlook/Exchange 2003, Exchange will refuse to return the
- object if the <Recurrence> field is requested, reporting
- 'Corrupt Data'.
-
- Tested with Exchange 2007.
- */
- if (icaltz && !allday) {
- dtstart = icaltime_convert_to_zone(dtstart, icaltz);
- dtend = icaltime_convert_to_zone(dtend, icaltz);
- }
- if (allday) {
- dtstart.is_date = 1;
- dtend.is_date = 1;
- }
- if (!icaltime_is_null_time(dtstart))
- icalcomponent_set_dtstart(comp, dtstart);
-
- if (!icaltime_is_null_time(dtend))
- icalcomponent_set_dtend(comp, dtend);
-
- icalcomponent_add_component(calcomp, comp);
- return calcomp;
-}
-
-int process_mailbox(xmlNode *xml_node, const char **r_name, const char **r_email)
-{
- const char *type = NULL, *name = NULL, *email = NULL;
-
- 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, "Name"))
- name = (char *)xmlNodeGetContent(xml_node);
- if (!strcmp((char *)xml_node->name, "EmailAddress"))
- email = (char *)xmlNodeGetContent(xml_node);
- if (!strcmp((char *)xml_node->name, "RoutingType")) {
- type = (char *)xmlNodeGetContent(xml_node);
- }
- }
-
- /* We seem to get EX routing for people who don't exist any more */
- if (type && strcmp(type, "SMTP")) {
- if (strcmp(type, "EX"))
- fprintf(stderr, "Unknown RoutingType '%s' ('%s' '%s')\n",
- type, name, email);
- return -1;
- }
- *r_name = name;
- *r_email = email;
- return 0;
-}
-
-int process_organizer(icalcomponent *comp, xmlNode *xml_node)
-{
- icalproperty *prop;
- icalparameter *param;
-
- 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, "Mailbox")) {
- const char *name = NULL, *email = NULL;
- char *mailtoname;
- if (process_mailbox(xml_node, &name, &email))
- return -1;
-
- mailtoname = g_strdup_printf("mailto:%s", email);
-
- prop = icalproperty_new_organizer(mailtoname);
- free(mailtoname);
- param = icalparameter_new_cn(name);
- icalproperty_add_parameter(prop, param);
- icalcomponent_add_property(comp, prop);
- }
- }
- return 0;
-}
-
-int process_attendee(icalcomponent *comp, xmlNode *xml_node, icalparameter_role role)
-{
- icalproperty *prop;
- icalparameter *param;
-
- 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, "Mailbox")) {
- const char *name = NULL, *email = NULL;
- char *mailtoname;
- if (process_mailbox(xml_node, &name, &email))
- return -1;
-
- mailtoname = g_strdup_printf("mailto:%s", email);
-
- prop = icalproperty_new_attendee(mailtoname);
- free(mailtoname);
- param = icalparameter_new_cn(name);
- icalproperty_add_parameter(prop, param);
- param = icalparameter_new_role(role);
- icalproperty_add_parameter(prop, param);
- icalcomponent_add_property(comp, prop);
- }
- }
- return 0;
-}
-
-int process_required_attendees(icalcomponent *comp, xmlNode *xml_node)
-{
- 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, "Attendee")) {
- if (process_attendee(comp, xml_node,
- ICAL_ROLE_REQPARTICIPANT))
- { }
- }
- }
- return 0;
-}
-
-int process_optional_attendees(icalcomponent *comp, xmlNode *xml_node)
-{
- 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, "Attendee")) {
- if (process_attendee(comp, xml_node,
- ICAL_ROLE_OPTPARTICIPANT))
- { }
- }
- }
- return 0;
-}
-
-int process_time(icalcomponent *comp, xmlNode *xml_node, icaltimetype *ical_time)
-{
- char *ews_time = (char *)xmlNodeGetContent(xml_node);
-
- if (!ews_time)
- return -1;
- *ical_time = icaltime_from_string(ews_time);
- return 0;
-}
-
-int process_truefalse(icalcomponent *comp, xmlNode *xml_node, gboolean *val)
-{
- char *truth = (char *)xmlNodeGetContent(xml_node);
-
- if (!truth)
- return -1;
- if (!strcmp(truth, "true"))
- *val = TRUE;
- else if (!strcmp(truth, "false"))
- *val = FALSE;
- else {
- fprintf(stderr, "Unrecognised truth value '%s' in %s node\n",
- truth, xml_node->name);
- return -1;
- }
- return 0;
-}
-
-int process_location (icalcomponent *comp, xmlNode *xml_node)
-{
- const char *loc = (char *)xmlNodeGetContent(xml_node);
-
- if (!loc)
- return -1;
- icalcomponent_set_location(comp, loc);
- return 0;
-}
-
-int process_body(icalcomponent *comp, xmlNode *xml_node)
-{
- const char *body = (char *)xmlNodeGetContent(xml_node);
-
- if (!body)
- return -1;
-
- if (!strncasecmp(body, "<html", 5)) {
-#if 0 /* XX: libical doesn't seem to escape this properly */
- icalproperty *prop;
- icalparameter *param;
-
- prop = icalproperty_new_x(body);
- icalproperty_set_x_name(prop, "X-ALT-DESC");
-
- param = icalparameter_new_fmttype("text/html");
- icalproperty_add_parameter(prop, param);
- icalcomponent_add_property(comp, prop);
-#endif
- /* FIXME: html2text */
- icalcomponent_set_description(comp, body);
- } else {
- icalcomponent_set_description(comp, body);
- }
-
-
- return 0;
-}
-
-int process_subject(icalcomponent *comp, xmlNode *xml_node)
-{
- const char *subject = (char *)xmlNodeGetContent(xml_node);
-
- if (!subject)
- return -1;
-
- icalcomponent_set_summary(comp, subject);
- return 0;
-}
-
-
-static int month_to_number(const char *month)
-{
- static char *months[] = {
- "January", "February", "March", "April", "May", "June", "July",
- "August", "September", "October", "November", "December"
- };
- int monthnr;
- for (monthnr = 0; monthnr < 12; monthnr++) {
- if (!strcmp(month, months[monthnr]))
- return monthnr + 1;
- }
-
- fprintf(stderr, "Unrecognised month name '%s'\n", month);
- return 0;
-}
-static int weekday_to_number(const char *day, int accept)
-{
- static char *days[] = {
- "Sunday", "Monday", "Tuesday", "Wednesday",
- "Thursday", "Friday", "Saturday",
- "Day", "Weekday", "WeekendDay"
- };
- int daynr;
- for (daynr = 0; daynr < accept; 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 int weekindex_to_ical(const char *week)
-{
- static struct {
- char *exch;
- int week;
- } table[] = {
- { "First", 1 },
- { "Second", 2 },
- { "Third", 3 },
- { "Fourth", 4 },
- { "Last", -1 }
- };
- int i;
-
- for (i = 0; i < ARRAY_SIZE(table); i++) {
- if (!strcmp(week, table[i].exch))
- return table[i].week;
- }
- fprintf(stderr, "Unrecognised DayOfWeekIndex '%s'\n", week);
- return 0;
-}
-
-int process_recurrence(icalcomponent *comp, xmlNode *xml_node, icaltimezone *zone)
-{
- struct icalrecurrencetype ical_recur;
- char *end_date = NULL, *nr_occurrences = NULL;
- icalproperty *prop;
- xmlNode *xml_node2;
-
- ical_recur.freq = ICAL_NO_RECURRENCE;
- if (!zone)
- fprintf(stderr, "Recurrence with no recognised TimeZone. Hope this is an all-day event\n");
-
- 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, "WeeklyRecurrence")) {
- if (process_weeklyrecurrence(xml_node, &ical_recur))
- return -1;
- } else if (!strcmp((char *)xml_node->name, "DailyRecurrence")) {
- if (process_dailyrecurrence(xml_node, &ical_recur))
- return -1;
- } else if (!strcmp((char *)xml_node->name, "AbsoluteYearlyRecurrence")) {
- if (process_absoluteyearlyrecurrence(xml_node, &ical_recur))
- return -1;
- } else if (!strcmp((char *)xml_node->name, "RelativeYearlyRecurrence")) {
- if (process_relativeyearlyrecurrence(xml_node, &ical_recur))
- return -1;
- } else if (!strcmp((char *)xml_node->name, "AbsoluteMonthlyRecurrence")) {
- if (process_absolutemonthlyrecurrence(xml_node, &ical_recur))
- return -1;
- } else if (!strcmp((char *)xml_node->name, "RelativeMonthlyRecurrence")) {
- if (process_relativemonthlyrecurrence(xml_node, &ical_recur))
- return -1;
- } else if (!strcmp((char *)xml_node->name, "EndDateRecurrence")) {
- for (xml_node2 = xml_node->children; xml_node2;
- xml_node2 = xml_node2->next) {
- if (xml_node2->type != XML_ELEMENT_NODE)
- continue;
- if (!strcmp((char *)xml_node2->name, "EndDate"))
- end_date = (char *)xmlNodeGetContent(xml_node2);
- }
- } else if (!strcmp((char *)xml_node->name, "NumberedRecurrence")) {
- for (xml_node2 = xml_node->children; xml_node2;
- xml_node2 = xml_node2->next) {
- if (xml_node2->type != XML_ELEMENT_NODE)
- continue;
- if (!strcmp((char *)xml_node2->name, "NumberOfOccurrences"))
- nr_occurrences = (char *)xmlNodeGetContent(xml_node2);
- }
- }
- }
- if (ical_recur.freq == ICAL_NO_RECURRENCE) {
- fprintf(stderr, "No recognised Recurrence type\n");
- return -1;
- }
-
- if (end_date) {
- if (strlen(end_date) != 11 || end_date[4] != '-' ||
- end_date[7] != '-' || end_date[10] != 'Z') {
- fprintf(stderr, "Failed to parse Recurrence EndDate '%s'\n",
- end_date);
- return -1;
- }
- end_date = strdup(end_date);
- end_date[10] = 0;
- ical_recur.until = icaltime_from_string(end_date);
- } else if (nr_occurrences) {
- ical_recur.count = strtol(nr_occurrences, NULL, 10);
- }
- prop = icalproperty_new_rrule(ical_recur);
- icalcomponent_add_property(comp, prop);
- return 0;
-}
-
-int process_itemid(icalcomponent *comp, xmlNode *xml_node)
-{
- const char *id = (char *)xmlGetProp(xml_node, (unsigned char *)"Id");
- if (!id)
- return -1;
-
- icalcomponent_set_uid(comp, id);
- return 0;
-}
-int process_reminder_mins(icalcomponent *calcomp, xmlNode *xml_node)
-{
- const char *minutes;
- int minutesnr;
- icalcomponent *comp;
- icalproperty *prop;
- struct icaltriggertype trig;
-
- minutes = (char *)xmlNodeGetContent(xml_node);
- if (!minutes)
- return -1;
-
- minutesnr = strtol(minutes, NULL, 10);
-
- comp = icalcomponent_new_valarm();
- prop = icalproperty_new_action(ICAL_ACTION_DISPLAY);
- icalcomponent_add_property(comp, prop);
- prop = icalproperty_new_description("REMINDER");
- icalcomponent_add_property(comp, prop);
- trig = icaltriggertype_from_int(-minutesnr * 60);
- prop = icalproperty_new_trigger(trig);
- icalcomponent_add_property(comp, prop);
-
- icalcomponent_add_component(calcomp, comp);
- return 0;
-}
-
-static const char *ews_tz_to_ical(const char *ewstz)
-{
- static struct {
- const char *exch;
- const char *ical;
- } table[] = {
- /* List found at http://forums.asp.net/p/1518462/3641104.aspx */
- { "(UTC) Casablanca", "Africa/Casablanca" },
- { "(UTC) Coordinated Universal Time", "UTC" },
- { "(UTC) Dublin, Edinburgh, Lisbon, London", "Europe/London" },
- { "(UTC) Monrovia, Reykjavik", "Atlantic/Reykjavik" },
- { "(UTC+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna", "Europe/Amsterdam" },
- { "(UTC+01:00) Belgrade, Bratislava, Budapest, Ljubljana, Prague", "Europe/Belgrade" },
- { "(UTC+01:00) Brussels, Copenhagen, Madrid, Paris", "Europe/Brussels" },
- { "(UTC+01:00) Sarajevo, Skopje, Warsaw, Zagreb", "Europe/Sarajevo" },
- { "(UTC+01:00) West Central Africa", "Africa/Douala" },
- { "(UTC+02:00) Amman", "Asia/Amman" },
- { "(UTC+02:00) Athens, Bucharest, Istanbul", "Europe/Athens" },
- { "(UTC+02:00) Beirut", "Asia/Beirut" },
- { "(UTC+02:00) Cairo", "Africa/Cairo" },
- { "(UTC+02:00) Harare, Pretoria", "Africa/Harare" },
- { "(UTC+02:00) Helsinki, Kyiv, Riga, Sofia, Tallinn, Vilnius", "Europe/Helsinki" },
- { "(UTC+02:00) Jerusalem", "Asia/Jerusalem" },
- { "(UTC+02:00) Minsk", "Europe/Minsk" },
- { "(UTC+02:00) Windhoek", "Africa/Windhoek" },
- { "(UTC+03:00) Baghdad", "Asia/Baghdad" },
- { "(UTC+03:00) Kuwait, Riyadh", "Asia/Kuwait" },
- { "(UTC+03:00) Moscow, St. Petersburg, Volgograd", "Europe/Moscow" },
- { "(UTC+03:00) Nairobi", "Africa/Nairobi" },
- { "(UTC+03:30) Tehran", "Asia/Tehran" },
- { "(UTC+04:00) Abu Dhabi, Muscat", "Asia/Muscat" },
- { "(UTC+04:00) Baku", "Asia/Baku" },
- { "(UTC+04:00) Port Louis", "Indian/Mauritius" },
- { "(UTC+04:00) Tbilisi", "Asia/Tbilisi" },
- { "(UTC+04:00) Yerevan", "Asia/Yerevan" },
- { "(UTC+04:30) Kabul", "Asia/Kabul" },
- { "(UTC+05:00) Ekaterinburg", "Asia/Yekaterinburg" },
- { "(UTC+05:00) Islamabad, Karachi", "Asia/Karachi" },
- { "(UTC+05:00) Tashkent", "Asia/Tashkent" },
- { "(UTC+05:30) Chennai, Kolkata, Mumbai, New Delhi", "Asia/Kolkata" },
- { "(UTC+05:30) Sri Jayawardenepura", "Asia/Colombo" },
- { "(UTC+05:45) Kathmandu", "Asia/Katmandu" },
- { "(UTC+06:00) Astana, Dhaka", "Asia/Dhaka" },
- { "(UTC+06:00) Novosibirsk", "Asia/Novosibirsk" },
- { "(UTC+06:30) Yangon (Rangoon)", "Asia/Rangoon" },
- { "(UTC+07:00) Bangkok, Hanoi, Jakarta", "Asia/Bangkok" },
- { "(UTC+07:00) Krasnoyarsk", "Asia/Krasnoyarsk" },
- { "(UTC+08:00) Beijing, Chongqing, Hong Kong, Urumqi", "Asia/Shanghai" },
- { "(UTC+08:00) Irkutsk", "Asia/Irkutsk" },
- { "(UTC+08:00) Kuala Lumpur, Singapore", "Asia/Kuala_Lumpur" },
- { "(UTC+08:00) Perth", "Australia/Perth" },
- { "(UTC+08:00) Taipei", "Asia/Taipei" },
- { "(UTC+08:00) Ulaanbaatar", "Asia/Ulaanbaatar" },
- { "(UTC+09:00) Osaka, Sapporo, Tokyo", "Asia/Tokyo" },
- { "(UTC+09:00) Seoul", "Asia/Seoul" },
- { "(UTC+09:00) Yakutsk", "Asia/Yakutsk" },
- { "(UTC+09:30) Adelaide", "Australia/Adelaide" },
- { "(UTC+09:30) Darwin", "Australia/Darwin" },
- { "(UTC+10:00) Brisbane", "Australia/Brisbane" },
- { "(UTC+10:00) Canberra, Melbourne, Sydney", "Australia/Melbourne" },
- { "(UTC+10:00) Guam, Port Moresby", "Pacific/Guam" },
- { "(UTC+10:00) Hobart", "Australia/Hobart" },
- { "(UTC+10:00) Vladivostok", "Asia/Vladivostok" },
- { "(UTC+11:00) Magadan, Solomon Is., New Caledonia", "Asia/Magadan" },
- { "(UTC+12:00) Auckland, Wellington", "Pacific/Auckland" },
- { "(UTC+12:00) Fiji, Marshall Is.", "Pacific/Fiji" },
- { "(UTC+12:00) Petropavlovsk-Kamchatsky", "Asia/Kamchatka" },
- { "(UTC+13:00) Nuku'alofa", "Pacific/Tongatapu" },
- { "(UTC-01:00) Azores", "Atlantic/Azores" },
- { "(UTC-01:00) Cape Verde Is.", "Atlantic/Cape_Verde" },
- { "(UTC-02:00) Mid-Atlantic", "Atlantic/South_Georgia" },
- { "(UTC-03:00) Brasilia", "America/Sao_Paulo" },
- { "(UTC-03:00) Buenos Aires", "America/Argentina/Buenos_Aires" },
- { "(UTC-03:00) Cayenne", "America/Cayenne" },
- { "(UTC-03:00) Greenland", "America/Godthab" },
- { "(UTC-03:00) Montevideo", "America/Montevideo" },
- { "(UTC-03:30) Newfoundland", "America/St_Johns" },
- { "(UTC-04:00) Asuncion", "America/Asuncion" },
- { "(UTC-04:00) Atlantic Time (Canada)", "America/Halifax" },
- { "(UTC-04:00) Georgetown, La Paz, San Juan", "America/Argentina/San_Juan" },
- { "(UTC-04:00) Manaus", "America/Manaus" },
- { "(UTC-04:00) Santiago", "America/Santiago" },
- { "(UTC-04:30) Caracas", "America/Caracas" },
- { "(UTC-05:00) Bogota, Lima, Quito", "America/Bogota" },
- { "(UTC-05:00) Eastern Time (US & Canada)", "America/New_York" },
- { "(UTC-05:00) Indiana (East)", "America/Indiana/Indianapolis" },
- { "(UTC-06:00) Central America", "America/Costa_Rica" },
- { "(UTC-06:00) Central Time (US & Canada)", "America/Chicago" },
- /* ? */ { "(UTC-06:00) Guadalajara, Mexico City, Monterrey", "America/Mexico_City" },
- { "(UTC-06:00) Saskatchewan", "Canada/Saskatchewan" },
- { "(UTC-07:00) Arizona", "America/Phoenix" },
- { "(UTC-07:00) Chihuahua, La Paz, Mazatlan", "America/Chihuahua" },
- { "(UTC-07:00) Mountain Time (US & Canada)", "America/Denver" },
- { "(UTC-08:00) Pacific Time (US & Canada)", "America/Los_Angeles" },
- { "(UTC-08:00) Tijuana, Baja California", "America/Tijuana" },
- { "(UTC-09:00) Alaska", "America/Anchorage" },
- { "(UTC-10:00) Hawaii", "Pacific/Honolulu" },
- { "(UTC-11:00) Midway Island, Samoa", "Pacific/Midway" },
- /* ? */ { "(UTC-12:00) International Date Line West", "Pacific/Apia" },
-
- /* Extra zones I've seen in testing. Is there *no* sanity in
- *any* part of what Microsoft does with time zones? */
- { "(GMT) Greenwich Mean Time : Dublin, Edinburgh, Lisbon, London", "Europe/London" },
- { "Pacific Standard Time", "America/Los_Angeles" },
- };
- int i;
- int offset = 0;
-
- if (!ewstz)
- return NULL;
-
- /* Sometimes it says 'UTC'; sometimes 'GMT' */
- if (!strncmp(ewstz, "(GMT", 4))
- offset = 4;
-
- for (i = 0; i < ARRAY_SIZE(table); i++) {
- if (!strcmp(ewstz + offset, table[i].exch + offset))
- return table[i].ical;
- }
-
- fprintf(stderr, "Unrecognised TimeZone '%s'\n", ewstz);
- return NULL;
-
-}
-
-icaltimezone *get_timezone(xmlNode *xml_node)
-{
- icaltimezone *zone = NULL;
- const char *ews_tzname = NULL;
- const char *tzname = NULL;
-
- 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, "TimeZone"))
- break;
- }
- if (!xml_node) {
- fprintf(stderr, "Failed to find TimeZone element; falling back to UTC\n");
- return NULL;
- }
-
- ews_tzname = (char *)xmlNodeGetContent(xml_node);
-
- /* FIXME: Look for manual timezone definitions in the XML and compare against
- those first, before using the standard Windows ones */
-
- tzname = ews_tz_to_ical(ews_tzname);
- if (!tzname)
- return NULL;
-
- zone = icaltimezone_get_builtin_timezone(tzname);
- if (zone)
- return zone;
-
- fprintf(stderr, "Failed to load ical timezone for '%s' (%s)\n", tzname,
- 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, "RelativeYearlyRecurrence 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, 10);
- if (!daynr)
- return -1;
-
- icalrecurrencetype_clear(ical_recur);
- ical_recur->freq = ICAL_YEARLY_RECURRENCE;
- ical_recur->by_month[0] = monthnr;
-
-
- if (daynr < 8) {
- if (weeknr > 0)
- ical_recur->by_day[0] = daynr + (weeknr * 8);
- else
- ical_recur->by_day[0] = -8 - daynr;
- } else if (daynr == 8) { /* Day */
- ical_recur->by_month_day[0] = weeknr;
- } else if (daynr == 9) { /* Weekday */
- ical_recur->by_day[0] = 2;
- ical_recur->by_day[1] = 3;
- ical_recur->by_day[2] = 4;
- ical_recur->by_day[3] = 5;
- ical_recur->by_day[4] = 6;
- ical_recur->by_set_pos[0] = weeknr;
- } else if (daynr == 10) { /* WeekendDay */
- ical_recur->by_day[0] = 1;
- ical_recur->by_day[1] = 7;
- ical_recur->by_set_pos[0] = weeknr;
- }
-
- return 0;
-}
-int process_absoluteyearlyrecurrence(xmlNode *xml_node, struct icalrecurrencetype *ical_recur)
-{
- const char *day_of_month = NULL;
- const char *month = NULL;
- int daynr, monthnr;
-
- 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, "DayOfMonth"))
- day_of_month = (char *)xmlNodeGetContent(xml_node);
- else if (!strcmp((char *)xml_node->name, "Month"))
- month = (char *)xmlNodeGetContent(xml_node);
- }
- if (!day_of_month || !month) {
- fprintf(stderr, "AbsoluteYearlyRecurrence missing essential fields (%s,%s)\n",
- day_of_month, month);
- return -1;
- }
- daynr = strtol(day_of_month, NULL, 10);
- if (!daynr) {
- fprintf(stderr, "Failed to parse DayOfMonth '%s'\n", day_of_month);
- return -1;
- }
- monthnr = month_to_number(month);
- if (!month)
- return -1;
-
- icalrecurrencetype_clear(ical_recur);
- ical_recur->freq = ICAL_YEARLY_RECURRENCE;
- ical_recur->by_month[0] = monthnr;
- ical_recur->by_month_day[0] = daynr;
- return 0;
-}
-
-int process_relativemonthlyrecurrence(xmlNode *xml_node, struct icalrecurrencetype *ical_recur)
-{
- const char *week = NULL, *interval = NULL, *weekday = NULL;
- int weeknr, intervalnr, 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, "Interval")) {
- interval = (char *)xmlNodeGetContent(xml_node);
- } else if (!strcmp((char *)xml_node->name, "DayOfWeekIndex")) {
- week = (char *)xmlNodeGetContent(xml_node);
- }
- }
- if (!week || !interval || !weekday) {
- fprintf(stderr, "RelativeMonthlyRecurrence missing essential fields (%s,%s,%s)\n",
- week, interval, weekday);
- return -1;
- }
- intervalnr = strtol(interval, NULL, 10);
- if (!intervalnr) {
- fprintf(stderr, "Failed to parse Interval '%s'\n", interval);
- return -1;
- }
- weeknr = weekindex_to_ical(week);
- if (!weeknr)
- return -1;
-
- daynr = weekday_to_number(weekday, 10);
- if (!daynr)
- return -1;
-
- icalrecurrencetype_clear(ical_recur);
- ical_recur->freq = ICAL_MONTHLY_RECURRENCE;
- ical_recur->interval = intervalnr;
- if (daynr < 8) {
- if (weeknr > 0)
- ical_recur->by_day[0] = daynr + (weeknr * 8);
- else
- ical_recur->by_day[0] = -8 - daynr;
- } else if (daynr == 8) { /* Day */
- ical_recur->by_month_day[0] = weeknr;
- } else if (daynr == 9) { /* Weekday */
- ical_recur->by_day[0] = 2;
- ical_recur->by_day[1] = 3;
- ical_recur->by_day[2] = 4;
- ical_recur->by_day[3] = 5;
- ical_recur->by_day[4] = 6;
- ical_recur->by_set_pos[0] = weeknr;
- } else if (daynr == 10) { /* WeekendDay */
- ical_recur->by_day[0] = 1;
- ical_recur->by_day[1] = 7;
- ical_recur->by_set_pos[0] = weeknr;
- }
-
- return 0;
-}
-int process_absolutemonthlyrecurrence(xmlNode *xml_node, struct icalrecurrencetype *ical_recur)
-{
- const char *interval = NULL, *monthday = NULL;
- int intervalnr, monthdaynr;
-
- 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, "DayOfMonth")) {
- monthday = (char *)xmlNodeGetContent(xml_node);
- } else if (!strcmp((char *)xml_node->name, "Interval")) {
- interval = (char *)xmlNodeGetContent(xml_node);
- }
- }
- if (!interval || !monthday) {
- fprintf(stderr, "AbsoluteMonthlyRecurrence missing essential fields (%s,%s)\n",
- interval, monthday);
- return -1;
- }
- intervalnr = strtol(interval, NULL, 10);
- if (!intervalnr) {
- fprintf(stderr, "Failed to parse Interval '%s'\n", interval);
- return -1;
- }
- monthdaynr = strtol(monthday, NULL, 10);
- if (!monthday) {
- fprintf(stderr, "Failed to parse DayOfMonth '%s'\n", monthday);
- return -1;
- }
-
- icalrecurrencetype_clear(ical_recur);
- ical_recur->freq = ICAL_MONTHLY_RECURRENCE;
- ical_recur->interval = intervalnr;
- ical_recur->by_month_day[0] = monthdaynr;
- return 0;
-}
-
-static int weekdays_to_recur_byday(const char *days, struct icalrecurrencetype *ical_recur)
-{
- const char *space;
- const char *day;
- int count = 0;
- int daynr;
-
- do {
- space = strchr(days, ' ');
- if (space)
- day = g_strndup(days, space - days);
- else
- day = days;
-
- daynr = weekday_to_number(day, 7);
- if (!daynr)
- return -1;
-
- if (count == ICAL_BY_DAY_SIZE) {
- fprintf(stderr, "Too many days in DaysOfWeek list\n");
- return -1;
- }
-
- ical_recur->by_day[count++] = daynr;
- if (space) {
- free((char *)day);
- days = space + 1;
- }
- } while (space);
- return 0;
-}
-
-
-
-int process_weeklyrecurrence(xmlNode *xml_node, struct icalrecurrencetype *ical_recur)
-{
- const char *interval = NULL, *weekday = NULL, *firstday = NULL;
- int intervalnr, firstdaynr;
-
- 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, "Interval")) {
- interval = (char *)xmlNodeGetContent(xml_node);
- } else if (!strcmp((char *)xml_node->name, "FirstDayOfWeek")) {
- firstday = (char *)xmlNodeGetContent(xml_node);
- }
- }
- if (!interval || !weekday) {
- fprintf(stderr, "WeeklyRecurrence missing essential fields (%s,%s)\n",
- interval, weekday);
- return -1;
- }
- intervalnr = strtol(interval, NULL, 10);
- if (!intervalnr) {
- fprintf(stderr, "Failed to parse Interval '%s'\n", interval);
- return -1;
- }
- if (firstday)
- firstdaynr = weekday_to_number(firstday, 7);
- else
- firstdaynr = 0;
-
- icalrecurrencetype_clear(ical_recur);
- ical_recur->interval = intervalnr;
- ical_recur->week_start = firstdaynr;
-
- if (weekdays_to_recur_byday(weekday, ical_recur))
- return -1;
-
- ical_recur->freq = ICAL_WEEKLY_RECURRENCE;
- return 0;
-}
-int process_dailyrecurrence(xmlNode *xml_node, struct icalrecurrencetype *ical_recur)
-{
- const char *interval = NULL;
- int intervalnr;
-
- 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, "Interval")) {
- interval = (char *)xmlNodeGetContent(xml_node);
- }
- }
- if (!interval) {
- fprintf(stderr, "DailyRecurrence missing essential fields (%s)\n",
- interval);
- return -1;
- }
- intervalnr = strtol(interval, NULL, 10);
- if (!intervalnr) {
- fprintf(stderr, "Failed to parse Interval '%s'\n", interval);
- return -1;
- }
-
- icalrecurrencetype_clear(ical_recur);
- ical_recur->freq = ICAL_DAILY_RECURRENCE;
- ical_recur->interval = intervalnr;
-
- 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;
- icaltimezone *z;
- 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) {
- 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);
- } else {
- if (!std_zone) {
- std_zone = icalcomponent_new(ICAL_XSTANDARD_COMPONENT);
-
- prop = icalproperty_new_tzoffsetto(std_offset);
- icalcomponent_add_property(std_zone, prop);
-
- prop = icalproperty_new_tzoffsetfrom(std_offset);
- icalcomponent_add_property(std_zone, prop);
- }
- icalcomponent_add_component(comp, std_zone);
- }
-
- z = icaltimezone_new();
- icaltimezone_set_component(z, comp);
- return z;
-}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <unistd.h>
#include <libxml/parser.h>
#include <libxml/tree.h>
+#include <libical/icalcomponent.h>
+#define ITEM_CREATE 1
+#define ITEM_DELETE 2
+#define ITEM_UPDATE 3
+
+extern icalcomponent *ews_calitem_to_ical(xmlNode *xml_node);
+
+struct item_change {
+ struct item_change *next;
+ int type;
+ char itemid[0];
+};
+
+int process_changes(xmlNode *node, struct item_change **changes);
+int fetch_xml_item(SoupSession *sess, char *url, const char *itemid,
+ const char *xml_filename, const char *ics_filename);
struct ews_auth {
const char *username;
struct ews_auth auth;
char *url;
char *responseclass;
+ struct item_change *changes = NULL;
+ gchar *syncstate = NULL;
+ gboolean last_in_range = TRUE;
+ char *statefilename;
+ gsize length;
if (argc != 4) {
usage:
url = argv[1];
+ statefilename = g_build_filename (g_get_home_dir() , ".ews-syncstate", NULL);
+ g_file_get_contents(statefilename, &syncstate, &length, NULL);
+
sess = soup_session_sync_new_with_options(SOUP_SESSION_USE_NTLM, TRUE, NULL);
g_signal_connect (sess, "authenticate",
G_CALLBACK(souptest_authenticate), &auth);
child = xmlNewChild (node, messages_ns, (xmlChar *)"SyncFolderId", NULL);
xmlNewProp(xmlNewChild(child, types_ns, (xmlChar *)"DistinguishedFolderId", NULL),
(xmlChar *)"Id", (xmlChar *)"calendar");
+ if (syncstate)
+ child = xmlNewTextChild (node, messages_ns, (xmlChar *)"SyncState",
+ (xmlChar *)syncstate);
+
child = xmlNewTextChild (node, messages_ns, (xmlChar *)"MaxChangesReturned",
(xmlChar *)"5");
responseclass);
exit(1);
}
- fprintf(stderr, "WRITE ME\n");
+ for (node = node->children; node; node = node->next) {
+ if (node->type != XML_ELEMENT_NODE)
+ continue;
+ if (!strcmp((char *)node->name, "LastItemInRange")) {
+ const char *truth = (const char *)xmlNodeGetContent(node);
+ if (!strcmp(truth, "true"))
+ last_in_range = TRUE;
+ else if (!strcmp(truth, "false"))
+ last_in_range = FALSE;
+ else {
+ fprintf(stderr, "Invalid value for <LastItemInRange>: %s\n",
+ truth);
+ exit(1);
+ }
+ } else if (!strcmp((char *)node->name, "SyncState")) {
+ syncstate = (char *)xmlNodeGetContent(node);
+ } else if (!strcmp((char *)node->name, "Changes")) {
+ if (process_changes(node, &changes))
+ exit(1);
+ }
+ }
+ xmlFreeDoc(doc);
+
+ while (changes) {
+ struct item_change *this = changes;
+ char *xml_filename = g_strdup_printf("%s/ews-sync/%s.xml",
+ g_get_home_dir(), this->itemid);
+ char *ics_filename = g_strdup_printf("%s/ews-sync/%s.ics",
+ g_get_home_dir(), this->itemid);
+ if (this->type == ITEM_DELETE) {
+ unlink(xml_filename);
+ unlink(ics_filename);
+ } else {
+ if (fetch_xml_item(sess, url, this->itemid,
+ xml_filename, ics_filename))
+ return -1;
+ }
+ changes = this->next;
+ free(this);
+ }
+ g_file_set_contents(statefilename, syncstate, strlen(syncstate), NULL);
+ return 0;
+}
+int fetch_xml_item(SoupSession *sess, char *url, const char *itemid,
+ const char *xml_filename, const char *ics_filename)
+{
+ SoupMessage *msg;
+ xmlDoc *doc;
+ xmlNode *node, *child;
+ xmlNs *soap_ns, *types_ns, *messages_ns;
+ xmlOutputBuffer *buf;
+ int status;
+ char *responseclass;
+ icalcomponent *calcomp;
+ char *outbuf;
+
+ msg = soup_message_new("POST", url);
+
+ soup_message_headers_append (msg->request_headers,
+ "User-Agent", "libews/0.1");
+
+ doc = xmlNewDoc((xmlChar *) "1.0");
+ node = xmlNewDocNode(doc, NULL, (xmlChar *)"Envelope", NULL);
+ xmlDocSetRootElement(doc, node);
+
+ soap_ns = xmlNewNs (node, (xmlChar *)"http://schemas.xmlsoap.org/soap/envelope/",
+ (xmlChar *)"soap");
+ xmlSetNs(node, soap_ns);
+ types_ns = xmlNewNs (node, (xmlChar *)"http://schemas.microsoft.com/exchange/services/2006/types",
+ (xmlChar *)"types");
+ node = xmlNewChild(node, soap_ns, (xmlChar *)"Body", NULL);
+ node = xmlNewChild(node, NULL, (xmlChar *)"GetItem", NULL);
+
+ messages_ns = xmlNewNs (node, (xmlChar *)"http://schemas.microsoft.com/exchange/services/2006/messages", NULL);
+ xmlSetNs(node, messages_ns);
+ child = xmlNewChild (node, messages_ns, (xmlChar *)"ItemShape", NULL);
+ xmlNewTextChild(child, types_ns, (xmlChar *)"BaseShape", (xmlChar *)"AllProperties");
+ xmlNewTextChild(child, types_ns, (xmlChar *)"BodyType", (xmlChar *)"Text");
+ child = xmlNewChild (node, messages_ns, (xmlChar *)"ItemIds", NULL);
+ xmlNewProp(xmlNewChild(child, types_ns, (xmlChar *)"ItemId", NULL),
+ (xmlChar *)"Id", (xmlChar *)itemid);
+
+
+ buf = xmlAllocOutputBuffer(NULL);
+ xmlNodeDumpOutput(buf, doc, xmlDocGetRootElement(doc), 0, 1, NULL);
+ xmlOutputBufferFlush(buf);
+
+ soup_message_set_request(msg, "text/xml", SOUP_MEMORY_COPY,
+ (gchar *)buf->buffer->content,
+ buf->buffer->use);
+
+ status = soup_session_send_message(sess, msg);
+
+ xmlOutputBufferClose (buf);
+ xmlFreeDoc (doc);
+
+ if (status != 200) {
+ fprintf(stderr, "Unexpected response from server: %d\n", status);
+ exit(1);
+ }
+ g_file_set_contents(xml_filename, msg->response_body->data,
+ msg->response_body->length, NULL);
+ printf("Got XML file %s\n", itemid);
+ doc = xmlReadMemory (msg->response_body->data, msg->response_body->length,
+ "syncresponse.xml", NULL, 0);
+ if (!doc) {
+ fprintf(stderr, "Failed to parse autodiscover response XML\n");
+ exit(1);
+ }
+ node = xmlDocGetRootElement(doc);
+ if (strcmp((char *)node->name, "Envelope")) {
+ fprintf(stderr, "Failed to find SOAP <Envelope> element\n");
+ exit (1);
+ }
+ for (node = node->children; node; node = node->next) {
+ if (node->type == XML_ELEMENT_NODE &&
+ !strcmp((char *)node->name, "Body"))
+ break;
+ }
+ if (!node) {
+ fprintf(stderr, "Failed to find SOAP <Body> element\n");
+ exit (1);
+ }
+ for (node = node->children; node; node = node->next) {
+ if (node->type == XML_ELEMENT_NODE &&
+ !strcmp((char *)node->name, "GetItemResponse"))
+ break;
+ }
+ if (!node) {
+ fprintf(stderr, "Failed to find <GetItemResponse> element\n");
+ exit (1);
+ }
+ for (node = node->children; node; node = node->next) {
+ if (node->type == XML_ELEMENT_NODE &&
+ !strcmp((char *)node->name, "ResponseMessages"))
+ break;
+ }
+ if (!node) {
+ fprintf(stderr, "Failed to find <ResponseMessages> element\n");
+ exit (1);
+ }
+ for (node = node->children; node; node = node->next) {
+ if (node->type == XML_ELEMENT_NODE &&
+ !strcmp((char *)node->name, "GetItemResponseMessage"))
+ break;
+ }
+ responseclass = (char *)xmlGetProp(node, (xmlChar *)"ResponseClass");
+ if (!strcmp(responseclass, "Error")) {
+ for (node = node->children; node; node = node->next) {
+ if (node->type != XML_ELEMENT_NODE)
+ continue;
+ if (!strcmp((char *)node->name, "MessageText")) {
+ fprintf(stderr, "Server returned error: %s\n",
+ xmlNodeGetContent(node));
+ } else if (!strcmp((char *)node->name, "ResponseCode")) {
+ fprintf(stderr, "Response code: %s\n",
+ xmlNodeGetContent(node));
+ }
+ }
+ exit(1);
+ } else if (strcmp(responseclass, "Success")) {
+ fprintf(stderr, "Unknown response class '%s' from server\n",
+ responseclass);
+ exit(1);
+ }
+
+ for (node = node->children; node; node = node->next) {
+ if (node->type == XML_ELEMENT_NODE &&
+ !strcmp((char *)node->name, "Items"))
+ break;
+ }
+ if (!node) {
+ fprintf(stderr, "Failed to find <Items> element\n");
+ exit (1);
+ }
+
+ for (node = node->children; node; node = node->next) {
+ if (node->type == XML_ELEMENT_NODE &&
+ !strcmp((char *)node->name, "CalendarItem"))
+ break;
+ }
+ if (!node) {
+ fprintf(stderr, "Failed to find <CalendarItem> element\n");
+ exit (1);
+ }
+
+ calcomp = ews_calitem_to_ical(node);
+
+ outbuf = icalcomponent_as_ical_string_r(calcomp);
+
+ g_file_set_contents(ics_filename, outbuf, strlen(outbuf), NULL);
+ printf("Got ICS file %s\n", ics_filename);
+ free(outbuf);
+
+
+ xmlFreeDoc(doc);
+
+
+ return 0;
+}
+int process_changes(xmlNode *node, struct item_change **changes)
+{
+ xmlNode *node2;
+
+ for (node = node->children; node; node = node->next) {
+ int type;
+ char *itemid;
+ struct item_change *new_change;
+
+ if (node->type != XML_ELEMENT_NODE)
+ continue;
+ if (!strcmp((char *)node->name, "Create"))
+ type = ITEM_CREATE;
+ else if (!strcmp((char *)node->name, "Delete"))
+ type = ITEM_DELETE;
+ else if (!strcmp((char *)node->name, "Update"))
+ type = ITEM_UPDATE;
+ else {
+ fprintf(stderr, "Unknown change type '%s'\n",
+ node->name);
+ return -1;
+ }
+ for (node2 = node->children; node2; node2 = node2->next) {
+ if (node2->type != XML_ELEMENT_NODE)
+ continue;
+ if (!strcmp((char *)node2->name, "CalendarItem"))
+ break;
+ }
+ if (!node2) {
+ fprintf(stderr, "<%s> node has no <CalendarItem> child\n",
+ node->name);
+ return -1;
+ }
+ for (node2 = node2->children; node2; node2 = node2->next) {
+ if (node2->type != XML_ELEMENT_NODE)
+ continue;
+ if (!strcmp((char *)node2->name, "ItemId"))
+ break;
+ }
+ if (!node2) {
+ fprintf(stderr, "<%s> node has no <ItemId> child\n",
+ node->name);
+ return -1;
+ }
+ itemid = (char *)xmlGetProp(node2, (xmlChar *)"Id");
+
+ new_change = malloc(sizeof(*new_change) + strlen(itemid) + 1);
+ if (!new_change) {
+ fprintf(stderr, "Out of memory\n");
+ return -1;
+ }
+ new_change->next = *changes;
+ new_change->type = type;
+ strcpy(new_change->itemid, itemid);
+ *changes = new_change;
+ }
return 0;
}