#include <unistd.h>
#include <string.h>
#include <ctype.h>
+#include <time.h>
FILE *calfile;
int process_organizer(xmlNode *xml_node);
int process_required_attendees(xmlNode *xml_node);
-int process_start(xmlNode *xml_node);
-int process_end(xmlNode *xml_node);
+int process_start(xmlNode *xml_node, const char *zone);
+int process_end(xmlNode *xml_node, const char *zone);
int process_location(xmlNode *xml_node);
int process_body(xmlNode *xml_node);
int process_subject(xmlNode *xml_node);
int process_recurrence(xmlNode *xml_node);
int process_itemid(xmlNode *xmlnode);
+const char *set_timezone(xmlNode *xmlnode);
int main(int argc, char **argv)
{
xmlDocPtr xml_doc;
xmlNode *xml_node;
+ const char *tzname;
int xmlfd = 0; /* stdin */
char buf[1];
fprintf(calfile, "METHOD:PUBLISH\n");
fprintf(calfile, "VERSION:2.0\n");
fprintf(calfile, "BEGIN:VEVENT\n");
+
+ tzname = set_timezone(xml_node);
+
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, "RequiredAttendees"))
process_required_attendees(xml_node);
else if (!strcmp((char *)xml_node->name, "Start"))
- process_start(xml_node);
+ process_start(xml_node, tzname);
else if (!strcmp((char *)xml_node->name, "End"))
- process_end(xml_node);
+ process_end(xml_node, tzname);
else if (!strcmp((char *)xml_node->name, "Body"))
process_body(xml_node);
else if (!strcmp((char *)xml_node->name, "Location"))
return 0;
}
-int process_time(xmlNode *xml_node, char *name)
+int process_time(xmlNode *xml_node, const char *name, const char *zone)
{
- char *tm = (char *)xmlNodeGetContent(xml_node);
- if (!tm)
- return -1;
+ char *ews_time = (char *)xmlNodeGetContent(xml_node);
+ struct tm tm_time;
+ time_t t;
- fprintf(calfile, "%s:", name);
- while (*tm) {
- if (*tm == '-' || *tm == ':')
- tm++;
- fputc(*tm, calfile);
- tm++;
+ 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;
}
- fputc('\n', calfile);
+
+ 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);
+ 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);
+
+ /* Now we have a number of seconds since epoch in UTC; convert
+ to the time zone we're using. Would be nice if there was a
+ sane way to do this which didn't involve setting $TZ for the
+ whole application -- we'll care about that when we're a plugin
+ for something else rather than running standalone. */
+
+ localtime_r(&t, &tm_time);
+
+ fprintf(calfile, "%s", name);
+ if (zone)
+ fprintf(calfile, ";TZID=%s", zone);
+ fprintf(calfile, ":%04d%02d%02d%02d%02d%02d%s\n",
+ tm_time.tm_year, tm_time.tm_mon, tm_time.tm_mday,
+ tm_time.tm_hour, tm_time.tm_min, tm_time.tm_sec,
+ zone?"":"Z");
return 0;
}
-int process_start(xmlNode *xml_node)
+int process_start(xmlNode *xml_node, const char *zone)
{
- return process_time(xml_node, "DTSTART");
+ return process_time(xml_node, "DTSTART", zone);
}
-int process_end(xmlNode *xml_node)
+int process_end(xmlNode *xml_node, const char *zone)
{
- return process_time(xml_node, "DTEND");
+ return process_time(xml_node, "DTEND", zone);
}
int process_location (xmlNode *xml_node)
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", "" },
+ { "(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", "" },
+ { "(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", "" },
+ // { "(UTC+13:00) Nuku'alofa", "" },
+ { "(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", "" },
+ { "(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", "" },
+ // { "(UTC-07:00) Arizona", "" },
+ { "(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" },
+ };
+ 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;
+
+}
+
+const char *set_timezone(xmlNode *xml_node)
+{
+ const char *tzname = NULL;
+#if 0
+ 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)
+ tzname = ews_tz_to_ical((char *)xmlNodeGetContent(xml_node));
+#endif
+ if (tzname)
+ setenv("TZ", tzname, 1);
+ else
+ setenv("TZ", "UTC", 1);
+ tzset();
+ return tzname;
+}