diff options
Diffstat (limited to 'src/libicalvcal/icalvcal.c')
-rw-r--r-- | src/libicalvcal/icalvcal.c | 1650 |
1 files changed, 1650 insertions, 0 deletions
diff --git a/src/libicalvcal/icalvcal.c b/src/libicalvcal/icalvcal.c new file mode 100644 index 0000000..f495af2 --- /dev/null +++ b/src/libicalvcal/icalvcal.c @@ -0,0 +1,1650 @@ +/*====================================================================== + FILE: icalvcal.c + CREATOR: eric 25 May 00 + + $Id: icalvcal.c,v 1.9 2008-02-03 16:10:46 dothebart Exp $ + + + (C) COPYRIGHT 2000, Eric Busboom, http://www.softwarestudio.org + + This program is free software; you can redistribute it and/or modify + it under the terms of either: + + The LGPL as published by the Free Software Foundation, version + 2.1, available at: http://www.fsf.org/copyleft/lesser.html + + Or: + + The Mozilla Public License Version 1.0. You may obtain a copy of + the License at http://www.mozilla.org/MPL/ + + The original code is icalvcal.c + + + + The icalvcal_convert routine calls icalvcal_traverse_objects to do + its work.s his routine steps through through all of the properties + and components of a VObject. For each name of a property or a + component, icalvcal_traverse_objects looks up the name in + conversion_table[]. This table indicates wether the name is of a + component or a property, lists a routine to handle conversion, and + has extra data for the conversion. + + The conversion routine will create new iCal components or properties + and add them to the iCal component structure. + + The most common conversion routine is dc_prop. This routine converts + properties for which the text representation of the vCal component + is identical the iCal representation. + + ======================================================================*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "icalvcal.h" +#include <string.h> + +#ifdef WIN32 +#define snprintf _snprintf +#define strcasecmp stricmp +#endif + +enum datatype { + COMPONENT, + PROPERTY, + PARAMETER, + UNSUPPORTED, + IGNORE +}; + +/* The indices must match between the strings and the codes. */ +char *weekdays[] = { "SU", "MO", "TU", "WE", "TH", "FR", "SA" }; +int weekday_codes[] = { + ICAL_SUNDAY_WEEKDAY, + ICAL_MONDAY_WEEKDAY, + ICAL_TUESDAY_WEEKDAY, + ICAL_WEDNESDAY_WEEKDAY, + ICAL_THURSDAY_WEEKDAY, + ICAL_FRIDAY_WEEKDAY, + ICAL_SATURDAY_WEEKDAY +}; + + +struct conversion_table_struct { + char* vcalname; + enum datatype type; + void* (*conversion_func)(int icaltype, VObject *o, icalcomponent *comp, + icalvcal_defaults *defaults); + int icaltype; +}; + +void* dc_prop(int icaltype, VObject *object, icalcomponent *comp, + icalvcal_defaults *defaults); + + + +/* Creates an error property with the given message. */ +static icalproperty* create_parse_error_property (const char *message, + const char *property_name, + const char *property_value) +{ + char temp[4096]; + icalparameter *error_param; + icalproperty *error_prop; + + snprintf (temp, 1024, "%s: %s:%s", message, property_name, property_value); + + error_param = icalparameter_new_xlicerrortype (ICAL_XLICERRORTYPE_VCALPROPPARSEERROR); + error_prop = icalproperty_new_xlicerror (temp); + icalproperty_add_parameter (error_prop, error_param); + + return error_prop; +} + + +char* get_string_value (VObject *object, int *free_string) +{ + switch (vObjectValueType(object)) { + case VCVT_USTRINGZ: + *free_string = 1; + return fakeCString(vObjectUStringZValue(object)); + + case VCVT_STRINGZ: + *free_string = 0; + return (char*) vObjectStringZValue(object); + } + + *free_string = 0; + + /* We return "" here, to cut down on the risk of crashing. */ + return ""; +} + + +static void convert_floating_time_to_utc (struct icaltimetype *itt) +{ + struct tm tmp_tm = { 0 }, *utc_tm; + time_t t; + + /* We assume the floating time is using the current Unix timezone. + So we convert to a time_t using mktime(), and then back to a struct tm + using gmtime, so it is the UTC time. */ + tmp_tm.tm_year = itt->year - 1900; + tmp_tm.tm_mon = itt->month - 1; + tmp_tm.tm_mday = itt->day; + tmp_tm.tm_hour = itt->hour; + tmp_tm.tm_min = itt->minute; + tmp_tm.tm_sec = itt->second; + tmp_tm.tm_isdst = -1; + + /* Convert to a time_t. */ + t = mktime (&tmp_tm); + + /* Now convert back to a struct tm, but with a UTC time. */ + utc_tm = gmtime (&t); + + /* Now put it back into the icaltime. */ + itt->year = utc_tm->tm_year + 1900; + itt->month = utc_tm->tm_mon + 1; + itt->day = utc_tm->tm_mday; + itt->hour = utc_tm->tm_hour; + itt->minute = utc_tm->tm_min; + itt->second = utc_tm->tm_sec; + + /* Set the is_utc flag. */ + itt->is_utc = 1; +} + +static void icalvcal_traverse_objects(VObject *, + icalcomponent *, + icalproperty *, + icalvcal_defaults *); + +icalcomponent* icalvcal_convert_with_defaults (VObject *object, + icalvcal_defaults *defaults) +{ + + char* name = (char*)vObjectName(object); + icalcomponent* container = icalcomponent_new(ICAL_XROOT_COMPONENT); + icalcomponent* root; + icalproperty *prop; + + icalerror_check_arg_rz( (object!=0),"Object"); + + /* The root object must be a VCALENDAR */ + if(*name==0 || strcmp(name,VCCalProp) != 0){ + return 0; /* HACK. Should return an error */ + } + +#if 0 + /* Just for testing. */ + printf ("This is the internal VObject representation:\n"); + printf ("===========================================\n"); + printVObject(stdout, object); + printf ("===========================================\n"); +#endif + + icalvcal_traverse_objects(object,container,0,defaults); + + /* HACK. I am using the extra 'container' component because I am + lazy. I know there is a way to get rid of it, but I did not care + to find it. */ + + root = icalcomponent_get_first_component(container,ICAL_ANY_COMPONENT); + + icalcomponent_remove_component(container, root); + icalcomponent_free(container); + + /* We add a VERSION and PRODID here, to make it a valid iCalendar object, + but the application may change them if necessary. */ + prop = icalproperty_new_prodid ("-//Softwarestudio.org//" ICAL_PACKAGE + " version " ICAL_VERSION "//EN"); + icalcomponent_add_property (root, prop); + + prop = icalproperty_new_version ("2.0"); + icalcomponent_add_property (root, prop); + + return root; + +} + +icalcomponent* icalvcal_convert (VObject *object) +{ + return icalvcal_convert_with_defaults (object, NULL); +} + + +/* comp() is useful for most components, but alarm, daylight and + * timezone are different. In vcal, they are properties, and in ical, + * they are components. Although because of the way that vcal treats + * everything as a property, alarm_comp() daylight_comp() and + * timezone_comp() may not really be necessary, I think it would be + * easier to use them. */ + +void* comp(int icaltype, VObject *o, icalcomponent *comp, + icalvcal_defaults *defaults) +{ + icalcomponent_kind kind = (icalcomponent_kind)icaltype; + + icalcomponent* c = icalcomponent_new(kind); + + return (void* )c; +} + +/* vCalendar has 4 properties for alarms: AALARM, DALARM, MALARM, PALARM + (for audio, display, mail, and procedure alarms). + + AALARM has Run Time, Snooze Time, Repeat Count, Audio Content. + It may also have a TYPE parameter specifying the MIME type, e.g. + AALARM;TYPE=WAVE;VALUE=URL:19960415T235959; ; ; file:///mmedia/taps.wav + AALARM;TYPE=WAVE;VALUE=CONTENT-ID:19960903T060000;PT15M;4;<jsmith.part2.= + 960901T083000.xyzMail@host1.com> + + DALARM has Run Time, Snooze Time, Repeat Count, Display String. + DALARM:19960415T235000;PT5M;2;Your Taxes Are Due !!! + + MALARM has Run Time, Snooze Time, Repeat Count, Email Address, Note. + MALARM:19960416T000000;PT1H;24;IRS@us.gov;The Check Is In The Mail! + + PALARM has Run Time, Snooze Time, Repeat Count, Procedure Name. + PALARM;VALUE=URL:19960415T235000;PT5M;2;file:///myapps/shockme.exe + + AALARM and PALARM: Check the VALUE is a URL. We won't support CONTENT-ID. + + + iCalendar uses one component, VALARM, for all of these, and uses an ACTION + property of "AUDIO", "DISPLAY", "EMAIL" or "PROCEDURE". + + The Run Time value can be copied into the iCalendar TRIGGER property, + except it must be UTC in iCalendar. If it is floating, we'll convert to + UTC using the current Unix timezone. + + The Snooze Time becomes DURATION, and the Repeat Count becomes REPEAT. + + For AALARM, the Audio Content becomes the ATTACH property, and the TYPE + becomes a FMTTYPE of this property (PCM -> 'audio/basic' (?), + WAVE -> 'audio/x-wav', AIFF -> 'audio/x-aiff'), e.g. + ATTACH;FMTTYPE=audio/basic:ftp://host.com/pub/sounds/bell-01.aud + + For DALARM, Display String becomes the DESCRIPTION property. + + For MALARM, Email Address becomes an ATTENDEE property, e.g. + ATTENDEE:MAILTO:john_doe@host.com + + For PALARM, the Procedure Name becomes an ATTACH property, like AALARM, e.g. + ATTACH;FMTTYPE=application/binary:ftp://host.com/novo-procs/felizano.exe +*/ + +/* This converts the vCalendar alarm properties into iCalendar properties and + adds them to the component. It returns 1 if the alarm is valid, 0 if not. */ +static int get_alarm_properties (icalcomponent *comp, VObject *object, + int icaltype, icalvcal_defaults *defaults) +{ + VObjectIterator iterator; + icalproperty *trigger_prop = NULL, *duration_prop = NULL; + icalproperty *repeat_prop = NULL, *attach_prop = NULL; + icalproperty *summary_prop = NULL, *description_prop = NULL; + icalproperty *action_prop, *attendee_prop = NULL; + icalparameter *fmttype_param = NULL; + enum icalproperty_action action; + int value_is_url = 0, is_valid_alarm = 1; + + initPropIterator (&iterator, object); + while (moreIteration (&iterator)) { + VObject *eachProp = nextVObject (&iterator); + const char *name = vObjectName (eachProp); + char *s; + int free_string; + + s = get_string_value (eachProp, &free_string); + + /* Common properties. */ + if (!strcmp (name, VCRunTimeProp)) { + if (*s) { + struct icaltriggertype t; + icalparameter *param; + + /* Convert it to an icaltimetype. */ + t.time = icaltime_from_string (s); + + /* If it is a floating time, convert it to a UTC time. */ + if (!t.time.is_utc) + convert_floating_time_to_utc (&t.time); + + /* Create a TRIGGER property. */ + trigger_prop = icalproperty_new_trigger (t); + + /* vCalendar triggers are always specific DATE-TIME values. */ + param = icalparameter_new_value (ICAL_VALUE_DATETIME); + icalproperty_add_parameter (trigger_prop, param); + + icalcomponent_add_property (comp, trigger_prop); + } + + } else if (!strcmp (name, VCSnoozeTimeProp)) { + struct icaldurationtype d; + + /* Parse the duration string. + FIXME: vCalendar also permits 'Y' (Year) and 'M' (Month) here, + which we don't handle at present. Though it is unlikely they + will be used as a snooze time between repeated alarms! */ + d = icaldurationtype_from_string (s); + + duration_prop = icalproperty_new_duration (d); + icalcomponent_add_property (comp, duration_prop); + + } else if (!strcmp (name, VCRepeatCountProp)) { + /* If it starts with a digit convert it into a REPEAT property. */ + if (*s && *s >= '0' && *s <= '9') { + repeat_prop = icalproperty_new_repeat (atoi (s)); + icalcomponent_add_property (comp, repeat_prop); + } + + } else if (!strcmp (name, VCValueProp)) { + /* We just remember if the value is a URL. */ + if (!strcmp (s, "URL")) { + value_is_url = 1; + } + + /* Audio properties && Procedure properties. */ + } else if (!strcmp (name, VCAudioContentProp) + || !strcmp (name, VCProcedureNameProp)) { + if (*s && !attach_prop) { + icalattach *attach; + + attach = icalattach_new_from_url (s); + attach_prop = icalproperty_new_attach (attach); + icalcomponent_add_property (comp, attach_prop); + + /* We output a "application/binary" FMTTYPE for Procedure + alarms. */ + if (!strcmp (name, VCProcedureNameProp) && !fmttype_param) + fmttype_param = icalparameter_new_fmttype ("application/binary"); + } + + } else if (!strcmp (name, "TYPE")) { + char *fmttype = NULL; + + if (!strcmp (s, "PCM")) + fmttype = "audio/basic"; + else if (!strcmp (s, "AIFF")) + fmttype = "audio/x-aiff"; + else if (!strcmp (s, "WAVE")) + fmttype = "audio/x-wav"; + + if (fmttype) + fmttype_param = icalparameter_new_fmttype (fmttype); + + /* Display properties. */ + } else if (!strcmp (name, VCDisplayStringProp)) { + if (!description_prop) { + description_prop = icalproperty_new_description (s); + icalcomponent_add_property (comp, description_prop); + } + + /* Mail properties. */ + } else if (!strcmp (name, VCEmailAddressProp)) { + if (*s && strlen (s) < 1000) { + char buffer[1024]; + + /* We need to add 'MAILTO:' before the email address, to make + it valid iCalendar. */ + sprintf (buffer, "MAILTO:%s", s); + attendee_prop = icalproperty_new_attendee (buffer); + icalcomponent_add_property (comp, attendee_prop); + } + + } else if (!strcmp (name, VCNoteProp)) { + if (!description_prop) { + description_prop = icalproperty_new_description (s); + icalcomponent_add_property (comp, description_prop); + } + + /* We also copy the Note to the SUMMARY property, since that is + required in iCalendar. */ + if (!summary_prop) { + summary_prop = icalproperty_new_summary (s); + icalcomponent_add_property (comp, summary_prop); + } + } + + if (free_string) + deleteStr (s); + } + + /* Add the FMTTYPE parameter to the ATTACH property if it exists. */ + if (fmttype_param) { + if (attach_prop) { + icalproperty_add_parameter (attach_prop, fmttype_param); + } else { + icalparameter_free (fmttype_param); + } + } + + + /* Now check if the alarm is valid, i.e. it has the required properties + according to its type. */ + + /* All alarms must have a trigger. */ + if (!trigger_prop) + is_valid_alarm = 0; + + /* If there is a Duration but not a Repeat Count, we just remove the + Duration so the alarm only occurs once. */ + if (duration_prop && !repeat_prop) { + icalcomponent_remove_property (comp, duration_prop); + icalproperty_free (duration_prop); + duration_prop = NULL; + } + + /* Similarly if we have a Repeat Count but no Duration, we remove it. */ + if (repeat_prop && !duration_prop) { + icalcomponent_remove_property (comp, repeat_prop); + icalproperty_free (repeat_prop); + repeat_prop = NULL; + } + + switch (icaltype) { + case ICAL_XAUDIOALARM_COMPONENT: + action = ICAL_ACTION_AUDIO; + + /* Audio alarms must have an ATTACH property, which is a URL. + If they don't have one, we use the default alarm URL. */ + if (!attach_prop || !value_is_url) { + if (defaults && defaults->alarm_audio_url + && defaults->alarm_audio_fmttype) { + icalattach *attach; + + if (attach_prop) { + icalcomponent_remove_property (comp, attach_prop); + icalproperty_free (attach_prop); + } + + attach = icalattach_new_from_url (defaults->alarm_audio_url); + attach_prop = icalproperty_new_attach (attach); + icalcomponent_add_property (comp, attach_prop); + + fmttype_param = icalparameter_new_fmttype (defaults->alarm_audio_fmttype); + icalproperty_add_parameter (attach_prop, fmttype_param); + } else { + is_valid_alarm = 0; + } + } + break; + + case ICAL_XDISPLAYALARM_COMPONENT: + action = ICAL_ACTION_DISPLAY; + + /* Display alarms must have a DESCRIPTION. */ + if (!description_prop) { + if (defaults && defaults->alarm_description) { + description_prop = icalproperty_new_description (defaults->alarm_description); + icalcomponent_add_property (comp, description_prop); + } else { + is_valid_alarm = 0; + } + } + break; + + case ICAL_XEMAILALARM_COMPONENT: + action = ICAL_ACTION_EMAIL; + + /* Email alarms must have a SUMMARY, a DESCRIPTION, and an ATTENDEE. */ + if (!attendee_prop) { + is_valid_alarm = 0; + } else if (!summary_prop || !description_prop) { + if (!summary_prop && defaults->alarm_description) { + summary_prop = icalproperty_new_summary (defaults->alarm_description); + icalcomponent_add_property (comp, summary_prop); + } + + if (!description_prop && defaults->alarm_description) { + description_prop = icalproperty_new_description (defaults->alarm_description); + icalcomponent_add_property (comp, description_prop); + } + + if (!summary_prop || !description_prop) + is_valid_alarm = 0; + } + break; + + case ICAL_XPROCEDUREALARM_COMPONENT: + action = ICAL_ACTION_PROCEDURE; + + /* Procedure alarms must have an ATTACH property, which is a URL. + We don't support inline data. */ + if (!attach_prop) { + is_valid_alarm = 0; + } else if (!value_is_url) { + icalattach *attach; + const char *url; + + attach = icalproperty_get_attach (attach_prop); + url = icalattach_get_url (attach); + + /* Check for Gnome Calendar, which will just save a pathname. */ + if (url && url[0] == '/') { + int len; + char *new_url; + icalattach *new_attach; + + /* Turn it into a proper file: URL. */ + len = strlen (url) + 12; + + new_url = malloc (len); + strcpy (new_url, "file://"); + strcat (new_url, url); + + new_attach = icalattach_new_from_url (new_url); + free (new_url); + + icalproperty_set_attach (attach_prop, new_attach); + + } else { + is_valid_alarm = 0; + } + } + break; + + default: + /* Shouldn't reach here ever. */ + assert(0); + break; + } + + action_prop = icalproperty_new_action (action); + icalcomponent_add_property (comp, action_prop); + + return is_valid_alarm; +} + + +void* alarm_comp(int icaltype, VObject *o, icalcomponent *comp, + icalvcal_defaults *defaults) +{ +/* icalcomponent_kind kind = (icalcomponent_kind)icaltype; */ + int is_valid_alarm; + + icalcomponent* c = icalcomponent_new(ICAL_VALARM_COMPONENT); + + is_valid_alarm = get_alarm_properties (c, o, icaltype, defaults); + + if (is_valid_alarm) { + return (void*)c; + } else { + icalcomponent_free (c); + return NULL; + } +} + + +/* These #defines indicate conversion routines that are not defined yet. */ + +#define parameter 0 +#define rsvp_parameter 0 + +void* transp_prop(int icaltype, VObject *object, icalcomponent *comp, + icalvcal_defaults *defaults) +{ + icalproperty *prop = NULL; + char *s; + int free_string; + + s = get_string_value (object, &free_string); + + + /* In vCalendar "0" means opaque, "1" means transparent, and >1 is + implementation-specific. So we just check for "1" and output + TRANSPARENT. For anything else, the default OPAQUE will be used. */ + if (!strcmp (s, "1")) { + prop = icalproperty_new_transp (ICAL_TRANSP_TRANSPARENT); + } + + if (free_string) + deleteStr (s); + + return (void*)prop; +} + +void* sequence_prop(int icaltype, VObject *object, icalcomponent *comp, + icalvcal_defaults *defaults) +{ + icalproperty *prop = NULL; + char *s; + int free_string, sequence; + + s = get_string_value (object, &free_string); + + /* GnomeCalendar outputs '-1' for this. I have no idea why. + So we just check it is a valid +ve integer, and output 0 if it isn't. */ + sequence = atoi (s); + if (sequence < 0) + sequence = 0; + + prop = icalproperty_new_sequence (sequence); + + if (free_string) + deleteStr (s); + + return (void*)prop; +} + + +/* This handles properties which have multiple values, which are separated by + ';' in vCalendar but ',' in iCalendar. So we just switch those. */ +void* multivalued_prop(int icaltype, VObject *object, icalcomponent *comp, + icalvcal_defaults *defaults) +{ + icalproperty_kind kind = (icalproperty_kind)icaltype; + icalproperty *prop = NULL; + icalvalue *value; + icalvalue_kind value_kind; + char *s, *tmp_copy, *p; + int free_string; + + s = get_string_value (object, &free_string); + + tmp_copy = strdup (s); + + if (free_string) + deleteStr (s); + + if (tmp_copy) { + prop = icalproperty_new(kind); + + value_kind = icalenum_property_kind_to_value_kind (icalproperty_isa (prop)); + + for (p = tmp_copy; *p; p++) { + if (*p == ';') + *p = ','; + } + + value = icalvalue_new_from_string (value_kind, tmp_copy); + icalproperty_set_value (prop, value); + + free (tmp_copy); + } + + return (void*)prop; +} + + +void* status_prop(int icaltype, VObject *object, icalcomponent *comp, + icalvcal_defaults *defaults) +{ + icalproperty *prop = NULL; + char *s; + int free_string; + icalcomponent_kind kind; + + kind = icalcomponent_isa (comp); + + s = get_string_value (object, &free_string); + + /* In vCalendar: + VEVENT can have: "NEEDS ACTION" (default), "SENT", "TENTATIVE", + "CONFIRMED", "DECLINED", "DELEGATED". + VTODO can have: "ACCEPTED", "NEEDS ACTION" (default), "SENT", + "DECLINED", "COMPLETED", "DELEGATED". + (Those are the only 2 components - there is no VJOURNAL) + + In iCalendar: + VEVENT can have: "TENTATIVE", "CONFIRMED", "CANCELLED". + VTODO can have: "NEEDS-ACTION", "COMPLETED", "IN-PROCESS", "CANCELLED". + + So for VEVENT if it is "TENTATIVE" or "CONFIRMED" we keep it, otherwise + we skip it. + + For a VTODO if it is "NEEDS ACTION" we convert to "NEEDS-ACTION", if it + is "COMPLETED" we keep it, otherwise we skip it. + */ + if (kind == ICAL_VEVENT_COMPONENT) { + if (!strcmp (s, "TENTATIVE")) + prop = icalproperty_new_status (ICAL_STATUS_TENTATIVE); + else if (!strcmp (s, "CONFIRMED")) + prop = icalproperty_new_status (ICAL_STATUS_CONFIRMED); + + } else if (kind == ICAL_VTODO_COMPONENT) { + if (!strcmp (s, "NEEDS ACTION")) + prop = icalproperty_new_status (ICAL_STATUS_NEEDSACTION); + else if (!strcmp (s, "COMPLETED")) + prop = icalproperty_new_status (ICAL_STATUS_COMPLETED); + + } + + if (free_string) + deleteStr (s); + + return (void*)prop; +} + + +void* utc_datetime_prop(int icaltype, VObject *object, icalcomponent *comp, + icalvcal_defaults *defaults) +{ + icalproperty_kind kind = (icalproperty_kind)icaltype; + icalproperty *prop; + icalvalue *value; + icalvalue_kind value_kind; + char *s; + int free_string; + struct icaltimetype itt; + + prop = icalproperty_new(kind); + + value_kind = icalenum_property_kind_to_value_kind (icalproperty_isa(prop)); + + s = get_string_value (object, &free_string); + + /* Convert it to an icaltimetype. */ + itt = icaltime_from_string (s); + + /* If it is a floating time, convert it to a UTC time. */ + if (!itt.is_utc) + convert_floating_time_to_utc (&itt); + + value = icalvalue_new_datetime (itt); + icalproperty_set_value(prop,value); + + if (free_string) + deleteStr (s); + + return (void*)prop; +} + + +/* Parse the interval from the RRULE, returning a pointer to the first char + after the interval and any whitespace. s points to the start of the + interval. error_message is set if an error occurs. */ +static char* rrule_parse_interval (char *s, struct icalrecurrencetype *recur, + char **error_message) +{ + int interval = 0; + + /* It must start with a digit. */ + if (*s < '0' || *s > '9') { + *error_message = "Invalid Interval"; + return NULL; + } + + while (*s >= '0' && *s <= '9') + interval = (interval * 10) + (*s++ - '0'); + + /* It must be followed by whitespace. I'm not sure if anything else is + allowed. */ + if (*s != ' ' && *s != '\t') { + *error_message = "Invalid Interval"; + return NULL; + } + + /* Skip any whitespace. */ + while (*s == ' ' || *s == '\t') + s++; + + recur->interval = interval; + return s; +} + + +/* Parse the duration from the RRULE, either a COUNT, e.g. '#5', or an UNTIL + date, e.g. 20020124T000000. error_message is set if an error occurs. + If no duration is given, '#2' is assumed. */ +static char* rrule_parse_duration (char *s, struct icalrecurrencetype *recur, + char **error_message) +{ + /* If we've already found an error, just return. */ + if (*error_message) + return NULL; + + if (!s || *s == '\0') { + /* If we are at the end of the string, assume '#2'. */ + recur->count = 2; + + } else if (*s == '#') { + /* If it starts with a '#' it is the COUNT. Note that in vCalendar + #0 means forever, and setting recur->count to 0 means the same. */ + int count = 0; + + s++; + while (*s >= '0' && *s <= '9') + count = (count * 10) + (*s++ - '0'); + + recur->count = count; + + } else if (*s >= '0' && *s <= '9') { + /* If it starts with a digit it must be the UNTIL date. */ + char *e, buffer[20]; + int len; + + /* Find the end of the date. */ + e = s; + while ((*e >= '0' && *e <= '9') || *e == 'T' || *e == 'Z') + e++; + + /* Check it is a suitable length. */ + len = e - s; + if (len != 8 && len != 15 && len != 16) { + *error_message = "Invalid End Date"; + return NULL; + } + + /* Copy the date to our buffer and null-terminate it. */ + strncpy (buffer, s, len); + buffer[len] = '\0'; + + /* Parse it into the until field. */ + recur->until = icaltime_from_string (buffer); + + /* In iCalendar UNTIL must be UTC if it is a DATE-TIME. But we + don't really know what timezone the vCalendar times are in. So if + it can be converted to a DATE value, we do that. Otherwise we just + use the current Unix timezone. Should be OK 99% of the time. */ + if (!recur->until.is_utc) { + if (recur->until.hour == 0 && recur->until.minute == 0 + && recur->until.second == 0) + recur->until.is_date = 1; + else + convert_floating_time_to_utc (&recur->until); + } + + s = e; + + } else { + *error_message = "Invalid Duration"; + return NULL; + } + + + /* It must be followed by whitespace or the end of the string. + I'm not sure if anything else is allowed. */ + if (*s != '\0' && *s != ' ' && *s != '\t') { + *error_message = "Invalid Duration"; + return NULL; + } + + return s; +} + + +static char* rrule_parse_weekly_days (char *s, + struct icalrecurrencetype *recur, + char **error_message) +{ + int i; + + /* If we've already found an error, just return. */ + if (*error_message) + return NULL; + + for (i = 0; i < ICAL_BY_DAY_SIZE; i++) { + char *e = s; + int found_day, day; + + found_day = -1; + for (day = 0; day < 7; day++) { + if (!strncmp (weekdays[day], s, 2)) { + /* Check the next char is whitespace or the end of string. */ + e = s + 2; + if (*e == ' ' || *e == '\t' || *e == '\0') { + found_day = day; + break; + } + } + } + + if (found_day == -1) + break; + + recur->by_day[i] = weekday_codes[day]; + + s = e; + /* Skip any whitespace. */ + while (*s == ' ' || *s == '\t') + s++; + } + + /* Terminate the array, if it isn't full. */ + if (i < ICAL_BY_DAY_SIZE) + recur->by_day[i] = ICAL_RECURRENCE_ARRAY_MAX; + + return s; +} + + +static char* rrule_parse_monthly_days (char *s, + struct icalrecurrencetype *recur, + char **error_message) +{ + int i; + + /* If we've already found an error, just return. */ + if (*error_message) + return NULL; + + for (i = 0; i < ICAL_BY_MONTHDAY_SIZE; i++) { + char *e; + int month_day; + + if (!strncmp (s, "LD", 2)) { + month_day = -1; + e = s + 2; + } else { + month_day = strtol (s, &e, 10); + + /* Check we got a valid day. */ + if (month_day < 1 || month_day > 31) + break; + + /* See if it is followed by a '+' or '-'. */ + if (*e == '+') { + e++; + } else if (*e == '-') { + e++; + month_day = -month_day; + } + } + + /* Check the next char is whitespace or the end of the string. */ + if (*e != ' ' && *e != '\t' && *e != '\0') + break; + + recur->by_month_day[i] = month_day; + + s = e; + /* Skip any whitespace. */ + while (*s == ' ' || *s == '\t') + s++; + } + + /* Terminate the array, if it isn't full. */ + if (i < ICAL_BY_MONTHDAY_SIZE) + recur->by_month_day[i] = ICAL_RECURRENCE_ARRAY_MAX; + + return s; +} + + +static char* rrule_parse_monthly_positions (char *s, + struct icalrecurrencetype *recur, + char **error_message) +{ + int occurrences[ICAL_BY_DAY_SIZE]; + int found_weekdays[7] = { 0 }; + int i, num_positions, elems, month_position, day; + int num_weekdays, only_weekday = 0; + char *e; + + /* If we've already found an error, just return. */ + if (*error_message) + return NULL; + + /* First read the month position into our local occurrences array. */ + for (i = 0; i < ICAL_BY_DAY_SIZE; i++) { + int month_position; + + /* Check we got a valid position number. */ + month_position = *s - '0'; + if (month_position < 0 || month_position > 5) + break; + + /* See if it is followed by a '+' or '-'. */ + e = s + 1; + if (*e == '+') { + e++; + } else if (*e == '-') { + e++; + month_position = -month_position; + } + + /* Check the next char is whitespace or the end of the string. */ + if (*e != ' ' && *e != '\t' && *e != '\0') + break; + + occurrences[i] = month_position; + + s = e; + /* Skip any whitespace. */ + while (*s == ' ' || *s == '\t') + s++; + } + num_positions = i; + + /* Now read the weekdays in. */ + for (;;) { + char *e = s; + int found_day, day; + + found_day = -1; + for (day = 0; day < 7; day++) { + if (!strncmp (weekdays[day], s, 2)) { + /* Check the next char is whitespace or the end of string. */ + e = s + 2; + if (*e == ' ' || *e == '\t' || *e == '\0') { + found_day = day; + break; + } + } + } + + if (found_day == -1) + break; + + found_weekdays[found_day] = 1; + + s = e; + /* Skip any whitespace. */ + while (*s == ' ' || *s == '\t') + s++; + } + + /* Now merge them together into the recur->by_day array. If there is a + single position & weekday we output something like + 'BYDAY=TU;BYSETPOS=2', so Outlook will understand it. */ + num_weekdays = 0; + for (day = 0; day < 7; day++) { + if (found_weekdays[day]) { + num_weekdays++; + only_weekday = day; + } + } + if (num_positions == 1 && num_weekdays == 1) { + recur->by_day[0] = weekday_codes[only_weekday]; + recur->by_day[1] = ICAL_RECURRENCE_ARRAY_MAX; + + recur->by_set_pos[0] = occurrences[0]; + recur->by_set_pos[1] = ICAL_RECURRENCE_ARRAY_MAX; + } else { + elems = 0; + for (i = 0; i < num_positions; i++) { + month_position = occurrences[i]; + + for (day = 0; day < 7; day++) { + if (found_weekdays[day]) { + recur->by_day[elems] = (abs (month_position) * 8 + weekday_codes[day]) * ((month_position < 0) ? -1 : 1); + elems++; + if (elems == ICAL_BY_DAY_SIZE) + break; + } + } + + if (elems == ICAL_BY_DAY_SIZE) + break; + } + + /* Terminate the array, if it isn't full. */ + if (elems < ICAL_BY_DAY_SIZE) + recur->by_day[elems] = ICAL_RECURRENCE_ARRAY_MAX; + } + + return s; +} + + +static char* rrule_parse_yearly_months (char *s, + struct icalrecurrencetype *recur, + char **error_message) +{ + int i; + + /* If we've already found an error, just return. */ + if (*error_message) + return NULL; + + for (i = 0; i < ICAL_BY_MONTH_SIZE; i++) { + char *e; + int month; + + month = strtol (s, &e, 10); + + /* Check we got a valid month. */ + if (month < 1 || month > 12) + break; + + /* Check the next char is whitespace or the end of the string. */ + if (*e != ' ' && *e != '\t' && *e != '\0') + break; + + recur->by_month[i] = month; + + s = e; + /* Skip any whitespace. */ + while (*s == ' ' || *s == '\t') + s++; + } + + /* Terminate the array, if it isn't full. */ + if (i < ICAL_BY_MONTH_SIZE) + recur->by_month[i] = ICAL_RECURRENCE_ARRAY_MAX; + + return s; +} + + +static char* rrule_parse_yearly_days (char *s, + struct icalrecurrencetype *recur, + char **error_message) +{ + int i; + + /* If we've already found an error, just return. */ + if (*error_message) + return NULL; + + for (i = 0; i < ICAL_BY_YEARDAY_SIZE; i++) { + char *e; + int year_day; + + year_day = strtol (s, &e, 10); + + /* Check we got a valid year_day. */ + if (year_day < 1 || year_day > 366) + break; + + /* Check the next char is whitespace or the end of the string. */ + if (*e != ' ' && *e != '\t' && *e != '\0') + break; + + recur->by_year_day[i] = year_day; + + s = e; + /* Skip any whitespace. */ + while (*s == ' ' || *s == '\t') + s++; + } + + /* Terminate the array, if it isn't full. */ + if (i < ICAL_BY_YEARDAY_SIZE) + recur->by_year_day[i] = ICAL_RECURRENCE_ARRAY_MAX; + + return s; +} + + + + +/* Converts an RRULE/EXRULE property. + NOTE: There are a few things that this doesn't handle: + 1) vCalendar RRULE properties can contain an UNTIL date and a COUNT, and + the first to occur specifies the end of the recurrence. However they + are mutually exclusive in iCalendar. For now we just use the COUNT. + 2) For MONTHLY By Position recurrences, if no modifiers are given they + are to be calculated based on the DTSTART, e.g. if DTSTART is on the + 3rd Wednesday of the month then all occurrences are on the 3rd Wed. + This is awkward to do as we need to access the DTSTART property, which + may be after the RRULE property. So we don't do this at present. + 3) The Extended Recurrence Rule Grammar - we only support the Basic rules. + The extended grammar supports rules embedded in other rules, MINUTELY + recurrences, time modifiers in DAILY rules and maybe other stuff. +*/ + +void* rule_prop(int icaltype, VObject *object, icalcomponent *comp, + icalvcal_defaults *defaults) +{ +/* icalproperty_kind kind = (icalproperty_kind)icaltype;*/ + icalproperty *prop = NULL; +/* icalvalue *value; */ +/* icalvalue_kind value_kind; */ + char *s, *p, *error_message = NULL; + const char *property_name; + int free_string; + struct icalrecurrencetype recur; + + s = get_string_value (object, &free_string); + + property_name = vObjectName (object); + + icalrecurrencetype_clear (&recur); + + if (*s == 'D') { + /* The DAILY RRULE only has an interval and duration (COUNT/UNTIL). */ + recur.freq = ICAL_DAILY_RECURRENCE; + p = rrule_parse_interval (s + 1, &recur, &error_message); + p = rrule_parse_duration (p, &recur, &error_message); + } else if (*s == 'W') { + /* The WEEKLY RRULE has weekday modifiers - MO TU WE. */ + recur.freq = ICAL_WEEKLY_RECURRENCE; + p = rrule_parse_interval (s + 1, &recur, &error_message); + p = rrule_parse_weekly_days (p, &recur, &error_message); + p = rrule_parse_duration (p, &recur, &error_message); + } else if (*s == 'M' && *(s + 1) == 'D') { + /* The MONTHLY By Day RRULE has day number modifiers - 1 1- LD. */ + recur.freq = ICAL_MONTHLY_RECURRENCE; + p = rrule_parse_interval (s + 2, &recur, &error_message); + p = rrule_parse_monthly_days (p, &recur, &error_message); + p = rrule_parse_duration (p, &recur, &error_message); + } else if (*s == 'M' && *(s + 1) == 'P') { + /* The MONTHLY By Position RRULE has position modifiers - 1 2- and + weekday modifiers - MO TU. */ + recur.freq = ICAL_MONTHLY_RECURRENCE; + p = rrule_parse_interval (s + 2, &recur, &error_message); + p = rrule_parse_monthly_positions (p, &recur, &error_message); + p = rrule_parse_duration (p, &recur, &error_message); + } else if (*s == 'Y' && *(s + 1) == 'M') { + /* The YEARLY By Month RRULE has month modifiers - 1 3 12. */ + recur.freq = ICAL_YEARLY_RECURRENCE; + p = rrule_parse_interval (s + 2, &recur, &error_message); + p = rrule_parse_yearly_months (p, &recur, &error_message); + p = rrule_parse_duration (p, &recur, &error_message); + } else if (*s == 'Y' && *(s + 1) == 'D') { + /* The YEARLY By Day RRULE has day number modifiers - 100 200. */ + recur.freq = ICAL_YEARLY_RECURRENCE; + p = rrule_parse_interval (s + 2, &recur, &error_message); + p = rrule_parse_yearly_days (p, &recur, &error_message); + p = rrule_parse_duration (p, &recur, &error_message); + } else { + error_message = "Invalid RRULE Frequency"; + } + + if (error_message) { + prop = create_parse_error_property (error_message, property_name, s); + } else { + if (!strcmp (property_name, "RRULE")) + prop = icalproperty_new_rrule (recur); + else + prop = icalproperty_new_exrule (recur); + } + + if (free_string) + deleteStr (s); + + return (void*)prop; +} + + + +/* directly convertable property. The string representation of vcal is + the same as ical */ + +void* dc_prop(int icaltype, VObject *object, icalcomponent *comp, + icalvcal_defaults *defaults) +{ + icalproperty_kind kind = (icalproperty_kind)icaltype; + icalproperty *prop; + icalvalue *value; + icalvalue_kind value_kind; + char *s; +/*/,*t=0; */ + int free_string; + + + prop = icalproperty_new(kind); + + value_kind = icalenum_property_kind_to_value_kind (icalproperty_isa(prop)); + + s = get_string_value (object, &free_string); + + value = icalvalue_new_from_string(value_kind,s); + + if (free_string) + deleteStr (s); + + icalproperty_set_value(prop,value); + + return (void*)prop; +} + + +/* My extraction program screwed up, so this table does not have all +of the vcal properties in it. I didn't feel like re-doing the entire +table, so you'll have to find the missing properties the hard way -- +the code will assert */ + +static const struct conversion_table_struct conversion_table[] = +{ +{VCCalProp, COMPONENT, comp, ICAL_VCALENDAR_COMPONENT}, +{VCTodoProp, COMPONENT, comp, ICAL_VTODO_COMPONENT}, +{VCEventProp, COMPONENT, comp, ICAL_VEVENT_COMPONENT}, +{VCAAlarmProp, COMPONENT, alarm_comp, ICAL_XAUDIOALARM_COMPONENT}, +{VCDAlarmProp, COMPONENT, alarm_comp, ICAL_XDISPLAYALARM_COMPONENT}, +{VCMAlarmProp, COMPONENT, alarm_comp, ICAL_XEMAILALARM_COMPONENT}, +{VCPAlarmProp, COMPONENT, alarm_comp, ICAL_XPROCEDUREALARM_COMPONENT}, + +/* These can all be converted directly by parsing the string into a libical + value. */ +{VCClassProp, PROPERTY, dc_prop, ICAL_CLASS_PROPERTY}, +{VCDescriptionProp, PROPERTY, dc_prop, ICAL_DESCRIPTION_PROPERTY}, +{VCAttendeeProp, PROPERTY, dc_prop, ICAL_ATTENDEE_PROPERTY}, +{VCDTendProp, PROPERTY, dc_prop, ICAL_DTEND_PROPERTY}, +{VCDTstartProp, PROPERTY, dc_prop, ICAL_DTSTART_PROPERTY}, +{VCDueProp, PROPERTY, dc_prop, ICAL_DUE_PROPERTY}, +{VCLocationProp, PROPERTY, dc_prop, ICAL_LOCATION_PROPERTY}, +{VCSummaryProp, PROPERTY, dc_prop, ICAL_SUMMARY_PROPERTY}, +{VCUniqueStringProp, PROPERTY, dc_prop, ICAL_UID_PROPERTY}, +{VCURLProp, PROPERTY, dc_prop, ICAL_URL_PROPERTY}, +{VCPriorityProp, PROPERTY, dc_prop, ICAL_PRIORITY_PROPERTY}, + +/* These can contain multiple values, which are separated in ';' in vCalendar + but ',' in iCalendar. */ +{VCCategoriesProp, PROPERTY, multivalued_prop,ICAL_CATEGORIES_PROPERTY}, +{VCRDateProp, PROPERTY, multivalued_prop,ICAL_RDATE_PROPERTY}, +{VCExpDateProp, PROPERTY, multivalued_prop,ICAL_EXDATE_PROPERTY}, + +/* These can be in floating time in vCalendar, but must be in UTC in iCalendar. + */ +{VCDCreatedProp, PROPERTY, utc_datetime_prop,ICAL_CREATED_PROPERTY}, +{VCLastModifiedProp, PROPERTY, utc_datetime_prop,ICAL_LASTMODIFIED_PROPERTY}, +{VCCompletedProp, PROPERTY, utc_datetime_prop,ICAL_COMPLETED_PROPERTY}, + +{VCTranspProp, PROPERTY, transp_prop, ICAL_TRANSP_PROPERTY}, +{VCSequenceProp, PROPERTY, sequence_prop, ICAL_SEQUENCE_PROPERTY}, +{VCStatusProp, PROPERTY, status_prop, ICAL_STATUS_PROPERTY}, +{VCRRuleProp, PROPERTY, rule_prop, ICAL_RRULE_PROPERTY}, +{VCXRuleProp, PROPERTY, rule_prop, ICAL_EXRULE_PROPERTY}, + +{VCRSVPProp, UNSUPPORTED, rsvp_parameter,ICAL_RSVP_PARAMETER }, +{VCEncodingProp, UNSUPPORTED, parameter, ICAL_ENCODING_PARAMETER}, +{VCRoleProp, UNSUPPORTED, parameter, ICAL_ROLE_PARAMETER}, + +/* We don't want the old VERSION or PRODID properties copied across as they + are now incorrect. New VERSION & PRODID properties are added instead. */ +{VCVersionProp, IGNORE, 0, 0}, +{VCProdIdProp, IGNORE, 0, 0}, + +/* We ignore DAYLIGHT and TZ properties of the toplevel object, since we can't + really do much with them. */ +{VCDayLightProp, IGNORE, 0, 0}, +{VCTimeZoneProp, IGNORE, 0, 0}, + +/* These are all alarm properties. We handle these when the alarm component + is created, so we ignore them when doing the automatic conversions. + "TYPE" is used in AALARM, but doesn't seem to have a name in vobject.h. */ +{"TYPE", IGNORE,0, 0}, +{VCRunTimeProp, IGNORE,0, 0}, +{VCSnoozeTimeProp, IGNORE,0, 0}, +{VCRepeatCountProp, IGNORE,0, 0}, +{VCValueProp, IGNORE,0, 0}, +{VCProcedureNameProp, IGNORE,0, 0}, +{VCDisplayStringProp, IGNORE,0, 0}, +{VCEmailAddressProp, IGNORE,0, 0}, +{VCNoteProp, IGNORE,0, 0}, + +{VCQuotedPrintableProp,UNSUPPORTED,0, 0}, +{VC7bitProp, UNSUPPORTED,0, 0}, +{VC8bitProp, UNSUPPORTED,0, 0}, +{VCAdditionalNamesProp,UNSUPPORTED,0, 0}, +{VCAdrProp, UNSUPPORTED,0, 0}, +{VCAgentProp, UNSUPPORTED,0, 0}, +{VCAIFFProp, UNSUPPORTED,0, 0}, +{VCAOLProp, UNSUPPORTED,0, 0}, +{VCAppleLinkProp, UNSUPPORTED,0, 0}, +{VCAttachProp, UNSUPPORTED,0, 0}, +{VCATTMailProp, UNSUPPORTED,0, 0}, +{VCAudioContentProp, UNSUPPORTED,0, 0}, +{VCAVIProp, UNSUPPORTED,0, 0}, +{VCBase64Prop, UNSUPPORTED,0, 0}, +{VCBBSProp, UNSUPPORTED,0, 0}, +{VCBirthDateProp, UNSUPPORTED,0, 0}, +{VCBMPProp, UNSUPPORTED,0, 0}, +{VCBodyProp, UNSUPPORTED,0, 0}, +{VCCaptionProp, UNSUPPORTED,0, 0}, +{VCCarProp, UNSUPPORTED,0, 0}, +{VCCellularProp, UNSUPPORTED,0, 0}, +{VCCGMProp, UNSUPPORTED,0, 0}, +{VCCharSetProp, UNSUPPORTED,0, 0}, +{VCCIDProp, UNSUPPORTED,0, 0}, +{VCCISProp, UNSUPPORTED,0, 0}, +{VCCityProp, UNSUPPORTED,0, 0}, +{VCCommentProp, UNSUPPORTED,0, 0}, +{VCCountryNameProp, UNSUPPORTED,0, 0}, +{VCDataSizeProp, UNSUPPORTED,0, 0}, +{VCDeliveryLabelProp, UNSUPPORTED,0, 0}, +{VCDIBProp, UNSUPPORTED,0, 0}, +{VCDomesticProp, UNSUPPORTED,0, 0}, +{VCEndProp, UNSUPPORTED,0, 0}, +{VCEWorldProp, UNSUPPORTED,0, 0}, +{VCExNumProp, UNSUPPORTED,0, 0}, +{VCExpectProp, UNSUPPORTED,0, 0}, +{VCFamilyNameProp, UNSUPPORTED,0, 0}, +{VCFaxProp, UNSUPPORTED,0, 0}, +{VCFullNameProp, UNSUPPORTED,0, 0}, +{VCGeoProp, UNSUPPORTED,0, 0}, +{VCGeoLocationProp, UNSUPPORTED,0, 0}, +{VCGIFProp, UNSUPPORTED,0, 0}, +{VCGivenNameProp, UNSUPPORTED,0, 0}, +{VCGroupingProp, UNSUPPORTED,0, 0}, +{VCHomeProp, UNSUPPORTED,0, 0}, +{VCIBMMailProp, UNSUPPORTED,0, 0}, +{VCInlineProp, UNSUPPORTED,0, 0}, +{VCInternationalProp, UNSUPPORTED,0, 0}, +{VCInternetProp, UNSUPPORTED,0, 0}, +{VCISDNProp, UNSUPPORTED,0, 0}, +{VCJPEGProp, UNSUPPORTED,0, 0}, +{VCLanguageProp, UNSUPPORTED,0, 0}, +{VCLastRevisedProp, UNSUPPORTED,0, 0}, +{VCLogoProp, UNSUPPORTED,0, 0}, +{VCMailerProp, UNSUPPORTED,0, 0}, +{VCMCIMailProp, UNSUPPORTED,0, 0}, +{VCMessageProp, UNSUPPORTED,0, 0}, +{VCMETProp, UNSUPPORTED,0, 0}, +{VCModemProp, UNSUPPORTED,0, 0}, +{VCMPEG2Prop, UNSUPPORTED,0, 0}, +{VCMPEGProp, UNSUPPORTED,0, 0}, +{VCMSNProp, UNSUPPORTED,0, 0}, +{VCNamePrefixesProp, UNSUPPORTED,0, 0}, +{VCNameProp, UNSUPPORTED,0, 0}, +{VCNameSuffixesProp, UNSUPPORTED,0, 0}, +{VCOrgNameProp, UNSUPPORTED,0, 0}, +{VCOrgProp, UNSUPPORTED,0, 0}, +{VCOrgUnit2Prop, UNSUPPORTED,0, 0}, +{VCOrgUnit3Prop, UNSUPPORTED,0, 0}, +{VCOrgUnit4Prop, UNSUPPORTED,0, 0}, +{VCOrgUnitProp, UNSUPPORTED,0, 0}, +{VCPagerProp, UNSUPPORTED,0, 0}, +{VCParcelProp, UNSUPPORTED,0, 0}, +{VCPartProp, UNSUPPORTED,0, 0}, +{VCPCMProp, UNSUPPORTED,0, 0}, +{VCPDFProp, UNSUPPORTED,0, 0}, +{VCPGPProp, UNSUPPORTED,0, 0}, +{VCPhotoProp, UNSUPPORTED,0, 0}, +{VCPICTProp, UNSUPPORTED,0, 0}, +{VCPMBProp, UNSUPPORTED,0, 0}, +{VCPostalBoxProp, UNSUPPORTED,0, 0}, +{VCPostalCodeProp, UNSUPPORTED,0, 0}, +{VCPostalProp, UNSUPPORTED,0, 0}, +{VCPowerShareProp, UNSUPPORTED,0, 0}, +{VCPreferredProp, UNSUPPORTED,0, 0}, +{VCProdigyProp, UNSUPPORTED,0, 0}, +{VCPronunciationProp, UNSUPPORTED,0, 0}, +{VCPSProp, UNSUPPORTED,0, 0}, +{VCPublicKeyProp, UNSUPPORTED,0, 0}, +{VCQPProp, UNSUPPORTED,0, 0}, +{VCQuickTimeProp, UNSUPPORTED,0, 0}, +{VCRegionProp, UNSUPPORTED,0, 0}, +{VCResourcesProp, UNSUPPORTED,0, 0}, +{VCRNumProp, UNSUPPORTED,0, 0}, +{VCStartProp, UNSUPPORTED,0, 0}, +{VCStreetAddressProp, UNSUPPORTED,0, 0}, +{VCSubTypeProp, UNSUPPORTED,0, 0}, +{VCTelephoneProp, UNSUPPORTED,0, 0}, +{VCTIFFProp, UNSUPPORTED,0, 0}, +{VCTitleProp, UNSUPPORTED,0, 0}, +{VCTLXProp, UNSUPPORTED,0, 0}, +{VCURLValueProp, UNSUPPORTED,0, 0}, +{VCVideoProp, UNSUPPORTED,0, 0}, +{VCVoiceProp, UNSUPPORTED,0, 0}, +{VCWAVEProp, UNSUPPORTED,0, 0}, +{VCWMFProp, UNSUPPORTED,0, 0}, +{VCWorkProp, UNSUPPORTED,0, 0}, +{VCX400Prop, UNSUPPORTED,0, 0}, +{VCX509Prop, UNSUPPORTED,0, 0}, + +{0,0,0,0} +}; + + +static void icalvcal_traverse_objects(VObject *object, + icalcomponent* last_comp, + icalproperty* last_prop, + icalvcal_defaults *defaults) +{ + VObjectIterator iterator; + char* name = "[No Name]"; + icalcomponent* subc = 0; + int i; + + if ( vObjectName(object)== 0){ + printf("ERROR, object has no name"); + assert(0); + return; + } + + name = (char*)vObjectName(object); + + /* Lookup this object in the conversion table */ + for (i = 0; conversion_table[i].vcalname != 0; i++){ + if(strcmp(conversion_table[i].vcalname, name) == 0){ + break; + } + } + + /* Did not find the object. It may be an X-property, or an unknown + property */ + if (conversion_table[i].vcalname == 0){ + + /* Handle X properties */ + if(strncmp(name, "X-",2) == 0){ + icalproperty* prop = (icalproperty*)dc_prop(ICAL_X_PROPERTY,object, + last_comp, defaults); + icalproperty_set_x_name(prop,name); + icalcomponent_add_property(last_comp,prop); + } else { + return; + } + + } else { + + /* The vCal property is in the table, and it is not an X + property, so try to convert it to an iCal component, + property or parameter. */ + + switch(conversion_table[i].type){ + + + case COMPONENT: { + subc = + (icalcomponent*)(conversion_table[i].conversion_func + (conversion_table[i].icaltype, + object, last_comp, defaults)); + + if (subc) { + icalcomponent_add_component(last_comp,subc); + } + break; + } + + case PROPERTY: { + + if (vObjectValueType(object) && + conversion_table[i].conversion_func != 0 ) { + + icalproperty* prop = + (icalproperty*)(conversion_table[i].conversion_func + (conversion_table[i].icaltype, + object, last_comp, defaults)); + + if (prop) + icalcomponent_add_property(last_comp,prop); + + last_prop = prop; + + } + break; + } + + case PARAMETER: { + break; + } + + case UNSUPPORTED: { + + /* If the property is listed as UNSUPPORTED, insert a + X_LIC_ERROR property to note this fact. */ + + char temp[1024]; + char* message = "Unsupported vCal property"; + icalparameter *error_param; + icalproperty *error_prop; + + snprintf(temp,1024,"%s: %s",message,name); + + error_param = icalparameter_new_xlicerrortype( + ICAL_XLICERRORTYPE_UNKNOWNVCALPROPERROR + ); + + error_prop = icalproperty_new_xlicerror(temp); + icalproperty_add_parameter(error_prop, error_param); + + icalcomponent_add_property(last_comp,error_prop); + + break; + } + + case IGNORE: { + /* Do Nothing. */ + break; + } + + } + } + + + /* Now, step down into the next vCalproperty */ + + initPropIterator(&iterator,object); + while (moreIteration(&iterator)) { + VObject *eachProp = nextVObject(&iterator); + + /* If 'object' is a component, then the next traversal down + should use it as the 'last_comp' */ + + if(subc!=0){ + icalvcal_traverse_objects(eachProp,subc,last_prop,defaults); + + } else { + icalvcal_traverse_objects(eachProp,last_comp,last_prop,defaults); + } + } +} + +#if 0 + switch (vObjectValueType(object)) { + case VCVT_USTRINGZ: { + char c; + char *t,*s; + s = t = fakeCString(vObjectUStringZValue(object)); + printf(" ustringzstring:%s\n",s); + deleteStr(s); + break; + } + case VCVT_STRINGZ: { + char c; + const char *s = vObjectStringZValue(object); + printf(" stringzstring:%s\n",s); + break; + } + case VCVT_UINT: + { + int i = vObjectIntegerValue(object); + printf(" int:%d\n",i); + break; + } + case VCVT_ULONG: + { + long l = vObjectLongValue(object); + printf(" int:%d\n",l); + break; + } + case VCVT_VOBJECT: + { + printf("ERROR, should not get here\n"); + break; + } + case VCVT_RAW: + case 0: + default: + break; + } + +#endif |