aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDamon Chaplin <damon@ximian.com>2002-02-07 07:16:12 +0800
committerDamon Chaplin <damon@src.gnome.org>2002-02-07 07:16:12 +0800
commitd64e7cf12f73a73a0c5b99806fd695d428bea72f (patch)
treee2c8ce675a8ff8c7b8683996f12281ab973071c9
parent411fd545df9ba41609b00e67e96369f493fe0610 (diff)
downloadgsoc2013-evolution-d64e7cf12f73a73a0c5b99806fd695d428bea72f.tar
gsoc2013-evolution-d64e7cf12f73a73a0c5b99806fd695d428bea72f.tar.gz
gsoc2013-evolution-d64e7cf12f73a73a0c5b99806fd695d428bea72f.tar.bz2
gsoc2013-evolution-d64e7cf12f73a73a0c5b99806fd695d428bea72f.tar.lz
gsoc2013-evolution-d64e7cf12f73a73a0c5b99806fd695d428bea72f.tar.xz
gsoc2013-evolution-d64e7cf12f73a73a0c5b99806fd695d428bea72f.tar.zst
gsoc2013-evolution-d64e7cf12f73a73a0c5b99806fd695d428bea72f.zip
major changes to support RRULE/EXRULE, VALARMS and several other
2002-02-06 Damon Chaplin <damon@ximian.com> * src/libicalvcal/icalvcal.c: major changes to support RRULE/EXRULE, VALARMS and several other properties. * src/libicalvcal/icalvcal.h (icalvcal_convert_with_defaults): new function to pass defaults for a few values into the importer. These are used when the vCalendar file doesn't provide the property but it is required in iCalendar. * src/libicalvcal/vcc.y: support multi-valued properties, by appending new ones and separating by ';'. This was pinched from our changes to evolution/libversit/vcc.y. * src/libicalvcal/Makefile.am: renamed library to libicalvcal-evolution Don't install the headers. * design-data/parameters.csv: added new error for error's parsing vCalendar properties. svn path=/trunk/; revision=15582
-rw-r--r--libical/ChangeLog20
-rw-r--r--libical/design-data/parameters.csv2
-rw-r--r--libical/src/libicalvcal/Makefile.am25
-rw-r--r--libical/src/libicalvcal/icalvcal.c1269
-rw-r--r--libical/src/libicalvcal/icalvcal.h15
-rw-r--r--libical/src/libicalvcal/vcc.y24
6 files changed, 1268 insertions, 87 deletions
diff --git a/libical/ChangeLog b/libical/ChangeLog
index 5d215cedfb..f4818cd132 100644
--- a/libical/ChangeLog
+++ b/libical/ChangeLog
@@ -1,3 +1,23 @@
+2002-02-06 Damon Chaplin <damon@ximian.com>
+
+ * src/libicalvcal/icalvcal.c: major changes to support RRULE/EXRULE,
+ VALARMS and several other properties.
+
+ * src/libicalvcal/icalvcal.h (icalvcal_convert_with_defaults): new
+ function to pass defaults for a few values into the importer. These
+ are used when the vCalendar file doesn't provide the property but it
+ is required in iCalendar.
+
+ * src/libicalvcal/vcc.y: support multi-valued properties, by appending
+ new ones and separating by ';'. This was pinched from our changes to
+ evolution/libversit/vcc.y.
+
+ * src/libicalvcal/Makefile.am: renamed library to libicalvcal-evolution
+ Don't install the headers.
+
+ * design-data/parameters.csv: added new error for error's parsing
+ vCalendar properties.
+
2002-01-28 Dan Winship <danw@ximian.com>
* src/libical/icalrecur.c (icalrecur_add_bydayrules): Skip over
diff --git a/libical/design-data/parameters.csv b/libical/design-data/parameters.csv
index c71eb115eb..f223b415a8 100644
--- a/libical/design-data/parameters.csv
+++ b/libical/design-data/parameters.csv
@@ -20,5 +20,5 @@
"TZID","const char*",
"VALUE","icalparameter_value","BINARY;BOOLEAN;DATE;DURATION;FLOAT;INTEGER;PERIOD;RECUR;TEXT;TIME;URI;ERROR;DATE-TIME;UTC-OFFSET;CAL-ADDRESS"
"X","const char*",
-"X-LIC-ERRORTYPE","icalparameter_xlicerrortype","COMPONENT-PARSE-ERROR;PROPERTY-PARSE-ERROR;PARAMETER-NAME-PARSE-ERROR;PARAMETER-VALUE-PARSE-ERROR;VALUE-PARSE-ERROR;INVALID-ITIP;UNKNOWN-VCAL-PROP-ERROR;MIME-PARSE-ERROR"
+"X-LIC-ERRORTYPE","icalparameter_xlicerrortype","COMPONENT-PARSE-ERROR;PROPERTY-PARSE-ERROR;PARAMETER-NAME-PARSE-ERROR;PARAMETER-VALUE-PARSE-ERROR;VALUE-PARSE-ERROR;INVALID-ITIP;UNKNOWN-VCAL-PROP-ERROR;MIME-PARSE-ERROR;VCAL-PROP-PARSE-ERROR"
"X-LIC-COMPARETYPE","icalparameter_xliccomparetype","EQUAL;NOTEQUAL;LESS;GREATER;LESSEQUAL;GREATEREQUAL;REGEX"
diff --git a/libical/src/libicalvcal/Makefile.am b/libical/src/libicalvcal/Makefile.am
index c409347d14..e8e8310941 100644
--- a/libical/src/libicalvcal/Makefile.am
+++ b/libical/src/libicalvcal/Makefile.am
@@ -1,4 +1,4 @@
-lib_LTLIBRARIES = libicalvcal.la
+lib_LTLIBRARIES = libicalvcal-evolution.la
INCLUDES = \
-I$(top_builddir) \
@@ -6,22 +6,27 @@ INCLUDES = \
-I$(top_builddir)/src/libical \
-I$(top_srcdir)/src/libicalss
-libicalvcal_la_LDFLAGS = -version-info 0:0:0
+libicalvcal_evolution_la_LDFLAGS = -version-info 0:0:0
-libicalvcal_la_SOURCES = \
+libicalvcal_evolution_la_SOURCES = \
+ icalvcal.h \
icalvcal.c \
+ port.h \
+ vcc.h \
vcc.y \
+ vobject.h \
vobject.c \
+ vcaltmp.h \
vcaltmp.c
-libicalvcalincludedir = $(includedir)/libicalvcal
+#libicalvcalincludedir = $(includedir)/evolution/libicalvcal
-libicalvcalinclude_HEADERS = \
- icalvcal.h \
- port.h \
- vcc.h \
- vobject.h \
- vcaltmp.h
+#libicalvcalinclude_HEADERS = \
+# icalvcal.h \
+# port.h \
+# vcc.h \
+# vobject.h \
+# vcaltmp.h
EXTRA_DIST = \
README.TXT \
diff --git a/libical/src/libicalvcal/icalvcal.c b/libical/src/libicalvcal/icalvcal.c
index 24c3155178..f070c9cd57 100644
--- a/libical/src/libicalvcal/icalvcal.c
+++ b/libical/src/libicalvcal/icalvcal.c
@@ -46,22 +46,113 @@ enum datatype {
COMPONENT,
PROPERTY,
PARAMETER,
- UNSUPPORTED
+ 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);
+ void* (*conversion_func)(int icaltype, VObject *o, icalcomponent *comp,
+ icalvcal_defaults *defaults);
int icaltype;
};
struct conversion_table_struct conversion_table[];
-void* dc_prop(int icaltype, VObject *object);
+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 *object,icalcomponent* last_comp,
- icalproperty* last_prop)
+
+static void icalvcal_traverse_objects(VObject *object,
+ icalcomponent* last_comp,
+ icalproperty* last_prop,
+ icalvcal_defaults *defaults)
{
VObjectIterator iterator;
char* name = "[No Name]";
@@ -89,7 +180,8 @@ static void icalvcal_traverse_objects(VObject *object,icalcomponent* last_comp,
/* Handle X properties */
if(strncmp(name, "X-",2) == 0){
- icalproperty* prop = (icalproperty*)dc_prop(ICAL_X_PROPERTY,object);
+ 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 {
@@ -110,11 +202,11 @@ static void icalvcal_traverse_objects(VObject *object,icalcomponent* last_comp,
subc =
(icalcomponent*)(conversion_table[i].conversion_func
(conversion_table[i].icaltype,
- object));
-
- icalcomponent_add_component(last_comp,subc);
+ object, last_comp, defaults));
- assert(subc!=0);
+ if (subc) {
+ icalcomponent_add_component(last_comp,subc);
+ }
break;
}
@@ -127,9 +219,11 @@ static void icalvcal_traverse_objects(VObject *object,icalcomponent* last_comp,
icalproperty* prop =
(icalproperty*)(conversion_table[i].conversion_func
(conversion_table[i].icaltype,
- object));
+ object, last_comp, defaults));
- icalcomponent_add_property(last_comp,prop);
+ if (prop)
+ icalcomponent_add_property(last_comp,prop);
+
last_prop = prop;
}
@@ -163,6 +257,11 @@ static void icalvcal_traverse_objects(VObject *object,icalcomponent* last_comp,
break;
}
+
+ case IGNORE: {
+ /* Do Nothing. */
+ break;
+ }
}
}
@@ -177,19 +276,21 @@ static void icalvcal_traverse_objects(VObject *object,icalcomponent* last_comp,
should use it as the 'last_comp' */
if(subc!=0){
- icalvcal_traverse_objects(eachProp,subc,last_prop);
+ icalvcal_traverse_objects(eachProp,subc,last_prop,defaults);
} else {
- icalvcal_traverse_objects(eachProp,last_comp,last_prop);
+ icalvcal_traverse_objects(eachProp,last_comp,last_prop,defaults);
}
}
}
-icalcomponent* icalvcal_convert(VObject *object){
-
+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");
@@ -198,8 +299,15 @@ icalcomponent* icalvcal_convert(VObject *object){
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);
+ 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
@@ -210,10 +318,25 @@ icalcomponent* icalvcal_convert(VObject *object){
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
@@ -221,7 +344,8 @@ icalcomponent* icalvcal_convert(VObject *object){
* timezone_comp() may not really be necessary, I think it would be
* easier to use them. */
-void* comp(int icaltype, VObject *o)
+void* comp(int icaltype, VObject *o, icalcomponent *comp,
+ icalvcal_defaults *defaults)
{
icalcomponent_kind kind = (icalcomponent_kind)icaltype;
@@ -230,80 +354,1051 @@ void* comp(int icaltype, VObject *o)
return (void* )c;
}
-void* alarm_comp(int icaltype, VObject *o)
+/* 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)
{
- icalcomponent_kind kind = (icalcomponent_kind)icaltype;
+ 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);
+ }
- icalcomponent* c = icalcomponent_new(kind);
+ } 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. */
- return (void*)c;
+ /* 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* daylight_comp(int icaltype, VObject *o)
+
+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(kind);
-
- return (void*)c;
+ 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;
+ }
}
-void* timezone_comp(int icaltype, VObject *o)
+/* 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)
{
- icalcomponent_kind kind = (icalcomponent_kind)icaltype;
+ icalproperty *prop = NULL;
+ char *s;
+ int free_string;
- icalcomponent* c = icalcomponent_new(kind);
+ s = get_string_value (object, &free_string);
- return (void*)c;
+ /* 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 ("TRANSPARENT");
+ }
+
+ if (free_string)
+ deleteStr (s);
+
+ return (void*)prop;
}
-/* These #defines indicate conversion routines that are not defined yet. */
+void* sequence_prop(int icaltype, VObject *object, icalcomponent *comp,
+ icalvcal_defaults *defaults)
+{
+ icalproperty *prop = NULL;
+ char *s;
+ int free_string, sequence;
-#define categories_prop 0
-#define transp_prop 0
-#define status_prop 0
+ s = get_string_value (object, &free_string);
-#define parameter 0
-#define rsvp_parameter 0
+ /* 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;
+}
-/* directly convertable property. The string representation of vcal is
- the same as ical */
-void* dc_prop(int icaltype, VObject *object)
+/* 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,*t=0;
+ char *s;
+ int free_string;
+ struct icaltimetype itt;
prop = icalproperty_new(kind);
- value_kind =
- icalenum_property_kind_to_value_kind(
- icalproperty_isa(prop));
+ value_kind = icalenum_property_kind_to_value_kind (icalproperty_isa(prop));
+ s = get_string_value (object, &free_string);
- switch (vObjectValueType(object)) {
- case VCVT_USTRINGZ: {
- s = t = fakeCString(vObjectUStringZValue(object));
+ /* 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;
+ 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;
+ 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;
}
- case VCVT_STRINGZ: {
- s = (char*)vObjectStringZValue(object);
+
+ /* 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;
+ 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;
+}
+
- value = icalvalue_new_from_string(value_kind,s);
- if(t!=0){
- deleteStr(t);
+
+/* 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;
+ 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;
@@ -320,35 +1415,72 @@ 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},
-{VCDayLightProp, COMPONENT, daylight_comp,0},
-{VCTimeZoneProp, COMPONENT, timezone_comp, ICAL_VTIMEZONE_COMPONENT},
-{VCProdIdProp, PROPERTY, dc_prop, ICAL_PRODID_PROPERTY},
+
+/* These can all be converted directly by parsing the string into a libical
+ value. */
{VCClassProp, PROPERTY, dc_prop, ICAL_CLASS_PROPERTY},
-{VCDCreatedProp, PROPERTY, dc_prop, ICAL_CREATED_PROPERTY},
{VCDescriptionProp, PROPERTY, dc_prop, ICAL_DESCRIPTION_PROPERTY},
{VCAttendeeProp, PROPERTY, dc_prop, ICAL_ATTENDEE_PROPERTY},
-{VCCategoriesProp, PROPERTY, categories_prop,ICAL_CATEGORIES_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},
-{VCVersionProp, PROPERTY, dc_prop, ICAL_VERSION_PROPERTY},
-{VCTranspProp, PROPERTY, transp_prop, ICAL_TRANSP_PROPERTY},
{VCUniqueStringProp, PROPERTY, dc_prop, ICAL_UID_PROPERTY},
{VCURLProp, PROPERTY, dc_prop, ICAL_URL_PROPERTY},
-{VCLastModifiedProp, PROPERTY, dc_prop, ICAL_LASTMODIFIED_PROPERTY},
-{VCSequenceProp, PROPERTY, dc_prop, ICAL_SEQUENCE_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},
-{VCStatusProp, UNSUPPORTED, parameter, ICAL_STATUS_PROPERTY},
+
+/* 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},
+{VCAudioContentProp, 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},
@@ -360,7 +1492,6 @@ struct conversion_table_struct conversion_table[] =
{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},
@@ -376,18 +1507,14 @@ struct conversion_table_struct conversion_table[] =
{VCCISProp, UNSUPPORTED,0, 0},
{VCCityProp, UNSUPPORTED,0, 0},
{VCCommentProp, UNSUPPORTED,0, 0},
-{VCCompletedProp, UNSUPPORTED,0, 0},
{VCCountryNameProp, UNSUPPORTED,0, 0},
{VCDataSizeProp, UNSUPPORTED,0, 0},
{VCDeliveryLabelProp, UNSUPPORTED,0, 0},
{VCDIBProp, UNSUPPORTED,0, 0},
-{VCDisplayStringProp, UNSUPPORTED,0, 0},
{VCDomesticProp, UNSUPPORTED,0, 0},
-{VCEmailAddressProp, UNSUPPORTED,0, 0},
{VCEndProp, UNSUPPORTED,0, 0},
{VCEWorldProp, UNSUPPORTED,0, 0},
{VCExNumProp, UNSUPPORTED,0, 0},
-{VCExpDateProp, UNSUPPORTED,0, 0},
{VCExpectProp, UNSUPPORTED,0, 0},
{VCFamilyNameProp, UNSUPPORTED,0, 0},
{VCFaxProp, UNSUPPORTED,0, 0},
@@ -418,7 +1545,6 @@ struct conversion_table_struct conversion_table[] =
{VCNamePrefixesProp, UNSUPPORTED,0, 0},
{VCNameProp, UNSUPPORTED,0, 0},
{VCNameSuffixesProp, UNSUPPORTED,0, 0},
-{VCNoteProp, UNSUPPORTED,0, 0},
{VCOrgNameProp, UNSUPPORTED,0, 0},
{VCOrgProp, UNSUPPORTED,0, 0},
{VCOrgUnit2Prop, UNSUPPORTED,0, 0},
@@ -439,21 +1565,15 @@ struct conversion_table_struct conversion_table[] =
{VCPostalProp, UNSUPPORTED,0, 0},
{VCPowerShareProp, UNSUPPORTED,0, 0},
{VCPreferredProp, UNSUPPORTED,0, 0},
-{VCProcedureNameProp, 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},
-{VCRDateProp, UNSUPPORTED,0, 0},
{VCRegionProp, UNSUPPORTED,0, 0},
-{VCRepeatCountProp, UNSUPPORTED,0, 0},
{VCResourcesProp, UNSUPPORTED,0, 0},
{VCRNumProp, UNSUPPORTED,0, 0},
-{VCRRuleProp, UNSUPPORTED,0, 0},
-{VCRunTimeProp, UNSUPPORTED,0, 0},
-{VCSnoozeTimeProp, UNSUPPORTED,0, 0},
{VCStartProp, UNSUPPORTED,0, 0},
{VCStreetAddressProp, UNSUPPORTED,0, 0},
{VCSubTypeProp, UNSUPPORTED,0, 0},
@@ -462,7 +1582,6 @@ struct conversion_table_struct conversion_table[] =
{VCTitleProp, UNSUPPORTED,0, 0},
{VCTLXProp, UNSUPPORTED,0, 0},
{VCURLValueProp, UNSUPPORTED,0, 0},
-{VCValueProp, UNSUPPORTED,0, 0},
{VCVideoProp, UNSUPPORTED,0, 0},
{VCVoiceProp, UNSUPPORTED,0, 0},
{VCWAVEProp, UNSUPPORTED,0, 0},
@@ -470,7 +1589,7 @@ struct conversion_table_struct conversion_table[] =
{VCWorkProp, UNSUPPORTED,0, 0},
{VCX400Prop, UNSUPPORTED,0, 0},
{VCX509Prop, UNSUPPORTED,0, 0},
-{VCXRuleProp, UNSUPPORTED,0, 0},
+
{0,0,0,0}
};
diff --git a/libical/src/libicalvcal/icalvcal.h b/libical/src/libicalvcal/icalvcal.h
index f2316c2d0f..96e94b9f2e 100644
--- a/libical/src/libicalvcal/icalvcal.h
+++ b/libical/src/libicalvcal/icalvcal.h
@@ -29,10 +29,25 @@
#include "ical.h"
#include "vcc.h"
+/* These are used as default values if the values are missing in the vCalendar
+ file. Gnome Calendar, for example, does not save the URL of the audio alarm,
+ so we have to add a value here to make a valid iCalendar object. */
+typedef struct _icalvcal_defaults icalvcal_defaults;
+struct _icalvcal_defaults {
+ char *alarm_audio_url;
+ char *alarm_audio_fmttype;
+ char *alarm_description;
+};
+
+
/* Convert a vObject into an icalcomponent */
icalcomponent* icalvcal_convert(VObject *object);
+icalcomponent* icalvcal_convert_with_defaults (VObject *object,
+ icalvcal_defaults *defaults);
+
+
#endif /* !ICALVCAL_H */
diff --git a/libical/src/libicalvcal/vcc.y b/libical/src/libicalvcal/vcc.y
index 70feefab57..4397198b1b 100644
--- a/libical/src/libicalvcal/vcc.y
+++ b/libical/src/libicalvcal/vcc.y
@@ -414,9 +414,31 @@ static void enterValues(const char *value)
}
else {
if (value) {
- setVObjectUStringZValue_(curProp,fakeUnicode(value,0));
+ char *p1, *p2;
+ wchar_t *p3;
+ int i;
+
+ /* If the property already has a string value, we append this one,
+ using ';' to separate the values. */
+ if (vObjectUStringZValue(curProp)) {
+ p1 = fakeCString(vObjectUStringZValue(curProp));
+ p2 = malloc((strlen(p1)+strlen(value)+1));
+ strcpy(p2, p1);
+ deleteStr(p1);
+
+ i = strlen(p2);
+ p2[i] = ';';
+ p2[i+1] = '\0';
+ p2 = strcat(p2, value);
+ p3 = (wchar_t *) vObjectUStringZValue(curProp);
+ free(p3);
+ setVObjectUStringZValue_(curProp,fakeUnicode(p2,0));
+ deleteStr(p2);
+ } else {
+ setVObjectUStringZValue_(curProp,fakeUnicode(value,0));
}
}
+ }
deleteStr(value);
}