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