author | zautrix <zautrix> | 2004-06-29 11:59:46 (UTC) |
---|---|---|
committer | zautrix <zautrix> | 2004-06-29 11:59:46 (UTC) |
commit | da43dbdc6c82453228f34766fc74585615cba938 (patch) (side-by-side diff) | |
tree | 16576932cea08bf117b2d0320b0d5f66ee8ad093 /libical/src/libical/icalcomponent.c | |
parent | 627489ea2669d3997676bc3cee0f5d0d0c16c4d4 (diff) | |
download | kdepimpi-da43dbdc6c82453228f34766fc74585615cba938.zip kdepimpi-da43dbdc6c82453228f34766fc74585615cba938.tar.gz kdepimpi-da43dbdc6c82453228f34766fc74585615cba938.tar.bz2 |
New lib ical.Some minor changes as well.
Diffstat (limited to 'libical/src/libical/icalcomponent.c') (more/less context) (ignore whitespace changes)
-rw-r--r-- | libical/src/libical/icalcomponent.c | 1891 |
1 files changed, 1499 insertions, 392 deletions
diff --git a/libical/src/libical/icalcomponent.c b/libical/src/libical/icalcomponent.c index af0d3ec..fbd0492 100644 --- a/libical/src/libical/icalcomponent.c +++ b/libical/src/libical/icalcomponent.c @@ -4,7 +4,6 @@ $Id$ - (C) COPYRIGHT 2000, Eric Busboom, http://www.softwarestudio.org This program is free software; you can redistribute it and/or modify @@ -33,18 +32,20 @@ #include "icalmemory.h" #include "icalenums.h" #include "icaltime.h" +#include "icalarray.h" +#include "icaltimezone.h" #include "icalduration.h" #include "icalperiod.h" #include "icalparser.h" +#include "icalrestriction.h" #include <stdlib.h> /* for malloc */ #include <stdarg.h> /* for va_list, etc */ #include <errno.h> #include <assert.h> #include <stdio.h> /* for fprintf */ -#include <string.h> - -#define MAX_TMP 1024 +#include <string.h> /* for strdup */ +#include <limits.h> /* for INT_MAX */ struct icalcomponent_impl { @@ -56,18 +57,42 @@ struct icalcomponent_impl pvl_list components; pvl_elem component_iterator; icalcomponent* parent; + + /** An array of icaltimezone structs. We use this so we can do fast + lookup of timezones using binary searches. timezones_sorted is + set to 0 whenever we add a timezone, so we remember to sort the + array before doing a binary search. */ + icalarray* timezones; + int timezones_sorted; }; /* icalproperty functions that only components get to use */ void icalproperty_set_parent(icalproperty* property, icalcomponent* component); icalcomponent* icalproperty_get_parent(icalproperty* property); -void icalcomponent_add_children(struct icalcomponent_impl *impl,va_list args); -icalcomponent* icalcomponent_new_impl (icalcomponent_kind kind); -int icalcomponent_property_sorter(void *a, void *b); - - -void icalcomponent_add_children(struct icalcomponent_impl *impl,va_list args) +void icalcomponent_add_children(icalcomponent *impl,va_list args); +static icalcomponent* icalcomponent_new_impl (icalcomponent_kind kind); + +static void icalcomponent_merge_vtimezone (icalcomponent *comp, + icalcomponent *vtimezone, + icalarray *tzids_to_rename); +static void icalcomponent_handle_conflicting_vtimezones (icalcomponent *comp, + icalcomponent *vtimezone, + icalproperty *tzid_prop, + const char *tzid, + icalarray *tzids_to_rename); +static unsigned int icalcomponent_get_tzid_prefix_len (const char *tzid); +static void icalcomponent_rename_tzids(icalcomponent* comp, + icalarray* rename_table); +static void icalcomponent_rename_tzids_callback(icalparameter *param, + void *data); +static int icalcomponent_compare_vtimezones (icalcomponent *vtimezone1, + icalcomponent *vtimezone2); +static int icalcomponent_compare_timezone_fn (const void *elem1, + const void *elem2); + + +void icalcomponent_add_children(icalcomponent *impl, va_list args) { void* vp; @@ -77,25 +102,23 @@ void icalcomponent_add_children(struct icalcomponent_impl *impl,va_list args) icalproperty_isa_property(vp) != 0 ) ; if (icalcomponent_isa_component(vp) != 0 ){ - - icalcomponent_add_component((icalcomponent*)impl, - (icalcomponent*)vp); + icalcomponent_add_component(impl, (icalcomponent*)vp); } else if (icalproperty_isa_property(vp) != 0 ){ - - icalcomponent_add_property((icalcomponent*)impl, - (icalproperty*)vp); + icalcomponent_add_property(impl, (icalproperty*)vp); } } } -icalcomponent* +static icalcomponent* icalcomponent_new_impl (icalcomponent_kind kind) { - struct icalcomponent_impl* comp; + icalcomponent* comp; - if ( ( comp = (struct icalcomponent_impl*) - malloc(sizeof(struct icalcomponent_impl))) == 0) { + if (!icalcomponent_kind_is_valid(kind)) + return NULL; + + if ( ( comp = (icalcomponent*) malloc(sizeof(icalcomponent))) == 0) { icalerror_set_errno(ICAL_NEWFAILED_ERROR); return 0; } @@ -109,22 +132,28 @@ icalcomponent_new_impl (icalcomponent_kind kind) comp->component_iterator = 0; comp->x_name = 0; comp->parent = 0; + comp->timezones = NULL; + comp->timezones_sorted = 1; return comp; } +/** @brief Constructor + */ icalcomponent* icalcomponent_new (icalcomponent_kind kind) { - return (icalcomponent*)icalcomponent_new_impl(kind); + return icalcomponent_new_impl(kind); } +/** @brief Constructor + */ icalcomponent* icalcomponent_vanew (icalcomponent_kind kind, ...) { va_list args; - struct icalcomponent_impl *impl = icalcomponent_new_impl(kind); + icalcomponent *impl = icalcomponent_new_impl(kind); if (impl == 0){ return 0; @@ -134,23 +163,26 @@ icalcomponent_vanew (icalcomponent_kind kind, ...) icalcomponent_add_children(impl, args); va_end(args); - return (icalcomponent*) impl; + return impl; } +/** @brief Constructor + */ icalcomponent* icalcomponent_new_from_string(char* str) { return icalparser_parse_string(str); } -icalcomponent* icalcomponent_new_clone(icalcomponent* component) +/** @brief Constructor + */ +icalcomponent* icalcomponent_new_clone(icalcomponent* old) { - struct icalcomponent_impl *old = (struct icalcomponent_impl*)component; - struct icalcomponent_impl *new; + icalcomponent *new; icalproperty *p; icalcomponent *c; pvl_elem itr; - icalerror_check_arg_rz( (component!=0), "component"); + icalerror_check_arg_rz( (old!=0), "component"); new = icalcomponent_new_impl(old->kind); @@ -180,15 +212,15 @@ icalcomponent* icalcomponent_new_clone(icalcomponent* component) } - +/*** @brief Destructor + */ void -icalcomponent_free (icalcomponent* component) +icalcomponent_free (icalcomponent* c) { icalproperty* prop; icalcomponent* comp; - struct icalcomponent_impl *c = (struct icalcomponent_impl*)component; - icalerror_check_arg_rv( (component!=0), "component"); + icalerror_check_arg_rv( (c!=0), "component"); #ifdef ICAL_FREE_ON_LIST_IS_ERROR icalerror_assert( (c->parent ==0),"Tried to free a component that is still attached to a parent component"); @@ -198,19 +230,22 @@ icalcomponent_free (icalcomponent* component) } #endif - if(component != 0 ){ + if(c != 0 ){ - while( (prop=pvl_pop(c->properties)) != 0){ - assert(prop != 0); - icalproperty_set_parent(prop,0); - icalproperty_free(prop); - } - - pvl_free(c->properties); + if ( c->properties != 0 ) + { + while( (prop=pvl_pop(c->properties)) != 0){ + assert(prop != 0); + icalproperty_set_parent(prop,0); + icalproperty_free(prop); + } + pvl_free(c->properties); + } + while( (comp=pvl_data(pvl_head(c->components))) != 0){ assert(comp!=0); - icalcomponent_remove_component(component,comp); + icalcomponent_remove_component(c,comp); icalcomponent_free(comp); } @@ -220,6 +255,9 @@ icalcomponent_free (icalcomponent* component) free(c->x_name); } + if (c->timezones) + icaltimezone_array_free (c->timezones); + c->kind = ICAL_NO_COMPONENT; c->properties = 0; c->property_iterator = 0; @@ -227,36 +265,41 @@ icalcomponent_free (icalcomponent* component) c->component_iterator = 0; c->x_name = 0; c->id[0] = 'X'; + c->timezones = NULL; free(c); } } char* -icalcomponent_as_ical_string (icalcomponent* component) +icalcomponent_as_ical_string (icalcomponent* impl) { char* buf, *out_buf; const char* tmp_buf; size_t buf_size = 1024; char* buf_ptr = 0; pvl_elem itr; - struct icalcomponent_impl *impl = (struct icalcomponent_impl*)component; +/* WIN32 automatically adds the \r, Anybody else need it? #ifdef ICAL_UNIX_NEWLINE +*/ char newline[] = "\n"; +/* #else - char newline[] = "\n"; + char newline[] = "\r\n"; #endif +*/ + icalcomponent *c; icalproperty *p; - icalcomponent_kind kind = icalcomponent_isa(component); + icalcomponent_kind kind = icalcomponent_isa(impl); const char* kind_string; buf = icalmemory_new_buffer(buf_size); buf_ptr = buf; - icalerror_check_arg_rz( (component!=0), "component"); + icalerror_check_arg_rz( (impl!=0), "component"); icalerror_check_arg_rz( (kind!=ICAL_NO_COMPONENT), "component kind is ICAL_NO_COMPONENT"); kind_string = icalcomponent_kind_to_string(kind); @@ -267,13 +310,12 @@ icalcomponent_as_ical_string (icalcomponent* component) icalmemory_append_string(&buf, &buf_ptr, &buf_size, kind_string); icalmemory_append_string(&buf, &buf_ptr, &buf_size, newline); - + + for( itr = pvl_head(impl->properties); itr != 0; itr = pvl_next(itr)) - { - // printf("3333calcomponent_as_ical_string System Timezone2: %s %s \n", *tzname, getenv("TZ") ); - + { p = (icalproperty*)pvl_data(itr); icalerror_assert((p!=0),"Got a null property"); @@ -286,19 +328,19 @@ icalcomponent_as_ical_string (icalcomponent* component) for( itr = pvl_head(impl->components); itr != 0; itr = pvl_next(itr)) - { - + { c = (icalcomponent*)pvl_data(itr); tmp_buf = icalcomponent_as_ical_string(c); + icalmemory_append_string(&buf, &buf_ptr, &buf_size, tmp_buf); } - icalmemory_append_string(&buf, &buf_ptr, &buf_size, "END:"); //tzset(); + icalmemory_append_string(&buf, &buf_ptr, &buf_size, "END:"); icalmemory_append_string(&buf, &buf_ptr, &buf_size, icalcomponent_kind_to_string(kind)); - icalmemory_append_string(&buf, &buf_ptr, &buf_size, newline); //tzset(); + icalmemory_append_string(&buf, &buf_ptr, &buf_size, newline); out_buf = icalmemory_tmp_copy(buf); free(buf); @@ -310,11 +352,8 @@ icalcomponent_as_ical_string (icalcomponent* component) int icalcomponent_is_valid (icalcomponent* component) { - struct icalcomponent_impl *impl = (struct icalcomponent_impl *)component; - - - if ( (strcmp(impl->id,"comp") == 0) && - impl->kind != ICAL_NO_COMPONENT){ + if ( (strcmp(component->id,"comp") == 0) && + component->kind != ICAL_NO_COMPONENT){ return 1; } else { return 0; @@ -324,14 +363,13 @@ icalcomponent_is_valid (icalcomponent* component) icalcomponent_kind -icalcomponent_isa (icalcomponent* component) +icalcomponent_isa (const icalcomponent* component) { - struct icalcomponent_impl *impl = (struct icalcomponent_impl *)component; - icalerror_check_arg_rz( (component!=0), "component"); + icalerror_check_arg_rx( (component!=0), "component", ICAL_NO_COMPONENT); if(component != 0) { - return impl->kind; + return component->kind; } return ICAL_NO_COMPONENT; @@ -341,7 +379,7 @@ icalcomponent_isa (icalcomponent* component) int icalcomponent_isa_component (void* component) { - struct icalcomponent_impl *impl = (struct icalcomponent_impl *)component; + icalcomponent *impl = component; icalerror_check_arg_rz( (component!=0), "component"); @@ -353,63 +391,32 @@ icalcomponent_isa_component (void* component) } -int icalcomponent_property_sorter(void *a, void *b) -{ - icalproperty_kind kinda, kindb; - const char *ksa, *ksb; - - kinda = icalproperty_isa((icalproperty*)a); - kindb = icalproperty_isa((icalproperty*)b); - - ksa = icalproperty_kind_to_string(kinda); - ksb = icalproperty_kind_to_string(kindb); - - return strcmp(ksa,ksb); -} - - void icalcomponent_add_property (icalcomponent* component, icalproperty* property) { - struct icalcomponent_impl *impl; - icalerror_check_arg_rv( (component!=0), "component"); icalerror_check_arg_rv( (property!=0), "property"); - impl = (struct icalcomponent_impl*)component; - icalerror_assert( (!icalproperty_get_parent(property)),"The property has already been added to a component. Remove the property with icalcomponent_remove_property before calling icalcomponent_add_property"); icalproperty_set_parent(property,component); -#ifdef ICAL_INSERT_ORDERED - pvl_insert_ordered(impl->properties, - icalcomponent_property_sorter,property); -#else - pvl_push(impl->properties,property); -#endif - + pvl_push(component->properties,property); } void icalcomponent_remove_property (icalcomponent* component, icalproperty* property) { - struct icalcomponent_impl *impl; pvl_elem itr, next_itr; - struct icalproperty_impl *pimpl; icalerror_check_arg_rv( (component!=0), "component"); icalerror_check_arg_rv( (property!=0), "property"); - impl = (struct icalcomponent_impl*)component; - - pimpl = (struct icalproperty_impl*)property; - icalerror_assert( (icalproperty_get_parent(property)),"The property is not a member of a component"); - for( itr = pvl_head(impl->properties); + for( itr = pvl_head(component->properties); itr != 0; itr = next_itr) { @@ -417,11 +424,11 @@ icalcomponent_remove_property (icalcomponent* component, icalproperty* property) if( pvl_data(itr) == (void*)property ){ - if (impl->property_iterator == itr){ - impl->property_iterator = pvl_next(itr); + if (component->property_iterator == itr){ + component->property_iterator = pvl_next(itr); } - pvl_remove( impl->properties, itr); + pvl_remove( component->properties, itr); icalproperty_set_parent(property,0); } } @@ -433,11 +440,10 @@ icalcomponent_count_properties (icalcomponent* component, { int count=0; pvl_elem itr; - struct icalcomponent_impl *impl = (struct icalcomponent_impl*)component; icalerror_check_arg_rz( (component!=0), "component"); - for( itr = pvl_head(impl->properties); + for( itr = pvl_head(component->properties); itr != 0; itr = pvl_next(itr)) { @@ -454,23 +460,19 @@ icalcomponent_count_properties (icalcomponent* component, icalproperty* icalcomponent_get_current_property (icalcomponent* component) { - - struct icalcomponent_impl *c = (struct icalcomponent_impl*)component; icalerror_check_arg_rz( (component!=0),"component"); - if ((c->property_iterator==0)){ + if ((component->property_iterator==0)){ return 0; } - return (icalproperty*) pvl_data(c->property_iterator); - + return (icalproperty*) pvl_data(component->property_iterator); } icalproperty* -icalcomponent_get_first_property (icalcomponent* component, icalproperty_kind kind) +icalcomponent_get_first_property (icalcomponent* c, icalproperty_kind kind) { - struct icalcomponent_impl *c = (struct icalcomponent_impl*)component; - icalerror_check_arg_rz( (component!=0),"component"); + icalerror_check_arg_rz( (c!=0),"component"); for( c->property_iterator = pvl_head(c->properties); c->property_iterator != 0; @@ -487,10 +489,9 @@ icalcomponent_get_first_property (icalcomponent* component, icalproperty_kind ki } icalproperty* -icalcomponent_get_next_property (icalcomponent* component, icalproperty_kind kind) +icalcomponent_get_next_property (icalcomponent* c, icalproperty_kind kind) { - struct icalcomponent_impl *c = (struct icalcomponent_impl*)component; - icalerror_check_arg_rz( (component!=0),"component"); + icalerror_check_arg_rz( (c!=0),"component"); if (c->property_iterator == 0){ return 0; @@ -519,37 +520,57 @@ icalcomponent_get_properties (icalcomponent* component, icalproperty_kind kind); void icalcomponent_add_component (icalcomponent* parent, icalcomponent* child) { - struct icalcomponent_impl *impl, *cimpl; - icalerror_check_arg_rv( (parent!=0), "parent"); icalerror_check_arg_rv( (child!=0), "child"); - impl = (struct icalcomponent_impl*)parent; - cimpl = (struct icalcomponent_impl*)child; - - if (cimpl->parent !=0) { + if (child->parent !=0) { icalerror_set_errno(ICAL_USAGE_ERROR); } - cimpl->parent = parent; + child->parent = parent; + + pvl_push(parent->components,child); - pvl_push(impl->components,child); + /* If the new component is a VTIMEZONE, add it to our array. */ + if (child->kind == ICAL_VTIMEZONE_COMPONENT) { + /* FIXME: Currently we are also creating this array when loading in + a builtin VTIMEZONE, when we don't need it. */ + if (!parent->timezones) + parent->timezones = icaltimezone_array_new (); + + icaltimezone_array_append_from_vtimezone (parent->timezones, child); + + /* Flag that we need to sort it before doing any binary searches. */ + parent->timezones_sorted = 0; + } } void icalcomponent_remove_component (icalcomponent* parent, icalcomponent* child) { - struct icalcomponent_impl *impl,*cimpl; pvl_elem itr, next_itr; icalerror_check_arg_rv( (parent!=0), "parent"); icalerror_check_arg_rv( (child!=0), "child"); - impl = (struct icalcomponent_impl*)parent; - cimpl = (struct icalcomponent_impl*)child; - - for( itr = pvl_head(impl->components); + /* If the component is a VTIMEZONE, remove it from our array as well. */ + if (child->kind == ICAL_VTIMEZONE_COMPONENT) { + icaltimezone *zone; + int i, num_elements; + + num_elements = parent->timezones ? parent->timezones->num_elements : 0; + for (i = 0; i < num_elements; i++) { + zone = icalarray_element_at (parent->timezones, i); + if (icaltimezone_get_component (zone) == child) { + icaltimezone_free (zone, 0); + icalarray_remove_element_at (parent->timezones, i); + break; + } + } + } + + for( itr = pvl_head(parent->components); itr != 0; itr = next_itr) { @@ -557,16 +578,16 @@ icalcomponent_remove_component (icalcomponent* parent, icalcomponent* child) if( pvl_data(itr) == (void*)child ){ - if (impl->component_iterator == itr){ + if (parent->component_iterator == itr){ /* Don't let the current iterator become invalid */ /* HACK. The semantics for this are troubling. */ - impl->component_iterator = - pvl_next(impl->component_iterator); + parent->component_iterator = + pvl_next(parent->component_iterator); } - pvl_remove( impl->components, itr); - cimpl->parent = 0; + pvl_remove( parent->components, itr); + child->parent = 0; break; } } @@ -579,11 +600,10 @@ icalcomponent_count_components (icalcomponent* component, { int count=0; pvl_elem itr; - struct icalcomponent_impl *impl = (struct icalcomponent_impl*)component; icalerror_check_arg_rz( (component!=0), "component"); - for( itr = pvl_head(impl->components); + for( itr = pvl_head(component->components); itr != 0; itr = pvl_next(itr)) { @@ -599,24 +619,20 @@ icalcomponent_count_components (icalcomponent* component, icalcomponent* icalcomponent_get_current_component(icalcomponent* component) { - struct icalcomponent_impl *c = (struct icalcomponent_impl*)component; - icalerror_check_arg_rz( (component!=0),"component"); - if (c->component_iterator == 0){ + if (component->component_iterator == 0){ return 0; } - return (icalcomponent*) pvl_data(c->component_iterator); + return (icalcomponent*) pvl_data(component->component_iterator); } icalcomponent* -icalcomponent_get_first_component (icalcomponent* component, +icalcomponent_get_first_component (icalcomponent* c, icalcomponent_kind kind) { - struct icalcomponent_impl *c = (struct icalcomponent_impl*)component; - - icalerror_check_arg_rz( (component!=0),"component"); + icalerror_check_arg_rz( (c!=0),"component"); for( c->component_iterator = pvl_head(c->components); c->component_iterator != 0; @@ -635,11 +651,9 @@ icalcomponent_get_first_component (icalcomponent* component, icalcomponent* -icalcomponent_get_next_component (icalcomponent* component, icalcomponent_kind kind) +icalcomponent_get_next_component (icalcomponent* c, icalcomponent_kind kind) { - struct icalcomponent_impl *c = (struct icalcomponent_impl*)component; - - icalerror_check_arg_rz( (component!=0),"component"); + icalerror_check_arg_rz( (c!=0),"component"); if (c->component_iterator == 0){ return 0; @@ -673,83 +687,52 @@ icalcomponent* icalcomponent_get_first_real_component(icalcomponent *c) if(kind == ICAL_VEVENT_COMPONENT || kind == ICAL_VTODO_COMPONENT || kind == ICAL_VJOURNAL_COMPONENT || - kind == ICAL_VFREEBUSY_COMPONENT ){ + kind == ICAL_VFREEBUSY_COMPONENT || + kind == ICAL_VQUERY_COMPONENT || + kind == ICAL_VAGENDA_COMPONENT){ return comp; } } return 0; } -time_t icalcomponent_convert_time(icalproperty *p) -{ - struct icaltimetype sict; - time_t convt; - icalproperty *tzp; - - - /* Though it says _dtstart, it will work for dtend too */ - sict = icalproperty_get_dtstart(p); - - tzp = icalproperty_get_first_parameter(p,ICAL_TZID_PARAMETER); - - if (sict.is_utc == 1 && tzp != 0){ - icalerror_warn("icalcomponent_get_span: component has a UTC DTSTART with a timezone specified "); - icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR); - return 0; - } - if(sict.is_utc == 1){ - /* _as_timet will use gmtime() to do the conversion */ - convt = icaltime_as_timet(sict); - -#ifdef TEST_CONVERT_TIME - printf("convert time: use as_timet:\n %s\n %s", - icalproperty_as_ical_string(p), ctime(&convt)); -#endif - - } else if (sict.is_utc == 0 && tzp == 0 ) { - time_t offset; - - /* _as_timet will use localtime() to do the conversion */ - convt = icaltime_as_timet(sict); - offset = icaltime_utc_offset(sict,0); - convt += offset; - -#ifdef TEST_CONVERT_TIME - printf("convert time: use as_timet and adjust:\n %s\n %s", - icalproperty_as_ical_string(p), ctime(&convt)); -#endif - } else { - /* Convert the time to UTC for the named timezone*/ - const char* timezone = icalparameter_get_tzid(tzp); - convt = icaltime_as_timet(icaltime_as_utc(sict,timezone)); - -#ifdef TEST_CONVERT_TIME - printf("convert time: use _as_utc:\n %s\n %s", - icalproperty_as_ical_string(p), ctime(&convt)); -#endif - } - - return convt; -} -struct icaltime_span icalcomponent_get_span(icalcomponent* comp) +/** @brief Get the timespan covered by this component, in UTC + * (deprecated) + * + * see icalcomponent_foreach_recurrence() for a better way to + * extract spans from an component. + * + * This method can be called on either a VCALENDAR or any real + * component. If the VCALENDAR contains no real component, but + * contains a VTIMEZONE, we return that span instead. + * This might not be a desirable behavior; we keep it for now + * for backward compatibility, but it might be deprecated at a + * future time. + * + * FIXME this API needs to be clarified. DTEND is defined as the + * first available time after the end of this event, so the span + * should actually end 1 second before DTEND. + */ + +icaltime_span icalcomponent_get_span(icalcomponent* comp) { icalcomponent *inner; - icalproperty *p, *duration; icalcomponent_kind kind; - struct icaltime_span span; - struct icaltimetype start; + icaltime_span span; + struct icaltimetype start, end; span.start = 0; span.end = 0; span.is_busy= 1; /* initial Error checking */ + if (comp == NULL) { + return span; + } -/* icalerror_check_arg_rz( (comp!=0),"comp");*/ - + /* FIXME this might go away */ kind = icalcomponent_isa(comp); - if(kind == ICAL_VCALENDAR_COMPONENT){ inner = icalcomponent_get_first_real_component(comp); @@ -781,67 +764,329 @@ struct icaltime_span icalcomponent_get_span(icalcomponent* comp) } + /* Get to work. starting with DTSTART */ + start = icalcomponent_get_dtstart(comp); + if (icaltime_is_null_time(start)) { + return span; + } + span.start = icaltime_as_timet_with_zone(start, + icaltimezone_get_utc_timezone()); + /* The end time could be specified as either a DTEND or a DURATION */ + /* icalcomponent_get_dtend takes care of these cases. */ + end = icalcomponent_get_dtend(comp); + if (icaltime_is_null_time(end)) { + if (!icaltime_is_date(start)) { + /* If dtstart is a DATE-TIME and there is no DTEND nor DURATION + it takes no time */ + span.start = 0; + return span; + } else { + end = start; + } + } - /* Get to work. starting with DTSTART */ + span.end = icaltime_as_timet_with_zone(end, + icaltimezone_get_utc_timezone()); + if (icaltime_is_date(start)) { + /* Until the end of the day*/ + span.end += 60*60*24 - 1; + } - p = icalcomponent_get_first_property(inner, ICAL_DTSTART_PROPERTY); + return span; - if (p ==0 ) { - icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR); - /*icalerror_warn("icalcomponent_get_span: component has no DTSTART time");*/ - return span; +} + +/** + * Decide if this recurrance is acceptable + * + * @param comp A valid icalcomponent. + * @param dtstart The base dtstart value for this component. + * @param recurtime The time to test against. + * + * @return true if the recurrence value is excluded, false otherwise. + * + * This function decides if a specific recurrence value is + * excluded by EXRULE or EXDATE properties. + * + * It's not the most efficient code. You might get better performance + * if you assume that recurtime is always increasing for each + * call. Then you could: + * + * - sort the EXDATE values + * - save the state of each EXRULE iterator for the next call. + * + * In this case though you don't need to worry how you call this + * function. It will always return the correct result. + */ + +int icalproperty_recurrence_is_excluded(icalcomponent *comp, + struct icaltimetype *dtstart, + struct icaltimetype *recurtime) { + icalproperty *exdate, *exrule; + + if (comp == NULL || + dtstart == NULL || + recurtime == NULL || + icaltime_is_null_time(*recurtime)) + /* BAD DATA */ + return 1; + + /** first test against the exdate values **/ + for (exdate = icalcomponent_get_first_property(comp,ICAL_EXDATE_PROPERTY); + exdate != NULL; + exdate = icalcomponent_get_next_property(comp,ICAL_EXDATE_PROPERTY)) { + + struct icaltimetype exdatetime = icalproperty_get_exdate(exdate); + + if (icaltime_compare(*recurtime, exdatetime) == 0) { + /** MATCHED **/ + return 1; + } + } + + /** Now test against the EXRULEs **/ + for (exrule = icalcomponent_get_first_property(comp,ICAL_EXRULE_PROPERTY); + exdate != NULL; + exdate = icalcomponent_get_next_property(comp,ICAL_EXRULE_PROPERTY)) { + + struct icalrecurrencetype recur = icalproperty_get_exrule(exrule); + icalrecur_iterator *exrule_itr = icalrecur_iterator_new(recur, *dtstart); + struct icaltimetype exrule_time; + + while (1) { + int result; + exrule_time = icalrecur_iterator_next(exrule_itr); + + if (icaltime_is_null_time(exrule_time)) + break; + + result = icaltime_compare(*recurtime, exrule_time); + if (result == 0) { + icalrecur_iterator_free(exrule_itr); + return 1; /** MATCH **/ + } + if (result == 1) + break; /** exrule_time > recurtime **/ } + icalrecur_iterator_free(exrule_itr); + } - start = icalproperty_get_dtstart(p); + return 0; /** no matches **/ +} - icalerror_clear_errno(); +/** + * @brief Return the busy status based on the TRANSP property. + * + * @param comp A valid icalcomponent. + * + * @return 1 if the event is a busy item, 0 if it is not. + */ - span.start = icalcomponent_convert_time(p); +static int icalcomponent_is_busy(icalcomponent *comp) { + icalproperty *transp; + enum icalproperty_status status; + int ret = 1; -#ifdef TEST_CONVERT_TIME - printf("convert time:\n %s %s", - icalproperty_as_ical_string(p), ctime(&span.start)); -#endif + /** @todo check access control here, converting busy->free if the + permissions do not allow access... */ - if(icalerrno != ICAL_NO_ERROR){ - span.start = 0; - return span; + /* Is this a busy time? Check the TRANSP property */ + transp = icalcomponent_get_first_property(comp, ICAL_TRANSP_PROPERTY); + + if (transp) { + icalvalue *transp_val = icalproperty_get_value(transp); + + switch (icalvalue_get_transp(transp_val)) { + case ICAL_TRANSP_OPAQUE: + case ICAL_TRANSP_OPAQUENOCONFLICT: + case ICAL_TRANSP_NONE: + ret = 1; + break; + case ICAL_TRANSP_TRANSPARENT: + case ICAL_TRANSP_TRANSPARENTNOCONFLICT: + ret = 0; + break; + default: + ret = 0; + break; } + } + status = icalcomponent_get_status(comp); + if (ret && status) { + switch (status) { + case ICAL_STATUS_CANCELLED: + case ICAL_STATUS_TENTATIVE: + ret = 0; + break; + default: + break; + } + } + return(ret); +} - /* The end time could be specified as either a DTEND or a DURATION */ - p = icalcomponent_get_first_property(inner, ICAL_DTEND_PROPERTY); - duration = icalcomponent_get_first_property(inner, ICAL_DURATION_PROPERTY); - if (p==0 && duration == 0 && start.is_date != 1) { - icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR); - /*icalerror_warn("icalcomponent_get_span: component has neither DTEND nor DURATION time");*/ - span.start = 0; - return span; - } - if (p!=0){ - span.end = icalcomponent_convert_time(p); - } else if (start.is_date == 1) { - /* Duration is all day */ - span.end = span.start + 60*60*24; - } else { - /* Use the duration */ - struct icaldurationtype dur; - time_t durt; - - - dur = icalproperty_get_duration(duration); - durt = icaldurationtype_as_int(dur); - span.end = span.start+durt; +/** + * @brief cycle through all recurrances of an event + * + * @param comp A valid VEVENT component + * @param start Ignore timespans before this + * @param end Ignore timespans after this + * @param callback Function called for each timespan within the range + * @param callback_data Pointer passed back to the callback function + * + * This function will call the specified callback function for once + * for the base value of DTSTART, and foreach recurring date/time + * value. + * + * It will filter out events that are specified as an EXDATE or an EXRULE. + * + * @todo We do not filter out duplicate RRULES/RDATES + * @todo We do not handle RDATEs with explicit periods + */ + +void icalcomponent_foreach_recurrence(icalcomponent* comp, + struct icaltimetype start, + struct icaltimetype end, + void (*callback)(icalcomponent *comp, + struct icaltime_span *span, + void *data), + void *callback_data) +{ + struct icaltimetype dtstart, dtend; + icaltime_span recurspan, basespan, limit_span; + time_t limit_start, limit_end; + int dtduration; + icalproperty *rrule, *rdate; + struct icaldurationtype dur; + pvl_elem property_iterator; /* for saving the iterator */ + + if (comp == NULL || callback == NULL) + return; + + dtstart = icalcomponent_get_dtstart(comp); + + if (icaltime_is_null_time(dtstart)) + return; + + + /* The end time could be specified as either a DTEND or a DURATION */ + /* icalcomponent_get_dtend takes care of these cases. */ + dtend = icalcomponent_get_dtend(comp); + + /* Now set up the base span for this item, corresponding to the + base DTSTART and DTEND */ + basespan = icaltime_span_new(dtstart, dtend, 1); + + basespan.is_busy = icalcomponent_is_busy(comp); + + + /** Calculate the ceiling and floor values.. **/ + limit_start = icaltime_as_timet_with_zone(start, icaltimezone_get_utc_timezone()); + if (!icaltime_is_null_time(end)) + limit_end = icaltime_as_timet_with_zone(end, icaltimezone_get_utc_timezone()); + else + limit_end = INT_MAX; /* max 32 bit time_t */ + + limit_span.start = limit_start; + limit_span.end = limit_end; + + + /* Do the callback for the initial DTSTART entry */ + + if (!icalproperty_recurrence_is_excluded(comp, &dtstart, &dtstart)) { + /** call callback action **/ + if (icaltime_span_overlaps(&basespan, &limit_span)) + (*callback) (comp, &basespan, callback_data); + } + + recurspan = basespan; + dtduration = basespan.end - basespan.start; + + /* Now cycle through the rrule entries */ + for (rrule = icalcomponent_get_first_property(comp,ICAL_RRULE_PROPERTY); + rrule != NULL; + rrule = icalcomponent_get_next_property(comp,ICAL_RRULE_PROPERTY)) { + + struct icalrecurrencetype recur = icalproperty_get_rrule(rrule); + icalrecur_iterator *rrule_itr = icalrecur_iterator_new(recur, dtstart); + struct icaltimetype rrule_time = icalrecur_iterator_next(rrule_itr); + /** note that icalrecur_iterator_next always returns dtstart + the first time.. **/ + + while (1) { + rrule_time = icalrecur_iterator_next(rrule_itr); + + if (icaltime_is_null_time(rrule_time)) + break; + + dur = icaltime_subtract(rrule_time, dtstart); + + recurspan.start = basespan.start + icaldurationtype_as_int(dur); + recurspan.end = recurspan.start + dtduration; + + /** save the iterator ICK! **/ + property_iterator = comp->property_iterator; + + if (!icalproperty_recurrence_is_excluded(comp, &dtstart, &rrule_time)) { + /** call callback action **/ + if (icaltime_span_overlaps(&recurspan, &limit_span)) + (*callback) (comp, &recurspan, callback_data); + } + comp->property_iterator = property_iterator; + } /* end of iteration over a specific RRULE */ + + icalrecur_iterator_free(rrule_itr); + } /* end of RRULE loop */ + + + /** Now process RDATE entries **/ + for (rdate = icalcomponent_get_first_property(comp,ICAL_RDATE_PROPERTY); + rdate != NULL; + rdate = icalcomponent_get_next_property(comp,ICAL_RDATE_PROPERTY)) { + + struct icaldatetimeperiodtype rdate_period = icalproperty_get_rdate(rdate); + + /** RDATES can specify raw datetimes, periods, or dates. + we only support raw datetimes for now.. + + @todo Add support for other types **/ + + if (icaltime_is_null_time(rdate_period.time)) + continue; + + dur = icaltime_subtract(rdate_period.time, dtstart); + + recurspan.start = basespan.start + icaldurationtype_as_int(dur); + recurspan.end = recurspan.start + dtduration; + + /** save the iterator ICK! **/ + property_iterator = comp->property_iterator; + + if (!icalproperty_recurrence_is_excluded(comp, &dtstart, &rdate_period.time)) { + /** call callback action **/ + (*callback) (comp, &recurspan, callback_data); } + comp->property_iterator = property_iterator; + } +} + - return span; +int icalcomponent_check_restrictions(icalcomponent* comp){ + icalerror_check_arg_rz(comp!=0,"comp"); + return icalrestriction_check(comp); } +/** @brief returns the number of errors encountered parsing the data + * + * This function counts the number times the X-LIC-ERROR occurs + * in the data structure. + */ int icalcomponent_count_errors(icalcomponent* component) { @@ -849,9 +1094,8 @@ int icalcomponent_count_errors(icalcomponent* component) icalproperty *p; icalcomponent *c; pvl_elem itr; - struct icalcomponent_impl *impl = (struct icalcomponent_impl*)component; - for( itr = pvl_head(impl->properties); + for( itr = pvl_head(component->properties); itr != 0; itr = pvl_next(itr)) { @@ -864,7 +1108,7 @@ int icalcomponent_count_errors(icalcomponent* component) } - for( itr = pvl_head(impl->components); + for( itr = pvl_head(component->components); itr != 0; itr = pvl_next(itr)) { @@ -883,9 +1127,8 @@ void icalcomponent_strip_errors(icalcomponent* component) icalproperty *p; icalcomponent *c; pvl_elem itr, next_itr; - struct icalcomponent_impl *impl = (struct icalcomponent_impl*)component; - for( itr = pvl_head(impl->properties); + for( itr = pvl_head(component->properties); itr != 0; itr = next_itr) { @@ -898,7 +1141,7 @@ void icalcomponent_strip_errors(icalcomponent* component) } } - for( itr = pvl_head(impl->components); + for( itr = pvl_head(component->components); itr != 0; itr = pvl_next(itr)) { @@ -952,6 +1195,7 @@ void icalcomponent_convert_errors(icalcomponent* component) } default: { + break; } } if (rst.code != ICAL_UNKNOWN_STATUS){ @@ -976,16 +1220,12 @@ void icalcomponent_convert_errors(icalcomponent* component) icalcomponent* icalcomponent_get_parent(icalcomponent* component) { - struct icalcomponent_impl *c = (struct icalcomponent_impl*)component; - - return c->parent; + return component->parent; } void icalcomponent_set_parent(icalcomponent* component, icalcomponent* parent) { - struct icalcomponent_impl *c = (struct icalcomponent_impl*)component; - - c->parent = parent; + component->parent = parent; } icalcompiter icalcompiter_null = {ICAL_NO_COMPONENT,0}; @@ -1004,6 +1244,7 @@ static struct icalcomponent_kind_map component_map[] = { ICAL_VTODO_COMPONENT, "VTODO" }, { ICAL_VJOURNAL_COMPONENT, "VJOURNAL" }, { ICAL_VCALENDAR_COMPONENT, "VCALENDAR" }, + { ICAL_VAGENDA_COMPONENT, "VAGENDA" }, { ICAL_VFREEBUSY_COMPONENT, "VFREEBUSY" }, { ICAL_VTIMEZONE_COMPONENT, "VTIMEZONE" }, { ICAL_VALARM_COMPONENT, "VALARM" }, @@ -1028,6 +1269,16 @@ static struct icalcomponent_kind_map component_map[] = }; +int icalcomponent_kind_is_valid(const icalcomponent_kind kind) +{ + int i = 0; + do { + if (component_map[i].kind == kind) + return 1; + } while (component_map[i++].kind != ICAL_NO_COMPONENT); + + return 0; +} const char* icalcomponent_kind_to_string(icalcomponent_kind kind) { @@ -1065,15 +1316,15 @@ icalcomponent_kind icalcomponent_string_to_kind(const char* string) icalcompiter icalcomponent_begin_component(icalcomponent* component,icalcomponent_kind kind) { - struct icalcomponent_impl *impl = (struct icalcomponent_impl*)component; - icalcompiter itr = icalcompiter_null; + icalcompiter itr; pvl_elem i; itr.kind = kind; + itr.iter = NULL; - icalerror_check_arg_re( (component!=0),"component",icalcompiter_null); + icalerror_check_arg_re(component!=0,"component",icalcompiter_null); - for( i = pvl_head(impl->components); i != 0; i = pvl_next(itr.iter)) { + for( i = pvl_head(component->components); i != 0; i = pvl_next(i)) { icalcomponent *c = (icalcomponent*) pvl_data(i); @@ -1091,15 +1342,14 @@ icalcomponent_begin_component(icalcomponent* component,icalcomponent_kind kind) icalcompiter icalcomponent_end_component(icalcomponent* component,icalcomponent_kind kind) { - struct icalcomponent_impl *impl = (struct icalcomponent_impl*)component; icalcompiter itr; pvl_elem i; itr.kind = kind; - icalerror_check_arg_re( (component!=0),"component",icalcompiter_null); + icalerror_check_arg_re(component!=0,"component",icalcompiter_null); - for( i = pvl_tail(impl->components); i != 0; i = pvl_prior(i)) { + for( i = pvl_tail(component->components); i != 0; i = pvl_prior(i)) { icalcomponent *c = (icalcomponent*) pvl_data(i); @@ -1180,140 +1430,231 @@ icalcomponent* icalcomponent_get_inner(icalcomponent* comp) } } +/** @brief sets the METHOD property to the given method + */ -void icalcomponent_set_dtstart(icalcomponent* comp, struct icaltimetype v) +void icalcomponent_set_method(icalcomponent* comp, icalproperty_method method) { + icalproperty *prop + = icalcomponent_get_first_property(comp, ICAL_METHOD_PROPERTY); - icalcomponent *inner = icalcomponent_get_inner(comp); + + if (prop == 0){ + prop = icalproperty_new_method(method); + icalcomponent_add_property(comp, prop); + } + + icalproperty_set_method(prop,method); + +} + +/** @brief returns the METHOD property + */ + +icalproperty_method icalcomponent_get_method(icalcomponent* comp) +{ icalproperty *prop - = icalcomponent_get_first_property(inner, ICAL_DTSTART_PROPERTY); + = icalcomponent_get_first_property(comp,ICAL_METHOD_PROPERTY); + if (prop == 0){ + return ICAL_METHOD_NONE; + } + + return icalproperty_get_method(prop); +} + +#define ICALSETUPSET(p_kind) \ + icalcomponent *inner; \ + icalproperty *prop; \ + icalerror_check_arg_rv(comp!=0,"comp");\ + inner = icalcomponent_get_inner(comp); \ + if(inner == 0){\ + icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);\ + return;\ + }\ + prop = icalcomponent_get_first_property(inner, p_kind); + + +/** @brief Set DTSTART property to given icaltime + * + * This method respects the icaltime type (DATE vs DATE-TIME) and + * timezone (or lack thereof). + */ +void icalcomponent_set_dtstart(icalcomponent* comp, struct icaltimetype v) +{ + char *tzid; + ICALSETUPSET(ICAL_DTSTART_PROPERTY); if (prop == 0){ prop = icalproperty_new_dtstart(v); icalcomponent_add_property(inner, prop); + } else { + icalproperty_remove_parameter_by_kind(prop, ICAL_TZID_PARAMETER); } icalproperty_set_dtstart(prop,v); + if ((tzid = icaltime_get_tzid(v)) != NULL && !icaltime_is_utc(v)) { + icalproperty_add_parameter(prop, icalparameter_new_tzid(tzid)); + } } +/** @brief Get a DATE or DATE-TIME property as an icaltime + * + * If the property is a DATE-TIME with a timezone parameter and a + * corresponding VTIMEZONE is present in the component, the + * returned component will already be in the correct timezone; + * otherwise the caller is responsible for converting it. + * + * FIXME this is useless until we can flag the failure + */ +static struct icaltimetype +icalcomponent_get_datetime(icalcomponent *comp, icalproperty *prop) { + + icalparameter *param; + struct icaltimetype ret; + + ret = icalvalue_get_datetime(icalproperty_get_value(prop)); + + if ((param = icalproperty_get_first_parameter(prop, ICAL_TZID_PARAMETER)) + != NULL) { + const char *tzid = icalparameter_get_tzid(param); + icaltimezone *tz; + + if ((tz = icalcomponent_get_timezone(comp, tzid)) != NULL) { + icaltime_set_timezone(&ret, tz); + } + } + + return ret; +} +/** @brief Get DTSTART property as an icaltime + * + * If DTSTART is a DATE-TIME with a timezone parameter and a + * corresponding VTIMEZONE is present in the component, the + * returned component will already be in the correct timezone; + * otherwise the caller is responsible for converting it. + * + * FIXME this is useless until we can flag the failure + */ struct icaltimetype icalcomponent_get_dtstart(icalcomponent* comp) { icalcomponent *inner = icalcomponent_get_inner(comp); - icalproperty *prop - = icalcomponent_get_first_property(inner,ICAL_DTSTART_PROPERTY); + icalproperty *prop; + prop = icalcomponent_get_first_property(inner,ICAL_DTSTART_PROPERTY); if (prop == 0){ return icaltime_null_time(); } - - return icalproperty_get_dtstart(prop); -} + return icalcomponent_get_datetime(comp, prop); +} +/** @brief Get DTEND property as an icaltime + * + * If a DTEND property is not present but a DURATION is, we use + * that to determine the proper end. + * + * If DTSTART is a DATE-TIME with a timezone parameter and a + * corresponding VTIMEZONE is present in the component, the + * returned component will already be in the correct timezone; + * otherwise the caller is responsible for converting it. + * + * FIXME this is useless until we can flag the failure + */ struct icaltimetype icalcomponent_get_dtend(icalcomponent* comp) { icalcomponent *inner = icalcomponent_get_inner(comp); - icalproperty *end_prop = icalcomponent_get_first_property(inner,ICAL_DTEND_PROPERTY); - icalproperty *dur_prop = icalcomponent_get_first_property(inner, ICAL_DURATION_PROPERTY); + struct icaltimetype ret = icaltime_null_time(); - - if( end_prop == 0 && dur_prop == 0){ - return icaltime_null_time(); - } else if ( end_prop != 0) { - return icalproperty_get_dtend(end_prop); + if ( end_prop != 0) { + ret = icalcomponent_get_datetime(comp, end_prop); } else if ( dur_prop != 0) { - + struct icaltimetype start = icalcomponent_get_dtstart(inner); struct icaldurationtype duration = icalproperty_get_duration(dur_prop); - - struct icaltimetype end = icaltime_add(start,duration); - return end; - - } else { - /* Error, both duration and dtend have been specified */ - icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR); - return icaltime_null_time(); + struct icaltimetype end = icaltime_add(start,duration); + ret = end; } - -} + return ret; +} +/** @brief Set DTEND property to given icaltime + * + * This method respects the icaltime type (DATE vs DATE-TIME) and + * timezone (or lack thereof). + * + * This also checks that a DURATION property isn't already there, + * and returns an error if it is. It's the caller's responsibility + * to remove it. + */ void icalcomponent_set_dtend(icalcomponent* comp, struct icaltimetype v) { - icalcomponent *inner = icalcomponent_get_inner(comp); - - icalproperty *end_prop - = icalcomponent_get_first_property(inner,ICAL_DTEND_PROPERTY); - - icalproperty *dur_prop - = icalcomponent_get_first_property(inner,ICAL_DURATION_PROPERTY); - + char *tzid; + ICALSETUPSET(ICAL_DTEND_PROPERTY); - if( end_prop == 0 && dur_prop == 0){ - end_prop = icalproperty_new_dtend(v); - icalcomponent_add_property(inner,end_prop); - } else if ( end_prop != 0) { - icalproperty_set_dtend(end_prop,v); - } else if ( dur_prop != 0) { - struct icaltimetype start = - icalcomponent_get_dtstart(inner); - - struct icaltimetype end = - icalcomponent_get_dtend(inner); + if (icalcomponent_get_first_property(inner,ICAL_DURATION_PROPERTY) + != NULL) { + icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR); + return; + } - struct icaldurationtype dur - = icaltime_subtract(end,start); + if (prop == 0) { + prop = icalproperty_new_dtend(v); + icalcomponent_add_property(inner, prop); + } else { + icalproperty_remove_parameter_by_kind(prop, ICAL_TZID_PARAMETER); + } - icalproperty_set_duration(dur_prop,dur); + icalproperty_set_dtend(prop,v); - } else { - /* Error, both duration and dtend have been specified */ - icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR); + if ((tzid = icaltime_get_tzid(v)) != NULL && !icaltime_is_utc(v)) { + icalproperty_add_parameter(prop, icalparameter_new_tzid(tzid)); } } +/** @brief Set DURATION property to given icalduration + * + * This method respects the icaltime type (DATE vs DATE-TIME) and + * timezone (or lack thereof). + * + * This also checks that a DTEND property isn't already there, + * and returns an error if it is. It's the caller's responsibility + * to remove it. + */ void icalcomponent_set_duration(icalcomponent* comp, struct icaldurationtype v) { - icalcomponent *inner = icalcomponent_get_inner(comp); - - icalproperty *end_prop - = icalcomponent_get_first_property(inner,ICAL_DTEND_PROPERTY); - - icalproperty *dur_prop - = icalcomponent_get_first_property(inner,ICAL_DURATION_PROPERTY); - - - if( end_prop == 0 && dur_prop == 0){ - dur_prop = icalproperty_new_duration(v); - icalcomponent_add_property(inner, dur_prop); - } else if ( end_prop != 0) { - struct icaltimetype start = - icalcomponent_get_dtstart(inner); - - struct icaltimetype new_end = icaltime_add(start,v); + ICALSETUPSET(ICAL_DURATION_PROPERTY); - icalproperty_set_dtend(end_prop,new_end); + if (icalcomponent_get_first_property(inner,ICAL_DTEND_PROPERTY) != NULL) { + icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR); + return; + } - } else if ( dur_prop != 0) { - icalproperty_set_duration(dur_prop,v); + if (prop == 0) { + prop = icalproperty_new_duration(v); + icalcomponent_add_property(inner, prop); } else { - /* Error, both duration and dtend have been specified */ - icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR); + icalproperty_set_duration(prop,v); } } +/** @brief Get DURATION property as an icalduration + * + * If a DURATION property is not present but a DTEND is, we use + * that to determine the proper end. + */ struct icaldurationtype icalcomponent_get_duration(icalcomponent* comp) { icalcomponent *inner = icalcomponent_get_inner(comp); @@ -1324,131 +1665,329 @@ struct icaldurationtype icalcomponent_get_duration(icalcomponent* comp) icalproperty *dur_prop = icalcomponent_get_first_property(inner,ICAL_DURATION_PROPERTY); - struct icaldurationtype null_duration; - memset(&null_duration,0,sizeof(struct icaldurationtype)); + struct icaldurationtype ret = icaldurationtype_null_duration(); + if ( dur_prop != 0 && end_prop == 0) { + ret = icalproperty_get_duration(dur_prop); - if( end_prop == 0 && dur_prop == 0){ - return null_duration; - } else if ( end_prop != 0) { + } else if ( end_prop != 0 && dur_prop == 0) { + /** + * FIXME + * We assume DTSTART and DTEND are not in different time zones. + * Does the standard actually guarantee this? + */ struct icaltimetype start = icalcomponent_get_dtstart(inner); - time_t startt = icaltime_as_timet(start); - struct icaltimetype end = icalcomponent_get_dtend(inner); - time_t endt = icaltime_as_timet(end); - - return icaldurationtype_from_int(endt-startt); - } else if ( dur_prop != 0) { - return icalproperty_get_duration(dur_prop); + + ret = icaltime_subtract(end, start); } else { /* Error, both duration and dtend have been specified */ icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR); - return null_duration; } + return ret; } -void icalcomponent_set_method(icalcomponent* comp, icalproperty_method method) +void icalcomponent_set_dtstamp(icalcomponent* comp, struct icaltimetype v) { - icalproperty *prop - = icalcomponent_get_first_property(comp, ICAL_METHOD_PROPERTY); + ICALSETUPSET(ICAL_DTSTAMP_PROPERTY); if (prop == 0){ - prop = icalproperty_new_method(method); - icalcomponent_add_property(comp, prop); + prop = icalproperty_new_dtstamp(v); + icalcomponent_add_property(inner, prop); } - icalproperty_set_method(prop,method); + icalproperty_set_dtstamp(prop,v); } -icalproperty_method icalcomponent_get_method(icalcomponent* comp) + +struct icaltimetype icalcomponent_get_dtstamp(icalcomponent* comp) { + icalcomponent *inner = icalcomponent_get_inner(comp); icalproperty *prop - = icalcomponent_get_first_property(comp,ICAL_METHOD_PROPERTY); + = icalcomponent_get_first_property(inner,ICAL_DTSTAMP_PROPERTY); if (prop == 0){ - return ICAL_METHOD_NONE; + return icaltime_null_time(); } - return icalproperty_get_method(prop); + return icalproperty_get_dtstamp(prop); } -void icalcomponent_set_dtstamp(icalcomponent* comp, struct icaltimetype v) + +void icalcomponent_set_summary(icalcomponent* comp, const char* v) +{ + ICALSETUPSET(ICAL_SUMMARY_PROPERTY) + + if (prop == 0){ + prop = icalproperty_new_summary(v); + icalcomponent_add_property(inner, prop); + } + + icalproperty_set_summary(prop,v); +} + + +const char* icalcomponent_get_summary(icalcomponent* comp) { + icalcomponent *inner; + icalproperty *prop; + icalerror_check_arg_rz(comp!=0,"comp"); - icalcomponent *inner = icalcomponent_get_inner(comp); - icalproperty *prop - = icalcomponent_get_first_property(inner, ICAL_DTSTAMP_PROPERTY); + inner = icalcomponent_get_inner(comp); + + if(inner == 0){ + icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR); + return 0; + } + prop= icalcomponent_get_first_property(inner,ICAL_SUMMARY_PROPERTY); if (prop == 0){ - prop = icalproperty_new_dtstamp(v); - icalcomponent_add_property(inner, prop); + return 0; } - icalproperty_set_dtstamp(prop,v); + return icalproperty_get_summary(prop); } - -struct icaltimetype icalcomponent_get_dtstamp(icalcomponent* comp) +void icalcomponent_set_comment(icalcomponent* comp, const char* v) { - icalcomponent *inner = icalcomponent_get_inner(comp); - icalproperty *prop - = icalcomponent_get_first_property(inner,ICAL_DTSTAMP_PROPERTY); + ICALSETUPSET(ICAL_COMMENT_PROPERTY); if (prop == 0){ - return icaltime_null_time(); + prop = icalproperty_new_comment(v); + icalcomponent_add_property(inner, prop); } - - return icalproperty_get_dtstamp(prop); + + icalproperty_set_summary(prop,v); + } +const char* icalcomponent_get_comment(icalcomponent* comp){ + icalcomponent *inner; + icalproperty *prop; + icalerror_check_arg_rz(comp!=0,"comp"); + inner = icalcomponent_get_inner(comp); -void icalcomponent_set_summary(icalcomponent* comp, const char* v) + if(inner == 0){ + icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR); + return 0; + } + + prop= icalcomponent_get_first_property(inner,ICAL_COMMENT_PROPERTY); + + if (prop == 0){ + return 0; + } + + return icalproperty_get_comment(prop); +} + +void icalcomponent_set_uid(icalcomponent* comp, const char* v) { - icalcomponent *inner = icalcomponent_get_inner(comp); - icalproperty *prop - = icalcomponent_get_first_property(inner, ICAL_SUMMARY_PROPERTY); + ICALSETUPSET(ICAL_UID_PROPERTY); if (prop == 0){ - prop = icalproperty_new_summary(v); + prop = icalproperty_new_uid(v); icalcomponent_add_property(inner, prop); } - + icalproperty_set_summary(prop,v); + } +const char* icalcomponent_get_uid(icalcomponent* comp){ + icalcomponent *inner; + icalproperty *prop; + icalerror_check_arg_rz(comp!=0,"comp"); + inner = icalcomponent_get_inner(comp); -const char* icalcomponent_get_summary(icalcomponent* comp) -{ - icalcomponent *inner = icalcomponent_get_inner(comp); - icalproperty *prop - = icalcomponent_get_first_property(inner,ICAL_SUMMARY_PROPERTY); + if(inner == 0){ + icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR); + return 0; + } + + prop= icalcomponent_get_first_property(inner,ICAL_UID_PROPERTY); if (prop == 0){ return 0; } - return icalproperty_get_summary(prop); + return icalproperty_get_uid(prop); +} +void icalcomponent_set_recurrenceid(icalcomponent* comp, struct icaltimetype v) +{ + ICALSETUPSET(ICAL_RECURRENCEID_PROPERTY); + + if (prop == 0){ + prop = icalproperty_new_recurrenceid(v); + icalcomponent_add_property(inner, prop); + } + + icalproperty_set_recurrenceid(prop,v); } +struct icaltimetype icalcomponent_get_recurrenceid(icalcomponent* comp) +{ + icalcomponent *inner; + icalproperty *prop; + if (comp == 0) { + icalerror_set_errno(ICAL_BADARG_ERROR); + return icaltime_null_time(); + } -void icalcomponent_set_comment(icalcomponent* comp, const char* v); -const char* icalcomponent_get_comment(icalcomponent* comp); + inner = icalcomponent_get_inner(comp); -void icalcomponent_set_uid(icalcomponent* comp, const char* v); -const char* icalcomponent_get_uid(icalcomponent* comp); + if(inner == 0){ + icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR); + return icaltime_null_time(); + } -void icalcomponent_set_recurrenceid(icalcomponent* comp, - struct icaltimetype v); -struct icaltimetype icalcomponent_get_recurrenceid(icalcomponent* comp); + prop= icalcomponent_get_first_property(inner, ICAL_RECURRENCEID_PROPERTY); + if (prop == 0){ + return icaltime_null_time(); + } + + return icalproperty_get_recurrenceid(prop); +} + +void icalcomponent_set_description(icalcomponent* comp, const char* v) +{ + ICALSETUPSET(ICAL_DESCRIPTION_PROPERTY); + + if (prop == 0){ + prop = icalproperty_new_description(v); + icalcomponent_add_property(inner, prop); + } + + icalproperty_set_description(prop,v); +} +const char* icalcomponent_get_description(icalcomponent* comp) +{ + icalcomponent *inner; + icalproperty *prop; + icalerror_check_arg_rz(comp!=0,"comp"); + + inner = icalcomponent_get_inner(comp); + + if(inner == 0){ + icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR); + return 0; + } + + prop= icalcomponent_get_first_property(inner,ICAL_DESCRIPTION_PROPERTY); + + if (prop == 0){ + return 0; + } + + return icalproperty_get_description(prop); +} + +void icalcomponent_set_location(icalcomponent* comp, const char* v) +{ + ICALSETUPSET(ICAL_LOCATION_PROPERTY) + + if (prop == 0){ + prop = icalproperty_new_location(v); + icalcomponent_add_property(inner, prop); + } + + icalproperty_set_location(prop,v); +} +const char* icalcomponent_get_location(icalcomponent* comp) +{ + icalcomponent *inner; + icalproperty *prop; + icalerror_check_arg_rz(comp!=0,"comp"); + + inner = icalcomponent_get_inner(comp); + + if(inner == 0){ + icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR); + return 0; + } + + prop= icalcomponent_get_first_property(inner,ICAL_LOCATION_PROPERTY); + if (prop == 0){ + return 0; + } + + return icalproperty_get_location(prop); +} + +void icalcomponent_set_sequence(icalcomponent* comp, int v) +{ + ICALSETUPSET(ICAL_SEQUENCE_PROPERTY); + + if (prop == 0){ + prop = icalproperty_new_sequence(v); + icalcomponent_add_property(inner, prop); + } + + icalproperty_set_sequence(prop,v); + +} +int icalcomponent_get_sequence(icalcomponent* comp){ + icalcomponent *inner; + icalproperty *prop; + icalerror_check_arg_rz(comp!=0,"comp"); + + inner = icalcomponent_get_inner(comp); + + if(inner == 0){ + icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR); + return 0; + } + + prop= icalcomponent_get_first_property(inner,ICAL_SEQUENCE_PROPERTY); + + if (prop == 0){ + return 0; + } + + return icalproperty_get_sequence(prop); +} + + +void icalcomponent_set_status(icalcomponent* comp, enum icalproperty_status v) +{ + ICALSETUPSET(ICAL_STATUS_PROPERTY); + + if (prop == 0){ + prop = icalproperty_new_status(v); + icalcomponent_add_property(inner, prop); + } + + icalproperty_set_status(prop,v); + +} +enum icalproperty_status icalcomponent_get_status(icalcomponent* comp){ + icalcomponent *inner; + icalproperty *prop; + icalerror_check_arg_rz(comp!=0,"comp"); + + inner = icalcomponent_get_inner(comp); + + if(inner == 0){ + icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR); + return 0; + } + prop= icalcomponent_get_first_property(inner,ICAL_STATUS_PROPERTY); + + if (prop == 0){ + return 0; + } + + return icalproperty_get_status(prop); +} icalcomponent* icalcomponent_new_vcalendar() { @@ -1486,3 +2025,571 @@ icalcomponent* icalcomponent_new_xdaylight() { return icalcomponent_new(ICAL_XDAYLIGHT_COMPONENT); } +icalcomponent* icalcomponent_new_vagenda() +{ + return icalcomponent_new(ICAL_VAGENDA_COMPONENT); +} +icalcomponent* icalcomponent_new_vquery() +{ + return icalcomponent_new(ICAL_VQUERY_COMPONENT); +} + +/* + * Timezone stuff. + */ + + +/** + * This takes 2 VCALENDAR components and merges the second one into the first, + * resolving any problems with conflicting TZIDs. comp_to_merge will no + * longer exist after calling this function. + */ +void icalcomponent_merge_component(icalcomponent* comp, + icalcomponent* comp_to_merge) +{ + icalcomponent *subcomp, *next_subcomp; + icalarray *tzids_to_rename; + int i; + + /* Check that both components are VCALENDAR components. */ + assert (icalcomponent_isa(comp) == ICAL_VCALENDAR_COMPONENT); + assert (icalcomponent_isa(comp_to_merge) == ICAL_VCALENDAR_COMPONENT); + + /* Step through each subcomponent of comp_to_merge, looking for VTIMEZONEs. + For each VTIMEZONE found, check if we need to add it to comp and if we + need to rename it and all TZID references to it. */ + tzids_to_rename = icalarray_new (sizeof (char*), 16); + subcomp = icalcomponent_get_first_component (comp_to_merge, + ICAL_VTIMEZONE_COMPONENT); + while (subcomp) { + next_subcomp = icalcomponent_get_next_component (comp_to_merge, + ICAL_VTIMEZONE_COMPONENT); + /* This will add the VTIMEZONE to comp, if necessary, and also update + the array of TZIDs we need to rename. */ + icalcomponent_merge_vtimezone (comp, subcomp, tzids_to_rename); + /* FIXME: Handle possible NEWFAILED error. */ + + subcomp = next_subcomp; + } + + /* If we need to do any renaming of TZIDs, do it now. */ + if (tzids_to_rename->num_elements != 0) { + icalcomponent_rename_tzids (comp_to_merge, tzids_to_rename); + + /* Now free the tzids_to_rename array. */ + for (i = 0; i < tzids_to_rename->num_elements; i++) { + free (icalarray_element_at (tzids_to_rename, i)); + } + icalarray_free (tzids_to_rename); + } + + /* Now move all the components from comp_to_merge to comp, excluding + VTIMEZONE components. */ + subcomp = icalcomponent_get_first_component (comp_to_merge, + ICAL_ANY_COMPONENT); + while (subcomp) { + next_subcomp = icalcomponent_get_next_component (comp_to_merge, + ICAL_ANY_COMPONENT); + if (icalcomponent_isa(subcomp) != ICAL_VTIMEZONE_COMPONENT) { + icalcomponent_remove_component (comp_to_merge, subcomp); + icalcomponent_add_component (comp, subcomp); + } + subcomp = next_subcomp; + } + + /* Free comp_to_merge. We have moved most of the subcomponents over to + comp now. */ + icalcomponent_free (comp_to_merge); +} + + +static void icalcomponent_merge_vtimezone (icalcomponent *comp, + icalcomponent *vtimezone, + icalarray *tzids_to_rename) +{ + icalproperty *tzid_prop; + const char *tzid; + char *tzid_copy; + icaltimezone *existing_vtimezone; + + /* Get the TZID of the VTIMEZONE. */ + tzid_prop = icalcomponent_get_first_property (vtimezone, ICAL_TZID_PROPERTY); + if (!tzid_prop) + return; + + tzid = icalproperty_get_tzid (tzid_prop); + if (!tzid) + return; + + /* See if there is already a VTIMEZONE in comp with the same TZID. */ + existing_vtimezone = icalcomponent_get_timezone (comp, tzid); + + /* If there is no existing VTIMEZONE with the same TZID, we can just move + the VTIMEZONE to comp and return. */ + if (!existing_vtimezone) { + icalcomponent_remove_component (icalcomponent_get_parent (vtimezone), + vtimezone); + icalcomponent_add_component (comp, vtimezone); + return; + } + + /* If the TZID has a '/' prefix, then we don't have to worry about the + clashing TZIDs, as they are supposed to be exactly the same VTIMEZONE. */ + if (tzid[0] == '/') + return; + + /* Now we have two VTIMEZONEs with the same TZID (which isn't a globally + unique one), so we compare the VTIMEZONE components to see if they are + the same. If they are, we don't need to do anything. We make a copy of + the tzid, since the parameter may get modified in these calls. */ + tzid_copy = strdup (tzid); + if (!tzid_copy) { + icalerror_set_errno(ICAL_NEWFAILED_ERROR); + return; + } + + if (!icalcomponent_compare_vtimezones (comp, vtimezone)) { + /* FIXME: Handle possible NEWFAILED error. */ + + /* Now we have two different VTIMEZONEs with the same TZID. */ + icalcomponent_handle_conflicting_vtimezones (comp, vtimezone, tzid_prop, + tzid_copy, tzids_to_rename); + } + free (tzid_copy); +} + + +static void +icalcomponent_handle_conflicting_vtimezones (icalcomponent *comp, + icalcomponent *vtimezone, + icalproperty *tzid_prop, + const char *tzid, + icalarray *tzids_to_rename) +{ + int i, suffix, max_suffix = 0, num_elements; + unsigned int tzid_len; + char *tzid_copy, *new_tzid, suffix_buf[32]; + + /* Find the length of the TZID without any trailing digits. */ + tzid_len = icalcomponent_get_tzid_prefix_len (tzid); + + /* Step through each of the VTIMEZONEs in comp. We may already have the + clashing VTIMEZONE in the calendar, but it may have been renamed + (i.e. a unique number added on the end of the TZID, e.g. 'London2'). + So we compare the new VTIMEZONE with any VTIMEZONEs that have the + same prefix (e.g. 'London'). If it matches any of those, we have to + rename the TZIDs to that TZID, else we rename to a new TZID, using + the biggest numeric suffix found + 1. */ + num_elements = comp->timezones ? comp->timezones->num_elements : 0; + for (i = 0; i < num_elements; i++) { + icaltimezone *zone; + char *existing_tzid, *existing_tzid_copy; + unsigned int existing_tzid_len; + + zone = icalarray_element_at (comp->timezones, i); + existing_tzid = icaltimezone_get_tzid (zone); + + /* Find the length of the TZID without any trailing digits. */ + existing_tzid_len = icalcomponent_get_tzid_prefix_len (existing_tzid); + + /* Check if we have the same prefix. */ + if (tzid_len == existing_tzid_len + && !strncmp (tzid, existing_tzid, tzid_len)) { + /* Compare the VTIMEZONEs. */ + if (icalcomponent_compare_vtimezones (icaltimezone_get_component (zone), + vtimezone)) { + /* The VTIMEZONEs match, so we can use the existing VTIMEZONE. But + we have to rename TZIDs to this TZID. */ + tzid_copy = strdup (tzid); + existing_tzid_copy = strdup (existing_tzid); + if (!tzid_copy || !existing_tzid_copy) { + icalerror_set_errno(ICAL_NEWFAILED_ERROR); + } else { + icalarray_append (tzids_to_rename, tzid_copy); + icalarray_append (tzids_to_rename, existing_tzid_copy); + } + return; + } else { + /* FIXME: Handle possible NEWFAILED error. */ + + /* Convert the suffix to an integer and remember the maximum numeric + suffix found. */ + suffix = atoi (existing_tzid + existing_tzid_len); + if (max_suffix < suffix) + max_suffix = suffix; + } + } + } + + /* We didn't find a VTIMEZONE that matched, so we have to rename the TZID, + using the maximum numerical suffix found + 1. */ + tzid_copy = strdup (tzid); + sprintf (suffix_buf, "%i", max_suffix + 1); + new_tzid = malloc (tzid_len + strlen (suffix_buf) + 1); + if (!new_tzid || !tzid_copy) { + icalerror_set_errno(ICAL_NEWFAILED_ERROR); + return; + } + + strncpy (new_tzid, tzid, tzid_len); + strcpy (new_tzid + tzid_len, suffix_buf); + icalarray_append (tzids_to_rename, tzid_copy); + icalarray_append (tzids_to_rename, new_tzid); +} + + +/* Returns the length of the TZID, without any trailing digits. */ +static unsigned int icalcomponent_get_tzid_prefix_len (const char *tzid) +{ + int len; + const char *p; + + len = strlen (tzid); + p = tzid + len - 1; + while (len > 0 && *p >= '0' && *p <= '9') { + p--; + len--; + } + + return len; +} + + +/** + * Renames all references to the given TZIDs to a new name. rename_table + * contains pairs of strings - a current TZID, and the new TZID to rename it + * to. + */ +static void icalcomponent_rename_tzids(icalcomponent* comp, + icalarray* rename_table) +{ + icalcomponent_foreach_tzid (comp, icalcomponent_rename_tzids_callback, + rename_table); +} + + +static void icalcomponent_rename_tzids_callback(icalparameter *param, void *data) +{ + icalarray *rename_table = data; + const char *tzid; + int i; + + tzid = icalparameter_get_tzid (param); + if (!tzid) + return; + + /* Step through the rename table to see if the current TZID matches + any of the ones we want to rename. */ + for (i = 0; i < rename_table->num_elements - 1; i += 2) { + if (!strcmp (tzid, icalarray_element_at (rename_table, i))) { + icalparameter_set_tzid (param, icalarray_element_at (rename_table, i + 1)); + break; + } + } +} + + +/** + * Calls the given function for each TZID parameter found in the component. + */ +void icalcomponent_foreach_tzid(icalcomponent* comp, + void (*callback)(icalparameter *param, void *data), + void *callback_data) +{ + icalproperty *prop; + icalproperty_kind kind; + icalparameter *param; + icalcomponent *subcomp; + + /* First look for any TZID parameters used in this component itself. */ + prop = icalcomponent_get_first_property (comp, ICAL_ANY_PROPERTY); + while (prop) { + kind = icalproperty_isa (prop); + + /* These are the only properties that can have a TZID. Note that + COMPLETED, CREATED, DTSTAMP & LASTMODIFIED must be in UTC. */ + if (kind == ICAL_DTSTART_PROPERTY || kind == ICAL_DTEND_PROPERTY + || kind == ICAL_DUE_PROPERTY || kind == ICAL_EXDATE_PROPERTY + || kind == ICAL_RDATE_PROPERTY) { + param = icalproperty_get_first_parameter (prop, ICAL_TZID_PARAMETER); + if (param) + (*callback) (param, callback_data); + } + + prop = icalcomponent_get_next_property (comp, ICAL_ANY_PROPERTY); + } + + /* Now recursively check child components. */ + subcomp = icalcomponent_get_first_component (comp, ICAL_ANY_COMPONENT); + while (subcomp) { + icalcomponent_foreach_tzid (subcomp, callback, callback_data); + subcomp = icalcomponent_get_next_component (comp, ICAL_ANY_COMPONENT); + } +} + + + +/** + * Returns the icaltimezone from the component corresponding to the given + * TZID, or NULL if the component does not have a corresponding VTIMEZONE. + */ +icaltimezone* icalcomponent_get_timezone(icalcomponent* comp, const char *tzid) +{ + icaltimezone *zone; + int lower, upper, middle, cmp; + char *zone_tzid; + + if (!comp->timezones) + return NULL; + + /* Sort the array if necessary (by the TZID string). */ + if (!comp->timezones_sorted) { + icalarray_sort (comp->timezones, icalcomponent_compare_timezone_fn); + comp->timezones_sorted = 1; + } + + /* Do a simple binary search. */ + lower = middle = 0; + upper = comp->timezones->num_elements; + + while (lower < upper) { + middle = (lower + upper) >> 1; + zone = icalarray_element_at (comp->timezones, middle); + zone_tzid = icaltimezone_get_tzid (zone); + cmp = strcmp (tzid, zone_tzid); + if (cmp == 0) + return zone; + else if (cmp < 0) + upper = middle; + else + lower = middle + 1; + } + + return NULL; +} + + +/** + * A function to compare 2 icaltimezone elements, used for qsort(). + */ +static int icalcomponent_compare_timezone_fn (const void *elem1, + const void *elem2) +{ + icaltimezone *zone1, *zone2; + const char *zone1_tzid, *zone2_tzid; + + zone1 = (icaltimezone*) elem1; + zone2 = (icaltimezone*) elem2; + + zone1_tzid = icaltimezone_get_tzid (zone1); + zone2_tzid = icaltimezone_get_tzid (zone2); + + return strcmp (zone1_tzid, zone2_tzid); +} + + +/** + * Compares 2 VTIMEZONE components to see if they match, ignoring their TZIDs. + * It returns 1 if they match, 0 if they don't, or -1 on error. + */ +static int icalcomponent_compare_vtimezones (icalcomponent *vtimezone1, + icalcomponent *vtimezone2) +{ + icalproperty *prop1, *prop2; + const char *tzid1, *tzid2; + char *tzid2_copy, *string1, *string2; + int cmp; + + /* Get the TZID property of the first VTIMEZONE. */ + prop1 = icalcomponent_get_first_property (vtimezone1, ICAL_TZID_PROPERTY); + if (!prop1) + return -1; + + tzid1 = icalproperty_get_tzid (prop1); + if (!tzid1) + return -1; + + /* Get the TZID property of the second VTIMEZONE. */ + prop2 = icalcomponent_get_first_property (vtimezone2, ICAL_TZID_PROPERTY); + if (!prop2) + return -1; + + tzid2 = icalproperty_get_tzid (prop2); + if (!tzid2) + return -1; + + /* Copy the second TZID, and set the property to the same as the first + TZID, since we don't care if these match of not. */ + tzid2_copy = strdup (tzid2); + if (!tzid2_copy) { + icalerror_set_errno (ICAL_NEWFAILED_ERROR); + return 0; + } + + icalproperty_set_tzid (prop2, tzid1); + + /* Now convert both VTIMEZONEs to strings and compare them. */ + string1 = icalcomponent_as_ical_string (vtimezone1); + if (!string1) { + free (tzid2_copy); + return -1; + } + + string2 = icalcomponent_as_ical_string (vtimezone2); + if (!string2) { + free (string1); + free (tzid2_copy); + return -1; + } + + cmp = strcmp (string1, string2); + + free (string1); + free (string2); + + /* Now reset the second TZID. */ + icalproperty_set_tzid (prop2, tzid2_copy); + free (tzid2_copy); + + return (cmp == 0) ? 1 : 0; +} + + + + + + +/** + * @brief set the RELCALID property of a component. + * + * @param comp Valid calendar component. + * @param v Relcalid URL value + */ + +void icalcomponent_set_relcalid(icalcomponent* comp, const char* v) +{ + ICALSETUPSET(ICAL_RELCALID_PROPERTY); + + if (prop == 0){ + prop = icalproperty_new_relcalid(v); + icalcomponent_add_property(inner, prop); + } + + icalproperty_set_relcalid(prop,v); + +} + + +/** + * @brief get the RELCALID property of a component. + * + * @param comp Valid calendar component. + */ + +const char* icalcomponent_get_relcalid(icalcomponent* comp){ + icalcomponent *inner; + icalproperty *prop; + icalerror_check_arg_rz(comp!=0,"comp"); + + inner = icalcomponent_get_inner(comp); + + if(inner == 0){ + return 0; + } + + prop= icalcomponent_get_first_property(inner,ICAL_RELCALID_PROPERTY); + + if (prop == 0){ + return 0; + } + + return icalproperty_get_relcalid(prop); +} + + +/** @brief Return the time a TODO task is DUE. + * + * @param comp Valid calendar component. + * + * Uses the DUE: property if it exists, otherwise we calculate the DUE + * value by adding the task's duration to the DTSTART time + */ + +struct icaltimetype icalcomponent_get_due(icalcomponent* comp) +{ + icalcomponent *inner = icalcomponent_get_inner(comp); + + icalproperty *due_prop + = icalcomponent_get_first_property(inner,ICAL_DUE_PROPERTY); + + icalproperty *dur_prop + = icalcomponent_get_first_property(inner, ICAL_DURATION_PROPERTY); + + if( due_prop == 0 && dur_prop == 0){ + return icaltime_null_time(); + } else if ( due_prop != 0) { + return icalproperty_get_due(due_prop); + } else if ( dur_prop != 0) { + + struct icaltimetype start = + icalcomponent_get_dtstart(inner); + struct icaldurationtype duration = + icalproperty_get_duration(dur_prop); + + struct icaltimetype due = icaltime_add(start,duration); + + return due; + + } else { + /* Error, both duration and due have been specified */ + icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR); + return icaltime_null_time(); + + } + +} + +/** @brief Set the due date of a VTODO task. + * + * @param comp Valid VTODO component. + * @param v Valid due date time. + * + * - If no duration or due properties then set the DUE property. + * - If a DUE property is already set, then reset it to the value v. + * - If a DURATION property is already set, then calculate the new + * duration based on the supplied value of v. + */ + +void icalcomponent_set_due(icalcomponent* comp, struct icaltimetype v) +{ + icalcomponent *inner = icalcomponent_get_inner(comp); + + icalproperty *due_prop + = icalcomponent_get_first_property(inner,ICAL_DUE_PROPERTY); + + icalproperty *dur_prop + = icalcomponent_get_first_property(inner,ICAL_DURATION_PROPERTY); + + + if( due_prop == 0 && dur_prop == 0){ + due_prop = icalproperty_new_due(v); + icalcomponent_add_property(inner,due_prop); + } else if ( due_prop != 0) { + icalproperty_set_due(due_prop,v); + } else if ( dur_prop != 0) { + struct icaltimetype start = + icalcomponent_get_dtstart(inner); + + struct icaltimetype due = + icalcomponent_get_due(inner); + + struct icaldurationtype dur + = icaltime_subtract(due,start); + + icalproperty_set_duration(dur_prop,dur); + + } else { + /* Error, both duration and due have been specified */ + icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR); + } +} |