summaryrefslogtreecommitdiffabout
path: root/libical/src/libical/icalcomponent.c
authorzautrix <zautrix>2004-06-29 11:59:46 (UTC)
committer zautrix <zautrix>2004-06-29 11:59:46 (UTC)
commitda43dbdc6c82453228f34766fc74585615cba938 (patch) (side-by-side diff)
tree16576932cea08bf117b2d0320b0d5f66ee8ad093 /libical/src/libical/icalcomponent.c
parent627489ea2669d3997676bc3cee0f5d0d0c16c4d4 (diff)
downloadkdepimpi-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.c1891
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);
+ }
+}