diff options
author | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
---|---|---|
committer | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
commit | 460c52653ab0dcca6f19a4f492ed2c5e4e963ab0 (patch) | |
tree | 67208f7c145782a7e90b123b982ca78d88cc2c87 /libkcal/libical/src | |
download | tdepim-460c52653ab0dcca6f19a4f492ed2c5e4e963ab0.tar.gz tdepim-460c52653ab0dcca6f19a4f492ed2c5e4e963ab0.zip |
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdepim@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'libkcal/libical/src')
59 files changed, 21435 insertions, 0 deletions
diff --git a/libkcal/libical/src/Makefile.am b/libkcal/libical/src/Makefile.am new file mode 100644 index 000000000..ef6408034 --- /dev/null +++ b/libkcal/libical/src/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = libical libicalss diff --git a/libkcal/libical/src/libical/Makefile.am b/libkcal/libical/src/libical/Makefile.am new file mode 100644 index 000000000..e0d51321f --- /dev/null +++ b/libkcal/libical/src/libical/Makefile.am @@ -0,0 +1,232 @@ +#====================================================================== +# FILE: Makefile.am +# CREATOR: eric +# +# +# +# (C) COPYRIGHT 2000, Eric Busboom, http://www.softwarestudio.org +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of either: +# +# The LGPL as published by the Free Software Foundation, version +# 2.1, available at: http://www.fsf.org/copyleft/lesser.html +# +# Or: +# +# The Mozilla Public License Version 1.0. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# The original code is icalcomponent.c +# +#====================================================================== + + +DESIGNDATA = $(srcdir)/../../design-data +ICALSCRIPTS = $(srcdir)/../../scripts + +noinst_LTLIBRARIES = libical.la + +AM_CPPFLAGS = -DPACKAGE_DATA_DIR=\""$(kde_datadir)/libical"\" + +AM_YFLAGS = -d -v -t -pical_yy +AM_LFLAGS = -Pical_yy -olex.yy.c + +INCLUDES = \ + -I$(top_builddir)/libkcal/libical \ + -I$(top_srcdir)/libkcal/libical/src \ + -I$(top_builddir)/libkcal/libical/src \ + -I$(srcdir) + +libical_la_LDFLAGS = -no-undefined + +# Order is important! +libical_la_SOURCES = \ + caldate.c \ + icalarray.c \ + icalattach.c \ + icalderivedparameter.c \ + icalrecur.c \ + icalrestriction.c \ + icalcomponent.c \ + icalenums.c \ + icalerror.c \ + icalmemory.c \ + icalmime.c \ + icalparameter.c \ + icalparser.c \ + icalderivedproperty.c \ + icalproperty.c \ + icaltime.c \ + icalduration.c \ + icalperiod.c \ + icaltimezone.c \ + icaltypes.c \ + icalvalue.c \ + icalderivedvalue.c \ + pvl.c \ + sspm.c \ + vsnprintf.c + + +noinst_HEADERS = ical.h \ + astime.h \ + icalarray.h \ + icalattach.h \ + icalattachimpl.h \ + icalderivedparameter.h \ + icalrecur.h \ + icalcomponent.h \ + icalenums.h \ + icalerror.h \ + icalmemory.h \ + icalmime.h \ + icalparameter.h \ + icalparameterimpl.h \ + icalparser.h \ + icalderivedproperty.h \ + icalproperty.h \ + icaltime.h \ + icalduration.h \ + icalperiod.h \ + icaltimezone.h \ + icaltypes.h \ + icalvalue.h \ + icalvalueimpl.h \ + icalderivedvalue.h \ + pvl.h \ + sspm.h + +# ORDERING OF HEADERS IS SIGNIFICANT. Don't change this ordering. It +# is required to make the combined header ical.h properly +COMBINEDHEADERS = \ + $(srcdir)/icalversion.h \ + $(srcdir)/icaltime.h \ + $(srcdir)/icalduration.h \ + $(srcdir)/icalperiod.h \ + $(srcdir)/icalenums.h \ + $(srcdir)/icaltypes.h \ + $(srcdir)/icalrecur.h \ + $(srcdir)/icalattach.h \ + icalderivedvalue.h \ + icalderivedparameter.h \ + $(srcdir)/icalvalue.h \ + $(srcdir)/icalparameter.h \ + icalderivedproperty.h \ + $(srcdir)/icalproperty.h \ + $(srcdir)/pvl.h \ + $(srcdir)/icalarray.h \ + $(srcdir)/icalcomponent.h \ + $(srcdir)/icaltimezone.h \ + $(srcdir)/icalparser.h \ + $(srcdir)/icalmemory.h \ + $(srcdir)/icalerror.h \ + $(srcdir)/icalrestriction.h \ + $(srcdir)/sspm.h \ + $(srcdir)/icalmime.h + +ical.h: $(COMBINEDHEADERS) + echo '#ifdef __cplusplus' > ical.h + echo 'extern "C" {' >> ical.h + echo '#endif' >> ical.h + cat $(COMBINEDHEADERS) \ + | egrep -v "#include.*\"ical" \ + | egrep -v "#include.*\"config" \ + | egrep -v "#include.*\"pvl\.h\"" \ + | egrep -v '\$$(Id|Locker): .+\$$'>> ical.h + echo '#ifdef __cplusplus' >> ical.h + echo '}' >> ical.h + echo '#endif' >> ical.h + +# parameters + +PARAMETERDEPS = \ + $(ICALSCRIPTS)/mkderivedparameters.pl \ + $(DESIGNDATA)/parameters.csv \ + icalderivedparameter.c.in \ + icalderivedparameter.h.in + +icalderivedparameter.h: $(PARAMETERDEPS) + $(PERL) -I $(ICALSCRIPTS) $(ICALSCRIPTS)/mkderivedparameters.pl -i $(srcdir)/icalderivedparameter.h.in -h $(DESIGNDATA)/parameters.csv > icalderivedparameter.h || rm -f icalderivedparameter.h + + +icalderivedparameter.c: $(PARAMETERDEPS) icalparameter.h + $(PERL) -I $(ICALSCRIPTS) $(ICALSCRIPTS)/mkderivedparameters.pl -i $(srcdir)/icalderivedparameter.c.in -c $(DESIGNDATA)/parameters.csv > icalderivedparameter.c || rm -f icalderivedparameter.c + + +# properties + +PROPERTYDEPS = \ + $(ICALSCRIPTS)/mkderivedproperties.pl \ + $(DESIGNDATA)/properties.csv \ + $(DESIGNDATA)/value-types.csv \ + icalderivedproperty.c.in \ + icalderivedproperty.h.in + + +icalderivedproperty.h: $(PROPERTYDEPS) + $(PERL) -I$(ICALSCRIPTS) $(ICALSCRIPTS)/mkderivedproperties.pl \ + -i $(srcdir)/icalderivedproperty.h.in -h $(DESIGNDATA)/properties.csv\ + ${DESIGNDATA}/value-types.csv > icalderivedproperty.h || rm -f icalderivedproperty.h + +icalderivedproperty.c: $(PROPERTYDEPS) icalproperty.h + $(PERL) -I$(ICALSCRIPTS) $(ICALSCRIPTS)/mkderivedproperties.pl \ + -i $(srcdir)/icalderivedproperty.c.in -c $(DESIGNDATA)/properties.csv \ + ${DESIGNDATA}/value-types.csv > icalderivedproperty.c || rm -f icalderivedproperty.c + +# restrictions + +RESTRICTIONDEPS = \ + $(ICALSCRIPTS)/mkrestrictiontable.pl \ + $(DESIGNDATA)/restrictions.csv \ + icalrestriction.c.in + +icalrestriction.c: $(RESTRICTIONDEPS) + $(PERL) $(ICALSCRIPTS)/mkrestrictiontable.pl -i $(srcdir)/icalrestriction.c.in \ + $(DESIGNDATA)/restrictions.csv > icalrestriction.c || rm -f icalrestriction.c + +# values + +VALUEDEPS = \ + $(ICALSCRIPTS)/mkderivedvalues.pl \ + $(DESIGNDATA)/value-types.csv \ + icalderivedvalue.c.in \ + icalderivedvalue.h.in + +icalderivedvalue.h: $(VALUEDEPS) + $(PERL) -I$(ICALSCRIPTS) $(ICALSCRIPTS)/mkderivedvalues.pl \ + -i $(srcdir)/icalderivedvalue.h.in -h $(DESIGNDATA)/value-types.csv > icalderivedvalue.h || rm -f icalderivedvalue.h + +icalderivedvalue.c: $(VALUEDEPS) icalderivedvalue.h + $(PERL) -I$(ICALSCRIPTS) $(ICALSCRIPTS)/mkderivedvalues.pl \ + -i $(srcdir)/icalderivedvalue.c.in -c $(DESIGNDATA)/value-types.csv > icalderivedvalue.c || rm -f icalderivedvalue.c + +# housekeeping +CLEANFILES = y.output \ + icalderivedvalue.h icalderivedproperty.h icalderivedparameter.h ical.h \ + icalderivedvalue.c icalderivedproperty.c icalderivedparameter.c \ + icalrestriction.c + +EXTRA_DIST = \ + icalderivedparameter.c.in \ + icalderivedparameter.h.in \ + icalderivedproperty.c.in \ + icalderivedproperty.h.in \ + icalrestriction.c.in \ + icalderivedvalue.c.in \ + icalderivedvalue.h.in \ + icalversion.h.in + + +icalcomponent.lo: icalderivedvalue.h icalderivedproperty.h icalderivedparameter.h +icalderivedparameter.lo: icalderivedvalue.h icalderivedproperty.h icalderivedparameter.h +icalderivedproperty.lo: icalderivedvalue.h icalderivedproperty.h icalderivedparameter.h +icalderivedvalue.lo: icalderivedvalue.h icalderivedproperty.h icalderivedparameter.h +icalduration.lo: icalderivedvalue.h icalderivedproperty.h icalderivedparameter.h +icalmime.lo: icalderivedvalue.h icalderivedproperty.h icalderivedparameter.h +icalparameter.lo: icalderivedvalue.h icalderivedproperty.h icalderivedparameter.h +icalparser.lo: icalderivedvalue.h icalderivedproperty.h icalderivedparameter.h +icalproperty.lo: icalderivedvalue.h icalderivedproperty.h icalderivedparameter.h +icalrestriction.lo: icalderivedvalue.h icalderivedproperty.h icalderivedparameter.h +icaltime.lo: icalderivedvalue.h icalderivedproperty.h icalderivedparameter.h +icalvalue.lo: icalderivedvalue.h icalderivedproperty.h icalderivedparameter.h diff --git a/libkcal/libical/src/libical/astime.h b/libkcal/libical/src/libical/astime.h new file mode 100644 index 000000000..6962c060a --- /dev/null +++ b/libkcal/libical/src/libical/astime.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 1986-2000, Hiram Clawson + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * Redistributions in binary form must reproduce the + * above copyright notice, this list of conditions and + * the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * Neither name of The Museum of Hiram nor the names of + * its contributors may be used to endorse or promote products + * derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file astime.h + * @brief contains definitions of structures used for time calculations. + */ + +#ifndef _astime_h_ +#define _astime_h_ + +typedef struct ut_instant { + double j_date; /**< julian decimal date, 0 = 01 Jan 4713 BC 12 HR UT */ + long year; /**< year, valid range [-4,713, +2,147,483,647] */ + int month; /**< [1-12] */ + int day; /**< [1-31] */ + int i_hour; /**< [0-23] */ + int i_minute; /**< [0-59] */ + int i_second; /**< [0-59] */ + double d_hour; /**< [0.0-23.9999] includes minute and second */ + double d_minute; /**< [0.0-59.9999] includes second */ + double d_second; /**< [0.0-59.9999] */ + int weekday; /**< [0-6] */ + int day_of_year; /**< [1-366] */ +} UTinstant, * UTinstantPtr; + +/* Functions in caldate.c */ + +long caldat( UTinstantPtr ); /** converts julian date to year,mo,da */ +double juldat( UTinstantPtr ); /** returns julian day from year,mo,da */ + +#endif /* _astime_h_ */ diff --git a/libkcal/libical/src/libical/caldate.c b/libkcal/libical/src/libical/caldate.c new file mode 100644 index 000000000..f5554f66f --- /dev/null +++ b/libkcal/libical/src/libical/caldate.c @@ -0,0 +1,176 @@ +/* + * Copyright (c) 1986-2000, Hiram Clawson + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * Redistributions in binary form must reproduce the + * above copyright notice, this list of conditions and + * the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * Neither name of The Museum of Hiram nor the names of + * its contributors may be used to endorse or promote products + * derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "astime.h" /* time structures */ + +/** + * caldat computes the day of the week, the day of the year + * the gregorian (or julian) calendar date and the universal + * time from the julian decimal date. + * for astronomical purposes, The Gregorian calendar reform occurred + * on 15 Oct. 1582. This is 05 Oct 1582 by the julian calendar. + + * Input: a ut_instant structure pointer, where the j_date element + * has been set. ( = 0 for 01 Jan 4713 B.C. 12 HR UT ) + * + * output: will set all the other elements of the structure. + * As a convienence, the function will also return the year. + * + * Reference: Astronomial formulae for calculators, meeus, p 23 + * from fortran program by F. Espenak - April 1982 Page 277, + * 50 Year canon of solar eclipses: 1986-2035 + * + */ + +long caldat( date ) +struct ut_instant * date; +{ + double frac; + long jd; + long ka; + long kb; + long kc; + long kd; + long ke; + long ialp; + + jd = (long) (date->j_date + 0.5); /* integer julian date */ + frac = date->j_date + 0.5 - (double) jd + 1.0e-10; /* day fraction */ + ka = (long) jd; + if ( jd >= 2299161L ) + { + ialp = ( (double) jd - 1867216.25 ) / ( 36524.25 ); + ka = jd + 1L + ialp - ( ialp >> 2 ); + } + kb = ka + 1524L; + kc = ( (double) kb - 122.1 ) / 365.25; + kd = (double) kc * 365.25; + ke = (double) ( kb - kd ) / 30.6001; + date->day = kb - kd - ((long) ( (double) ke * 30.6001 )); + if ( ke > 13L ) + date->month = ke - 13L; + else + date->month = ke - 1L; + if ( (date->month == 2) && (date->day > 28) ) + date->day = 29; + if ( (date->month == 2) && (date->day == 29) && (ke == 3L) ) + date->year = kc - 4716L; + else if ( date->month > 2 ) + date->year = kc - 4716L; + else + date->year = kc - 4715L; + date->i_hour = date->d_hour = frac * 24.0; /* hour */ + date->i_minute = date->d_minute = + ( date->d_hour - (double) date->i_hour ) * 60.0; /* minute */ + date->i_second = date->d_second = + ( date->d_minute - (double) date->i_minute ) * 60.0;/* second */ + date->weekday = (jd + 1L) % 7L; /* day of week */ + if ( date->year == ((date->year >> 2) << 2) ) + date->day_of_year = + ( ( 275 * date->month ) / 9) + - ((date->month + 9) / 12) + + date->day - 30; + else + date->day_of_year = + ( ( 275 * date->month ) / 9) + - (((date->month + 9) / 12) << 1) + + date->day - 30; + return( date->year ); +} + +/** + * juldat computes the julian decimal date (j_date) from + * the gregorian (or Julian) calendar date. + * for astronomical purposes, The Gregorian calendar reform occurred + * on 15 Oct. 1582. This is 05 Oct 1582 by the julian calendar. + * Input: a ut_instant structure pointer where Day, Month, Year and + * i_hour, i_minute, d_second have been set for the date + * in question. + * + * Output: the j_date and weekday elements of the structure will be set. + * Also, the return value of the function will be the j_date too. + * + * Reference: Astronomial formulae for calculators, meeus, p 23 + * from fortran program by F. Espenak - April 1982 Page 276, + * 50 Year canon of solar eclipses: 1986-2035 + */ + +double juldat( date ) +struct ut_instant * date; +{ + double frac, gyr; + long iy0, im0; + long ia, ib; + long jd; + + /* decimal day fraction */ + frac = (( double)date->i_hour/ 24.0) + + ((double) date->i_minute / 1440.0) + + (date->d_second / 86400.0); + /* convert date to format YYYY.MMDDdd */ + gyr = (double) date->year + + (0.01 * (double) date->month) + + (0.0001 * (double) date->day) + + (0.0001 * frac) + 1.0e-9; + /* conversion factors */ + if ( date->month <= 2 ) + { + iy0 = date->year - 1L; + im0 = date->month + 12; + } + else + { + iy0 = date->year; + im0 = date->month; + } + ia = iy0 / 100L; + ib = 2L - ia + (ia >> 2); + /* calculate julian date */ + if ( date->year < 0L ) + jd = (long) ((365.25 * (double) iy0) - 0.75) + + (long) (30.6001 * (im0 + 1L) ) + + (long) date->day + 1720994L; + else + jd = (long) (365.25 * (double) iy0) + + (long) (30.6001 * (double) (im0 + 1L)) + + (long) date->day + 1720994L; + if ( gyr >= 1582.1015 ) /* on or after 15 October 1582 */ + jd += ib; + date->j_date = (double) jd + frac + 0.5; + jd = (long) (date->j_date + 0.5); + date->weekday = (jd + 1L) % 7L; + return( date->j_date ); +} /* end of double juldat( date ) */ diff --git a/libkcal/libical/src/libical/icalarray.c b/libkcal/libical/src/libical/icalarray.c new file mode 100644 index 000000000..05e764c78 --- /dev/null +++ b/libkcal/libical/src/libical/icalarray.c @@ -0,0 +1,161 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- + ====================================================================== + FILE: icalarray.c + CREATOR: Damon Chaplin 07 March 2001 + + + (C) COPYRIGHT 2001, Ximian, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of either: + + The LGPL as published by the Free Software Foundation, version + 2.1, available at: http://www.fsf.org/copyleft/lesser.html + + Or: + + The Mozilla Public License Version 1.0. You may obtain a copy of + the License at http://www.mozilla.org/MPL/ + + + ======================================================================*/ + +/** @file icalarray.c + * + * @brief An array of arbitrarily-sized elements which grows + * dynamically as elements are added. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdlib.h> +#include <string.h> + +#include "icalarray.h" +#include "icalerror.h" + + +static void icalarray_expand (icalarray *array, + int space_needed); + +/** @brief Constructor + */ + +icalarray* +icalarray_new (int element_size, + int increment_size) +{ + icalarray *array; + + array = (icalarray*) malloc (sizeof (icalarray)); + if (!array) { + icalerror_set_errno(ICAL_NEWFAILED_ERROR); + return NULL; + } + + array->element_size = element_size; + array->increment_size = increment_size; + array->num_elements = 0; + array->space_allocated = 0; + array->data = NULL; + + return array; +} + +/** @brief Destructor + */ + +void +icalarray_free (icalarray *array) +{ + if (array->data) + free (array->data); + free (array); +} + + +void +icalarray_append (icalarray *array, + const void *element) +{ + if (array->num_elements >= array->space_allocated) + icalarray_expand (array, 1); + + memcpy ((char *)(array->data) + ( array->num_elements * array->element_size ), element, + array->element_size); + array->num_elements++; +} + + +void* +icalarray_element_at (icalarray *array, + int position) +{ + assert (position >= 0); + assert ((unsigned int)position < array->num_elements); + + return (char *)(array->data) + (position * array->element_size); +} + + +void +icalarray_remove_element_at (icalarray *array, + int position) +{ + void *dest; + int elements_to_move; + + assert (position >= 0); + assert ((unsigned int)position < array->num_elements); + + dest = (char *)array->data + (position * array->element_size); + elements_to_move = array->num_elements - position - 1; + + if (elements_to_move > 0) + memmove (dest, (char *)dest + array->element_size, + elements_to_move * array->element_size); + + array->num_elements--; +} + + +void +icalarray_sort (icalarray *array, + int (*compare) (const void *, + const void *)) +{ + qsort (array->data, array->num_elements, array->element_size, compare); +} + + +static void +icalarray_expand (icalarray *array, + int space_needed) +{ + int new_space_allocated; + void *new_data; + + new_space_allocated = array->space_allocated + array->increment_size; + + if ((unsigned int)space_needed > array->increment_size) + new_space_allocated += space_needed; + + /* + new_data = realloc (array->data, + new_space_allocated * array->element_size); + */ + new_data = malloc(new_space_allocated * array->element_size); + + if (new_data) { + memcpy(new_data,array->data,array->element_size*array->space_allocated); + free(array->data); + array->data = new_data; + array->space_allocated = new_space_allocated; + } else { + icalerror_set_errno(ICAL_ALLOCATION_ERROR); + } +} + + diff --git a/libkcal/libical/src/libical/icalarray.h b/libkcal/libical/src/libical/icalarray.h new file mode 100644 index 000000000..16d390343 --- /dev/null +++ b/libkcal/libical/src/libical/icalarray.h @@ -0,0 +1,61 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/*====================================================================== + FILE: icalarray.h + CREATOR: Damon Chaplin 07 March 2001 + + + + (C) COPYRIGHT 2001, Ximian, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of either: + + The LGPL as published by the Free Software Foundation, version + 2.1, available at: http://www.fsf.org/copyleft/lesser.html + + Or: + + The Mozilla Public License Version 1.0. You may obtain a copy of + the License at http://www.mozilla.org/MPL/ + + +======================================================================*/ + + +#ifndef ICALARRAY_H +#define ICALARRAY_H + +/** @file icalarray.h + * + * @brief An array of arbitrarily-sized elements which grows + * dynamically as elements are added. + */ + +typedef struct _icalarray icalarray; +struct _icalarray { + unsigned int element_size; + unsigned int increment_size; + unsigned int num_elements; + unsigned int space_allocated; + void *data; +}; + + + +icalarray *icalarray_new (int element_size, + int increment_size); +void icalarray_free (icalarray *array); + +void icalarray_append (icalarray *array, + const void *element); +void icalarray_remove_element_at (icalarray *array, + int position); + +void *icalarray_element_at (icalarray *array, + int position); + +void icalarray_sort (icalarray *array, + int (*compare) (const void *, const void *)); + + +#endif /* ICALARRAY_H */ diff --git a/libkcal/libical/src/libical/icalattach.c b/libkcal/libical/src/libical/icalattach.c new file mode 100644 index 000000000..23cdbeefd --- /dev/null +++ b/libkcal/libical/src/libical/icalattach.c @@ -0,0 +1,137 @@ +/* -*- Mode: C -*- + ====================================================================== + FILE: icalattach.c + CREATOR: acampi 28 May 02 + + + + (C) COPYRIGHT 2000, Andrea Campi <a.campi@inet.it> + + This program is free software; you can redistribute it and/or modify + it under the terms of either: + + The LGPL as published by the Free Software Foundation, version + 2.1, available at: http://www.fsf.org/copyleft/lesser.html + + Or: + + The Mozilla Public License Version 1.0. You may obtain a copy of + the License at http://www.mozilla.org/MPL/ + + The original code is icaltypes.c + + ======================================================================*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "icaltypes.h" +#include "icalerror.h" +#include "icalmemory.h" +#include "icalattachimpl.h" +#include <stdlib.h> /* for malloc and abs() */ +#include <errno.h> /* for errno */ +#include <string.h> /* for icalmemory_strdup */ +#include <assert.h> + +icalattach * +icalattach_new_from_url (const char *url) +{ + icalattach *attach; + char *url_copy; + + icalerror_check_arg_rz ((url != NULL), "url"); + + if ((attach = malloc (sizeof (icalattach))) == NULL) { + errno = ENOMEM; + return NULL; + } + + if ((url_copy = strdup (url)) == NULL) { + free (attach); + errno = ENOMEM; + return NULL; + } + + attach->refcount = 1; + attach->is_url = 1; + attach->u.url.url = url_copy; + + return attach; +} + +icalattach * +icalattach_new_from_data (unsigned char *data, icalattach_free_fn_t free_fn, + void *free_fn_data) +{ + icalattach *attach; + + icalerror_check_arg_rz ((data != NULL), "data"); + + if ((attach = malloc (sizeof (icalattach))) == NULL) { + errno = ENOMEM; + return NULL; + } + + attach->refcount = 1; + attach->is_url = 0; + attach->u.data.data = data; + attach->u.data.free_fn = free_fn; + attach->u.data.free_fn_data = free_fn_data; + + return attach; +} + +void +icalattach_ref (icalattach *attach) +{ + icalerror_check_arg_rv ((attach != NULL), "attach"); + icalerror_check_arg_rv ((attach->refcount > 0), "attach->refcount > 0"); + + attach->refcount++; +} + +void +icalattach_unref (icalattach *attach) +{ + icalerror_check_arg_rv ((attach != NULL), "attach"); + icalerror_check_arg_rv ((attach->refcount > 0), "attach->refcount > 0"); + + attach->refcount--; + + if (attach->refcount != 0) + return; + + if (attach->is_url) + free (attach->u.url.url); + else if (attach->u.data.free_fn) + (* attach->u.data.free_fn) (attach->u.data.data, attach->u.data.free_fn_data); + + free (attach); +} + +int +icalattach_get_is_url (icalattach *attach) +{ + icalerror_check_arg_rz ((attach != NULL), "attach"); + + return attach->is_url ? 1 : 0; +} + +const char * +icalattach_get_url (icalattach *attach) +{ + icalerror_check_arg_rz ((attach != NULL), "attach"); + icalerror_check_arg_rz ((attach->is_url), "attach->is_url"); + + return attach->u.url.url; +} + +unsigned char * +icalattach_get_data (icalattach *attach) +{ + icalerror_check_arg_rz ((attach != NULL), "attach"); + icalerror_check_arg_rz ((!attach->is_url), "!attach->is_url"); + + return attach->u.data.data; +} diff --git a/libkcal/libical/src/libical/icalattach.h b/libkcal/libical/src/libical/icalattach.h new file mode 100644 index 000000000..4189d4bb6 --- /dev/null +++ b/libkcal/libical/src/libical/icalattach.h @@ -0,0 +1,60 @@ +/* -*- Mode: C -*- */ +/*====================================================================== + FILE: icalattach.h + CREATOR: acampi 28 May 02 + + + (C) COPYRIGHT 2002, Andrea Campi <a.campi@inet.it> + + This program is free software; you can redistribute it and/or modify + it under the terms of either: + + The LGPL as published by the Free Software Foundation, version + 2.1, available at: http://www.fsf.org/copyleft/lesser.html + + Or: + + The Mozilla Public License Version 1.0. You may obtain a copy of + the License at http://www.mozilla.org/MPL/ + + The original code is icalattach.h + +======================================================================*/ + +#ifndef ICALATTACH_H +#define ICALATTACH_H + + +typedef struct icalattach_impl icalattach; + +typedef void (* icalattach_free_fn_t) (unsigned char *data, void *user_data); + +icalattach *icalattach_new_from_url (const char *url); +icalattach *icalattach_new_from_data (unsigned char *data, + icalattach_free_fn_t free_fn, void *free_fn_data); + +void icalattach_ref (icalattach *attach); +void icalattach_unref (icalattach *attach); + +int icalattach_get_is_url (icalattach *attach); +const char *icalattach_get_url (icalattach *attach); +unsigned char *icalattach_get_data (icalattach *attach); + +struct icalattachtype* icalattachtype_new(void); +void icalattachtype_add_reference(struct icalattachtype* v); +void icalattachtype_free(struct icalattachtype* v); + +void icalattachtype_set_url(struct icalattachtype* v, char* url); +char* icalattachtype_get_url(struct icalattachtype* v); + +void icalattachtype_set_base64(struct icalattachtype* v, char* base64, + int owns); +char* icalattachtype_get_base64(struct icalattachtype* v); + +void icalattachtype_set_binary(struct icalattachtype* v, char* binary, + int owns); +void* icalattachtype_get_binary(struct icalattachtype* v); + + + +#endif /* !ICALATTACH_H */ diff --git a/libkcal/libical/src/libical/icalattachimpl.h b/libkcal/libical/src/libical/icalattachimpl.h new file mode 100644 index 000000000..08c22fd85 --- /dev/null +++ b/libkcal/libical/src/libical/icalattachimpl.h @@ -0,0 +1,58 @@ +/* -*- Mode: C -*- */ +/*====================================================================== + FILE: icalattachimpl.h + CREATOR: acampi 28 May 02 + + + + (C) COPYRIGHT 2000, Andrea Campi <a.campi@inet.it> + + This program is free software; you can redistribute it and/or modify + it under the terms of either: + + The LGPL as published by the Free Software Foundation, version + 2.1, available at: http://www.fsf.org/copyleft/lesser.html + + Or: + + The Mozilla Public License Version 1.0. You may obtain a copy of + the License at http://www.mozilla.org/MPL/ + + The original code is icalattachimpl.h + + +======================================================================*/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#ifndef ICALATTACHIMPL_H +#define ICALATTACHIMPL_H + +#include "icalattach.h" + +/* Private structure for ATTACH values */ +struct icalattach_impl { + /* Reference count */ + int refcount; + + union { + /* URL attachment data */ + struct { + char *url; + } url; + + /* Inline data */ + struct { + unsigned char *data; + icalattach_free_fn_t free_fn; + void *free_fn_data; + } data; + } u; + + /* TRUE if URL, FALSE if inline data */ + unsigned int is_url : 1; +}; + +#endif diff --git a/libkcal/libical/src/libical/icalcomponent.c b/libkcal/libical/src/libical/icalcomponent.c new file mode 100644 index 000000000..bab0e72a6 --- /dev/null +++ b/libkcal/libical/src/libical/icalcomponent.c @@ -0,0 +1,2598 @@ +/*====================================================================== + FILE: icalcomponent.c + CREATOR: eric 28 April 1999 + + + (C) COPYRIGHT 2000, Eric Busboom <eric@softwarestudio.org> + http://www.softwarestudio.org + + This program is free software; you can redistribute it and/or modify + it under the terms of either: + + The LGPL as published by the Free Software Foundation, version + 2.1, available at: http://www.fsf.org/copyleft/lesser.html + + Or: + + The Mozilla Public License Version 1.0. You may obtain a copy of + the License at http://www.mozilla.org/MPL/ + + The original code is icalcomponent.c + +======================================================================*/ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "icalcomponent.h" +#include "pvl.h" /* "Pointer-to-void list" */ +#include "icalerror.h" +#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> /* for strdup */ +#include <limits.h> /* for INT_MAX */ + +struct icalcomponent_impl +{ + char id[5]; + icalcomponent_kind kind; + char* x_name; + pvl_list properties; + pvl_elem property_iterator; + 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(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; + + while((vp = va_arg(args, void*)) != 0) { + + assert (icalcomponent_isa_component(vp) != 0 || + icalproperty_isa_property(vp) != 0 ) ; + + if (icalcomponent_isa_component(vp) != 0 ){ + icalcomponent_add_component(impl, (icalcomponent*)vp); + + } else if (icalproperty_isa_property(vp) != 0 ){ + icalcomponent_add_property(impl, (icalproperty*)vp); + } + } +} + +static icalcomponent* +icalcomponent_new_impl (icalcomponent_kind kind) +{ + icalcomponent* comp; + + if (!icalcomponent_kind_is_valid(kind)) + return NULL; + + if ( ( comp = (icalcomponent*) malloc(sizeof(icalcomponent))) == 0) { + icalerror_set_errno(ICAL_NEWFAILED_ERROR); + return 0; + } + + strcpy(comp->id,"comp"); + + comp->kind = kind; + comp->properties = pvl_newlist(); + comp->property_iterator = 0; + comp->components = pvl_newlist(); + 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_new_impl(kind); +} + +/** @brief Constructor + */ +icalcomponent* +icalcomponent_vanew (icalcomponent_kind kind, ...) +{ + va_list args; + + icalcomponent *impl = icalcomponent_new_impl(kind); + + if (impl == 0){ + return 0; + } + + va_start(args,kind); + icalcomponent_add_children(impl, args); + va_end(args); + + return impl; +} + +/** @brief Constructor + */ +icalcomponent* icalcomponent_new_from_string(char* str) +{ + return icalparser_parse_string(str); +} + +/** @brief Constructor + */ +icalcomponent* icalcomponent_new_clone(icalcomponent* old) +{ + icalcomponent *new; + icalproperty *p; + icalcomponent *c; + pvl_elem itr; + + icalerror_check_arg_rz( (old!=0), "component"); + + new = icalcomponent_new_impl(old->kind); + + if (new == 0){ + return 0; + } + + + for( itr = pvl_head(old->properties); + itr != 0; + itr = pvl_next(itr)) + { + p = (icalproperty*)pvl_data(itr); + icalcomponent_add_property(new,icalproperty_new_clone(p)); + } + + + for( itr = pvl_head(old->components); + itr != 0; + itr = pvl_next(itr)) + { + c = (icalcomponent*)pvl_data(itr); + icalcomponent_add_component(new,icalcomponent_new_clone(c)); + } + + return new; + +} + +/*** @brief Destructor + */ +void +icalcomponent_free (icalcomponent* c) +{ + icalproperty* prop; + icalcomponent* comp; + + 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"); +#else + if(c->parent != 0){ + return; + } +#endif + + if(c != 0 ){ + + 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(c,comp); + icalcomponent_free(comp); + } + + pvl_free(c->components); + + if (c->x_name != 0) { + free(c->x_name); + } + + if (c->timezones) + icaltimezone_array_free (c->timezones); + + c->kind = ICAL_NO_COMPONENT; + c->properties = 0; + c->property_iterator = 0; + c->components = 0; + c->component_iterator = 0; + c->x_name = 0; + c->id[0] = 'X'; + c->timezones = NULL; + + free(c); + } +} + +char* +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; + +/* RFC 2445 explicitly says that the newline is *ALWAYS* a \r\n (CRLF)!!!! */ +char newline[] = "\r\n"; +/* WIN32 automatically adds the \r, Anybody else need it? +#ifdef ICAL_UNIX_NEWLINE + char newline[] = "\n"; +#else + char newline[] = "\r\n"; +#endif +*/ + + icalcomponent *c; + icalproperty *p; + icalcomponent_kind kind = icalcomponent_isa(impl); + + const char* kind_string; + + buf = icalmemory_new_buffer(buf_size); + buf_ptr = buf; + + 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); + + icalerror_check_arg_rz( (kind_string!=0),"Unknown kind of component"); + + icalmemory_append_string(&buf, &buf_ptr, &buf_size, "BEGIN:"); + 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)) + { + p = (icalproperty*)pvl_data(itr); + + icalerror_assert((p!=0),"Got a null property"); + tmp_buf = icalproperty_as_ical_string(p); + + icalmemory_append_string(&buf, &buf_ptr, &buf_size, tmp_buf); + } + + + 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:"); + icalmemory_append_string(&buf, &buf_ptr, &buf_size, + icalcomponent_kind_to_string(kind)); + icalmemory_append_string(&buf, &buf_ptr, &buf_size, newline); + icalmemory_append_string(&buf, &buf_ptr, &buf_size, newline); + + out_buf = icalmemory_tmp_copy(buf); + free(buf); + + return out_buf; +} + + +int +icalcomponent_is_valid (icalcomponent* component) +{ + if ( (strcmp(component->id,"comp") == 0) && + component->kind != ICAL_NO_COMPONENT){ + return 1; + } else { + return 0; + } + +} + + +icalcomponent_kind +icalcomponent_isa (const icalcomponent* component) +{ + icalerror_check_arg_rx( (component!=0), "component", ICAL_NO_COMPONENT); + + if(component != 0) + { + return component->kind; + } + + return ICAL_NO_COMPONENT; +} + + +int +icalcomponent_isa_component (void* component) +{ + icalcomponent *impl = component; + + icalerror_check_arg_rz( (component!=0), "component"); + + if (strcmp(impl->id,"comp") == 0) { + return 1; + } else { + return 0; + } + +} + +void +icalcomponent_add_property (icalcomponent* component, icalproperty* property) +{ + icalerror_check_arg_rv( (component!=0), "component"); + icalerror_check_arg_rv( (property!=0), "property"); + + 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); + + pvl_push(component->properties,property); +} + + +void +icalcomponent_remove_property (icalcomponent* component, icalproperty* property) +{ + pvl_elem itr, next_itr; + + icalerror_check_arg_rv( (component!=0), "component"); + icalerror_check_arg_rv( (property!=0), "property"); + + icalerror_assert( (icalproperty_get_parent(property)),"The property is not a member of a component"); + + + for( itr = pvl_head(component->properties); + itr != 0; + itr = next_itr) + { + next_itr = pvl_next(itr); + + if( pvl_data(itr) == (void*)property ){ + + if (component->property_iterator == itr){ + component->property_iterator = pvl_next(itr); + } + + pvl_remove( component->properties, itr); + icalproperty_set_parent(property,0); + } + } +} + +int +icalcomponent_count_properties (icalcomponent* component, + icalproperty_kind kind) +{ + int count=0; + pvl_elem itr; + + icalerror_check_arg_rz( (component!=0), "component"); + + for( itr = pvl_head(component->properties); + itr != 0; + itr = pvl_next(itr)) + { + if(kind == icalproperty_isa((icalproperty*)pvl_data(itr)) || + kind == ICAL_ANY_PROPERTY){ + count++; + } + } + + + return count; + +} + +icalproperty* icalcomponent_get_current_property (icalcomponent* component) +{ + icalerror_check_arg_rz( (component!=0),"component"); + + if ((component->property_iterator==0)){ + return 0; + } + + return (icalproperty*) pvl_data(component->property_iterator); +} + +icalproperty* +icalcomponent_get_first_property (icalcomponent* c, icalproperty_kind kind) +{ + icalerror_check_arg_rz( (c!=0),"component"); + + for( c->property_iterator = pvl_head(c->properties); + c->property_iterator != 0; + c->property_iterator = pvl_next(c->property_iterator)) { + + icalproperty *p = (icalproperty*) pvl_data(c->property_iterator); + + if (icalproperty_isa(p) == kind || kind == ICAL_ANY_PROPERTY) { + + return p; + } + } + return 0; +} + +icalproperty* +icalcomponent_get_next_property (icalcomponent* c, icalproperty_kind kind) +{ + icalerror_check_arg_rz( (c!=0),"component"); + + if (c->property_iterator == 0){ + return 0; + } + + for( c->property_iterator = pvl_next(c->property_iterator); + c->property_iterator != 0; + c->property_iterator = pvl_next(c->property_iterator)) { + + icalproperty *p = (icalproperty*) pvl_data(c->property_iterator); + + if (icalproperty_isa(p) == kind || kind == ICAL_ANY_PROPERTY) { + + return p; + } + } + + return 0; +} + + +icalproperty** +icalcomponent_get_properties (icalcomponent* component, icalproperty_kind kind); + + +void +icalcomponent_add_component (icalcomponent* parent, icalcomponent* child) +{ + icalerror_check_arg_rv( (parent!=0), "parent"); + icalerror_check_arg_rv( (child!=0), "child"); + + if (child->parent !=0) { + icalerror_set_errno(ICAL_USAGE_ERROR); + } + + child->parent = parent; + + pvl_push(parent->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) +{ + pvl_elem itr, next_itr; + + icalerror_check_arg_rv( (parent!=0), "parent"); + icalerror_check_arg_rv( (child!=0), "child"); + + /* 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) + { + next_itr = pvl_next(itr); + + if( pvl_data(itr) == (void*)child ){ + + if (parent->component_iterator == itr){ + /* Don't let the current iterator become invalid */ + + /* HACK. The semantics for this are troubling. */ + parent->component_iterator = + pvl_next(parent->component_iterator); + + } + pvl_remove( parent->components, itr); + child->parent = 0; + break; + } + } +} + + +int +icalcomponent_count_components (icalcomponent* component, + icalcomponent_kind kind) +{ + int count=0; + pvl_elem itr; + + icalerror_check_arg_rz( (component!=0), "component"); + + for( itr = pvl_head(component->components); + itr != 0; + itr = pvl_next(itr)) + { + if(kind == icalcomponent_isa((icalcomponent*)pvl_data(itr)) || + kind == ICAL_ANY_COMPONENT){ + count++; + } + } + + return count; +} + +icalcomponent* +icalcomponent_get_current_component(icalcomponent* component) +{ + icalerror_check_arg_rz( (component!=0),"component"); + + if (component->component_iterator == 0){ + return 0; + } + + return (icalcomponent*) pvl_data(component->component_iterator); +} + +icalcomponent* +icalcomponent_get_first_component (icalcomponent* c, + icalcomponent_kind kind) +{ + icalerror_check_arg_rz( (c!=0),"component"); + + for( c->component_iterator = pvl_head(c->components); + c->component_iterator != 0; + c->component_iterator = pvl_next(c->component_iterator)) { + + icalcomponent *p = (icalcomponent*) pvl_data(c->component_iterator); + + if (icalcomponent_isa(p) == kind || kind == ICAL_ANY_COMPONENT) { + + return p; + } + } + + return 0; +} + + +icalcomponent* +icalcomponent_get_next_component (icalcomponent* c, icalcomponent_kind kind) +{ + icalerror_check_arg_rz( (c!=0),"component"); + + if (c->component_iterator == 0){ + return 0; + } + + for( c->component_iterator = pvl_next(c->component_iterator); + c->component_iterator != 0; + c->component_iterator = pvl_next(c->component_iterator)) { + + icalcomponent *p = (icalcomponent*) pvl_data(c->component_iterator); + + if (icalcomponent_isa(p) == kind || kind == ICAL_ANY_COMPONENT) { + + return p; + } + } + + return 0; +} + +icalcomponent* icalcomponent_get_first_real_component(icalcomponent *c) +{ + icalcomponent *comp; + + for(comp = icalcomponent_get_first_component(c,ICAL_ANY_COMPONENT); + comp != 0; + comp = icalcomponent_get_next_component(c,ICAL_ANY_COMPONENT)){ + + icalcomponent_kind kind = icalcomponent_isa(comp); + + if(kind == ICAL_VEVENT_COMPONENT || + kind == ICAL_VTODO_COMPONENT || + kind == ICAL_VJOURNAL_COMPONENT || + kind == ICAL_VFREEBUSY_COMPONENT || + kind == ICAL_VQUERY_COMPONENT || + kind == ICAL_VAGENDA_COMPONENT){ + return comp; + } + } + return 0; +} + + +/** @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; + icalcomponent_kind kind; + 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; + } + + /* FIXME this might go away */ + kind = icalcomponent_isa(comp); + if(kind == ICAL_VCALENDAR_COMPONENT){ + inner = icalcomponent_get_first_real_component(comp); + + /* Maybe there is a VTIMEZONE in there */ + if (inner == 0){ + inner = icalcomponent_get_first_component(comp, + ICAL_VTIMEZONE_COMPONENT); + } + + } else { + inner = comp; + } + + if (inner == 0){ + icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR); + /*icalerror_warn("icalcomponent_get_span: no component specified, or empty VCALENDAR component");*/ + return span; + } + + kind = icalcomponent_isa(inner); + + if( !( kind == ICAL_VEVENT_COMPONENT || + kind == ICAL_VJOURNAL_COMPONENT || + kind == ICAL_VTODO_COMPONENT || + kind == ICAL_VFREEBUSY_COMPONENT )) { + icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR); + /*icalerror_warn("icalcomponent_get_span: no component specified, or empty VCALENDAR component");*/ + return span; + + } + + /* 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; + } + } + + 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; + } + + 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); + exrule != NULL; + exrule = 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); + } + + return 0; /** no matches **/ +} + +/** + * @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. + */ + +static int icalcomponent_is_busy(icalcomponent *comp) { + icalproperty *transp; + enum icalproperty_status status; + int ret = 1; + + /** @todo check access control here, converting busy->free if the + permissions do not allow access... */ + + /* 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); +} + + + + +/** + * @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; + } +} + + + +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) +{ + int errors = 0; + icalproperty *p; + icalcomponent *c; + pvl_elem itr; + + for( itr = pvl_head(component->properties); + itr != 0; + itr = pvl_next(itr)) + { + p = (icalproperty*)pvl_data(itr); + + if(icalproperty_isa(p) == ICAL_XLICERROR_PROPERTY) + { + errors++; + } + } + + + for( itr = pvl_head(component->components); + itr != 0; + itr = pvl_next(itr)) + { + c = (icalcomponent*)pvl_data(itr); + + errors += icalcomponent_count_errors(c); + + } + + return errors; +} + + +void icalcomponent_strip_errors(icalcomponent* component) +{ + icalproperty *p; + icalcomponent *c; + pvl_elem itr, next_itr; + + for( itr = pvl_head(component->properties); + itr != 0; + itr = next_itr) + { + p = (icalproperty*)pvl_data(itr); + next_itr = pvl_next(itr); + + if(icalproperty_isa(p) == ICAL_XLICERROR_PROPERTY) + { + icalcomponent_remove_property(component,p); + } + } + + for( itr = pvl_head(component->components); + itr != 0; + itr = pvl_next(itr)) + { + c = (icalcomponent*)pvl_data(itr); + icalcomponent_strip_errors(c); + } +} + +/* Hack. This will change the state of the iterators */ +void icalcomponent_convert_errors(icalcomponent* component) +{ + icalproperty *p, *next_p; + icalcomponent *c; + + for(p = icalcomponent_get_first_property(component,ICAL_ANY_PROPERTY); + p != 0; + p = next_p){ + + next_p = icalcomponent_get_next_property(component,ICAL_ANY_PROPERTY); + + if(icalproperty_isa(p) == ICAL_XLICERROR_PROPERTY) + { + struct icalreqstattype rst; + icalparameter *param = icalproperty_get_first_parameter + (p,ICAL_XLICERRORTYPE_PARAMETER); + + rst.code = ICAL_UNKNOWN_STATUS; + rst.desc = 0; + + switch(icalparameter_get_xlicerrortype(param)){ + + case ICAL_XLICERRORTYPE_PARAMETERNAMEPARSEERROR: { + rst.code = ICAL_3_2_INVPARAM_STATUS; + break; + } + case ICAL_XLICERRORTYPE_PARAMETERVALUEPARSEERROR: { + rst.code = ICAL_3_3_INVPARAMVAL_STATUS; + break; + } + case ICAL_XLICERRORTYPE_PROPERTYPARSEERROR: { + rst.code = ICAL_3_0_INVPROPNAME_STATUS; + break; + } + case ICAL_XLICERRORTYPE_VALUEPARSEERROR: { + rst.code = ICAL_3_1_INVPROPVAL_STATUS; + break; + } + case ICAL_XLICERRORTYPE_COMPONENTPARSEERROR: { + rst.code = ICAL_3_4_INVCOMP_STATUS; + break; + } + + default: { + break; + } + } + if (rst.code != ICAL_UNKNOWN_STATUS){ + + rst.debug = icalproperty_get_xlicerror(p); + icalcomponent_add_property(component, + icalproperty_new_requeststatus(rst)); + + icalcomponent_remove_property(component,p); + } + } + } + + for(c = icalcomponent_get_first_component(component,ICAL_ANY_COMPONENT); + c != 0; + c = icalcomponent_get_next_component(component,ICAL_ANY_COMPONENT)){ + + icalcomponent_convert_errors(c); + } +} + + +icalcomponent* icalcomponent_get_parent(icalcomponent* component) +{ + return component->parent; +} + +void icalcomponent_set_parent(icalcomponent* component, icalcomponent* parent) +{ + component->parent = parent; +} + +icalcompiter icalcompiter_null = {ICAL_NO_COMPONENT,0}; + + +struct icalcomponent_kind_map { + icalcomponent_kind kind; + char name[20]; +}; + + + +static struct icalcomponent_kind_map component_map[] = +{ + { ICAL_VEVENT_COMPONENT, "VEVENT" }, + { 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" }, + { ICAL_XSTANDARD_COMPONENT, "STANDARD" }, /*These are part of RFC2445 */ + { ICAL_XDAYLIGHT_COMPONENT, "DAYLIGHT" }, /*but are not really components*/ + { ICAL_X_COMPONENT, "X" }, + { ICAL_VSCHEDULE_COMPONENT, "SCHEDULE" }, + + /* CAP components */ + { ICAL_VQUERY_COMPONENT, "VQUERY" }, + { ICAL_VCAR_COMPONENT, "VCAR" }, + { ICAL_VCOMMAND_COMPONENT, "VCOMMAND" }, + + /* libical private components */ + { ICAL_XLICINVALID_COMPONENT, "X-LIC-UNKNOWN" }, + { ICAL_XLICMIMEPART_COMPONENT, "X-LIC-MIME-PART" }, + { ICAL_ANY_COMPONENT, "ANY" }, + { ICAL_XROOT_COMPONENT, "XROOT" }, + + /* End of list */ + { ICAL_NO_COMPONENT, "" }, +}; + + +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) +{ + int i; + + for (i=0; component_map[i].kind != ICAL_NO_COMPONENT; i++) { + if (component_map[i].kind == kind) { + return component_map[i].name; + } + } + + return 0; + +} + +icalcomponent_kind icalcomponent_string_to_kind(const char* string) +{ + int i; + + if (string ==0 ) { + return ICAL_NO_COMPONENT; + } + + for (i=0; component_map[i].kind != ICAL_NO_COMPONENT; i++) { + if (strcasecmp(component_map[i].name, string) == 0) { + return component_map[i].kind; + } + } + + return ICAL_NO_COMPONENT; +} + + + +icalcompiter +icalcomponent_begin_component(icalcomponent* component,icalcomponent_kind kind) +{ + icalcompiter itr; + pvl_elem i; + + itr.kind = kind; + itr.iter = NULL; + + icalerror_check_arg_re(component!=0,"component",icalcompiter_null); + + for( i = pvl_head(component->components); i != 0; i = pvl_next(i)) { + + icalcomponent *c = (icalcomponent*) pvl_data(i); + + if (icalcomponent_isa(c) == kind || kind == ICAL_ANY_COMPONENT) { + + itr.iter = i; + + return itr; + } + } + + return icalcompiter_null; +} + +icalcompiter +icalcomponent_end_component(icalcomponent* component,icalcomponent_kind kind) +{ + icalcompiter itr; + pvl_elem i; + + itr.kind = kind; + + icalerror_check_arg_re(component!=0,"component",icalcompiter_null); + + for( i = pvl_tail(component->components); i != 0; i = pvl_prior(i)) { + + icalcomponent *c = (icalcomponent*) pvl_data(i); + + if (icalcomponent_isa(c) == kind || kind == ICAL_ANY_COMPONENT) { + + itr.iter = pvl_next(i); + + return itr; + } + } + + return icalcompiter_null;; +} + + +icalcomponent* icalcompiter_next(icalcompiter* i) +{ + if (i->iter == 0){ + return 0; + } + + icalerror_check_arg_rz( (i!=0),"i"); + + for( i->iter = pvl_next(i->iter); + i->iter != 0; + i->iter = pvl_next(i->iter)) { + + icalcomponent *c = (icalcomponent*) pvl_data(i->iter); + + if (icalcomponent_isa(c) == i->kind + || i->kind == ICAL_ANY_COMPONENT) { + + return icalcompiter_deref(i);; + } + } + + return 0; + +} + +icalcomponent* icalcompiter_prior(icalcompiter* i) +{ + if (i->iter == 0){ + return 0; + } + + for( i->iter = pvl_prior(i->iter); + i->iter != 0; + i->iter = pvl_prior(i->iter)) { + + icalcomponent *c = (icalcomponent*) pvl_data(i->iter); + + if (icalcomponent_isa(c) == i->kind + || i->kind == ICAL_ANY_COMPONENT) { + + return icalcompiter_deref(i);; + } + } + + return 0; + +} +icalcomponent* icalcompiter_deref(icalcompiter* i) +{ + if(i->iter ==0){ + return 0; + } + + return pvl_data(i->iter); +} + +icalcomponent* icalcomponent_get_inner(icalcomponent* comp) +{ + if (icalcomponent_isa(comp) == ICAL_VCALENDAR_COMPONENT){ + return icalcomponent_get_first_real_component(comp); + } else { + return comp; + } +} + +/** @brief sets the METHOD property to the given method + */ + +void icalcomponent_set_method(icalcomponent* comp, icalproperty_method method) +{ + icalproperty *prop + = icalcomponent_get_first_property(comp, ICAL_METHOD_PROPERTY); + + + 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(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) +{ + const 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; + + prop = icalcomponent_get_first_property(inner,ICAL_DTSTART_PROPERTY); + if (prop == 0){ + return icaltime_null_time(); + } + + 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) { + 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); + + 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) +{ + const char *tzid; + ICALSETUPSET(ICAL_DTEND_PROPERTY); + + if (icalcomponent_get_first_property(inner,ICAL_DURATION_PROPERTY) + != NULL) { + icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR); + return; + } + + 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_dtend(prop,v); + + 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) +{ + ICALSETUPSET(ICAL_DURATION_PROPERTY); + + if (icalcomponent_get_first_property(inner,ICAL_DTEND_PROPERTY) != NULL) { + icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR); + return; + } + + if (prop == 0) { + prop = icalproperty_new_duration(v); + icalcomponent_add_property(inner, prop); + } else { + 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); + + icalproperty *end_prop + = icalcomponent_get_first_property(inner,ICAL_DTEND_PROPERTY); + + icalproperty *dur_prop + = icalcomponent_get_first_property(inner,ICAL_DURATION_PROPERTY); + + struct icaldurationtype ret = icaldurationtype_null_duration(); + + if ( dur_prop != 0 && end_prop == 0) { + ret = icalproperty_get_duration(dur_prop); + + } 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); + struct icaltimetype end = + icalcomponent_get_dtend(inner); + + ret = icaltime_subtract(end, start); + } else { + /* Error, both duration and dtend have been specified */ + icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR); + } + return ret; +} + +void icalcomponent_set_dtstamp(icalcomponent* comp, struct icaltimetype v) +{ + + ICALSETUPSET(ICAL_DTSTAMP_PROPERTY); + + if (prop == 0){ + prop = icalproperty_new_dtstamp(v); + icalcomponent_add_property(inner, prop); + } + + icalproperty_set_dtstamp(prop,v); + +} + + +struct icaltimetype icalcomponent_get_dtstamp(icalcomponent* comp) +{ + icalcomponent *inner = icalcomponent_get_inner(comp); + icalproperty *prop + = icalcomponent_get_first_property(inner,ICAL_DTSTAMP_PROPERTY); + + if (prop == 0){ + return icaltime_null_time(); + } + + return icalproperty_get_dtstamp(prop); +} + + +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"); + + 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){ + return 0; + } + + return icalproperty_get_summary(prop); + +} + +void icalcomponent_set_comment(icalcomponent* comp, const char* v) +{ + ICALSETUPSET(ICAL_COMMENT_PROPERTY); + + if (prop == 0){ + prop = icalproperty_new_comment(v); + icalcomponent_add_property(inner, 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); + + 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) +{ + ICALSETUPSET(ICAL_UID_PROPERTY); + + if (prop == 0){ + 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); + + 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_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(); + } + + inner = icalcomponent_get_inner(comp); + + if(inner == 0){ + icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR); + return icaltime_null_time(); + } + + 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() +{ + return icalcomponent_new(ICAL_VCALENDAR_COMPONENT); +} +icalcomponent* icalcomponent_new_vevent() +{ + return icalcomponent_new(ICAL_VEVENT_COMPONENT); +} +icalcomponent* icalcomponent_new_vtodo() +{ + return icalcomponent_new(ICAL_VTODO_COMPONENT); +} +icalcomponent* icalcomponent_new_vjournal() +{ + return icalcomponent_new(ICAL_VJOURNAL_COMPONENT); +} +icalcomponent* icalcomponent_new_valarm() +{ + return icalcomponent_new(ICAL_VALARM_COMPONENT); +} +icalcomponent* icalcomponent_new_vfreebusy() +{ + return icalcomponent_new(ICAL_VFREEBUSY_COMPONENT); +} +icalcomponent* icalcomponent_new_vtimezone() +{ + return icalcomponent_new(ICAL_VTIMEZONE_COMPONENT); +} +icalcomponent* icalcomponent_new_xstandard() +{ + return icalcomponent_new(ICAL_XSTANDARD_COMPONENT); +} +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; + unsigned 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]; + (void)tzid_prop; /* hack to stop unused variable warning */ + + /* 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; + const char *existing_tzid; + const char *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); + snprintf (suffix_buf, sizeof(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; (unsigned int)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; + const 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); + } +} diff --git a/libkcal/libical/src/libical/icalcomponent.h b/libkcal/libical/src/libical/icalcomponent.h new file mode 100644 index 000000000..ae8a5d323 --- /dev/null +++ b/libkcal/libical/src/libical/icalcomponent.h @@ -0,0 +1,283 @@ +/* -*- Mode: C -*- */ +/*====================================================================== + FILE: icalcomponent.h + CREATOR: eric 20 March 1999 + + + (C) COPYRIGHT 2000, Eric Busboom <eric@softwarestudio.org> + http://www.softwarestudio.org + + This program is free software; you can redistribute it and/or modify + it under the terms of either: + + The LGPL as published by the Free Software Foundation, version + 2.1, available at: http://www.fsf.org/copyleft/lesser.html + + Or: + + The Mozilla Public License Version 1.0. You may obtain a copy of + the License at http://www.mozilla.org/MPL/ + + The original code is icalcomponent.h + +======================================================================*/ + +#ifndef ICALCOMPONENT_H +#define ICALCOMPONENT_H + +#include "icalproperty.h" +#include "icalvalue.h" +#include "icalenums.h" /* defines icalcomponent_kind */ +#include "pvl.h" + +typedef struct icalcomponent_impl icalcomponent; + +#ifndef ICALTIMEZONE_DEFINED +#define ICALTIMEZONE_DEFINED +/** @brief An opaque struct representing a timezone. + * We declare this here to avoid a circular dependancy. + */ +typedef struct _icaltimezone icaltimezone; +#endif + + +/* This is exposed so that callers will not have to allocate and + deallocate iterators. Pretend that you can't see it. */ +typedef struct icalcompiter +{ + icalcomponent_kind kind; + pvl_elem iter; + +} icalcompiter; + +icalcomponent* icalcomponent_new(icalcomponent_kind kind); +icalcomponent* icalcomponent_new_clone(icalcomponent* component); +icalcomponent* icalcomponent_new_from_string(char* str); +icalcomponent* icalcomponent_vanew(icalcomponent_kind kind, ...); +void icalcomponent_free(icalcomponent* component); + +char* icalcomponent_as_ical_string(icalcomponent* component); + +int icalcomponent_is_valid(icalcomponent* component); + +icalcomponent_kind icalcomponent_isa(const icalcomponent* component); + +int icalcomponent_isa_component (void* component); + +/* + * Working with properties + */ + +void icalcomponent_add_property(icalcomponent* component, + icalproperty* property); + +void icalcomponent_remove_property(icalcomponent* component, + icalproperty* property); + +int icalcomponent_count_properties(icalcomponent* component, + icalproperty_kind kind); + +/* Iterate through the properties */ +icalproperty* icalcomponent_get_current_property(icalcomponent* component); + +icalproperty* icalcomponent_get_first_property(icalcomponent* component, + icalproperty_kind kind); +icalproperty* icalcomponent_get_next_property(icalcomponent* component, + icalproperty_kind kind); + + +/* + * Working with components + */ + + +/* Return the first VEVENT, VTODO or VJOURNAL sub-component of cop, or + comp if it is one of those types */ + +icalcomponent* icalcomponent_get_inner(icalcomponent* comp); + + +void icalcomponent_add_component(icalcomponent* parent, + icalcomponent* child); + +void icalcomponent_remove_component(icalcomponent* parent, + icalcomponent* child); + +int icalcomponent_count_components(icalcomponent* component, + icalcomponent_kind kind); + +/** + 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); + + +/* Iteration Routines. There are two forms of iterators, internal and +external. The internal ones came first, and are almost completely +sufficient, but they fail badly when you want to construct a loop that +removes components from the container.*/ + + +/* Iterate through components */ +icalcomponent* icalcomponent_get_current_component (icalcomponent* component); + +icalcomponent* icalcomponent_get_first_component(icalcomponent* component, + icalcomponent_kind kind); +icalcomponent* icalcomponent_get_next_component(icalcomponent* component, + icalcomponent_kind kind); + +/* Using external iterators */ +icalcompiter icalcomponent_begin_component(icalcomponent* component, + icalcomponent_kind kind); +icalcompiter icalcomponent_end_component(icalcomponent* component, + icalcomponent_kind kind); +icalcomponent* icalcompiter_next(icalcompiter* i); +icalcomponent* icalcompiter_prior(icalcompiter* i); +icalcomponent* icalcompiter_deref(icalcompiter* i); + + +/* Working with embedded error properties */ + + +/* Check the component against itip rules and insert error properties*/ +/* Working with embedded error properties */ +int icalcomponent_check_restrictions(icalcomponent* comp); + +/** Count embedded errors. */ +int icalcomponent_count_errors(icalcomponent* component); + +/** Remove all X-LIC-ERROR properties*/ +void icalcomponent_strip_errors(icalcomponent* component); + +/** Convert some X-LIC-ERROR properties into RETURN-STATUS properties*/ +void icalcomponent_convert_errors(icalcomponent* component); + +/* Internal operations. They are private, and you should not be using them. */ +icalcomponent* icalcomponent_get_parent(icalcomponent* component); +void icalcomponent_set_parent(icalcomponent* component, + icalcomponent* parent); + +/* Kind conversion routines */ + +int icalcomponent_kind_is_valid(const icalcomponent_kind kind); + +icalcomponent_kind icalcomponent_string_to_kind(const char* string); + +const char* icalcomponent_kind_to_string(icalcomponent_kind kind); + + +/************* Derived class methods. **************************** + +If the code was in an OO language, the remaining routines would be +members of classes derived from icalcomponent. Don't call them on the +wrong component subtypes. */ + +/** For VCOMPONENT: Return a reference to the first VEVENT, VTODO or + VJOURNAL */ +icalcomponent* icalcomponent_get_first_real_component(icalcomponent *c); + +/** For VEVENT, VTODO, VJOURNAL and VTIMEZONE: report the start and end + times of an event in UTC */ +struct icaltime_span icalcomponent_get_span(icalcomponent* comp); + +/******************** Convienience routines **********************/ + +void icalcomponent_set_dtstart(icalcomponent* comp, struct icaltimetype v); +struct icaltimetype icalcomponent_get_dtstart(icalcomponent* comp); + +/* For the icalcomponent routines only, dtend and duration are tied + together. If you call the set routine for one and the other exists, + the routine will calculate the change to the other. That is, if + there is a DTEND and you call set_duration, the routine will modify + DTEND to be the sum of DTSTART and the duration. If you call a get + routine for one and the other exists, the routine will calculate + the return value. If you call a set routine and neither exists, the + routine will create the apcompriate comperty */ + + +struct icaltimetype icalcomponent_get_dtend(icalcomponent* comp); +void icalcomponent_set_dtend(icalcomponent* comp, struct icaltimetype v); + +struct icaltimetype icalcomponent_get_due(icalcomponent* comp); +void icalcomponent_set_due(icalcomponent* comp, struct icaltimetype v); + +void icalcomponent_set_duration(icalcomponent* comp, + struct icaldurationtype v); +struct icaldurationtype icalcomponent_get_duration(icalcomponent* comp); + +void icalcomponent_set_method(icalcomponent* comp, icalproperty_method method); +icalproperty_method icalcomponent_get_method(icalcomponent* comp); + +struct icaltimetype icalcomponent_get_dtstamp(icalcomponent* comp); +void icalcomponent_set_dtstamp(icalcomponent* comp, struct icaltimetype v); + +void icalcomponent_set_summary(icalcomponent* comp, const char* v); +const char* icalcomponent_get_summary(icalcomponent* comp); + +void icalcomponent_set_comment(icalcomponent* comp, const char* v); +const char* icalcomponent_get_comment(icalcomponent* comp); + +void icalcomponent_set_uid(icalcomponent* comp, const char* v); +const char* icalcomponent_get_uid(icalcomponent* comp); + +void icalcomponent_set_relcalid(icalcomponent* comp, const char* v); +const char* icalcomponent_get_relcalid(icalcomponent* comp); + +void icalcomponent_set_recurrenceid(icalcomponent* comp, + struct icaltimetype v); +struct icaltimetype icalcomponent_get_recurrenceid(icalcomponent* comp); + +void icalcomponent_set_description(icalcomponent* comp, const char* v); +const char* icalcomponent_get_description(icalcomponent* comp); + +void icalcomponent_set_location(icalcomponent* comp, const char* v); +const char* icalcomponent_get_location(icalcomponent* comp); + +void icalcomponent_set_sequence(icalcomponent* comp, int v); +int icalcomponent_get_sequence(icalcomponent* comp); + +void icalcomponent_set_status(icalcomponent* comp, enum icalproperty_status v); +enum icalproperty_status icalcomponent_get_status(icalcomponent* comp); + + +/** Calls the given function for each TZID parameter found in the + component, and any subcomponents. */ +void icalcomponent_foreach_tzid(icalcomponent* comp, + void (*callback)(icalparameter *param, void *data), + void *callback_data); + +/** Returns the icaltimezone in the component corresponding to the + TZID, or NULL if it can't be found. */ +icaltimezone* icalcomponent_get_timezone(icalcomponent* comp, + const char *tzid); + +int icalproperty_recurrence_is_excluded(icalcomponent *comp, + struct icaltimetype *dtstart, + struct icaltimetype *recurtime); + +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); + + +/*************** Type Specific routines ***************/ + +icalcomponent* icalcomponent_new_vcalendar(); +icalcomponent* icalcomponent_new_vevent(); +icalcomponent* icalcomponent_new_vtodo(); +icalcomponent* icalcomponent_new_vjournal(); +icalcomponent* icalcomponent_new_valarm(); +icalcomponent* icalcomponent_new_vfreebusy(); +icalcomponent* icalcomponent_new_vtimezone(); +icalcomponent* icalcomponent_new_xstandard(); +icalcomponent* icalcomponent_new_xdaylight(); +icalcomponent* icalcomponent_new_vagenda(); +icalcomponent* icalcomponent_new_vquery(); + +#endif /* !ICALCOMPONENT_H */ diff --git a/libkcal/libical/src/libical/icalderivedparameter.c.in b/libkcal/libical/src/libical/icalderivedparameter.c.in new file mode 100644 index 000000000..db112be6d --- /dev/null +++ b/libkcal/libical/src/libical/icalderivedparameter.c.in @@ -0,0 +1,205 @@ +/* -*- Mode: C -*- + ====================================================================== + FILE: icalderivedparameters.{c,h} + CREATOR: eric 09 May 1999 + + + + (C) COPYRIGHT 2000, Eric Busboom, http://www.softwarestudio.org + + This program is free software; you can redistribute it and/or modify + it under the terms of either: + + The LGPL as published by the Free Software Foundation, version + 2.1, available at: http://www.fsf.org/copyleft/lesser.html + + Or: + + The Mozilla Public License Version 1.0. You may obtain a copy of + the License at http://www.mozilla.org/MPL/ + + The original code is icalderivedparameters.{c,h} + + Contributions from: + Graham Davison (g.m.davison@computer.org) + + ======================================================================*/ +/*#line 29 "icalparameter.c.in"*/ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + + +#include "icalparameter.h" +#include "icalparameterimpl.h" + +#include "icalproperty.h" +#include "icalerror.h" +#include "icalmemory.h" + +#include <stdlib.h> /* for malloc() */ +#include <errno.h> +#include <string.h> /* for memset() */ + +icalvalue_kind icalparameter_value_to_value_kind(icalparameter_value value); + +struct icalparameter_impl* icalparameter_new_impl(icalparameter_kind kind); + +/* This map associates each of the parameters with the string + representation of the parameter's name */ +struct icalparameter_kind_map { + icalparameter_kind kind; + const char *name; + +}; + +/* This map associates the enumerations for the VALUE parameter with + the kinds of VALUEs. */ + +struct icalparameter_value_kind_map { + icalparameter_value value; + icalvalue_kind kind; +}; + +/* This map associates the parameter enumerations with a specific parameter and the string representation of the enumeration */ + +struct icalparameter_map { + icalparameter_kind kind; + int enumeration; + const char* str; +}; + + + +<insert_code_here> + +const char* icalparameter_kind_to_string(icalparameter_kind kind) +{ + int i; + + for (i=0; parameter_map[i].kind != ICAL_NO_PARAMETER; i++) { + if (parameter_map[i].kind == kind) { + return parameter_map[i].name; + } + } + + return 0; + +} + +icalparameter_kind icalparameter_string_to_kind(const char* string) +{ + int i; + + if (string ==0 ) { + return ICAL_NO_PARAMETER; + } + + for (i=0; parameter_map[i].kind != ICAL_NO_PARAMETER; i++) { + + if (strcasecmp(parameter_map[i].name, string) == 0) { + return parameter_map[i].kind; + } + } + + if(strncmp(string,"X-",2)==0){ + return ICAL_X_PARAMETER; + } + + return ICAL_NO_PARAMETER; +} + + +icalvalue_kind icalparameter_value_to_value_kind(icalparameter_value value) +{ + int i; + + for (i=0; value_kind_map[i].kind != ICAL_NO_VALUE; i++) { + + if (value_kind_map[i].value == value) { + return value_kind_map[i].kind; + } + } + + return ICAL_NO_VALUE; +} + + +const char* icalparameter_enum_to_string(int e) +{ + int i; + + icalerror_check_arg_rz(e >= ICALPARAMETER_FIRST_ENUM,"e"); + icalerror_check_arg_rz(e <= ICALPARAMETER_LAST_ENUM,"e"); + + for (i=0; icalparameter_map[i].kind != ICAL_NO_PARAMETER; i++){ + if(e == icalparameter_map[i].enumeration){ + return icalparameter_map[i].str; + } + } + + return 0; +} + +int icalparameter_string_to_enum(const char* str) +{ + int i; + + icalerror_check_arg_rz(str != 0,"str"); + + for (i=0; icalparameter_map[i].kind != ICAL_NO_PARAMETER; i++){ + if(strcasecmp(str,icalparameter_map[i].str) == 0) { + return icalparameter_map[i].enumeration; + } + } + + return 0; +} + +icalparameter* icalparameter_new_from_value_string(icalparameter_kind kind,const char* val) +{ + + struct icalparameter_impl* param=0; + int found_kind = 0; + int i; + + icalerror_check_arg_rz((val!=0),"val"); + + /* Search through the parameter map to find a matching kind */ + + param = icalparameter_new_impl(kind); + if (!param) + return 0; + + for (i=0; icalparameter_map[i].kind != ICAL_NO_PARAMETER; i++){ + if(kind == icalparameter_map[i].kind) { + found_kind = 1; + if(strcasecmp(val,icalparameter_map[i].str) == 0) { + + param->data = (int)icalparameter_map[i].enumeration; + return param; + } + } + } + + if(found_kind == 1){ + /* The kind was in the parameter map, but the string did not + match, so assume that it is an alternate value, like an + X-value.*/ + + icalparameter_set_xvalue(param, val); + + } else { + + /* If the kind was not found, then it must be a string type */ + + ((struct icalparameter_impl*)param)->string = icalmemory_strdup(val); + + } + + return param; +} + + + + diff --git a/libkcal/libical/src/libical/icalderivedparameter.h.in b/libkcal/libical/src/libical/icalderivedparameter.h.in new file mode 100644 index 000000000..99be6ff2d --- /dev/null +++ b/libkcal/libical/src/libical/icalderivedparameter.h.in @@ -0,0 +1,36 @@ +/* -*- Mode: C -*- */ +/*====================================================================== + FILE: icalparam.h + CREATOR: eric 20 March 1999 + + + + + + (C) COPYRIGHT 2000, Eric Busboom, http://www.softwarestudio.org + + This program is free software; you can redistribute it and/or modify + it under the terms of either: + + The LGPL as published by the Free Software Foundation, version + 2.1, available at: http://www.fsf.org/copyleft/lesser.html + + Or: + + The Mozilla Public License Version 1.0. You may obtain a copy of + the License at http://www.mozilla.org/MPL/ + + The original code is icalparam.h + + ======================================================================*/ + +#ifndef ICALDERIVEDPARAMETER_H +#define ICALDERIVEDPARAMETER_H + + +typedef struct icalparameter_impl icalparameter; + +const char* icalparameter_enum_to_string(int e); +int icalparameter_string_to_enum(const char* str); + +<insert_code_here> diff --git a/libkcal/libical/src/libical/icalderivedproperty.c.in b/libkcal/libical/src/libical/icalderivedproperty.c.in new file mode 100644 index 000000000..a8bcbc047 --- /dev/null +++ b/libkcal/libical/src/libical/icalderivedproperty.c.in @@ -0,0 +1,284 @@ +/* -*- Mode: C -*- */ + +/*====================================================================== + FILE: icalderivedproperty.c + CREATOR: eric 15 Feb 2001 + + + + (C) COPYRIGHT 2000, Eric Busboom, http://www.softwarestudio.org + + This program is free software; you can redistribute it and/or modify + it under the terms of either: + + The LGPL as published by the Free Software Foundation, version + 2.1, available at: http://www.fsf.org/copyleft/lesser.html + + Or: + + The Mozilla Public License Version 1.0. You may obtain a copy of + the License at http://www.mozilla.org/MPL/ + + The original code is icalproperty.c + +======================================================================*/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "icalproperty.h" +#include "icalcomponent.h" +#include "pvl.h" +#include "icalenums.h" +#include "icalerror.h" +#include "icalmemory.h" +#include "icalparser.h" + +#include <string.h> /* For icalmemory_strdup, rindex */ +#include <assert.h> +#include <stdlib.h> +#include <errno.h> +#include <stdio.h> /* for printf */ +#include <stdarg.h> /* for va_list, va_start, etc. */ + +struct icalproperty_impl* +icalproperty_new_impl (icalproperty_kind kind); +void icalproperty_add_parameters(struct icalproperty_impl *prop,va_list args); + +/* This map associates the property kinds with the string + representation of the property name and the kind of VALUE that the + property uses as a default */ + +struct icalproperty_map { + icalproperty_kind kind; + const char *name; + icalvalue_kind value; + +}; + +/* This map associates the property enumerations with the king of + property that they are used in and the string representation of the + enumeration */ + +struct icalproperty_enum_map { + icalproperty_kind prop; + int prop_enum; + const char* str; +}; + + +<insert_code_here> + +int icalproperty_kind_is_valid(const icalproperty_kind kind) +{ + int i = 0; + do { + if (property_map[i].kind == kind) + return 1; + } while (property_map[i++].kind != ICAL_NO_PROPERTY); + + return 0; +} + +const char* icalproperty_kind_to_string(icalproperty_kind kind) +{ + int i; + + for (i=0; property_map[i].kind != ICAL_NO_PROPERTY; i++) { + if (property_map[i].kind == kind) { + return property_map[i].name; + } + } + + return 0; + +} + + +icalproperty_kind icalproperty_string_to_kind(const char* string) +{ + int i; + + if (string ==0 ) { + return ICAL_NO_PROPERTY; + } + + + for (i=0; property_map[i].kind != ICAL_NO_PROPERTY; i++) { + if (strcasecmp(property_map[i].name, string) == 0) { + return property_map[i].kind; + } + } + + if(strncmp(string,"X-",2)==0){ + return ICAL_X_PROPERTY; + } + + + return ICAL_NO_PROPERTY; +} + + +icalproperty_kind icalproperty_value_kind_to_kind(icalvalue_kind kind) +{ + int i; + + for (i=0; property_map[i].kind != ICAL_NO_PROPERTY; i++) { + if ( property_map[i].value == kind ) { + return property_map[i].kind; + } + } + + return ICAL_NO_PROPERTY; +} + + + +icalvalue_kind icalproperty_kind_to_value_kind(icalproperty_kind kind) +{ + int i; + + for (i=0; property_map[i].kind != ICAL_NO_PROPERTY; i++) { + if ( property_map[i].kind == kind ) { + return property_map[i].value; + } + } + + return ICAL_NO_VALUE; +} + + +const char* icalproperty_enum_to_string(int e) +{ + icalerror_check_arg_rz(e >= ICALPROPERTY_FIRST_ENUM,"e"); + icalerror_check_arg_rz(e <= ICALPROPERTY_LAST_ENUM,"e"); + + return enum_map[e-ICALPROPERTY_FIRST_ENUM].str; +} + +int icalproperty_kind_and_string_to_enum(const int kind, const char* str) +{ + icalproperty_kind pkind; + int i; + + icalerror_check_arg_rz(str!=0,"str") + + if ((pkind = icalproperty_value_kind_to_kind(kind)) == ICAL_NO_VALUE) + return 0; + + while(*str == ' '){ + str++; + } + + for (i=ICALPROPERTY_FIRST_ENUM; i != ICALPROPERTY_LAST_ENUM; i++) { + if (enum_map[i-ICALPROPERTY_FIRST_ENUM].prop == pkind) + break; + } + if (i == ICALPROPERTY_LAST_ENUM) + return 0; + + for (; i != ICALPROPERTY_LAST_ENUM; i++) { + if ( strcasecmp(enum_map[i-ICALPROPERTY_FIRST_ENUM].str, str) == 0) { + return enum_map[i-ICALPROPERTY_FIRST_ENUM].prop_enum; + } + } + + return 0; +} + +/** @deprecated please use icalproperty_kind_and_string_to_enum instead */ +int icalproperty_string_to_enum(const char* str) +{ + int i; + + icalerror_check_arg_rz(str!=0,"str") + + while(*str == ' '){ + str++; + } + + for (i=ICALPROPERTY_FIRST_ENUM; i != ICALPROPERTY_LAST_ENUM; i++) { + if ( strcasecmp(enum_map[i-ICALPROPERTY_FIRST_ENUM].str, str) == 0) { + return enum_map[i-ICALPROPERTY_FIRST_ENUM].prop_enum; + } + } + + return 0; +} + +int icalproperty_enum_belongs_to_property(icalproperty_kind kind, int e) +{ + int i; + + + for (i=ICALPROPERTY_FIRST_ENUM; i != ICALPROPERTY_LAST_ENUM; i++) { + if(enum_map[i-ICALPROPERTY_FIRST_ENUM].prop_enum == e && + enum_map[i-ICALPROPERTY_FIRST_ENUM].prop == kind ){ + return 1; + } + } + + return 0; +} + + +const char* icalproperty_method_to_string(icalproperty_method method) +{ + icalerror_check_arg_rz(method >= ICAL_METHOD_X,"method"); + icalerror_check_arg_rz(method <= ICAL_METHOD_NONE,"method"); + + return enum_map[method-ICALPROPERTY_FIRST_ENUM].str; +} + +icalproperty_method icalproperty_string_to_method(const char* str) +{ + int i; + + icalerror_check_arg_rx(str!=0,"str",ICAL_METHOD_NONE) + + while(*str == ' '){ + str++; + } + + for (i=ICAL_METHOD_X-ICALPROPERTY_FIRST_ENUM; + i != ICAL_METHOD_NONE-ICALPROPERTY_FIRST_ENUM; + i++) { + if ( strcasecmp(enum_map[i].str, str) == 0) { + return (icalproperty_method)enum_map[i].prop_enum; + } + } + + return ICAL_METHOD_NONE; +} + + +const char* icalenum_status_to_string(icalproperty_status status) +{ + icalerror_check_arg_rz(status >= ICAL_STATUS_X,"status"); + icalerror_check_arg_rz(status <= ICAL_STATUS_NONE,"status"); + + return enum_map[status-ICALPROPERTY_FIRST_ENUM].str; +} + +icalproperty_status icalenum_string_to_status(const char* str) +{ + int i; + + icalerror_check_arg_rx(str!=0,"str",ICAL_STATUS_NONE) + + while(*str == ' '){ + str++; + } + + for (i=ICAL_STATUS_X-ICALPROPERTY_FIRST_ENUM; + i != ICAL_STATUS_NONE-ICALPROPERTY_FIRST_ENUM; + i++) { + if ( strcasecmp(enum_map[i].str, str) == 0) { + return (icalproperty_status)enum_map[i].prop_enum; + } + } + + return ICAL_STATUS_NONE; + +} diff --git a/libkcal/libical/src/libical/icalderivedproperty.h.in b/libkcal/libical/src/libical/icalderivedproperty.h.in new file mode 100644 index 000000000..195d27fc1 --- /dev/null +++ b/libkcal/libical/src/libical/icalderivedproperty.h.in @@ -0,0 +1,21 @@ +/* -*- Mode: C -*- + ====================================================================== + FILE: icalderivedproperties.{c,h} + CREATOR: eric 09 May 1999 + + + (C) COPYRIGHT 2000, Eric Busboom, http://www.softwarestudio.org + ======================================================================*/ + + +#ifndef ICALDERIVEDPROPERTY_H +#define ICALDERIVEDPROPERTY_H + +#include <time.h> +#include "icalparameter.h" +#include "icalderivedvalue.h" +#include "icalrecur.h" + +typedef struct icalproperty_impl icalproperty; + +<insert_code_here> diff --git a/libkcal/libical/src/libical/icalderivedvalue.c.in b/libkcal/libical/src/libical/icalderivedvalue.c.in new file mode 100644 index 000000000..e126867ae --- /dev/null +++ b/libkcal/libical/src/libical/icalderivedvalue.c.in @@ -0,0 +1,336 @@ +/* -*- Mode: C -*- */ +/*====================================================================== + FILE: icalvalue.c + CREATOR: eric 02 May 1999 + + + + (C) COPYRIGHT 2000, Eric Busboom, http://www.softwarestudio.org + + This program is free software; you can redistribute it and/or modify + it under the terms of either: + + The LGPL as published by the Free Software Foundation, version + 2.1, available at: http://www.fsf.org/copyleft/lesser.html + + Or: + + The Mozilla Public License Version 1.0. You may obtain a copy of + the License at http://www.mozilla.org/MPL/ + + The original code is icalvalue.c + + Contributions from: + Graham Davison (g.m.davison@computer.org) + + +======================================================================*/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "icalerror.h" +#include "icalmemory.h" +#include "icalparser.h" +#include "icalenums.h" + +#include "icalvalueimpl.h" + +#include <stdlib.h> /* for malloc */ +#include <stdio.h> /* for snprintf */ +#include <string.h> /* For memset, others */ +#include <stddef.h> /* For offsetof() macro */ +#include <errno.h> +#include <time.h> /* for mktime */ +#include <stdlib.h> /* for atoi and atof */ +#include <limits.h> /* for SHRT_MAX */ + +struct icalvalue_impl* icalvalue_new_impl(icalvalue_kind kind); + +/* This map associates each of the value types with its string + representation */ +struct icalvalue_kind_map { + icalvalue_kind kind; + char name[20]; +}; + +<insert_code_here> + + +int icalvalue_kind_is_valid(const icalvalue_kind kind) +{ + int i = 0; + do { + if (value_map[i].kind == kind) + return 1; + } while (value_map[i++].kind != ICAL_NO_VALUE); + + return 0; +} + +const char* icalvalue_kind_to_string(const icalvalue_kind kind) +{ + int i; + + for (i=0; value_map[i].kind != ICAL_NO_VALUE; i++) { + if (value_map[i].kind == kind) { + return value_map[i].name; + } + } + + return 0; +} + +icalvalue_kind icalvalue_string_to_kind(const char* str) +{ + int i; + + for (i=0; value_map[i].kind != ICAL_NO_VALUE; i++) { + if (strcasecmp(value_map[i].name,str) == 0) { + return value_map[i].kind; + } + } + + return value_map[i].kind; + +} + +icalvalue* icalvalue_new_x (const char* v){ + struct icalvalue_impl* impl = icalvalue_new_impl(ICAL_X_VALUE); + icalerror_check_arg_rz( (v!=0),"v"); + + icalvalue_set_x((icalvalue*)impl,v); + return (icalvalue*)impl; +} +void icalvalue_set_x(icalvalue* impl, const char* v) { + icalerror_check_arg_rv( (impl!=0),"value"); + icalerror_check_arg_rv( (v!=0),"v"); + + if(impl->x_value!=0) {free((void*)impl->x_value);} + + impl->x_value = icalmemory_strdup(v); + + if (impl->x_value == 0){ + errno = ENOMEM; + } + + } +const char* icalvalue_get_x(const icalvalue* value) { + + icalerror_check_arg( (value!=0),"value"); + icalerror_check_value_type(value, ICAL_X_VALUE); + return value->x_value; +} + +/* Recur is a special case, so it is not auto generated. */ +icalvalue* +icalvalue_new_recur (struct icalrecurrencetype v) +{ + struct icalvalue_impl* impl = icalvalue_new_impl(ICAL_RECUR_VALUE); + + icalvalue_set_recur((icalvalue*)impl,v); + + return (icalvalue*)impl; +} + +void +icalvalue_set_recur(icalvalue* impl, struct icalrecurrencetype v) +{ + icalerror_check_arg_rv( (impl!=0),"value"); + icalerror_check_value_type(value, ICAL_RECUR_VALUE); + + if (impl->data.v_recur != 0){ + free(impl->data.v_recur); + impl->data.v_recur = 0; + } + + impl->data.v_recur = malloc(sizeof(struct icalrecurrencetype)); + + if (impl->data.v_recur == 0){ + icalerror_set_errno(ICAL_NEWFAILED_ERROR); + return; + } else { + memcpy(impl->data.v_recur, &v, sizeof(struct icalrecurrencetype)); + } + +} + +struct icalrecurrencetype +icalvalue_get_recur(const icalvalue* value) +{ + icalerror_check_arg( (value!=0),"value"); + icalerror_check_value_type(value, ICAL_RECUR_VALUE); + + return *(value->data.v_recur); +} + + + + +icalvalue* +icalvalue_new_trigger (struct icaltriggertype v) +{ + struct icalvalue_impl* impl = icalvalue_new_impl(ICAL_TRIGGER_VALUE); + + icalvalue_set_trigger((icalvalue*)impl,v); + + return (icalvalue*)impl; +} + +void +icalvalue_set_trigger(icalvalue* value, struct icaltriggertype v) +{ + icalerror_check_arg_rv( (value!=0),"value"); + + if(!icaltime_is_null_time(v.time)){ + icalvalue_set_datetime(value,v.time); + value->kind = ICAL_DATETIME_VALUE; + } else { + icalvalue_set_duration(value,v.duration); + value->kind = ICAL_DURATION_VALUE; + } +} + +struct icaltriggertype +icalvalue_get_trigger(const icalvalue* impl) +{ + struct icaltriggertype tr; + + icalerror_check_arg( (impl!=0),"value"); + icalerror_check_arg( (impl!=0),"value"); + + if(impl->kind == ICAL_DATETIME_VALUE){ + tr.duration = icaldurationtype_from_int(0); + tr.time = impl->data.v_time; + } else if(impl->kind == ICAL_DURATION_VALUE){ + tr.time = icaltime_null_time(); + tr.duration = impl->data.v_duration; + } else { + tr.duration = icaldurationtype_from_int(0); + tr.time = icaltime_null_time(); + icalerror_set_errno(ICAL_BADARG_ERROR); + } + + return tr; +} + +/* DATE-TIME-PERIOD is a special case, and is not auto generated */ + +icalvalue* +icalvalue_new_datetimeperiod (struct icaldatetimeperiodtype v) +{ + struct icalvalue_impl* impl = icalvalue_new_impl(ICAL_DATETIMEPERIOD_VALUE); + + icalvalue_set_datetimeperiod(impl,v); + + return (icalvalue*)impl; +} + +void +icalvalue_set_datetimeperiod(icalvalue* impl, struct icaldatetimeperiodtype v) +{ + icalerror_check_arg_rv( (impl!=0),"value"); + + icalerror_check_value_type(value, ICAL_DATETIMEPERIOD_VALUE); + + if(!icaltime_is_null_time(v.time)){ + if(!icaltime_is_valid_time(v.time)){ + icalerror_set_errno(ICAL_BADARG_ERROR); + return; + } + impl->kind = ICAL_DATETIME_VALUE; + icalvalue_set_datetime(impl,v.time); + } else if (!icalperiodtype_is_null_period(v.period)) { + if(!icalperiodtype_is_valid_period(v.period)){ + icalerror_set_errno(ICAL_BADARG_ERROR); + return; + } + impl->kind = ICAL_PERIOD_VALUE; + icalvalue_set_period(impl,v.period); + } else { + icalerror_set_errno(ICAL_BADARG_ERROR); + } +} + +struct icaldatetimeperiodtype +icalvalue_get_datetimeperiod(const icalvalue* impl) +{ + struct icaldatetimeperiodtype dtp; + + icalerror_check_arg( (impl!=0),"value"); + icalerror_check_value_type(value, ICAL_DATETIMEPERIOD_VALUE); + + if( impl->kind == ICAL_DATETIME_VALUE || impl->kind == ICAL_DATE_VALUE ){ + dtp.period = icalperiodtype_null_period(); + dtp.time = impl->data.v_time; + } else if(impl->kind == ICAL_PERIOD_VALUE) { + dtp.period = impl->data.v_period; + dtp.time = icaltime_null_time(); + } else { + dtp.period = icalperiodtype_null_period(); + dtp.time = icaltime_null_time(); + icalerror_set_errno(ICAL_BADARG_ERROR); + } + + return dtp; +} + + + +icalvalue * +icalvalue_new_attach (icalattach *attach) +{ + struct icalvalue_impl *impl; + + icalerror_check_arg_rz ((attach != NULL), "attach"); + + impl = icalvalue_new_impl (ICAL_ATTACH_VALUE); + if (!impl) { + errno = ENOMEM; + return NULL; + } + + icalvalue_set_attach ((icalvalue *) impl, attach); + return (icalvalue *) impl; +} + +void +icalvalue_set_attach (icalvalue *value, icalattach *attach) +{ + struct icalvalue_impl *impl; + + icalerror_check_arg_rv ((value != NULL), "value"); + icalerror_check_value_type (value, ICAL_ATTACH_VALUE); + icalerror_check_arg_rv ((attach != NULL), "attach"); + + impl = (struct icalvalue_impl *) value; + + icalattach_ref (attach); + + if (impl->data.v_attach) + icalattach_unref (impl->data.v_attach); + + impl->data.v_attach = attach; +} + +icalattach * +icalvalue_get_attach (const icalvalue *value) +{ + icalerror_check_arg_rz ((value != NULL), "value"); + icalerror_check_value_type (value, ICAL_ATTACH_VALUE); + + return value->data.v_attach; +} + + + + + + + +/* The remaining interfaces are 'new', 'set' and 'get' for each of the value + types */ + + +/* Everything below this line is machine generated. Do not edit. */ diff --git a/libkcal/libical/src/libical/icalderivedvalue.h.in b/libkcal/libical/src/libical/icalderivedvalue.h.in new file mode 100644 index 000000000..92c45340b --- /dev/null +++ b/libkcal/libical/src/libical/icalderivedvalue.h.in @@ -0,0 +1,63 @@ +/* -*- Mode: C -*- */ +/*====================================================================== + FILE: icalvalue.h + CREATOR: eric 20 March 1999 + + + + + + (C) COPYRIGHT 2000, Eric Busboom, http://www.softwarestudio.org + + This program is free software; you can redistribute it and/or modify + it under the terms of either: + + The LGPL as published by the Free Software Foundation, version + 2.1, available at: http://www.fsf.org/copyleft/lesser.html + + Or: + + The Mozilla Public License Version 1.0. You may obtain a copy of + the License at http://www.mozilla.org/MPL/ + + The original code is icalvalue.h + + ======================================================================*/ + +#ifndef ICALDERIVEDVALUE_H +#define ICALDERIVEDVALUE_H + +#include "icaltypes.h" +#include "icalrecur.h" +#include "icaltime.h" +#include "icalduration.h" +#include "icalperiod.h" +#include "icalattach.h" + +typedef struct icalvalue_impl icalvalue; + + + +void icalvalue_set_x(icalvalue* value, const char* v); +icalvalue* icalvalue_new_x(const char* v); +const char* icalvalue_get_x(const icalvalue* value); + +icalvalue* icalvalue_new_recur (struct icalrecurrencetype v); +void icalvalue_set_recur(icalvalue* value, struct icalrecurrencetype v); +struct icalrecurrencetype icalvalue_get_recur(const icalvalue* value); + +icalvalue* icalvalue_new_trigger (struct icaltriggertype v); +void icalvalue_set_trigger(icalvalue* value, struct icaltriggertype v); +struct icaltriggertype icalvalue_get_trigger(const icalvalue* value); + +icalvalue* icalvalue_new_datetimeperiod (struct icaldatetimeperiodtype v); +void icalvalue_set_datetimeperiod(icalvalue* value, struct icaldatetimeperiodtype v); +struct icaldatetimeperiodtype icalvalue_get_datetimeperiod(const icalvalue* value); + +icalvalue *icalvalue_new_attach (icalattach *attach); +void icalvalue_set_attach (icalvalue *value, icalattach *attach); +icalattach *icalvalue_get_attach (const icalvalue *value); + +void icalvalue_reset_kind(icalvalue* value); + +<insert_code_here> diff --git a/libkcal/libical/src/libical/icalduration.c b/libkcal/libical/src/libical/icalduration.c new file mode 100644 index 000000000..0d9856b23 --- /dev/null +++ b/libkcal/libical/src/libical/icalduration.c @@ -0,0 +1,331 @@ +/* -*- Mode: C -*- + ====================================================================== + FILE: icaltime.c + CREATOR: eric 02 June 2000 + + + (C) COPYRIGHT 2000, Eric Busboom <eric@softwarestudio.org> + http://www.softwarestudio.org + + This program is free software; you can redistribute it and/or modify + it under the terms of either: + + The LGPL as published by the Free Software Foundation, version + 2.1, available at: http://www.fsf.org/copyleft/lesser.html + + Or: + + The Mozilla Public License Version 1.0. You may obtain a copy of + the License at http://www.mozilla.org/MPL/ + + The Original Code is eric. The Initial Developer of the Original + Code is Eric Busboom + + + ======================================================================*/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "icalduration.h" + +#include <assert.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> + +#include "icalerror.h" +#include "icalmemory.h" +#include "icalvalue.h" + + + + +/* From Seth Alves, <alves@hungry.com> */ +struct icaldurationtype icaldurationtype_from_int(int t) +{ + struct icaldurationtype dur; + int used = 0; + + dur = icaldurationtype_null_duration(); + + if(t < 0){ + dur.is_neg = 1; + t = -t; + } + + if (t % (60 * 60 * 24 * 7) == 0) { + dur.weeks = t / (60 * 60 * 24 * 7); + } else { + used += dur.weeks * (60 * 60 * 24 * 7); + dur.days = (t - used) / (60 * 60 * 24); + used += dur.days * (60 * 60 * 24); + dur.hours = (t - used) / (60 * 60); + used += dur.hours * (60 * 60); + dur.minutes = (t - used) / (60); + used += dur.minutes * (60); + dur.seconds = (t - used); + } + + return dur; +} + +struct icaldurationtype icaldurationtype_from_string(const char* str) +{ + + int i; + int begin_flag = 0; + int time_flag = 0; + int date_flag = 0; + int week_flag = 0; + int digits=-1; + int scan_size = -1; + int size = strlen(str); + char p; + struct icaldurationtype d; + + memset(&d, 0, sizeof(struct icaldurationtype)); + + for(i=0;i != size;i++){ + p = str[i]; + + switch(p) + { + case '-': { + if(i != 0 || begin_flag == 1) goto error; + + d.is_neg = 1; + break; + } + + case 'P': { + if (i != 0 && i !=1 ) goto error; + begin_flag = 1; + break; + } + + case 'T': { + time_flag = 1; + break; + } + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + + /* HACK. Skip any more digits if the l;ast one + read has not been assigned */ + if(digits != -1){ + break; + } + + if (begin_flag == 0) goto error; + /* Get all of the digits, not one at a time */ + scan_size = sscanf(&str[i],"%d",&digits); + if(scan_size == 0) goto error; + break; + } + + case 'H': { + if (time_flag == 0||week_flag == 1||d.hours !=0||digits ==-1) + goto error; + d.hours = digits; digits = -1; + break; + } + case 'M': { + if (time_flag == 0||week_flag==1||d.minutes != 0||digits ==-1) + goto error; + d.minutes = digits; digits = -1; + break; + } + case 'S': { + if (time_flag == 0||week_flag==1||d.seconds!=0||digits ==-1) + goto error; + d.seconds = digits; digits = -1; + break; + } + case 'W': { + if (time_flag==1||date_flag==1||d.weeks!=0||digits ==-1) + goto error; + week_flag = 1; + d.weeks = digits; digits = -1; + break; + } + case 'D': { + if (time_flag==1||week_flag==1||d.days!=0||digits ==-1) + goto error; + date_flag = 1; + d.days = digits; digits = -1; + break; + } + default: { + goto error; + } + + } + } + + return d; + + + error: + icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR); + return icaldurationtype_bad_duration(); +} + +#define TMP_BUF_SIZE 1024 +static +void append_duration_segment(char** buf, char** buf_ptr, size_t* buf_size, + const char* sep, unsigned int value) { + + char temp[TMP_BUF_SIZE]; + + snprintf(temp,sizeof(temp),"%d",value); + + icalmemory_append_string(buf, buf_ptr, buf_size, temp); + icalmemory_append_string(buf, buf_ptr, buf_size, sep); + +} + +char* icaldurationtype_as_ical_string(struct icaldurationtype d) +{ + + char *buf, *output_line; + size_t buf_size = 256; + char* buf_ptr = 0; + int seconds; + + buf = (char*)icalmemory_new_buffer(buf_size); + buf_ptr = buf; + + + seconds = icaldurationtype_as_int(d); + + if(seconds !=0){ + + if(d.is_neg == 1){ + icalmemory_append_char(&buf, &buf_ptr, &buf_size, '-'); + } + + icalmemory_append_char(&buf, &buf_ptr, &buf_size, 'P'); + + if (d.weeks != 0 ) { + append_duration_segment(&buf, &buf_ptr, &buf_size, "W", d.weeks); + } + + if (d.days != 0 ) { + append_duration_segment(&buf, &buf_ptr, &buf_size, "D", d.days); + } + + if (d.hours != 0 || d.minutes != 0 || d.seconds != 0) { + + icalmemory_append_string(&buf, &buf_ptr, &buf_size, "T"); + + if (d.hours != 0 ) { + append_duration_segment(&buf, &buf_ptr, &buf_size, "H", d.hours); + } + if (d.minutes != 0 ) { + append_duration_segment(&buf, &buf_ptr, &buf_size, "M", + d.minutes); + } + if (d.seconds != 0 ) { + append_duration_segment(&buf, &buf_ptr, &buf_size, "S", + d.seconds); + } + + } + } else { + icalmemory_append_string(&buf, &buf_ptr, &buf_size, "PT0S"); + } + + output_line = icalmemory_tmp_copy(buf); + icalmemory_free_buffer(buf); + + return output_line; + +} + + +/* From Russel Steinthal */ +int icaldurationtype_as_int(struct icaldurationtype dur) +{ + return (int)( (dur.seconds + + (60 * dur.minutes) + + (60 * 60 * dur.hours) + + (60 * 60 * 24 * dur.days) + + (60 * 60 * 24 * 7 * dur.weeks)) + * (dur.is_neg==1? -1 : 1) ) ; +} + +struct icaldurationtype icaldurationtype_null_duration(void) +{ + struct icaldurationtype d; + + memset(&d,0,sizeof(struct icaldurationtype)); + + return d; +} + +int icaldurationtype_is_null_duration(struct icaldurationtype d) +{ + if(icaldurationtype_as_int(d) == 0){ + return 1; + } else { + return 0; + } +} + +/* in icalvalue_new_from_string_with_error, we should not call + icaldurationtype_is_null_duration() to see if there is an error + condition. Null duration is perfectly valid for an alarm. + We cannot depend on the caller to check icalerrno either, + following the philosophy of unix errno. we set the is_neg + to -1 to indicate that this is a bad duration. +*/ +struct icaldurationtype icaldurationtype_bad_duration() +{ + struct icaldurationtype d; + memset(&d,0,sizeof(struct icaldurationtype)); + d.is_neg = -1; + return d; +} + +int icaldurationtype_is_bad_duration(struct icaldurationtype d) +{ + return (d.is_neg == -1); +} + + +struct icaltimetype icaltime_add(struct icaltimetype t, + struct icaldurationtype d) +{ + int dt = icaldurationtype_as_int(d); + + t.second += dt; + + t = icaltime_normalize(t); + + return t; +} + +struct icaldurationtype icaltime_subtract(struct icaltimetype t1, + struct icaltimetype t2) +{ + + time_t t1t = icaltime_as_timet(t1); + time_t t2t = icaltime_as_timet(t2); + + return icaldurationtype_from_int((int)(t1t-t2t)); + + +} + diff --git a/libkcal/libical/src/libical/icalduration.h b/libkcal/libical/src/libical/icalduration.h new file mode 100644 index 000000000..fb79dbc38 --- /dev/null +++ b/libkcal/libical/src/libical/icalduration.h @@ -0,0 +1,61 @@ +/* -*- Mode: C -*- */ +/*====================================================================== + FILE: icalduration.h + CREATOR: eric 26 Jan 2001 + + + + (C) COPYRIGHT 2000, Eric Busboom <eric@softwarestudio.org> + http://www.softwarestudio.org + + This program is free software; you can redistribute it and/or modify + it under the terms of either: + + The LGPL as published by the Free Software Foundation, version + 2.1, available at: http://www.fsf.org/copyleft/lesser.html + + Or: + + The Mozilla Public License Version 1.0. You may obtain a copy of + the License at http://www.mozilla.org/MPL/ + + The Original Code is eric. The Initial Developer of the Original + Code is Eric Busboom + + +======================================================================*/ + +#ifndef ICALDURATION_H +#define ICALDURATION_H + +#include "icaltime.h" + +struct icaldurationtype +{ + int is_neg; + unsigned int days; + unsigned int weeks; + unsigned int hours; + unsigned int minutes; + unsigned int seconds; +}; + +struct icaldurationtype icaldurationtype_from_int(int t); +struct icaldurationtype icaldurationtype_from_string(const char*); +int icaldurationtype_as_int(struct icaldurationtype duration); +char* icaldurationtype_as_ical_string(struct icaldurationtype d); +struct icaldurationtype icaldurationtype_null_duration(void); +struct icaldurationtype icaldurationtype_bad_duration(void); +int icaldurationtype_is_null_duration(struct icaldurationtype d); +int icaldurationtype_is_bad_duration(struct icaldurationtype d); + +struct icaltimetype icaltime_add(struct icaltimetype t, + struct icaldurationtype d); + +struct icaldurationtype icaltime_subtract(struct icaltimetype t1, + struct icaltimetype t2); + +#endif /* !ICALDURATION_H */ + + + diff --git a/libkcal/libical/src/libical/icalenums.c b/libkcal/libical/src/libical/icalenums.c new file mode 100644 index 000000000..70d97fafe --- /dev/null +++ b/libkcal/libical/src/libical/icalenums.c @@ -0,0 +1,166 @@ +/* -*- Mode: C -*- */ +/*====================================================================== + FILE: icalenum.c + CREATOR: eric 29 April 1999 + + + + (C) COPYRIGHT 2000, Eric Busboom <eric@softwarestudio.org> + http://www.softwarestudio.org + + This program is free software; you can redistribute it and/or modify + it under the terms of either: + + The LGPL as published by the Free Software Foundation, version + 2.1, available at: http://www.fsf.org/copyleft/lesser.html + + Or: + + The Mozilla Public License Version 1.0. You may obtain a copy of + the License at http://www.mozilla.org/MPL/ + + The original code is icalenum.c + + ======================================================================*/ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "icalenums.h" + +#include <stdio.h> /* For fprintf */ +#include <stdio.h> /* For stderr */ +#include <string.h> /* For strncmp */ +#include <assert.h> +#include "icalmemory.h" + +/*** @brief Allowed request status values + */ +static struct { + enum icalrequeststatus kind; + int major; + int minor; + const char* str; +} request_status_map[] = { + {ICAL_2_0_SUCCESS_STATUS, 2,0,"Success."}, + {ICAL_2_1_FALLBACK_STATUS, 2,1,"Success but fallback taken on one or more property values."}, + {ICAL_2_2_IGPROP_STATUS, 2,2,"Success, invalid property ignored."}, + {ICAL_2_3_IGPARAM_STATUS, 2,3,"Success, invalid property parameter ignored."}, + {ICAL_2_4_IGXPROP_STATUS, 2,4,"Success, unknown non-standard property ignored."}, + {ICAL_2_5_IGXPARAM_STATUS, 2,5,"Success, unknown non standard property value ignored."}, + {ICAL_2_6_IGCOMP_STATUS, 2,6,"Success, invalid calendar component ignored."}, + {ICAL_2_7_FORWARD_STATUS, 2,7,"Success, request forwarded to Calendar User."}, + {ICAL_2_8_ONEEVENT_STATUS, 2,8,"Success, repeating event ignored. Scheduled as a single component."}, + {ICAL_2_9_TRUNC_STATUS, 2,9,"Success, truncated end date time to date boundary."}, + {ICAL_2_10_ONETODO_STATUS, 2,10,"Success, repeating VTODO ignored. Scheduled as a single VTODO."}, + {ICAL_2_11_TRUNCRRULE_STATUS, 2,11,"Success, unbounded RRULE clipped at some finite number of instances "}, + {ICAL_3_0_INVPROPNAME_STATUS, 3,0,"Invalid property name."}, + {ICAL_3_1_INVPROPVAL_STATUS, 3,1,"Invalid property value."}, + {ICAL_3_2_INVPARAM_STATUS, 3,2,"Invalid property parameter."}, + {ICAL_3_3_INVPARAMVAL_STATUS, 3,3,"Invalid property parameter value."}, + {ICAL_3_4_INVCOMP_STATUS, 3,4,"Invalid calendar component."}, + {ICAL_3_5_INVTIME_STATUS, 3,5,"Invalid date or time."}, + {ICAL_3_6_INVRULE_STATUS, 3,6,"Invalid rule."}, + {ICAL_3_7_INVCU_STATUS, 3,7,"Invalid Calendar User."}, + {ICAL_3_8_NOAUTH_STATUS, 3,8,"No authority."}, + {ICAL_3_9_BADVERSION_STATUS, 3,9,"Unsupported version."}, + {ICAL_3_10_TOOBIG_STATUS, 3,10,"Request entity too large."}, + {ICAL_3_11_MISSREQCOMP_STATUS, 3,11,"Required component or property missing."}, + {ICAL_3_12_UNKCOMP_STATUS, 3,12,"Unknown component or property found."}, + {ICAL_3_13_BADCOMP_STATUS, 3,13,"Unsupported component or property found"}, + {ICAL_3_14_NOCAP_STATUS, 3,14,"Unsupported capability."}, + {ICAL_3_15_INVCOMMAND, 3,15,"Invalid command."}, + {ICAL_4_0_BUSY_STATUS, 4,0,"Event conflict. Date/time is busy."}, + {ICAL_4_1_STORE_ACCESS_DENIED, 4,1,"Store Access Denied."}, + {ICAL_4_2_STORE_FAILED, 4,2,"Store Failed."}, + {ICAL_4_3_STORE_NOT_FOUND, 4,3,"Store not found."}, + {ICAL_5_0_MAYBE_STATUS, 5,0,"Request MAY supported."}, + {ICAL_5_1_UNAVAIL_STATUS, 5,1,"Service unavailable."}, + {ICAL_5_2_NOSERVICE_STATUS, 5,2,"Invalid calendar service."}, + {ICAL_5_3_NOSCHED_STATUS, 5,3,"No scheduling support for user."}, + {ICAL_6_1_CONTAINER_NOT_FOUND, 6,1,"Container not found."}, + {ICAL_9_0_UNRECOGNIZED_COMMAND, 9,0,"An unrecognized command was received."}, + {ICAL_UNKNOWN_STATUS, 0,0,"Error: Unknown request status"} +}; + + +/*** @brief Return the descriptive text for a request status + */ +const char* icalenum_reqstat_desc(icalrequeststatus stat) +{ + int i; + + for (i=0; request_status_map[i].kind != ICAL_UNKNOWN_STATUS; i++) { + if ( request_status_map[i].kind == stat) { + return request_status_map[i].str; + } + } + + return 0; +} + +/*** @brief Return the code for a request status + */ +char* icalenum_reqstat_code(icalrequeststatus stat) +{ + int i, major, minor; + char tmpbuf[36]; + + for (i=0; request_status_map[i].kind != ICAL_UNKNOWN_STATUS; i++) { + if ( request_status_map[i].kind == stat) { + major = request_status_map[i].major; + minor = request_status_map[i].minor; + snprintf(tmpbuf, sizeof(tmpbuf), "%i.%i", major, minor); + return icalmemory_tmp_copy(tmpbuf); + } + } + return NULL; +} + +/*** @brief Return the major number for a request status + */ +short icalenum_reqstat_major(icalrequeststatus stat) +{ + int i; + + for (i=0; request_status_map[i].kind != ICAL_UNKNOWN_STATUS; i++) { + if ( request_status_map[i].kind == stat) { + return request_status_map[i].major; + } + } + return -1; +} + +/*** @brief Return the minor number for a request status + */ +short icalenum_reqstat_minor(icalrequeststatus stat) +{ + int i; + + for (i=0; request_status_map[i].kind != ICAL_UNKNOWN_STATUS; i++) { + if ( request_status_map[i].kind == stat) { + return request_status_map[i].minor; + } + } + return -1; +} + + +/*** @brief Return a request status for major/minor status numbers + */ +icalrequeststatus icalenum_num_to_reqstat(short major, short minor) +{ + int i; + + for (i=0; request_status_map[i].kind != ICAL_UNKNOWN_STATUS; i++) { + if ( request_status_map[i].major == major && request_status_map[i].minor == minor) { + return request_status_map[i].kind; + } + } + return 0; +} + + + diff --git a/libkcal/libical/src/libical/icalenums.h b/libkcal/libical/src/libical/icalenums.h new file mode 100644 index 000000000..971655a3d --- /dev/null +++ b/libkcal/libical/src/libical/icalenums.h @@ -0,0 +1,166 @@ + +/* -*- Mode: C -*-*/ +/*====================================================================== + FILE: icalenums.h + + + + (C) COPYRIGHT 2000, Eric Busboom <eric@softwarestudio.org> + http://www.softwarestudio.org + + This program is free software; you can redistribute it and/or modify + it under the terms of either: + + The LGPL as published by the Free Software Foundation, version + 2.1, available at: http://www.fsf.org/copyleft/lesser.html + + Or: + + The Mozilla Public License Version 1.0. You may obtain a copy of + the License at http://www.mozilla.org/MPL/ + + The original code is icalenums.h + + Contributions from: + Graham Davison <g.m.davison@computer.org> + +======================================================================*/ + +#ifndef ICALENUMS_H +#define ICALENUMS_H + + + +/*********************************************************************** + * Component enumerations +**********************************************************************/ + +typedef enum icalcomponent_kind { + ICAL_NO_COMPONENT, + ICAL_ANY_COMPONENT, /* Used to select all components*/ + ICAL_XROOT_COMPONENT, + ICAL_XATTACH_COMPONENT, /* MIME attached data, returned by parser. */ + ICAL_VEVENT_COMPONENT, + ICAL_VTODO_COMPONENT, + ICAL_VJOURNAL_COMPONENT, + ICAL_VCALENDAR_COMPONENT, + ICAL_VAGENDA_COMPONENT, + ICAL_VFREEBUSY_COMPONENT, + ICAL_VALARM_COMPONENT, + ICAL_XAUDIOALARM_COMPONENT, + ICAL_XDISPLAYALARM_COMPONENT, + ICAL_XEMAILALARM_COMPONENT, + ICAL_XPROCEDUREALARM_COMPONENT, + ICAL_VTIMEZONE_COMPONENT, + ICAL_XSTANDARD_COMPONENT, + ICAL_XDAYLIGHT_COMPONENT, + ICAL_X_COMPONENT, + ICAL_VSCHEDULE_COMPONENT, + ICAL_VQUERY_COMPONENT, + ICAL_VCAR_COMPONENT, + ICAL_VCOMMAND_COMPONENT, + ICAL_XLICINVALID_COMPONENT, + ICAL_XLICMIMEPART_COMPONENT /* a non-stardard component that mirrors + structure of MIME data */ + +} icalcomponent_kind; + + + +/*********************************************************************** + * Request Status codes + **********************************************************************/ + +typedef enum icalrequeststatus { + ICAL_UNKNOWN_STATUS, + ICAL_2_0_SUCCESS_STATUS, + ICAL_2_1_FALLBACK_STATUS, + ICAL_2_2_IGPROP_STATUS, + ICAL_2_3_IGPARAM_STATUS, + ICAL_2_4_IGXPROP_STATUS, + ICAL_2_5_IGXPARAM_STATUS, + ICAL_2_6_IGCOMP_STATUS, + ICAL_2_7_FORWARD_STATUS, + ICAL_2_8_ONEEVENT_STATUS, + ICAL_2_9_TRUNC_STATUS, + ICAL_2_10_ONETODO_STATUS, + ICAL_2_11_TRUNCRRULE_STATUS, + ICAL_3_0_INVPROPNAME_STATUS, + ICAL_3_1_INVPROPVAL_STATUS, + ICAL_3_2_INVPARAM_STATUS, + ICAL_3_3_INVPARAMVAL_STATUS, + ICAL_3_4_INVCOMP_STATUS, + ICAL_3_5_INVTIME_STATUS, + ICAL_3_6_INVRULE_STATUS, + ICAL_3_7_INVCU_STATUS, + ICAL_3_8_NOAUTH_STATUS, + ICAL_3_9_BADVERSION_STATUS, + ICAL_3_10_TOOBIG_STATUS, + ICAL_3_11_MISSREQCOMP_STATUS, + ICAL_3_12_UNKCOMP_STATUS, + ICAL_3_13_BADCOMP_STATUS, + ICAL_3_14_NOCAP_STATUS, + ICAL_3_15_INVCOMMAND, + ICAL_4_0_BUSY_STATUS, + ICAL_4_1_STORE_ACCESS_DENIED, + ICAL_4_2_STORE_FAILED, + ICAL_4_3_STORE_NOT_FOUND, + ICAL_5_0_MAYBE_STATUS, + ICAL_5_1_UNAVAIL_STATUS, + ICAL_5_2_NOSERVICE_STATUS, + ICAL_5_3_NOSCHED_STATUS, + ICAL_6_1_CONTAINER_NOT_FOUND, + ICAL_9_0_UNRECOGNIZED_COMMAND +} icalrequeststatus; + + +const char* icalenum_reqstat_desc(icalrequeststatus stat); +short icalenum_reqstat_major(icalrequeststatus stat); +short icalenum_reqstat_minor(icalrequeststatus stat); +icalrequeststatus icalenum_num_to_reqstat(short major, short minor); +char* icalenum_reqstat_code(icalrequeststatus stat); + +/*********************************************************************** + * Conversion functions +**********************************************************************/ + + +/* Thse routines used to be in icalenums.c, but were moved into the + icalproperty, icalparameter, icalvalue, or icalcomponent modules. */ + +/* const char* icalproperty_kind_to_string(icalproperty_kind kind);*/ +#define icalenum_property_kind_to_string(x) icalproperty_kind_to_string(x) + +/*icalproperty_kind icalproperty_string_to_kind(const char* string)*/ +#define icalenum_string_to_property_kind(x) icalproperty_string_to_kind(x) + +/*icalvalue_kind icalproperty_kind_to_value_kind(icalproperty_kind kind);*/ +#define icalenum_property_kind_to_value_kind(x) icalproperty_kind_to_value_kind(x) + +/*const char* icalenum_method_to_string(icalproperty_method);*/ +#define icalenum_method_to_string(x) icalproperty_method_to_string(x) + +/*icalproperty_method icalenum_string_to_method(const char* string);*/ +#define icalenum_string_to_method(x) icalproperty_string_to_method(x) + +/*const char* icalenum_status_to_string(icalproperty_status);*/ +#define icalenum_status_to_string(x) icalproperty_status_to_string(x) + +/*icalproperty_status icalenum_string_to_status(const char* string);*/ +#define icalenum_string_to_status(x) icalproperty_string_to_status(x) + +/*icalvalue_kind icalenum_string_to_value_kind(const char* str);*/ +#define icalenum_string_to_value_kind(x) icalvalue_string_to_kind(x) + +/*const char* icalenum_value_kind_to_string(icalvalue_kind kind);*/ +#define icalenum_value_kind_to_string(x) icalvalue_kind_to_string(x) + +/*const char* icalenum_component_kind_to_string(icalcomponent_kind kind);*/ +#define icalenum_component_kind_to_string(x) icalcomponent_kind_to_string(x) + +/*icalcomponent_kind icalenum_string_to_component_kind(const char* string);*/ +#define icalenum_string_to_component_kind(x) icalcomponent_string_to_kind(x) + + +#endif /* !ICALENUMS_H */ + diff --git a/libkcal/libical/src/libical/icalerror.c b/libkcal/libical/src/libical/icalerror.c new file mode 100644 index 000000000..7cd44deaf --- /dev/null +++ b/libkcal/libical/src/libical/icalerror.c @@ -0,0 +1,250 @@ +/* -*- Mode: C -*- + ====================================================================== + FILE: icalerror.c + CREATOR: eric 16 May 1999 + + + + (C) COPYRIGHT 2000, Eric Busboom <eric@softwarestudio.org> + http://www.softwarestudio.org + + This program is free software; you can redistribute it and/or modify + it under the terms of either: + + The LGPL as published by the Free Software Foundation, version + 2.1, available at: http://www.fsf.org/copyleft/lesser.html + + Or: + + The Mozilla Public License Version 1.0. You may obtain a copy of + the License at http://www.mozilla.org/MPL/ + + The original code is icalerror.c + + ======================================================================*/ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdlib.h> /* for malloc() */ +#include <string.h> /* for strcmp */ +#include "icalerror.h" + +#ifdef HAVE_PTHREAD +#include <pthread.h> + +static pthread_key_t icalerrno_key; +static pthread_once_t icalerrno_key_once = PTHREAD_ONCE_INIT; + +static void icalerrno_destroy(void* buf) { + free(buf); + pthread_setspecific(icalerrno_key, NULL); +} + +static void icalerrno_key_alloc(void) { + pthread_key_create(&icalerrno_key, icalerrno_destroy); +} + +icalerrorenum *icalerrno_return(void) { + icalerrorenum *_errno; + + pthread_once(&icalerrno_key_once, icalerrno_key_alloc); + + _errno = (icalerrorenum*) pthread_getspecific(icalerrno_key); + + if (!_errno) { + _errno = malloc(sizeof(icalerrorenum)); + *_errno = ICAL_NO_ERROR; + pthread_setspecific(icalerrno_key, _errno); + } + return _errno; +} + +#else + +static icalerrorenum icalerrno_storage = ICAL_NO_ERROR; + +icalerrorenum *icalerrno_return(void) { + return &icalerrno_storage; +} + +#endif + + +static int foo; + +void icalerror_stop_here(void) +{ + foo++; /* Keep optimizers from removing routine */ +} + +void icalerror_crash_here(void) +{ + int *p=0; + *p = 1; + + assert( *p); +} + +#ifdef ICAL_SETERROR_ISFUNC +void icalerror_set_errno(icalerrorenum x) +{ + icalerrno = x; + if(icalerror_get_error_state(x)==ICAL_ERROR_FATAL || + (icalerror_get_error_state(x)==ICAL_ERROR_DEFAULT && + icalerror_errors_are_fatal == 1 )){ + icalerror_warn(icalerror_strerror(x)); + assert(0); + } + +} +#endif + +void icalerror_clear_errno() { + + icalerrno = ICAL_NO_ERROR; +} + +#ifdef ICAL_ERRORS_ARE_FATAL +int icalerror_errors_are_fatal = 1; +#else +int icalerror_errors_are_fatal = 0; +#endif + +struct icalerror_state { + icalerrorenum error; + icalerrorstate state; +}; + +static struct icalerror_state error_state_map[] = +{ + { ICAL_BADARG_ERROR,ICAL_ERROR_DEFAULT}, + { ICAL_NEWFAILED_ERROR,ICAL_ERROR_DEFAULT}, + { ICAL_ALLOCATION_ERROR,ICAL_ERROR_DEFAULT}, + { ICAL_MALFORMEDDATA_ERROR,ICAL_ERROR_DEFAULT}, + { ICAL_PARSE_ERROR,ICAL_ERROR_DEFAULT}, + { ICAL_INTERNAL_ERROR,ICAL_ERROR_DEFAULT}, + { ICAL_FILE_ERROR,ICAL_ERROR_DEFAULT}, + { ICAL_USAGE_ERROR,ICAL_ERROR_DEFAULT}, + { ICAL_UNIMPLEMENTED_ERROR,ICAL_ERROR_DEFAULT}, + { ICAL_UNKNOWN_ERROR,ICAL_ERROR_DEFAULT}, + { ICAL_NO_ERROR,ICAL_ERROR_DEFAULT} + +}; + +struct icalerror_string_map { + const char* str; + icalerrorenum error; + char name[160]; +}; + +static struct icalerror_string_map string_map[] = +{ + {"BADARG",ICAL_BADARG_ERROR,"BADARG: Bad argument to function"}, + { "NEWFAILED",ICAL_NEWFAILED_ERROR,"NEWFAILED: Failed to create a new object via a *_new() routine"}, + { "ALLOCATION",ICAL_ALLOCATION_ERROR,"ALLOCATION: Failed to allocate new memory"}, + {"MALFORMEDDATA",ICAL_MALFORMEDDATA_ERROR,"MALFORMEDDATA: An input string was not correctly formed or a component has missing or extra properties"}, + { "PARSE",ICAL_PARSE_ERROR,"PARSE: Failed to parse a part of an iCal component"}, + {"INTERNAL",ICAL_INTERNAL_ERROR,"INTERNAL: Random internal error. This indicates an error in the library code, not an error in use"}, + { "FILE",ICAL_FILE_ERROR,"FILE: An operation on a file failed. Check errno for more detail."}, + { "USAGE",ICAL_USAGE_ERROR,"USAGE: Failed to propertyl sequence calls to a set of interfaces"}, + { "UNIMPLEMENTED",ICAL_UNIMPLEMENTED_ERROR,"UNIMPLEMENTED: This feature has not been implemented"}, + { "NO",ICAL_NO_ERROR,"NO: No error"}, + {"UNKNOWN",ICAL_UNKNOWN_ERROR,"UNKNOWN: Unknown error type -- icalerror_strerror() was probably given bad input"} +}; + + +icalerrorenum icalerror_error_from_string(const char* str){ + + icalerrorenum e = ICAL_NO_ERROR; + int i = 0; + + for( i = 0; string_map[i].error != ICAL_NO_ERROR; i++){ + if (strcmp(string_map[i].str,str) == 0){ + e = string_map[i].error; + } + } + + return e; +} + +icalerrorstate icalerror_supress(const char* error){ + + icalerrorenum e = icalerror_error_from_string(error); + icalerrorstate es; + + if (e == ICAL_NO_ERROR){ + return ICAL_ERROR_UNKNOWN; + } + + + es = icalerror_get_error_state(e); + icalerror_set_error_state(e,ICAL_ERROR_NONFATAL); + + return es; +} + +char* icalerror_perror() +{ + return icalerror_strerror(icalerrno); +} + +void icalerror_restore(const char* error, icalerrorstate es){ + + + icalerrorenum e = icalerror_error_from_string(error); + + if (e != ICAL_NO_ERROR){ + icalerror_set_error_state(e,es); + } + +} + + + +void icalerror_set_error_state( icalerrorenum error, + icalerrorstate state) +{ + int i; + + for(i = 0; error_state_map[i].error!= ICAL_NO_ERROR;i++){ + if(error_state_map[i].error == error){ + error_state_map[i].state = state; + } + } +} + +icalerrorstate icalerror_get_error_state( icalerrorenum error) +{ + int i; + + for(i = 0; error_state_map[i].error!= ICAL_NO_ERROR;i++){ + if(error_state_map[i].error == error){ + return error_state_map[i].state; + } + } + + return ICAL_ERROR_UNKNOWN; +} + + + + +char* icalerror_strerror(icalerrorenum e) { + + int i; + + for (i=0; string_map[i].error != ICAL_UNKNOWN_ERROR; i++) { + if (string_map[i].error == e) { + return string_map[i].name; + } + } + + return string_map[i].name; /* Return string for ICAL_UNKNOWN_ERROR*/ + +} + + + diff --git a/libkcal/libical/src/libical/icalerror.h b/libkcal/libical/src/libical/icalerror.h new file mode 100644 index 000000000..0003b2f9e --- /dev/null +++ b/libkcal/libical/src/libical/icalerror.h @@ -0,0 +1,160 @@ +/* -*- Mode: C -*- */ +/*====================================================================== + FILE: icalerror.h + CREATOR: eric 09 May 1999 + + + + (C) COPYRIGHT 2000, Eric Busboom <eric@softwarestudio.org> + http://www.softwarestudio.org + + This program is free software; you can redistribute it and/or modify + it under the terms of either: + + The LGPL as published by the Free Software Foundation, version + 2.1, available at: http://www.fsf.org/copyleft/lesser.html + + Or: + + The Mozilla Public License Version 1.0. You may obtain a copy of + the License at http://www.mozilla.org/MPL/ + + The original code is icalerror.h + +======================================================================*/ + + +#ifndef ICALERROR_H +#define ICALERROR_H + +#include <assert.h> +#include <stdio.h> /* For icalerror_warn() */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#define ICAL_SETERROR_ISFUNC + + +/** This routine is called before any error is triggered. It is called + by icalerror_set_errno, so it does not appear in all of the macros + below */ +void icalerror_stop_here(void); + +void icalerror_crash_here(void); + +typedef enum icalerrorenum { + ICAL_NO_ERROR, /* icalerrno may not be initialized - put it first so and pray that the compiler initialize things to zero */ + ICAL_BADARG_ERROR, + ICAL_NEWFAILED_ERROR, + ICAL_ALLOCATION_ERROR, + ICAL_MALFORMEDDATA_ERROR, + ICAL_PARSE_ERROR, + ICAL_INTERNAL_ERROR, /* Like assert --internal consist. prob */ + ICAL_FILE_ERROR, + ICAL_USAGE_ERROR, + ICAL_UNIMPLEMENTED_ERROR, + ICAL_UNKNOWN_ERROR /* Used for problems in input to icalerror_strerror()*/ + +} icalerrorenum; + +icalerrorenum * icalerrno_return(void); +#define icalerrno (*(icalerrno_return())) + +/** If true, libicl aborts after a call to icalerror_set_error + * + * @warning NOT THREAD SAFE -- recommended that you do not change + * this in a multithreaded program. + */ +extern int icalerror_errors_are_fatal; + +/* Warning messages */ + +#ifdef __GNUC__ca +#define icalerror_warn(message) {fprintf(stderr,"%s(), %s:%d: %s\n",__FUNCTION__,__FILE__,__LINE__,message);} +#else /* __GNU_C__ */ +#define icalerror_warn(message) {fprintf(stderr,"%s:%d: %s\n",__FILE__,__LINE__,message);} +#endif /* __GNU_C__ */ + + +void icalerror_clear_errno(void); +void _icalerror_set_errno(icalerrorenum); + +/* Make an individual error fatal or non-fatal. */ +typedef enum icalerrorstate { + ICAL_ERROR_FATAL, /* Not fata */ + ICAL_ERROR_NONFATAL, /* Fatal */ + ICAL_ERROR_DEFAULT, /* Use the value of icalerror_errors_are_fatal*/ + ICAL_ERROR_UNKNOWN /* Asked state for an unknown error type */ +} icalerrorstate ; + +char* icalerror_strerror(icalerrorenum e); +char* icalerror_perror(); +void icalerror_set_error_state( icalerrorenum error, icalerrorstate); +icalerrorstate icalerror_get_error_state( icalerrorenum error); + +#ifndef ICAL_SETERROR_ISFUNC +#define icalerror_set_errno(x) \ +icalerrno = x; \ +if(icalerror_get_error_state(x)==ICAL_ERROR_FATAL || \ + (icalerror_get_error_state(x)==ICAL_ERROR_DEFAULT && \ + icalerror_errors_are_fatal == 1 )){ \ + icalerror_warn(icalerror_strerror(x)); \ + assert(0); \ +} +#else +void icalerror_set_errno(icalerrorenum x); +#endif + +#ifdef ICAL_ERRORS_ARE_FATAL +#undef NDEBUG +#endif + +#define icalerror_check_value_type(value,type); +#define icalerror_check_property_type(value,type); +#define icalerror_check_parameter_type(value,type); +#define icalerror_check_component_type(value,type); + +/* Assert with a message */ +#ifdef ICAL_ERRORS_ARE_FATAL + +#ifdef __GNUC__ +#define icalerror_assert(test,message) if(!(test)){fprintf(stderr,"%s(), %s:%d: %s\n",__FUNCTION__,__FILE__,__LINE__,message);icalerror_stop_here(); abort();} +#else /*__GNUC__*/ +#define icalerror_assert(test,message) if(!(test)){fprintf(stderr,"%s:%d: %s\n",__FILE__,__LINE__,message);icalerror_stop_here(); abort();} +#endif /*__GNUC__*/ + +#else /* ICAL_ERRORS_ARE_FATAL */ +#define icalerror_assert(test,message) +#endif /* ICAL_ERRORS_ARE_FATAL */ + +/* Check & abort if check fails */ +#define icalerror_check_arg(test,arg) if(!(test)) { icalerror_set_errno(ICAL_BADARG_ERROR); } + +/* Check & return void if check fails*/ +#define icalerror_check_arg_rv(test,arg) if(!(test)) {icalerror_set_errno(ICAL_BADARG_ERROR); return; } + +/* Check & return 0 if check fails*/ +#define icalerror_check_arg_rz(test,arg) if(!(test)) { icalerror_set_errno(ICAL_BADARG_ERROR); return 0;} + +/* Check & return an error if check fails*/ +#define icalerror_check_arg_re(test,arg,error) if(!(test)) { icalerror_stop_here(); assert(0); return error;} + +/* Check & return something*/ +#define icalerror_check_arg_rx(test,arg,x) if(!(test)) { icalerror_set_errno(ICAL_BADARG_ERROR); return x;} + + + +/* String interfaces to set an error to NONFATAL and restore it to its + original value */ + +icalerrorstate icalerror_supress(const char* error); +void icalerror_restore(const char* error, icalerrorstate es); + + +#endif /* !ICALERROR_H */ + + + diff --git a/libkcal/libical/src/libical/icalmemory.c b/libkcal/libical/src/libical/icalmemory.c new file mode 100644 index 000000000..32ab69c24 --- /dev/null +++ b/libkcal/libical/src/libical/icalmemory.c @@ -0,0 +1,373 @@ +/* -*- Mode: C -*- + ====================================================================== + FILE: icalmemory.c + CREATOR: eric 30 June 1999 + + (C) COPYRIGHT 1999, Eric Busboom <eric@softwarestudio.org> + http://www.softwarestudio.org + + The contents of this file are subject to the Mozilla Public License + Version 1.0 (the "License"); you may not use this file except in + compliance with the License. You may obtain a copy of the License at + http://www.mozilla.org/MPL/ + + Software distributed under the License is distributed on an "AS IS" + basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + the License for the specific language governing rights and + limitations under the License. + + + This program is free software; you can redistribute it and/or modify + it under the terms of either: + + The LGPL as published by the Free Software Foundation, version + 2.1, available at: http://www.fsf.org/copyleft/lesser.html + + Or: + + The Mozilla Public License Version 1.0. You may obtain a copy of + the License at http://www.mozilla.org/MPL/ + + The Original Code is icalmemory.h + + ======================================================================*/ + +/** + * @file icalmemory.c + * @brief Common memory management routines. + * + * libical often passes strings back to the caller. To make these + * interfaces simple, I did not want the caller to have to pass in a + * memory buffer, but having libical pass out newly allocated memory + * makes it difficult to de-allocate the memory. + * + * The ring buffer in this scheme makes it possible for libical to pass + * out references to memory which the caller does not own, and be able + * to de-allocate the memory later. The ring allows libical to have + * several buffers active simultaneously, which is handy when creating + * string representations of components. + */ + +#define ICALMEMORY_C + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef DMALLOC +#include "dmalloc.h" +#endif + +#include "icalmemory.h" +#include "icalerror.h" + +#include <stdio.h> /* for printf (debugging) */ +#include <stdlib.h> /* for malloc, realloc */ +#include <string.h> /* for memset(), strdup */ + +#ifdef WIN32 +#include <windows.h> +#endif + +#define BUFFER_RING_SIZE 2500 +#define MIN_BUFFER_SIZE 200 + + +/* HACK. Not threadsafe */ + +typedef struct { + int pos; + void *ring[BUFFER_RING_SIZE]; +} buffer_ring; + +void icalmemory_free_tmp_buffer (void* buf); +void icalmemory_free_ring_byval(buffer_ring *br); + +static buffer_ring* global_buffer_ring = 0; + +#ifdef HAVE_PTHREAD +#include <pthread.h> + +static pthread_key_t ring_key; +static pthread_once_t ring_key_once = PTHREAD_ONCE_INIT; + +static void ring_destroy(void * buf) { + if (buf) icalmemory_free_ring_byval((buffer_ring *) buf); + pthread_setspecific(ring_key, NULL); +} + +static void ring_key_alloc(void) { + pthread_key_create(&ring_key, ring_destroy); +} +#endif + + +static buffer_ring * buffer_ring_new(void) { + buffer_ring *br; + int i; + + br = (buffer_ring *)malloc(sizeof(buffer_ring)); + + for(i=0; i<BUFFER_RING_SIZE; i++){ + br->ring[i] = 0; + } + br->pos = 0; + return(br); +} + + +#ifdef HAVE_PTHREAD +static buffer_ring* get_buffer_ring_pthread(void) { + buffer_ring *br; + + pthread_once(&ring_key_once, ring_key_alloc); + + br = pthread_getspecific(ring_key); + + if (!br) { + br = buffer_ring_new(); + pthread_setspecific(ring_key, br); + } + return(br); +} +#endif + +/* get buffer ring via a single global for a non-threaded program */ +static buffer_ring* get_buffer_ring_global(void) { + if (global_buffer_ring == 0) { + global_buffer_ring = buffer_ring_new(); + } + return(global_buffer_ring); +} + +static buffer_ring *get_buffer_ring(void) { +#ifdef HAVE_PTHREAD + return(get_buffer_ring_pthread()); +#else + return get_buffer_ring_global(); +#endif +} + + +/** Add an existing buffer to the buffer ring */ +void icalmemory_add_tmp_buffer(void* buf) +{ + buffer_ring *br = get_buffer_ring(); + + + /* Wrap around the ring */ + if(++(br->pos) == BUFFER_RING_SIZE){ + br->pos = 0; + } + + /* Free buffers as their slots are overwritten */ + if ( br->ring[br->pos] != 0){ + free( br->ring[br->pos]); + } + + /* Assign the buffer to a slot */ + br->ring[br->pos] = buf; +} + + +/** + * Create a new temporary buffer on the ring. Libical owns these and + * will deallocate them. + */ + +void* +icalmemory_tmp_buffer (size_t size) +{ + char *buf; + + if (size < MIN_BUFFER_SIZE){ + size = MIN_BUFFER_SIZE; + } + + buf = (void*)malloc(size); + + if( buf == 0){ + icalerror_set_errno(ICAL_NEWFAILED_ERROR); + return 0; + } + + memset(buf,0,size); + + icalmemory_add_tmp_buffer(buf); + + return buf; +} + +/** get rid of this buffer ring */ +void icalmemory_free_ring_byval(buffer_ring *br) { + int i; + for(i=0; i<BUFFER_RING_SIZE; i++){ + if ( br->ring[i] != 0){ + free( br->ring[i]); + } + } + free(br); +} + +void icalmemory_free_ring() +{ + buffer_ring *br; + br = get_buffer_ring(); + + icalmemory_free_ring_byval(br); +#ifdef HAVE_PTHREAD + pthread_setspecific(ring_key, 0); +#else + global_buffer_ring = 0; +#endif + +} + + + +/** Like strdup, but the buffer is on the ring. */ +char* +icalmemory_tmp_copy(const char* str) +{ + char* b = icalmemory_tmp_buffer(strlen(str)+1); + + strcpy(b,str); + + return b; +} + + +char* icalmemory_strdup(const char *s) +{ + return strdup(s); +} + +void +icalmemory_free_tmp_buffer (void* buf) +{ + if(buf == 0) + { + return; + } + + free(buf); +} + + +/* + * These buffer routines create memory the old fashioned way -- so the + * caller will have to deallocate the new memory + */ + +void* icalmemory_new_buffer(size_t size) +{ + void *b = malloc(size); + + if( b == 0){ + icalerror_set_errno(ICAL_NEWFAILED_ERROR); + return 0; + } + + memset(b,0,size); + + return b; +} + +void* icalmemory_resize_buffer(void* buf, size_t size) +{ + void *b = realloc(buf, size); + + if( b == 0){ + icalerror_set_errno(ICAL_NEWFAILED_ERROR); + return 0; + } + + return b; +} + +void icalmemory_free_buffer(void* buf) +{ + free(buf); +} + +void +icalmemory_append_string(char** buf, char** pos, size_t* buf_size, + const char* string) +{ + char *new_buf; + char *new_pos; + + size_t data_length, final_length, string_length; + +#ifndef ICAL_NO_INTERNAL_DEBUG + icalerror_check_arg_rv( (buf!=0),"buf"); + icalerror_check_arg_rv( (*buf!=0),"*buf"); + icalerror_check_arg_rv( (pos!=0),"pos"); + icalerror_check_arg_rv( (*pos!=0),"*pos"); + icalerror_check_arg_rv( (buf_size!=0),"buf_size"); + icalerror_check_arg_rv( (*buf_size!=0),"*buf_size"); + icalerror_check_arg_rv( (string!=0),"string"); +#endif + + string_length = strlen(string); + data_length = (size_t)*pos - (size_t)*buf; + final_length = data_length + string_length; + + if ( final_length >= (size_t) *buf_size) { + + + *buf_size = (*buf_size) * 2 + final_length; + + new_buf = realloc(*buf,*buf_size); + + new_pos = (void*)((size_t)new_buf + data_length); + + *pos = new_pos; + *buf = new_buf; + } + + strcpy(*pos, string); + + *pos += string_length; +} + + +void +icalmemory_append_char(char** buf, char** pos, size_t* buf_size, + char ch) +{ + char *new_buf; + char *new_pos; + + size_t data_length, final_length; + +#ifndef ICAL_NO_INTERNAL_DEBUG + icalerror_check_arg_rv( (buf!=0),"buf"); + icalerror_check_arg_rv( (*buf!=0),"*buf"); + icalerror_check_arg_rv( (pos!=0),"pos"); + icalerror_check_arg_rv( (*pos!=0),"*pos"); + icalerror_check_arg_rv( (buf_size!=0),"buf_size"); + icalerror_check_arg_rv( (*buf_size!=0),"*buf_size"); +#endif + + data_length = (size_t)*pos - (size_t)*buf; + + final_length = data_length + 2; + + if ( final_length > (size_t) *buf_size ) { + + + *buf_size = (*buf_size) * 2 + final_length +1; + + new_buf = realloc(*buf,*buf_size); + + new_pos = (void*)((size_t)new_buf + data_length); + + *pos = new_pos; + *buf = new_buf; + } + + **pos = ch; + *pos += 1; + **pos = 0; +} diff --git a/libkcal/libical/src/libical/icalmemory.h b/libkcal/libical/src/libical/icalmemory.h new file mode 100644 index 000000000..ed07c3f17 --- /dev/null +++ b/libkcal/libical/src/libical/icalmemory.h @@ -0,0 +1,83 @@ +/* -*- Mode: C -*- */ +/*====================================================================== + FILE: icalmemory.h + CREATOR: eric 30 June 1999 + + + + This program is free software; you can redistribute it and/or modify + it under the terms of either: + + The LGPL as published by the Free Software Foundation, version + 2.1, available at: http://www.fsf.org/copyleft/lesser.html + + Or: + + The Mozilla Public License Version 1.0. You may obtain a copy of + the License at http://www.mozilla.org/MPL/ + + The Initial Developer of the Original Code is Eric Busboom + + (C) COPYRIGHT 2000, Eric Busboom <eric@softwarestudio.org> + http://www.softwarestudio.org +======================================================================*/ + +#ifndef ICALMEMORY_H +#define ICALMEMORY_H + +#ifndef WIN32 +#include <sys/types.h> /* for size_t */ +#else +#include <stddef.h> +#endif + +/* Tmp buffers are managed by ical. References can be returned to the + caller, although the caller will not own the memory. */ + +void* icalmemory_tmp_buffer(size_t size); +char* icalmemory_tmp_copy(const char* str); + +/** Add an externally allocated buffer to the ring. */ +void icalmemory_add_tmp_buffer(void*); + + +/** Free all memory used in the ring */ +void icalmemory_free_ring(void); + +/* Non-tmp buffers must be freed. These are mostly wrappers around + * malloc, etc, but are used so the caller can change the memory + * allocators in a future version of the library */ + +void* icalmemory_new_buffer(size_t size); +void* icalmemory_resize_buffer(void* buf, size_t size); +void icalmemory_free_buffer(void* buf); + +/** + icalmemory_append_string will copy the string 'string' to the + buffer 'buf' starting at position 'pos', reallocing 'buf' if it is + too small. 'buf_size' is the size of 'buf' and will be changed if + 'buf' is reallocated. 'pos' will point to the last byte of the new + string in 'buf', usually a '\0' */ + +/* THESE ROUTINES CAN NOT BE USED ON TMP BUFFERS. Only use them on + normally allocated memory, or on buffers created from + icalmemory_new_buffer, never with buffers created by + icalmemory_tmp_buffer. If icalmemory_append_string has to resize a + buffer on the ring, the ring will loose track of it an you will + have memory problems. */ + +void icalmemory_append_string(char** buf, char** pos, size_t* buf_size, + const char* string); + +/** icalmemory_append_char is similar, but is appends a character instead of a string */ +void icalmemory_append_char(char** buf, char** pos, size_t* buf_size, + char ch); + +/** A wrapper around strdup. Partly to trap calls to strdup, partly + because in -ansi, gcc on Red Hat claims that strdup is undeclared */ +char* icalmemory_strdup(const char *s); + +#endif /* !ICALMEMORY_H */ + + + diff --git a/libkcal/libical/src/libical/icalmime.c b/libkcal/libical/src/libical/icalmime.c new file mode 100644 index 000000000..0a186d144 --- /dev/null +++ b/libkcal/libical/src/libical/icalmime.c @@ -0,0 +1,393 @@ +/* -*- Mode: C -*-*/ +/*====================================================================== + FILE: icalmime.c + CREATOR: eric 26 July 2000 + + + + (C) COPYRIGHT 2000, Eric Busboom <eric@softwarestudio.org> + http://www.softwarestudio.org + + This program is free software; you can redistribute it and/or modify + it under the terms of either: + + The LGPL as published by the Free Software Foundation, version + 2.1, available at: http://www.fsf.org/copyleft/lesser.html + + Or: + + The Mozilla Public License Version 1.0. You may obtain a copy of + the License at http://www.mozilla.org/MPL/ + + The Original Code is eric. The Initial Developer of the Original + Code is Eric Busboom + + +======================================================================*/ + +#include "icalmime.h" +#include "icalerror.h" +#include "icalmemory.h" +#include "sspm.h" +#include "stdlib.h" +#include <string.h> /* For strdup */ +#include <stdio.h> /* for snprintf*/ + +#ifdef DMALLOC +#include "dmalloc.h" +#endif + +#ifdef WIN32 +#define snprintf _snprintf +#define strcasecmp stricmp +#endif + +/* These *_part routines are called by the MIME parser via the + local_action_map */ + +struct text_part +{ + char* buf; + char* buf_pos; + size_t buf_size; +}; + +void* icalmime_text_new_part() +{ + +#define BUF_SIZE 2048 + + struct text_part* impl; + + if ( ( impl = (struct text_part*) + malloc(sizeof(struct text_part))) == 0) { + return 0; + } + + impl->buf = icalmemory_new_buffer(BUF_SIZE); + impl->buf_pos = impl->buf; + impl->buf_size = BUF_SIZE; + + return impl; +} +void icalmime_text_add_line(void *part, + struct sspm_header *header, + const char* line, size_t size) +{ + struct text_part* impl = (struct text_part*) part; + (void)header; + (void)size; + + icalmemory_append_string(&(impl->buf),&(impl->buf_pos), + &(impl->buf_size),line); + +} + +void* icalmime_textcalendar_end_part(void* part) +{ + + struct text_part* impl = (struct text_part*) part; + icalcomponent *c = icalparser_parse_string(impl->buf); + + icalmemory_free_buffer(impl->buf); + free(impl); + + return c; + +} + +void* icalmime_text_end_part(void* part) +{ + struct text_part* impl = ( struct text_part*) part; + + icalmemory_add_tmp_buffer(impl->buf); + /* What for? free(impl); */ + + return impl->buf; +} + +void icalmime_text_free_part(void *part) +{ + part = part; +} + + +/* Ignore Attachments for now */ + +void* icalmime_attachment_new_part() +{ + return 0; +} +void icalmime_attachment_add_line(void *part, struct sspm_header *header, + const char* line, size_t size) +{ + (void)part; + (void)header; + (void)line; + (void)size; +} + +void* icalmime_attachment_end_part(void* part) +{ + (void)part; + return 0; +} + +void icalmime_attachment_free_part(void *part) +{ + (void)part; +} + + + + +struct sspm_action_map icalmime_local_action_map[] = +{ + {SSPM_TEXT_MAJOR_TYPE,SSPM_CALENDAR_MINOR_TYPE,icalmime_text_new_part,icalmime_text_add_line,icalmime_textcalendar_end_part,icalmime_text_free_part}, + {SSPM_TEXT_MAJOR_TYPE,SSPM_ANY_MINOR_TYPE,icalmime_text_new_part,icalmime_text_add_line,icalmime_text_end_part,icalmime_text_free_part}, + {SSPM_TEXT_MAJOR_TYPE,SSPM_PLAIN_MINOR_TYPE,icalmime_text_new_part,icalmime_text_add_line,icalmime_text_end_part,icalmime_text_free_part}, + {SSPM_APPLICATION_MAJOR_TYPE,SSPM_CALENDAR_MINOR_TYPE,icalmime_attachment_new_part,icalmime_attachment_add_line,icalmime_attachment_end_part,icalmime_attachment_free_part}, + {SSPM_IMAGE_MAJOR_TYPE,SSPM_CALENDAR_MINOR_TYPE,icalmime_attachment_new_part,icalmime_attachment_add_line,icalmime_attachment_end_part,icalmime_attachment_free_part}, + {SSPM_AUDIO_MAJOR_TYPE,SSPM_CALENDAR_MINOR_TYPE,icalmime_attachment_new_part,icalmime_attachment_add_line,icalmime_attachment_end_part,icalmime_attachment_free_part}, + {SSPM_IMAGE_MAJOR_TYPE,SSPM_CALENDAR_MINOR_TYPE,icalmime_attachment_new_part,icalmime_attachment_add_line,icalmime_attachment_end_part,icalmime_attachment_free_part}, + {SSPM_UNKNOWN_MAJOR_TYPE,SSPM_UNKNOWN_MINOR_TYPE,0,0,0,0} +}; + +#define NUM_PARTS 100 /* HACK. Hard Limit */ + + + +struct sspm_part* icalmime_make_part(icalcomponent* comp) +{ + comp = comp; + return 0; +} + +char* icalmime_as_mime_string(char* icalcomponent); + +icalcomponent* icalmime_parse(char* (*get_string)(char *s, size_t size, + void *d), + void *data) +{ + struct sspm_part *parts; + int i, last_level=0; + icalcomponent *root=0, *parent=0, *comp=0, *last = 0; + + if ( (parts = (struct sspm_part *) + malloc(NUM_PARTS*sizeof(struct sspm_part)))==0) { + icalerror_set_errno(ICAL_NEWFAILED_ERROR); + return 0; + } + + memset(parts,0,sizeof(parts)); + + sspm_parse_mime(parts, + NUM_PARTS, /* Max parts */ + icalmime_local_action_map, /* Actions */ + get_string, + data, /* data for get_string*/ + 0 /* First header */); + + + + for(i = 0; i <NUM_PARTS && parts[i].header.major != SSPM_NO_MAJOR_TYPE ; i++){ + +#define TMPSZ 1024 + char mimetype[TMPSZ]; + const char* major = sspm_major_type_string(parts[i].header.major); + const char* minor = sspm_minor_type_string(parts[i].header.minor); + + if(parts[i].header.minor == SSPM_UNKNOWN_MINOR_TYPE ){ + assert(parts[i].header.minor_text !=0); + minor = parts[i].header.minor_text; + } + + snprintf(mimetype,sizeof(mimetype),"%s/%s",major,minor); + + comp = icalcomponent_new(ICAL_XLICMIMEPART_COMPONENT); + + if(comp == 0){ + /* HACK Handle Error */ + assert(0); + } + + if(parts[i].header.error!=SSPM_NO_ERROR){ + const char *str="Unknown error"; + char temp[256]; + + if(parts[i].header.error==SSPM_UNEXPECTED_BOUNDARY_ERROR){ + str = "Got an unexpected boundary, possibly due to a MIME header for a MULTIPART part that is missing the Content-Type line"; + } + + if(parts[i].header.error==SSPM_WRONG_BOUNDARY_ERROR){ + str = "Got the wrong boundary for the opening of a MULTIPART part."; + } + + if(parts[i].header.error==SSPM_NO_BOUNDARY_ERROR){ + str = "Got a multipart header that did not specify a boundary"; + } + + if(parts[i].header.error==SSPM_NO_HEADER_ERROR){ + str = "Did not get a header for the part. Is there a blank\ +line between the header and the previous boundary\?"; + + } + + if(parts[i].header.error_text != 0){ + snprintf(temp,256, + "%s: %s",str,parts[i].header.error_text); + } else { + strcpy(temp,str); + } + + icalcomponent_add_property + (comp, + icalproperty_vanew_xlicerror( + temp, + icalparameter_new_xlicerrortype( + ICAL_XLICERRORTYPE_MIMEPARSEERROR), + 0)); + } + + if(parts[i].header.major != SSPM_NO_MAJOR_TYPE && + parts[i].header.major != SSPM_UNKNOWN_MAJOR_TYPE){ + + icalcomponent_add_property(comp, + icalproperty_new_xlicmimecontenttype((char*) + icalmemory_strdup(mimetype))); + + } + + if (parts[i].header.encoding != SSPM_NO_ENCODING){ + + icalcomponent_add_property(comp, + icalproperty_new_xlicmimeencoding( + sspm_encoding_string(parts[i].header.encoding))); + } + + if (parts[i].header.filename != 0){ + icalcomponent_add_property(comp, + icalproperty_new_xlicmimefilename(parts[i].header.filename)); + } + + if (parts[i].header.content_id != 0){ + icalcomponent_add_property(comp, + icalproperty_new_xlicmimecid(parts[i].header.content_id)); + } + + if (parts[i].header.charset != 0){ + icalcomponent_add_property(comp, + icalproperty_new_xlicmimecharset(parts[i].header.charset)); + } + + /* Add iCal components as children of the component */ + if(parts[i].header.major == SSPM_TEXT_MAJOR_TYPE && + parts[i].header.minor == SSPM_CALENDAR_MINOR_TYPE && + parts[i].data != 0){ + + icalcomponent_add_component(comp, + (icalcomponent*)parts[i].data); + parts[i].data = 0; + + } else if(parts[i].header.major == SSPM_TEXT_MAJOR_TYPE && + parts[i].header.minor != SSPM_CALENDAR_MINOR_TYPE && + parts[i].data != 0){ + + /* Add other text components as "DESCRIPTION" properties */ + + icalcomponent_add_property(comp, + icalproperty_new_description( + (char*)icalmemory_strdup((char*)parts[i].data))); + + parts[i].data = 0; + } + + + if(root!= 0 && parts[i].level == 0){ + /* We've already assigned the root, but there is another + part at the root level. This is probably a parse + error*/ + icalcomponent_free(comp); + continue; + } + + if(parts[i].level == last_level && last_level != 0){ + icalerror_assert(parent!=0,"No parent for adding component"); + + icalcomponent_add_component(parent,comp); + + } else if (parts[i].level == last_level && last_level == 0 && + root == 0) { + + root = comp; + parent = comp; + + } else if (parts[i].level > last_level){ + + parent = last; + icalcomponent_add_component(parent,comp); + + last_level = parts[i].level; + + } else if (parts[i].level < last_level){ + + parent = icalcomponent_get_parent(parent); + icalcomponent_add_component(parent,comp); + + last_level = parts[i].level; + } else { + assert(0); + } + + last = comp; + last_level = parts[i].level; + assert(parts[i].data == 0); + } + + sspm_free_parts(parts,NUM_PARTS); + free(parts); + + return root; +} + + + +int icalmime_test(char* (*get_string)(char *s, size_t size, void *d), + void *data) +{ + char *out; + struct sspm_part *parts; + int i; + + if ( (parts = (struct sspm_part *) + malloc(NUM_PARTS*sizeof(struct sspm_part)))==0) { + icalerror_set_errno(ICAL_NEWFAILED_ERROR); + return 0; + } + + memset(parts,0,sizeof(parts)); + + sspm_parse_mime(parts, + NUM_PARTS, /* Max parts */ + icalmime_local_action_map, /* Actions */ + get_string, + data, /* data for get_string*/ + 0 /* First header */); + + for(i = 0; i <NUM_PARTS && parts[i].header.major != SSPM_NO_MAJOR_TYPE ; + i++){ + if(parts[i].header.minor == SSPM_CALENDAR_MINOR_TYPE){ + parts[i].data = icalmemory_strdup( + icalcomponent_as_ical_string((icalcomponent*)parts[i].data)); + } + } + + sspm_write_mime(parts,NUM_PARTS,&out,"To: bob@bob.org"); + + printf("%s\n",out); + + return 0; + +} + + diff --git a/libkcal/libical/src/libical/icalmime.h b/libkcal/libical/src/libical/icalmime.h new file mode 100644 index 000000000..8d42dde3a --- /dev/null +++ b/libkcal/libical/src/libical/icalmime.h @@ -0,0 +1,42 @@ +/* -*- Mode: C -*- */ +/*====================================================================== + FILE: icalmime.h + CREATOR: eric 26 July 2000 + + + + (C) COPYRIGHT 2000, Eric Busboom <eric@softwarestudio.org> + http://www.softwarestudio.org + + This program is free software; you can redistribute it and/or modify + it under the terms of either: + + The LGPL as published by the Free Software Foundation, version + 2.1, available at: http://www.fsf.org/copyleft/lesser.html + + Or: + + The Mozilla Public License Version 1.0. You may obtain a copy of + the License at http://www.mozilla.org/MPL/ + +======================================================================*/ + +#ifndef ICALMIME_H +#define ICALMIME_H + +#include "icalcomponent.h" +#include "icalparser.h" + +icalcomponent* icalmime_parse( char* (*line_gen_func)(char *s, size_t size, + void *d), + void *data); + +/* The inverse of icalmime_parse, not implemented yet. Use sspm.h directly. */ +char* icalmime_as_mime_string(char* component); + + + +#endif /* !ICALMIME_H */ + + + diff --git a/libkcal/libical/src/libical/icalparameter.c b/libkcal/libical/src/libical/icalparameter.c new file mode 100644 index 000000000..52029e313 --- /dev/null +++ b/libkcal/libical/src/libical/icalparameter.c @@ -0,0 +1,384 @@ +/* -*- Mode: C -*- + ====================================================================== + FILE: icalderivedparameters.{c,h} + CREATOR: eric 09 May 1999 + + + + (C) COPYRIGHT 2000, Eric Busboom <eric@softwarestudio.org> + http://www.softwarestudio.org + + This program is free software; you can redistribute it and/or modify + it under the terms of either: + + The LGPL as published by the Free Software Foundation, version + 2.1, available at: http://www.fsf.org/copyleft/lesser.html + + Or: + + The Mozilla Public License Version 1.0. You may obtain a copy of + the License at http://www.mozilla.org/MPL/ + + The original code is icalderivedparameters.{c,h} + + Contributions from: + Graham Davison <g.m.davison@computer.org> + + ======================================================================*/ +/*#line 29 "icalparameter.c.in"*/ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + + +#include "icalparameter.h" +#include "icalproperty.h" +#include "icalerror.h" +#include "icalmemory.h" +#include "icalparameterimpl.h" + +#include <stdlib.h> /* for malloc() */ +#include <errno.h> +#include <string.h> /* for memset() */ + +/* In icalderivedparameter */ +icalparameter* icalparameter_new_from_value_string(icalparameter_kind kind,const char* val); + + +struct icalparameter_impl* icalparameter_new_impl(icalparameter_kind kind) +{ + struct icalparameter_impl* v; + + if ( ( v = (struct icalparameter_impl*) + malloc(sizeof(struct icalparameter_impl))) == 0) { + icalerror_set_errno(ICAL_NEWFAILED_ERROR); + return 0; + } + + strcpy(v->id,"para"); + + v->kind = kind; + v->size = 0; + v->string = 0; + v->x_name = 0; + v->parent = 0; + v->data = 0; + + return v; +} + +icalparameter* +icalparameter_new (icalparameter_kind kind) +{ + struct icalparameter_impl* v = icalparameter_new_impl(kind); + + return (icalparameter*) v; + +} + +void +icalparameter_free (icalparameter* param) +{ + +/* HACK. This always triggers, even when parameter is non-zero + icalerror_check_arg_rv((parameter==0),"parameter");*/ + + +#ifdef ICAL_FREE_ON_LIST_IS_ERROR + icalerror_assert( (param->parent ==0),"Tried to free a parameter that is still attached to a component. "); + +#else + if(param->parent !=0){ + return; + } +#endif + + + if (param->string != 0){ + free ((void*)param->string); + } + + if (param->x_name != 0){ + free ((void*)param->x_name); + } + + memset(param,0,sizeof(param)); + + param->parent = 0; + param->id[0] = 'X'; + free(param); +} + + + +icalparameter* +icalparameter_new_clone(icalparameter* old) +{ + struct icalparameter_impl *new; + + new = icalparameter_new_impl(old->kind); + + icalerror_check_arg_rz((old!=0),"param"); + + if (new == 0){ + return 0; + } + + memcpy(new,old,sizeof(struct icalparameter_impl)); + + if (old->string != 0){ + new->string = icalmemory_strdup(old->string); + if (new->string == 0){ + icalparameter_free(new); + return 0; + } + } + + if (old->x_name != 0){ + new->x_name = icalmemory_strdup(old->x_name); + if (new->x_name == 0){ + icalparameter_free(new); + return 0; + } + } + + return new; +} + +icalparameter* icalparameter_new_from_string(const char *str) +{ + char* eq; + char* cpy; + icalparameter_kind kind; + icalparameter *param; + + icalerror_check_arg_rz(str != 0,"str"); + + cpy = icalmemory_strdup(str); + + if (cpy == 0){ + icalerror_set_errno(ICAL_NEWFAILED_ERROR); + return 0; + } + + eq = strchr(cpy,'='); + + if(eq == 0){ + icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR); + return 0; + } + + *eq = '\0'; + + eq++; + + kind = icalparameter_string_to_kind(cpy); + + if(kind == ICAL_NO_PARAMETER){ + icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR); + return 0; + } + + param = icalparameter_new_from_value_string(kind,eq); + + if(kind == ICAL_X_PARAMETER){ + icalparameter_set_xname(param,cpy); + } + + free(cpy); + + return param; + +} + +/** + * Return a string representation of the parameter according to RFC2445. + * + * param = param-name "=" param-value + * param-name = iana-token / x-token + * param-value = paramtext /quoted-string + * paramtext = *SAFE-SHARE + * quoted-string= DQUOTE *QSAFE-CHARE DQUOTE + * QSAFE-CHAR = any character except CTLs and DQUOTE + * SAFE-CHAR = any character except CTLs, DQUOTE. ";", ":", "," + */ +char* +icalparameter_as_ical_string (icalparameter* param) +{ + size_t buf_size = 1024; + char* buf; + char* buf_ptr; + char *out_buf; + const char *kind_string; + + icalerror_check_arg_rz( (param!=0), "parameter"); + + /* Create new buffer that we can append names, parameters and a + value to, and reallocate as needed. Later, this buffer will be + copied to a icalmemory_tmp_buffer, which is managed internally + by libical, so it can be given to the caller without fear of + the caller forgetting to free it */ + + buf = icalmemory_new_buffer(buf_size); + buf_ptr = buf; + + if(param->kind == ICAL_X_PARAMETER) { + + icalmemory_append_string(&buf, &buf_ptr, &buf_size, + icalparameter_get_xname(param)); + + } else { + + kind_string = icalparameter_kind_to_string(param->kind); + + if (param->kind == ICAL_NO_PARAMETER || + param->kind == ICAL_ANY_PARAMETER || + kind_string == 0) + { + icalerror_set_errno(ICAL_BADARG_ERROR); + return 0; + } + + + /* Put the parameter name into the string */ + icalmemory_append_string(&buf, &buf_ptr, &buf_size, kind_string); + + } + + icalmemory_append_string(&buf, &buf_ptr, &buf_size, "="); + + if(param->string !=0){ + int qm = 0; + + /* Encapsulate the property in quotes if necessary */ + if (strpbrk(param->string, ";:,") != 0) { + icalmemory_append_char (&buf, &buf_ptr, &buf_size, '"'); + qm = 1; + } + icalmemory_append_string(&buf, &buf_ptr, &buf_size, param->string); + if (qm == 1) { + icalmemory_append_char (&buf, &buf_ptr, &buf_size, '"'); + } + } else if (param->data != 0){ + const char* str = icalparameter_enum_to_string(param->data); + icalmemory_append_string(&buf, &buf_ptr, &buf_size, str); + } else { + icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR); + return 0; + } + + /* Now, copy the buffer to a tmp_buffer, which is safe to give to + the caller without worring about de-allocating it. */ + + out_buf = icalmemory_tmp_buffer(strlen(buf)+1); + strcpy(out_buf, buf); + + icalmemory_free_buffer(buf); + + return out_buf; + +} + + +int +icalparameter_is_valid (icalparameter* parameter); + + +icalparameter_kind +icalparameter_isa (icalparameter* parameter) +{ + if(parameter == 0){ + return ICAL_NO_PARAMETER; + } + + return parameter->kind; +} + + +int +icalparameter_isa_parameter (void* parameter) +{ + struct icalparameter_impl *impl = (struct icalparameter_impl *)parameter; + + if (parameter == 0){ + return 0; + } + + if (strcmp(impl->id,"para") == 0) { + return 1; + } else { + return 0; + } +} + + +void +icalparameter_set_xname (icalparameter* param, const char* v) +{ + icalerror_check_arg_rv( (param!=0),"param"); + icalerror_check_arg_rv( (v!=0),"v"); + + if (param->x_name != 0){ + free((void*)param->x_name); + } + + param->x_name = icalmemory_strdup(v); + + if (param->x_name == 0){ + errno = ENOMEM; + } + +} + +const char* +icalparameter_get_xname (icalparameter* param) +{ + icalerror_check_arg_rz( (param!=0),"param"); + + return param->x_name; +} + +void +icalparameter_set_xvalue (icalparameter* param, const char* v) +{ + icalerror_check_arg_rv( (param!=0),"param"); + icalerror_check_arg_rv( (v!=0),"v"); + + if (param->string != 0){ + free((void*)param->string); + } + + param->string = icalmemory_strdup(v); + + if (param->string == 0){ + errno = ENOMEM; + } + +} + +const char* +icalparameter_get_xvalue (icalparameter* param) +{ + icalerror_check_arg_rz( (param!=0),"param"); + + return param->string; +} + +void icalparameter_set_parent(icalparameter* param, + icalproperty* property) +{ + icalerror_check_arg_rv( (param!=0),"param"); + + param->parent = property; +} + +icalproperty* icalparameter_get_parent(icalparameter* param) +{ + icalerror_check_arg_rz( (param!=0),"param"); + + return param->parent; +} + + +/* Everything below this line is machine generated. Do not edit. */ +/* ALTREP */ diff --git a/libkcal/libical/src/libical/icalparameter.h b/libkcal/libical/src/libical/icalparameter.h new file mode 100644 index 000000000..dc5d05489 --- /dev/null +++ b/libkcal/libical/src/libical/icalparameter.h @@ -0,0 +1,68 @@ +/* -*- Mode: C -*- */ +/*====================================================================== + FILE: icalparam.h + CREATOR: eric 20 March 1999 + + + + + + (C) COPYRIGHT 2000, Eric Busboom <eric@softwarestudio.org> + http://www.softwarestudio.org + + This program is free software; you can redistribute it and/or modify + it under the terms of either: + + The LGPL as published by the Free Software Foundation, version + 2.1, available at: http://www.fsf.org/copyleft/lesser.html + + Or: + + The Mozilla Public License Version 1.0. You may obtain a copy of + the License at http://www.mozilla.org/MPL/ + + The original code is icalparam.h + + ======================================================================*/ + +#ifndef ICALPARAM_H +#define ICALPARAM_H + +#include "icalderivedparameter.h" + +/* Declared in icalderivedparameter.h */ +/*typedef struct icalparameter_impl icalparameter;*/ + +icalparameter* icalparameter_new(icalparameter_kind kind); +icalparameter* icalparameter_new_clone(icalparameter* p); + +/* Create from string of form "PARAMNAME=VALUE" */ +icalparameter* icalparameter_new_from_string(const char* value); + +/* Create from just the value, the part after the "=" */ +icalparameter* icalparameter_new_from_value_string(icalparameter_kind kind, const char* value); + +void icalparameter_free(icalparameter* parameter); + +char* icalparameter_as_ical_string(icalparameter* parameter); + +int icalparameter_is_valid(icalparameter* parameter); + +icalparameter_kind icalparameter_isa(icalparameter* parameter); + +int icalparameter_isa_parameter(void* param); + +/* Access the name of an X parameer */ +void icalparameter_set_xname (icalparameter* param, const char* v); +const char* icalparameter_get_xname(icalparameter* param); +void icalparameter_set_xvalue (icalparameter* param, const char* v); +const char* icalparameter_get_xvalue(icalparameter* param); + +/* Convert enumerations */ + +const char* icalparameter_kind_to_string(icalparameter_kind kind); +icalparameter_kind icalparameter_string_to_kind(const char* string); + + + +#endif diff --git a/libkcal/libical/src/libical/icalparameterimpl.h b/libkcal/libical/src/libical/icalparameterimpl.h new file mode 100644 index 000000000..f2fed404e --- /dev/null +++ b/libkcal/libical/src/libical/icalparameterimpl.h @@ -0,0 +1,51 @@ +/* -*- Mode: C -*- + ====================================================================== + FILE: icalparameterimpl.h + CREATOR: eric 09 May 1999 + + + + (C) COPYRIGHT 2000, Eric Busboom <eric@softwarestudio.org> + http://www.softwarestudio.org + + This program is free software; you can redistribute it and/or modify + it under the terms of either: + + The LGPL as published by the Free Software Foundation, version + 2.1, available at: http://www.fsf.org/copyleft/lesser.html + + Or: + + The Mozilla Public License Version 1.0. You may obtain a copy of + the License at http://www.mozilla.org/MPL/ + + The original code is icalderivedparameters.{c,h} + + Contributions from: + Graham Davison (g.m.davison@computer.org) + + ======================================================================*/ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#ifndef ICALPARAMETER_IMPL +#define ICALPARAMETER_IMPL + +#include "icalparameter.h" +#include "icalproperty.h" + +struct icalparameter_impl +{ + icalparameter_kind kind; + char id[5]; + int size; + const char* string; + const char* x_name; + icalproperty* parent; + + int data; +}; + + +#endif /*ICALPARAMETER_IMPL*/ diff --git a/libkcal/libical/src/libical/icalparser.c b/libkcal/libical/src/libical/icalparser.c new file mode 100644 index 000000000..95843c962 --- /dev/null +++ b/libkcal/libical/src/libical/icalparser.c @@ -0,0 +1,1105 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 8; -*- + ====================================================================== + FILE: icalparser.c + CREATOR: eric 04 August 1999 + + + The contents of this file are subject to the Mozilla Public License + Version 1.0 (the "License"); you may not use this file except in + compliance with the License. You may obtain a copy of the License at + http://www.mozilla.org/MPL/ + + Software distributed under the License is distributed on an "AS IS" + basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + the License for the specific language governing rights and + limitations under the License. + + + This program is free software; you can redistribute it and/or modify + it under the terms of either: + + The LGPL as published by the Free Software Foundation, version + 2.1, available at: http://www.fsf.org/copyleft/lesser.html + + Or: + + The Mozilla Public License Version 1.0. You may obtain a copy of + the License at http://www.mozilla.org/MPL/ + + The Initial Developer of the Original Code is Eric Busboom + + (C) COPYRIGHT 2000, Eric Busboom <eric@softwarestudio.org> + http://www.softwarestudio.org + ======================================================================*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + + +#include "pvl.h" +#include "icalerror.h" +#include "icalvalue.h" +#include "icalderivedparameter.h" +#include "icalparameter.h" +#include "icalproperty.h" +#include "icalcomponent.h" + +#include <string.h> /* For strncpy & size_t */ +#include <stdio.h> /* For FILE and fgets and snprintf */ +#include <stdlib.h> /* for free */ +#include <ctype.h> + +#include "icalmemory.h" +#include "icalparser.h" + +#ifdef HAVE_WCTYPE_H +# include <wctype.h> +/* Some systems have an imcomplete implementation on wctype (FreeBSD, + * Darwin). Cope with that. */ +# ifndef HAVE_ISWSPACE +# define iswspace isspace +# endif +#else +# ifndef HAVE_ISWSPACE +# define iswspace isspace +# endif +#endif + +#ifdef WIN32 +#define snprintf _snprintf +#define strcasecmp stricmp +#endif + +char* icalparser_get_next_char(char c, char *str, int qm); +char* icalparser_get_next_parameter(char* line,char** end); +char* icalparser_get_next_value(char* line, char **end, icalvalue_kind kind); +char* icalparser_get_prop_name(char* line, char** end); +char* icalparser_get_param_name(char* line, char **end); + +#define TMP_BUF_SIZE 80 + +struct icalparser_impl +{ + int buffer_full; /* flag indicates that temp is smaller that + data being read into it*/ + int continuation_line; /* last line read was a continuation line */ + size_t tmp_buf_size; + char temp[TMP_BUF_SIZE]; + icalcomponent *root_component; + int version; + int level; + int lineno; + icalparser_state state; + pvl_list components; + + void *line_gen_data; + +}; + + +icalparser* icalparser_new(void) +{ + struct icalparser_impl* impl = 0; + if ( ( impl = (struct icalparser_impl*) + malloc(sizeof(struct icalparser_impl))) == 0) { + icalerror_set_errno(ICAL_NEWFAILED_ERROR); + return 0; + } + + impl->root_component = 0; + impl->components = pvl_newlist(); + impl->level = 0; + impl->state = ICALPARSER_SUCCESS; + impl->tmp_buf_size = TMP_BUF_SIZE; + impl->buffer_full = 0; + impl->continuation_line = 0; + impl->lineno = 0; + impl->continuation_line = 0; + memset(impl->temp,0, TMP_BUF_SIZE); + + return (icalparser*)impl; +} + + +void icalparser_free(icalparser* parser) +{ + icalcomponent *c; + + if (parser->root_component != 0){ + icalcomponent_free(parser->root_component); + } + + while( (c=pvl_pop(parser->components)) != 0){ + icalcomponent_free(c); + } + + pvl_free(parser->components); + + free(parser); +} + +void icalparser_set_gen_data(icalparser* parser, void* data) +{ + parser->line_gen_data = data; +} + + +icalvalue* icalvalue_new_From_string_with_error(icalvalue_kind kind, + char* str, + icalproperty **error); + + + +char* icalparser_get_next_char(char c, char *str, int qm) +{ + int quote_mode = 0; + char* p; + + for(p=str; *p!=0; p++){ + if (qm == 1) { + if ( quote_mode == 0 && *p=='"' && *(p-1) != '\\' ){ + quote_mode =1; + continue; + } + + if ( quote_mode == 1 && *p=='"' && *(p-1) != '\\' ){ + quote_mode =0; + continue; + } + } + + if (quote_mode == 0 && *p== c && *(p-1) != '\\' ){ + return p; + } + + } + + return 0; +} + + +/** make a new tmp buffer out of a substring */ +static char* make_segment(char* start, char* end) +{ + char *buf, *tmp; + size_t size = (size_t)end - (size_t)start; + + buf = icalmemory_tmp_buffer(size+1); + + + strncpy(buf,start,size); + *(buf+size) = 0; + + tmp = (buf+size); + while ( *tmp == '\0' || iswspace(*tmp) ) + { + *tmp = 0; + tmp--; + } + + return buf; +} + + +char* icalparser_get_prop_name(char* line, char** end) +{ + char* p; + char* v; + char *str; + + p = icalparser_get_next_char(';',line,1); + v = icalparser_get_next_char(':',line,1); + if (p== 0 && v == 0) { + return 0; + } + + /* There is no ';' or, it is after the ';' that marks the beginning of + the value */ + if (v!=0 && ( p == 0 || p > v)){ + str = make_segment(line,v); + *end = v+1; + } else { + str = make_segment(line,p); + *end = p+1; + } + + return str; +} + + +char* icalparser_get_param_name(char* line, char **end) +{ + char* next; + char *str; + + next = icalparser_get_next_char('=',line,1); + + if (next == 0) { + return 0; + } + + str = make_segment(line,next); + *end = next+1; + if (**end == '"') { + *end = *end+1; + next = icalparser_get_next_char('"',*end,0); + if (next == 0) { + return 0; + } + + *end = make_segment(*end,next); + } + + return str; +} + + +char* icalparser_get_next_paramvalue(char* line, char **end) +{ + char* next; + char *str; + + next = icalparser_get_next_char(',',line,1); + + if (next == 0){ + next = (char*)(size_t)line+(size_t)strlen(line);\ + } + + if (next == line){ + return 0; + } else { + str = make_segment(line,next); + *end = next+1; + return str; + } +} + + +/** + A property may have multiple values, if the values are separated by + commas in the content line. This routine will look for the next + comma after line and will set the next place to start searching in + end. */ + +char* icalparser_get_next_value(char* line, char **end, icalvalue_kind kind) +{ + + char* next; + char *p; + char *str; + size_t length = strlen(line); + + p = line; + while(1){ + + next = icalparser_get_next_char(',',p,1); + + /* Unforunately, RFC2445 says that for the RECUR value, COMMA + can both separate digits in a list, and it can separate + multiple recurrence specifications. This is not a friendly + part of the spec. This weirdness tries to + distinguish the two uses. it is probably a HACK*/ + + if( kind == ICAL_RECUR_VALUE ) { + if ( next != 0 && + (*end+length) > next+5 && + strncmp(next,"FREQ",4) == 0 + ) { + /* The COMMA was followed by 'FREQ', is it a real separator*/ + /* Fall through */ + } else if (next != 0){ + /* Not real, get the next COMMA */ + p = next+1; + next = 0; + continue; + } + } + /* ignore all , for query value. select dtstart, dtend etc ... */ + else if( kind == ICAL_QUERY_VALUE) { + if ( next != 0) { + p = next+1; + continue; + } + else + break; + } + + /* If the comma is preceded by a '\', then it is a literal and + not a value separator*/ + + if ( (next!=0 && *(next-1) == '\\') || + (next!=0 && *(next-3) == '\\') + ) + /*second clause for '/' is on prev line. HACK may be out of bounds */ + { + p = next+1; + } else { + break; + } + + } + + if (next == 0){ + next = (char*)(size_t)line+length; + *end = next; + } else { + *end = next+1; + } + + if (next == line){ + return 0; + } + + + str = make_segment(line,next); + return str; + +} + +char* icalparser_get_next_parameter(char* line,char** end) +{ + char *next; + char *v; + char *str; + + v = icalparser_get_next_char(':',line,1); + next = icalparser_get_next_char(';', line,1); + + /* There is no ';' or, it is after the ':' that marks the beginning of + the value */ + + if (next == 0 || next > v) { + next = icalparser_get_next_char(':', line,1); + } + + if (next != 0) { + str = make_segment(line,next); + *end = next+1; + return str; + } else { + *end = line; + return 0; + } +} + + +/** + * Get a single property line, from the property name through the + * final new line, and include any continuation lines + */ +char* icalparser_get_line(icalparser *parser, + char* (*line_gen_func)(char *s, size_t size, void *d)) +{ + char *line; + char *line_p; + size_t buf_size = parser->tmp_buf_size; + + line_p = line = icalmemory_new_buffer(buf_size); + line[0] = '\0'; + + /* Read lines by calling line_gen_func and putting the data into + parser->temp. If the line is a continuation line ( begins with a + space after a newline ) then append the data onto line and read + again. Otherwise, exit the loop. */ + + while(1) { + + /* The first part of the loop deals with the temp buffer, + which was read on he last pass through the loop. The + routine is split like this because it has to read lone line + ahead to determine if a line is a continuation line. */ + + + /* The tmp buffer is not clear, so transfer the data in it to the + output. This may be left over from a previous call */ + if (parser->temp[0] != '\0' ) { + + /* If the last position in the temp buffer is occupied, + mark the buffer as full. The means we will do another + read later, because the line is not finished */ + if (parser->temp[parser->tmp_buf_size-1] == 0 && + parser->temp[parser->tmp_buf_size-2] != '\n'&& + parser->temp[parser->tmp_buf_size-2] != 0 ){ + parser->buffer_full = 1; + } else { + parser->buffer_full = 0; + } + + /* Copy the temp to the output and clear the temp buffer. */ + if(parser->continuation_line==1){ + /* back up the pointer to erase the continuation characters */ + parser->continuation_line = 0; + line_p--; + + if ( *(line_p-1) == '\r'){ + line_p--; + } + + /* copy one space up to eliminate the leading space*/ + icalmemory_append_string(&line,&line_p,&buf_size, + parser->temp+1); + + } else { + icalmemory_append_string(&line,&line_p,&buf_size,parser->temp); + } + + parser->temp[0] = '\0' ; + } + + parser->temp[parser->tmp_buf_size-1] = 1; /* Mark end of buffer */ + + /****** Here is where the routine gets string data ******************/ + if ((*line_gen_func)(parser->temp,parser->tmp_buf_size,parser->line_gen_data) + ==0){/* Get more data */ + + /* If the first position is clear, it means we didn't get + any more data from the last call to line_ge_func*/ + if (parser->temp[0] == '\0'){ + + if(line[0] != '\0'){ + /* There is data in the output, so fall trhough and process it*/ + break; + } else { + /* No data in output; return and signal that there + is no more input*/ + free(line); + return 0; + } + } + } + + + /* If the output line ends in a '\n' and the temp buffer + begins with a ' ', then the buffer holds a continuation + line, so keep reading. */ + + if ( line_p > line+1 && *(line_p-1) == '\n' + && (parser->temp[0] == ' ' || parser->temp[0] == '\t') ) { + + parser->continuation_line = 1; + + } else if ( parser->buffer_full == 1 ) { + + /* The buffer was filled on the last read, so read again */ + + } else { + + /* Looks like the end of this content line, so break */ + break; + } + + + } + + /* Erase the final newline and/or carriage return*/ + if ( line_p > line+1 && *(line_p-1) == '\n') { + *(line_p-1) = '\0'; + if ( *(line_p-2) == '\r'){ + *(line_p-2) = '\0'; + } + + } else { + *(line_p) = '\0'; + } + + while ( (*line_p == '\0' || iswspace(*line_p)) && line_p > line ) + { + *line_p = '\0'; + line_p--; + } + + return line; + +} + +static void insert_error(icalcomponent* comp, const char* text, + const char* message, icalparameter_xlicerrortype type) +{ + char temp[1024]; + + if (text == 0){ + snprintf(temp,1024,"%s:",message); + } else { + snprintf(temp,1024,"%s: %s",message,text); + } + + icalcomponent_add_property + (comp, + icalproperty_vanew_xlicerror( + temp, + icalparameter_new_xlicerrortype(type), + 0)); +} + + +static int line_is_blank(char* line){ + int i=0; + + for(i=0; *(line+i)!=0; i++){ + char c = *(line+i); + + if(c != ' ' && c != '\n' && c != '\t'){ + return 0; + } + } + + return 1; +} + +icalcomponent* icalparser_parse(icalparser *parser, + char* (*line_gen_func)(char *s, size_t size, + void* d)) +{ + + char* line; + icalcomponent *c=0; + icalcomponent *root=0; + icalerrorstate es = icalerror_get_error_state(ICAL_MALFORMEDDATA_ERROR); + int cont; + + icalerror_check_arg_rz((parser !=0),"parser"); + + icalerror_set_error_state(ICAL_MALFORMEDDATA_ERROR,ICAL_ERROR_NONFATAL); + + do{ + line = icalparser_get_line(parser, line_gen_func); + + if ((c = icalparser_add_line(parser,line)) != 0){ + + if(icalcomponent_get_parent(c) !=0){ + /* This is bad news... assert? */ + } + + assert(parser->root_component == 0); + assert(pvl_count(parser->components) ==0); + + if (root == 0){ + /* Just one component */ + root = c; + } else if(icalcomponent_isa(root) != ICAL_XROOT_COMPONENT) { + /*Got a second component, so move the two components under + an XROOT container */ + icalcomponent *tempc = icalcomponent_new(ICAL_XROOT_COMPONENT); + icalcomponent_add_component(tempc, root); + icalcomponent_add_component(tempc, c); + root = tempc; + } else if(icalcomponent_isa(root) == ICAL_XROOT_COMPONENT) { + /* Already have an XROOT container, so add the component + to it*/ + icalcomponent_add_component(root, c); + + } else { + /* Badness */ + assert(0); + } + + c = 0; + + } + cont = 0; + if(line != 0){ + free(line); + cont = 1; + } + } while ( cont ); + + icalerror_set_error_state(ICAL_MALFORMEDDATA_ERROR,es); + + return root; + +} + + +icalcomponent* icalparser_add_line(icalparser* parser, + char* line) +{ + char *str; + char *end; + int vcount = 0; + icalproperty *prop; + icalproperty_kind prop_kind; + icalvalue *value; + icalvalue_kind value_kind = ICAL_NO_VALUE; + + + icalerror_check_arg_rz((parser != 0),"parser"); + + + if (line == 0) + { + parser->state = ICALPARSER_ERROR; + return 0; + } + + if(line_is_blank(line) == 1){ + return 0; + } + + /* Begin by getting the property name at the start of the line. The + property name may end up being "BEGIN" or "END" in which case it + is not really a property, but the marker for the start or end of + a component */ + + end = 0; + str = icalparser_get_prop_name(line, &end); + + if (str == 0 || strlen(str) == 0 ){ + /* Could not get a property name */ + icalcomponent *tail = pvl_data(pvl_tail(parser->components)); + + if (tail){ + insert_error(tail,line, + "Got a data line, but could not find a property name or component begin tag", + ICAL_XLICERRORTYPE_COMPONENTPARSEERROR); + } + tail = 0; + parser->state = ICALPARSER_ERROR; + return 0; + } + + /********************************************************************** + * Handle begin and end of components + **********************************************************************/ + /* If the property name is BEGIN or END, we are actually + starting or ending a new component */ + + + if(strcasecmp(str,"BEGIN") == 0){ + icalcomponent *c; + icalcomponent_kind comp_kind; + + + parser->level++; + str = icalparser_get_next_value(end,&end, value_kind); + + + comp_kind = icalenum_string_to_component_kind(str); + + + if (comp_kind == ICAL_NO_COMPONENT){ + + + c = icalcomponent_new(ICAL_XLICINVALID_COMPONENT); + insert_error(c,str,"Parse error in component name", + ICAL_XLICERRORTYPE_COMPONENTPARSEERROR); + } + + c = icalcomponent_new(comp_kind); + + if (c == 0){ + c = icalcomponent_new(ICAL_XLICINVALID_COMPONENT); + insert_error(c,str,"Parse error in component name", + ICAL_XLICERRORTYPE_COMPONENTPARSEERROR); + } + + pvl_push(parser->components,c); + + parser->state = ICALPARSER_BEGIN_COMP; + return 0; + + } else if (strcasecmp(str,"END") == 0 ) { + icalcomponent* tail; + + parser->level--; + str = icalparser_get_next_value(end,&end, value_kind); + + /* Pop last component off of list and add it to the second-to-last*/ + parser->root_component = pvl_pop(parser->components); + + tail = pvl_data(pvl_tail(parser->components)); + + if(tail != 0){ + icalcomponent_add_component(tail,parser->root_component); + } + + tail = 0; + + /* Return the component if we are back to the 0th level */ + if (parser->level == 0){ + icalcomponent *rtrn; + + if(pvl_count(parser->components) != 0){ + /* There are still components on the stack -- this means + that one of them did not have a proper "END" */ + pvl_push(parser->components,parser->root_component); + icalparser_clean(parser); /* may reset parser->root_component*/ + } + + assert(pvl_count(parser->components) == 0); + + parser->state = ICALPARSER_SUCCESS; + rtrn = parser->root_component; + parser->root_component = 0; + return rtrn; + + } else { + parser->state = ICALPARSER_END_COMP; + return 0; + } + } + + + /* There is no point in continuing if we have not seen a + component yet */ + + if(pvl_data(pvl_tail(parser->components)) == 0){ + parser->state = ICALPARSER_ERROR; + return 0; + } + + + /********************************************************************** + * Handle property names + **********************************************************************/ + + /* At this point, the property name really is a property name, + (Not a component name) so make a new property and add it to + the component */ + + + prop_kind = icalproperty_string_to_kind(str); + + prop = icalproperty_new(prop_kind); + + if (prop != 0){ + icalcomponent *tail = pvl_data(pvl_tail(parser->components)); + + if(prop_kind==ICAL_X_PROPERTY){ + icalproperty_set_x_name(prop,str); + } + + icalcomponent_add_property(tail, prop); + + /* Set the value kind for the default for this type of + property. This may be re-set by a VALUE parameter */ + value_kind = icalproperty_kind_to_value_kind(icalproperty_isa(prop)); + + } else { + icalcomponent* tail = pvl_data(pvl_tail(parser->components)); + + insert_error(tail,str,"Parse error in property name", + ICAL_XLICERRORTYPE_PROPERTYPARSEERROR); + + tail = 0; + parser->state = ICALPARSER_ERROR; + return 0; + } + + /********************************************************************** + * Handle parameter values + **********************************************************************/ + + /* Now, add any parameters to the last property */ + + while(1) { + + if (*(end-1) == ':'){ + /* if the last separator was a ":" and the value is a + URL, icalparser_get_next_parameter will find the + ':' in the URL, so better break now. */ + break; + } + + str = icalparser_get_next_parameter(end,&end); + + if (str != 0){ + char* name; + char* pvalue; + + icalparameter *param = 0; + icalparameter_kind kind; + icalcomponent *tail = pvl_data(pvl_tail(parser->components)); + + name = icalparser_get_param_name(str,&pvalue); + + if (name == 0){ + /* 'tail' defined above */ + insert_error(tail, str, "Cant parse parameter name", + ICAL_XLICERRORTYPE_PARAMETERNAMEPARSEERROR); + tail = 0; + break; + } + + kind = icalparameter_string_to_kind(name); + + if(kind == ICAL_X_PARAMETER){ + param = icalparameter_new(ICAL_X_PARAMETER); + + if(param != 0){ + icalparameter_set_xname(param,name); + icalparameter_set_xvalue(param,pvalue); + } + + + } else if (kind != ICAL_NO_PARAMETER){ + param = icalparameter_new_from_value_string(kind,pvalue); + } else { + /* Error. Failed to parse the parameter*/ + /* 'tail' defined above */ + insert_error(tail, str, "Cant parse parameter name", + ICAL_XLICERRORTYPE_PARAMETERNAMEPARSEERROR); + tail = 0; + parser->state = ICALPARSER_ERROR; + return 0; + } + + if (param == 0){ + /* 'tail' defined above */ + insert_error(tail,str,"Cant parse parameter value", + ICAL_XLICERRORTYPE_PARAMETERVALUEPARSEERROR); + + tail = 0; + parser->state = ICALPARSER_ERROR; + continue; + } + + /* If it is a VALUE parameter, set the kind of value*/ + if (icalparameter_isa(param)==ICAL_VALUE_PARAMETER){ + + value_kind = (icalvalue_kind) + icalparameter_value_to_value_kind( + icalparameter_get_value(param) + ); + + if (value_kind == ICAL_NO_VALUE){ + + /* Ooops, could not parse the value of the + parameter ( it was not one of the defined + values ), so reset the value_kind */ + + insert_error( + tail, str, + "Got a VALUE parameter with an unknown type", + ICAL_XLICERRORTYPE_PARAMETERVALUEPARSEERROR); + icalparameter_free(param); + + value_kind = + icalproperty_kind_to_value_kind( + icalproperty_isa(prop)); + + icalparameter_free(param); + tail = 0; + parser->state = ICALPARSER_ERROR; + return 0; + } + } + + /* Everything is OK, so add the parameter */ + icalproperty_add_parameter(prop,param); + tail = 0; + + } else { /* if ( str != 0) */ + /* If we did not get a param string, go on to looking + for a value */ + break; + } /* if ( str != 0) */ + + } /* while(1) */ + + /********************************************************************** + * Handle values + **********************************************************************/ + + /* Look for values. If there are ',' characters in the values, + then there are multiple values, so clone the current + parameter and add one part of the value to each clone */ + + vcount=0; + while(1) { + str = icalparser_get_next_value(end,&end, value_kind); + + if (str != 0){ + + if (vcount > 0){ + /* Actually, only clone after the second value */ + icalproperty* clone = icalproperty_new_clone(prop); + icalcomponent* tail = pvl_data(pvl_tail(parser->components)); + + icalcomponent_add_property(tail, clone); + prop = clone; + tail = 0; + } + + value = icalvalue_new_from_string(value_kind, str); + + /* Don't add properties without value */ + if (value == 0){ + char temp[200]; /* HACK */ + + icalproperty_kind prop_kind = icalproperty_isa(prop); + icalcomponent* tail = pvl_data(pvl_tail(parser->components)); + + snprintf(temp,sizeof(temp),"Can't parse as %s value in %s property. Removing entire property", + icalvalue_kind_to_string(value_kind), + icalproperty_kind_to_string(prop_kind)); + + insert_error(tail, str, temp, + ICAL_XLICERRORTYPE_VALUEPARSEERROR); + + /* Remove the troublesome property */ + icalcomponent_remove_property(tail,prop); + icalproperty_free(prop); + prop = 0; + tail = 0; + parser->state = ICALPARSER_ERROR; + return 0; + + } else { + vcount++; + icalproperty_set_value(prop, value); + } + + + } else { + if (vcount == 0){ + char temp[200]; /* HACK */ + + icalproperty_kind prop_kind = icalproperty_isa(prop); + icalcomponent *tail = pvl_data(pvl_tail(parser->components)); + + snprintf(temp,sizeof(temp),"No value for %s property. Removing entire property", + icalproperty_kind_to_string(prop_kind)); + + insert_error(tail, str, temp, + ICAL_XLICERRORTYPE_VALUEPARSEERROR); + + /* Remove the troublesome property */ + icalcomponent_remove_property(tail,prop); + icalproperty_free(prop); + prop = 0; + tail = 0; + parser->state = ICALPARSER_ERROR; + return 0; + } else { + + break; + } + } + } + + /**************************************************************** + * End of component parsing. + *****************************************************************/ + + if (pvl_data(pvl_tail(parser->components)) == 0 && + parser->level == 0){ + /* HACK. Does this clause ever get executed? */ + parser->state = ICALPARSER_SUCCESS; + assert(0); + return parser->root_component; + } else { + parser->state = ICALPARSER_IN_PROGRESS; + return 0; + } + +} + +icalparser_state icalparser_get_state(icalparser* parser) +{ + return parser->state; + +} + +icalcomponent* icalparser_clean(icalparser* parser) +{ + icalcomponent *tail; + + icalerror_check_arg_rz((parser != 0 ),"parser"); + + /* We won't get a clean exit if some components did not have an + "END" tag. Clear off any component that may be left in the list */ + + while((tail=pvl_data(pvl_tail(parser->components))) != 0){ + + insert_error(tail," ", + "Missing END tag for this component. Closing component at end of input.", + ICAL_XLICERRORTYPE_COMPONENTPARSEERROR); + + + parser->root_component = pvl_pop(parser->components); + tail=pvl_data(pvl_tail(parser->components)); + + if(tail != 0 && parser->root_component != NULL){ + if(icalcomponent_get_parent(parser->root_component)!=0){ + icalerror_warn("icalparser_clean is trying to attach a component for the second time"); + } else { + icalcomponent_add_component(tail,parser->root_component); + } + } + + } + + return parser->root_component; + +} + +struct slg_data { + const char* pos; + const char* str; +}; + + +char* icalparser_string_line_generator(char *out, size_t buf_size, void *d) +{ + char *n; + size_t size; + struct slg_data* data = (struct slg_data*)d; + + if(data->pos==0){ + data->pos=data->str; + } + + /* If the pointer is at the end of the string, we are done */ + if (*(data->pos)==0){ + return 0; + } + + n = strchr(data->pos,'\n'); + + if (n == 0){ + size = strlen(data->pos); + } else { + n++; /* include newline in output */ + size = (n-data->pos); + } + + if (size > buf_size-1){ + size = buf_size-1; + } + + + strncpy(out,data->pos,size); + + *(out+size) = '\0'; + + data->pos += size; + + return out; +} + +icalcomponent* icalparser_parse_string(const char* str) +{ + icalcomponent *c; + struct slg_data d; + icalparser *p; + + icalerrorstate es = icalerror_get_error_state(ICAL_MALFORMEDDATA_ERROR); + + d.pos = 0; + d.str = str; + + p = icalparser_new(); + icalparser_set_gen_data(p,&d); + + icalerror_set_error_state(ICAL_MALFORMEDDATA_ERROR,ICAL_ERROR_NONFATAL); + + c = icalparser_parse(p,icalparser_string_line_generator); + + icalerror_set_error_state(ICAL_MALFORMEDDATA_ERROR,es); + + icalparser_free(p); + + return c; + +} diff --git a/libkcal/libical/src/libical/icalparser.h b/libkcal/libical/src/libical/icalparser.h new file mode 100644 index 000000000..914f93846 --- /dev/null +++ b/libkcal/libical/src/libical/icalparser.h @@ -0,0 +1,96 @@ +/* -*- Mode: C -*- */ +/*====================================================================== + FILE: icalparser.h + CREATOR: eric 20 April 1999 + + + + (C) COPYRIGHT 2000, Eric Busboom <eric@softwarestudio.org> + http://www.softwarestudio.org + + This program is free software; you can redistribute it and/or modify + it under the terms of either: + + The LGPL as published by the Free Software Foundation, version + 2.1, available at: http://www.fsf.org/copyleft/lesser.html + + Or: + + The Mozilla Public License Version 1.0. You may obtain a copy of + the License at http://www.mozilla.org/MPL/ + + The original code is icalparser.h + +======================================================================*/ + + +#ifndef ICALPARSER_H +#define ICALPARSER_H + +#include "icalenums.h" +#include "icaltypes.h" +#include"icalcomponent.h" + +#include <stdio.h> /* For FILE* */ + +typedef struct icalparser_impl icalparser; + + +/** + * @file icalparser.h + * @brief Line-oriented parsing. + * + * Create a new parser via icalparse_new_parser, then add lines one at + * a time with icalparse_add_line(). icalparser_add_line() will return + * non-zero when it has finished with a component. + */ + +typedef enum icalparser_state { + ICALPARSER_ERROR, + ICALPARSER_SUCCESS, + ICALPARSER_BEGIN_COMP, + ICALPARSER_END_COMP, + ICALPARSER_IN_PROGRESS +} icalparser_state; + +icalparser* icalparser_new(void); +icalcomponent* icalparser_add_line(icalparser* parser, char* str ); +icalcomponent* icalparser_clean(icalparser* parser); +icalparser_state icalparser_get_state(icalparser* parser); +void icalparser_free(icalparser* parser); + + +/** + * Message oriented parsing. icalparser_parse takes a string that + * holds the text ( in RFC 2445 format ) and returns a pointer to an + * icalcomponent. The caller owns the memory. line_gen_func is a + * pointer to a function that returns one content line per invocation + */ + +icalcomponent* icalparser_parse(icalparser *parser, + char* (*line_gen_func)(char *s, size_t size, void *d)); + +/** + Set the data that icalparser_parse will give to the line_gen_func + as the parameter 'd' + */ +void icalparser_set_gen_data(icalparser* parser, void* data); + + +icalcomponent* icalparser_parse_string(const char* str); + + +/*********************************************************************** + * Parser support functions + ***********************************************************************/ + +/** Use the flex/bison parser to turn a string into a value type */ +icalvalue* icalparser_parse_value(icalvalue_kind kind, + const char* str, icalcomponent** errors); + +/** Given a line generator function, return a single iCal content line.*/ +char* icalparser_get_line(icalparser* parser, char* (*line_gen_func)(char *s, size_t size, void *d)); + +char* icalparser_string_line_generator(char *out, size_t buf_size, void *d); + +#endif /* !ICALPARSE_H */ diff --git a/libkcal/libical/src/libical/icalperiod.c b/libkcal/libical/src/libical/icalperiod.c new file mode 100644 index 000000000..0429a2e64 --- /dev/null +++ b/libkcal/libical/src/libical/icalperiod.c @@ -0,0 +1,169 @@ +/* -*- Mode: C -*- + ====================================================================== + FILE: icalperiod.c + CREATOR: eric 02 June 2000 + + + (C) COPYRIGHT 2000, Eric Busboom <eric@softwarestudio.org> + http://www.softwarestudio.org + + This program is free software; you can redistribute it and/or modify + it under the terms of either: + + The LGPL as published by the Free Software Foundation, version + 2.1, available at: http://www.fsf.org/copyleft/lesser.html + + Or: + + The Mozilla Public License Version 1.0. You may obtain a copy of + the License at http://www.mozilla.org/MPL/ + + The Original Code is eric. The Initial Developer of the Original + Code is Eric Busboom + + + ======================================================================*/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "icalperiod.h" + +#include <assert.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> + +#include "icalerror.h" +#include "icalmemory.h" + + + + +struct icalperiodtype icalperiodtype_from_string (const char* str) +{ + + struct icalperiodtype p, null_p; + char *s = icalmemory_strdup(str); + char *start, *end = s; + icalerrorstate es; + + /* Errors are normally generated in the following code, so save + the error state for resoration later */ + + icalerrorenum e = icalerrno; + + p.start = p.end = icaltime_null_time(); + p.duration = icaldurationtype_from_int(0); + + null_p = p; + + if(s == 0) goto error; + + start = s; + end = strchr(s, '/'); + + if(end == 0) goto error; + + *end = 0; + end++; + + p.start = icaltime_from_string(start); + + if (icaltime_is_null_time(p.start)) goto error; + + es = icalerror_get_error_state(ICAL_MALFORMEDDATA_ERROR); + icalerror_set_error_state(ICAL_MALFORMEDDATA_ERROR,ICAL_ERROR_NONFATAL); + + p.end = icaltime_from_string(end); + + icalerror_set_error_state(ICAL_MALFORMEDDATA_ERROR,es); + + + if (icaltime_is_null_time(p.end)){ + + p.duration = icaldurationtype_from_string(end); + + if(icaldurationtype_as_int(p.duration) == 0) goto error; + } + + icalerrno = e; + + icalmemory_free_buffer(s); + + return p; + + error: + icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR); + + if (s) + icalmemory_free_buffer (s); + return null_p; +} + + +const char* icalperiodtype_as_ical_string(struct icalperiodtype p) +{ + + const char* start; + const char* end; + + char *buf; + size_t buf_size = 40; + char* buf_ptr = 0; + + buf = (char*)icalmemory_new_buffer(buf_size); + buf_ptr = buf; + + + start = icaltime_as_ical_string(p.start); + + icalmemory_append_string(&buf, &buf_ptr, &buf_size, start); + + if(!icaltime_is_null_time(p.end)){ + end = icaltime_as_ical_string(p.end); + } else { + end = icaldurationtype_as_ical_string(p.duration); + } + + icalmemory_append_char(&buf, &buf_ptr, &buf_size, '/'); + + icalmemory_append_string(&buf, &buf_ptr, &buf_size, end); + + icalmemory_add_tmp_buffer(buf); + + return buf; +} + + + +struct icalperiodtype icalperiodtype_null_period(void) { + struct icalperiodtype p; + p.start = icaltime_null_time(); + p.end = icaltime_null_time(); + p.duration = icaldurationtype_null_duration(); + + return p; +} +int icalperiodtype_is_null_period(struct icalperiodtype p){ + + if(icaltime_is_null_time(p.start) && + icaltime_is_null_time(p.end) && + icaldurationtype_is_null_duration(p.duration)){ + return 1; + } else { + return 0; + } +} + +int icalperiodtype_is_valid_period(struct icalperiodtype p){ + if(icaltime_is_valid_time(p.start) && + (icaltime_is_valid_time(p.end) || icaltime_is_null_time(p.end)) ) + { + return 1; + } + + return 0; +} + diff --git a/libkcal/libical/src/libical/icalperiod.h b/libkcal/libical/src/libical/icalperiod.h new file mode 100644 index 000000000..af3040d66 --- /dev/null +++ b/libkcal/libical/src/libical/icalperiod.h @@ -0,0 +1,54 @@ +/* -*- Mode: C -*- */ +/*====================================================================== + FILE: icalperiod.h + CREATOR: eric 26 Jan 2001 + + + + (C) COPYRIGHT 2000, Eric Busboom <eric@softwarestudio.org> + http://www.softwarestudio.org + + This program is free software; you can redistribute it and/or modify + it under the terms of either: + + The LGPL as published by the Free Software Foundation, version + 2.1, available at: http://www.fsf.org/copyleft/lesser.html + + Or: + + The Mozilla Public License Version 1.0. You may obtain a copy of + the License at http://www.mozilla.org/MPL/ + + The Original Code is eric. The Initial Developer of the Original + Code is Eric Busboom + + +======================================================================*/ + +#ifndef ICALPERIOD_H +#define ICALPERIOD_H + +#include "icaltime.h" +#include "icalduration.h" + +struct icalperiodtype +{ + struct icaltimetype start; + struct icaltimetype end; + struct icaldurationtype duration; +}; + +struct icalperiodtype icalperiodtype_from_string (const char* str); + +const char* icalperiodtype_as_ical_string(struct icalperiodtype p); + +struct icalperiodtype icalperiodtype_null_period(void); + +int icalperiodtype_is_null_period(struct icalperiodtype p); + +int icalperiodtype_is_valid_period(struct icalperiodtype p); + +#endif /* !ICALTIME_H */ + + + diff --git a/libkcal/libical/src/libical/icalproperty.c b/libkcal/libical/src/libical/icalproperty.c new file mode 100644 index 000000000..67c158ebb --- /dev/null +++ b/libkcal/libical/src/libical/icalproperty.c @@ -0,0 +1,998 @@ +/* -*- Mode: C -*- */ + +/*====================================================================== + FILE: icalproperty.c + CREATOR: eric 28 April 1999 + + + + (C) COPYRIGHT 2000, Eric Busboom <eric@softwarestudio.org> + http://www.softwarestudio.org + + This program is free software; you can redistribute it and/or modify + it under the terms of either: + + The LGPL as published by the Free Software Foundation, version + 2.1, available at: http://www.fsf.org/copyleft/lesser.html + + Or: + + The Mozilla Public License Version 1.0. You may obtain a copy of + the License at http://www.mozilla.org/MPL/ + + The original code is icalproperty.c + +======================================================================*/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "icalproperty.h" +#include "icalparameter.h" +#include "icalcomponent.h" +#include "pvl.h" +#include "icalenums.h" +#include "icalerror.h" +#include "icalmemory.h" +#include "icalparser.h" + +#include <string.h> /* For icalmemory_strdup, rindex */ +#include <assert.h> +#include <stdlib.h> +#include <errno.h> +#include <stdio.h> /* for printf */ +#include <stdarg.h> /* for va_list, va_start, etc. */ + +#ifdef WIN32 +#define snprintf _snprintf +#define strcasecmp stricmp +#endif + +/* Private routines for icalproperty */ +void icalvalue_set_parent(icalvalue* value, + icalproperty* property); +icalproperty* icalvalue_get_parent(icalvalue* value); + +void icalparameter_set_parent(icalparameter* param, + icalproperty* property); +icalproperty* icalparameter_get_parent(icalparameter* value); + + +void icalproperty_set_x_name(icalproperty* prop, const char* name); + +struct icalproperty_impl +{ + char id[5]; + icalproperty_kind kind; + char* x_name; + pvl_list parameters; + pvl_elem parameter_iterator; + icalvalue* value; + icalcomponent *parent; +}; + +void icalproperty_add_parameters(icalproperty* prop, va_list args) +{ + void* vp; + + while((vp = va_arg(args, void*)) != 0) { + + if (icalvalue_isa_value(vp) != 0 ){ + } else if (icalparameter_isa_parameter(vp) != 0 ){ + + icalproperty_add_parameter((icalproperty*)prop, + (icalparameter*)vp); + } else { + icalerror_set_errno(ICAL_BADARG_ERROR); + } + + } +} + + +icalproperty* +icalproperty_new_impl(icalproperty_kind kind) +{ + icalproperty* prop; + + if (!icalproperty_kind_is_valid(kind)) + return NULL; + + if ( ( prop = (icalproperty*) malloc(sizeof(icalproperty))) == 0) { + icalerror_set_errno(ICAL_NEWFAILED_ERROR); + return 0; + } + + strcpy(prop->id,"prop"); + + prop->kind = kind; + prop->parameters = pvl_newlist(); + prop->parameter_iterator = 0; + prop->value = 0; + prop->x_name = 0; + prop->parent = 0; + + return prop; +} + + +icalproperty* +icalproperty_new (icalproperty_kind kind) +{ + if (kind == ICAL_NO_PROPERTY){ + return 0; + } + + return (icalproperty*)icalproperty_new_impl(kind); +} + + +icalproperty* +icalproperty_new_clone(icalproperty* old) +{ + icalproperty *new = icalproperty_new_impl(old->kind); + pvl_elem p; + + icalerror_check_arg_rz((old!=0),"old"); + icalerror_check_arg_rz((new!=0),"new"); + + if (old->value !=0) { + new->value = icalvalue_new_clone(old->value); + } + + if (old->x_name != 0) { + + new->x_name = icalmemory_strdup(old->x_name); + + if (new->x_name == 0) { + icalproperty_free(new); + icalerror_set_errno(ICAL_NEWFAILED_ERROR); + return 0; + } + } + + for(p=pvl_head(old->parameters);p != 0; p = pvl_next(p)){ + icalparameter *param = icalparameter_new_clone(pvl_data(p)); + + if (param == 0){ + icalproperty_free(new); + icalerror_set_errno(ICAL_NEWFAILED_ERROR); + return 0; + } + + pvl_push(new->parameters,param); + + } + + return new; + +} + +icalproperty* icalproperty_new_from_string(const char* str) +{ + + size_t buf_size = 1024; + char* buf = icalmemory_new_buffer(buf_size); + char* buf_ptr = buf; + icalproperty *prop; + icalcomponent *comp; + int errors = 0; + + icalerror_check_arg_rz( (str!=0),"str"); + + /* Is this a HACK or a crafty reuse of code? */ + + icalmemory_append_string(&buf, &buf_ptr, &buf_size, "BEGIN:VCALENDAR\n"); + icalmemory_append_string(&buf, &buf_ptr, &buf_size, str); + icalmemory_append_string(&buf, &buf_ptr, &buf_size, "\n"); + icalmemory_append_string(&buf, &buf_ptr, &buf_size, "END:VCALENDAR\n"); + + comp = icalparser_parse_string(buf); + + if(comp == 0){ + icalerror_set_errno(ICAL_PARSE_ERROR); + return 0; + } + + errors = icalcomponent_count_errors(comp); + + prop = icalcomponent_get_first_property(comp,ICAL_ANY_PROPERTY); + + icalcomponent_remove_property(comp,prop); + + icalcomponent_free(comp); + free(buf); + + if(errors > 0){ + icalproperty_free(prop); + return 0; + } else { + return prop; + } + +} + +void +icalproperty_free (icalproperty* p) +{ + icalparameter* param; + + icalerror_check_arg_rv((p!=0),"prop"); + +#ifdef ICAL_FREE_ON_LIST_IS_ERROR + icalerror_assert( (p->parent ==0),"Tried to free a property that is still attached to a component. "); + +#else + if(p->parent !=0){ + return; + } +#endif + + if (p->value != 0){ + icalvalue_set_parent(p->value,0); + icalvalue_free(p->value); + } + + while( (param = pvl_pop(p->parameters)) != 0){ + icalparameter_free(param); + } + + pvl_free(p->parameters); + + if (p->x_name != 0) { + free(p->x_name); + } + + p->kind = ICAL_NO_PROPERTY; + p->parameters = 0; + p->parameter_iterator = 0; + p->value = 0; + p->x_name = 0; + p->id[0] = 'X'; + + free(p); + +} + + +/* This returns where the start of the next line should be. chars_left does + not include the trailing '\0'. */ +#define MAX_LINE_LEN 75 +/*#define MAX_LINE_LEN 120*/ + +static char* +get_next_line_start (char *line_start, int chars_left) +{ + char *pos; + + /* If we have 74 chars or less left, we can output all of them. + we return a pointer to the '\0' at the end of the string. */ + if (chars_left < MAX_LINE_LEN) { + return line_start + chars_left; + } + + /* Now we jump to the last possible character of the line, and step back + trying to find a ';' ':' or ' '. If we find one, we return the character + after it. */ + pos = line_start + MAX_LINE_LEN - 2; + while (pos > line_start) { + if (*pos == ';' || *pos == ':' || *pos == ' ') { + return pos + 1; + } + pos--; + } + /* Now try to split on a UTF-8 boundary defined as a 7-bit + value or as a byte with the two high-most bits set: + 11xxxxxx. See http://czyborra.com/utf/ */ + + pos = line_start + MAX_LINE_LEN - 1; + while (pos > line_start) { + /* plain ascii */ + if ((*pos & 128) == 0) + return pos; + + /* utf8 escape byte */ + if ((*pos & 192) == 192) + return pos; + + pos--; + } + + /* Give up, just break at 74 chars (the 75th char is the space at + the start of the line). */ + + return line_start + MAX_LINE_LEN - 1; +} + + +/** This splits the property into lines less than 75 octects long (as + * specified in RFC2445). It tries to split after a ';' if it can. + * It returns a tmp buffer. NOTE: I'm not sure if it matters if we + * split a line in the middle of a UTF-8 character. It probably won't + * look nice in a text editor. + */ +static char* +fold_property_line (char *text) +{ + size_t buf_size; + char *buf, *buf_ptr, *line_start, *next_line_start, *out_buf; + int len, chars_left, first_line; + char ch; + + /* Start with a buffer twice the size of our property line, so we almost + certainly won't overflow it. */ + len = strlen (text); + buf_size = len * 2; + buf = icalmemory_new_buffer (buf_size); + buf_ptr = buf; + + /* Step through the text, finding each line to add to the output. */ + line_start = text; + chars_left = len; + first_line = 1; + for (;;) { + if (chars_left <= 0) + break; + + /* This returns the first character for the next line. */ + next_line_start = get_next_line_start (line_start, chars_left); + + /* If this isn't the first line, we need to output a newline and space + first. */ + if (!first_line) { + icalmemory_append_string (&buf, &buf_ptr, &buf_size, "\r\n "); + } + first_line = 0; + + /* This adds the line to our tmp buffer. We temporarily place a '\0' + in text, so we can copy the line in one go. */ + ch = *next_line_start; + *next_line_start = '\0'; + icalmemory_append_string (&buf, &buf_ptr, &buf_size, line_start); + *next_line_start = ch; + + /* Now we move on to the next line. */ + chars_left -= (next_line_start - line_start); + line_start = next_line_start; + } + + /* Copy it to a temporary buffer, and then free it. */ + out_buf = icalmemory_tmp_buffer (strlen (buf) + 1); + strcpy (out_buf, buf); + icalmemory_free_buffer (buf); + + return out_buf; +} + + +/* Determine what VALUE parameter to include. The VALUE parameters + are ignored in the normal parameter printing ( the block after + this one, so we need to do it here */ +static const char * +icalproperty_get_value_kind(icalproperty *prop) +{ + const char* kind_string = 0; + + icalparameter *orig_val_param + = icalproperty_get_first_parameter(prop,ICAL_VALUE_PARAMETER); + + icalvalue *value = icalproperty_get_value(prop); + + icalvalue_kind orig_kind = ICAL_NO_VALUE; + + icalvalue_kind this_kind = ICAL_NO_VALUE; + + icalvalue_kind default_kind + = icalproperty_kind_to_value_kind(prop->kind); + + if(orig_val_param){ + orig_kind = icalparameter_value_to_value_kind( icalparameter_get_value(orig_val_param) ); + } + + if(value != 0){ + this_kind = icalvalue_isa(value); + } + + if ( orig_kind != ICAL_NO_VALUE ) { + kind_string = icalvalue_kind_to_string( orig_kind ); + } else if(this_kind == default_kind && + orig_kind != ICAL_NO_VALUE){ + /* The kind is the default, so it does not need to be + included, but do it anyway, since it was explicit in + the property. But, use the default, not the one + specified in the property */ + + kind_string = icalvalue_kind_to_string(default_kind); + + } else if (this_kind != default_kind && this_kind != ICAL_NO_VALUE){ + /* Not the default, so it must be specified */ + kind_string = icalvalue_kind_to_string(this_kind); + } else { + /* Don'tinclude the VALUE parameter at all */ + } + + return kind_string; +} + +const char* +icalproperty_as_ical_string (icalproperty* prop) +{ + icalparameter *param; + + /* Create new buffer that we can append names, parameters and a + value to, and reallocate as needed. Later, this buffer will be + copied to a icalmemory_tmp_buffer, which is managed internally + by libical, so it can be given to the caller without fear of + the caller forgetting to free it */ + + const char* property_name = 0; + size_t buf_size = 1024; + char* buf = icalmemory_new_buffer(buf_size); + char* buf_ptr = buf; + icalvalue* value; + char *out_buf; + const char* kind_string = 0; + + char newline[] = "\r\n"; + + + icalerror_check_arg_rz( (prop!=0),"prop"); + + + /* Append property name */ + + if (prop->kind == ICAL_X_PROPERTY && prop->x_name != 0){ + property_name = prop->x_name; + } else { + property_name = icalproperty_kind_to_string(prop->kind); + } + + if (property_name == 0 ) { + icalerror_warn("Got a property of an unknown kind."); + icalmemory_free_buffer(buf); + return 0; + + } + + icalmemory_append_string(&buf, &buf_ptr, &buf_size, property_name); + + kind_string = icalproperty_get_value_kind(prop); + if(kind_string!=0){ + icalmemory_append_string(&buf, &buf_ptr, &buf_size, ";VALUE="); + icalmemory_append_string(&buf, &buf_ptr, &buf_size, kind_string); + } + + /* Append parameters */ + for(param = icalproperty_get_first_parameter(prop,ICAL_ANY_PARAMETER); + param != 0; + param = icalproperty_get_next_parameter(prop,ICAL_ANY_PARAMETER)) { + + icalparameter_kind kind = icalparameter_isa(param); + kind_string = icalparameter_as_ical_string(param); + + if(kind==ICAL_VALUE_PARAMETER){ + continue; + } + + if (kind_string == 0 ) { + icalerror_warn("Got a parameter of unknown kind for the following property"); + + icalerror_warn((property_name) ? property_name : "(NULL)"); + continue; + } + + icalmemory_append_string(&buf, &buf_ptr, &buf_size, ";"); + icalmemory_append_string(&buf, &buf_ptr, &buf_size, kind_string); + } + + /* Append value */ + + icalmemory_append_string(&buf, &buf_ptr, &buf_size, ":"); + + value = icalproperty_get_value(prop); + + if (value != 0){ + const char *str = icalvalue_as_ical_string(value); + icalerror_assert((str !=0),"Could not get string representation of a value"); + icalmemory_append_string(&buf, &buf_ptr, &buf_size, str); + } else { + icalmemory_append_string(&buf, &buf_ptr, &buf_size,"ERROR: No Value"); + + } + + icalmemory_append_string(&buf, &buf_ptr, &buf_size, newline); + + /* Now, copy the buffer to a tmp_buffer, which is safe to give to + the caller without worring about de-allocating it. */ + + /* We now use a function to fold the line properly every 75 characters. */ + out_buf = fold_property_line (buf); + + icalmemory_free_buffer(buf); + + return out_buf; +} + + + +icalproperty_kind +icalproperty_isa (icalproperty* p) +{ + if(p != 0){ + return p->kind; + } + + return ICAL_NO_PROPERTY; +} + +int +icalproperty_isa_property (void* property) +{ + icalproperty *impl = (icalproperty *) property; + + icalerror_check_arg_rz( (property!=0), "property"); + if (strcmp(impl->id,"prop") == 0) { + return 1; + } else { + return 0; + } +} + + +void +icalproperty_add_parameter (icalproperty* p,icalparameter* parameter) +{ + icalerror_check_arg_rv( (p!=0),"prop"); + icalerror_check_arg_rv( (parameter!=0),"parameter"); + + pvl_push(p->parameters, parameter); + +} + +void +icalproperty_set_parameter (icalproperty* prop,icalparameter* parameter) +{ + icalparameter_kind kind; + + icalerror_check_arg_rv( (prop!=0),"prop"); + icalerror_check_arg_rv( (parameter!=0),"parameter"); + + kind = icalparameter_isa(parameter); + if (kind != ICAL_X_PARAMETER) + icalproperty_remove_parameter_by_kind(prop,kind); + else + icalproperty_remove_parameter_by_name(prop, + icalparameter_get_xname(parameter)); + + icalproperty_add_parameter(prop,parameter); +} + +void icalproperty_set_parameter_from_string(icalproperty* prop, + const char* name, const char* value) +{ + + icalparameter_kind kind; + icalparameter *param; + + icalerror_check_arg_rv( (prop!=0),"prop"); + icalerror_check_arg_rv( (name!=0),"name"); + icalerror_check_arg_rv( (value!=0),"value"); + + kind = icalparameter_string_to_kind(name); + + if(kind == ICAL_NO_PARAMETER){ + icalerror_set_errno(ICAL_BADARG_ERROR); + return; + } + + param = icalparameter_new_from_value_string(kind,value); + + if (param == 0){ + icalerror_set_errno(ICAL_BADARG_ERROR); + return; + } + + if(kind == ICAL_X_PARAMETER){ + icalparameter_set_xname(param, name); + } + + icalproperty_set_parameter(prop,param); + +} + +const char* icalproperty_get_parameter_as_string(icalproperty* prop, + const char* name) +{ + icalparameter_kind kind; + icalparameter *param; + char* str; + char* pv; + + icalerror_check_arg_rz( (prop!=0),"prop"); + icalerror_check_arg_rz( (name!=0),"name"); + + kind = icalparameter_string_to_kind(name); + + if(kind == ICAL_NO_PARAMETER){ + /* icalenum_string_to_parameter_kind will set icalerrno */ + return 0; + } + + for(param = icalproperty_get_first_parameter(prop,kind); + param != 0; + param = icalproperty_get_next_parameter(prop,kind)) { + if (kind != ICAL_X_PARAMETER) { + break; + } + + if (strcmp(icalparameter_get_xname(param),name)==0) { + break; + } + } + + if (param == 0){ + return 0; + } + + + str = icalparameter_as_ical_string(param); + + pv = strchr(str,'='); + + if(pv == 0){ + icalerror_set_errno(ICAL_INTERNAL_ERROR); + return 0; + } + + return pv+1; + +} + +/** @see icalproperty_remove_parameter_by_kind() + * + * @deprecated Please use icalproperty_remove_parameter_by_kind() + * instead. + */ + +void +icalproperty_remove_parameter(icalproperty* prop, icalparameter_kind kind) +{ + icalproperty_remove_parameter_by_kind(prop, kind); +} + + +/** @brief Remove all parameters with the specified kind. + * + * @param prop A valid icalproperty. + * @param kind The kind to remove (ex. ICAL_TZID_PARAMETER) + * + * See icalproperty_remove_parameter_by_name() and + * icalproperty_remove_parameter_by_ref() for alternate ways of + * removing parameters + */ + +void +icalproperty_remove_parameter_by_kind(icalproperty* prop, icalparameter_kind kind) +{ + pvl_elem p; + + icalerror_check_arg_rv((prop!=0),"prop"); + + for(p=pvl_head(prop->parameters);p != 0; p = pvl_next(p)){ + icalparameter* param = (icalparameter *)pvl_data (p); + if (icalparameter_isa(param) == kind) { + pvl_remove (prop->parameters, p); + icalparameter_free(param); + break; + } + } +} + + +/** @brief Remove all parameters with the specified name. + * + * @param prop A valid icalproperty. + * @param name The name of the parameter to remove + * + * This function removes parameters with the given name. The name + * corresponds to either a built-in name (TZID, etc.) or the name of + * an extended parameter (X-FOO) + * + * See icalproperty_remove_parameter_by_kind() and + * icalproperty_remove_parameter_by_ref() for alternate ways of removing + * parameters + */ + + +void +icalproperty_remove_parameter_by_name(icalproperty* prop, const char *name) +{ + pvl_elem p; + + icalerror_check_arg_rv((prop!=0),"prop"); + + for(p=pvl_head(prop->parameters);p != 0; p = pvl_next(p)){ + icalparameter* param = (icalparameter *)pvl_data (p); + const char * kind_string; + + if (icalparameter_isa(param) == ICAL_X_PARAMETER) + kind_string = icalparameter_get_xname(param); + else + kind_string = icalparameter_kind_to_string(icalparameter_isa(param)); + + if (!kind_string) + continue; + + if (0 == strcmp(kind_string, name)) { + pvl_remove (prop->parameters, p); + break; + } + } +} + + +/** @brief Remove the specified parameter reference from the property. + * + * @param prop A valid icalproperty. + * @param parameter A reference to a specific icalparameter. + * + * This function removes the specified parameter reference from the + * property. + */ + +void +icalproperty_remove_parameter_by_ref(icalproperty* prop, icalparameter* parameter) +{ + pvl_elem p; + icalparameter_kind kind; + const char *name; + + icalerror_check_arg_rv((prop!=0),"prop"); + icalerror_check_arg_rv((parameter!=0),"parameter"); + + kind = icalparameter_isa(parameter); + name = icalparameter_get_xname(parameter); + + /* + * FIXME If it's an X- parameter, also compare the names. It would be nice + * to have a better abstraction like icalparameter_equals() + */ + for(p=pvl_head(prop->parameters);p != 0; p = pvl_next(p)){ + icalparameter* p_param = (icalparameter *)pvl_data (p); + if (icalparameter_isa(p_param) == kind && + (kind != ICAL_X_PARAMETER || + !strcmp(icalparameter_get_xname(p_param), name))) { + pvl_remove (prop->parameters, p); + icalparameter_free(p_param); + break; + } + } +} + + +int +icalproperty_count_parameters (const icalproperty* prop) +{ + if(prop != 0){ + return pvl_count(prop->parameters); + } + + icalerror_set_errno(ICAL_USAGE_ERROR); + return -1; +} + + +icalparameter* +icalproperty_get_first_parameter(icalproperty* p, icalparameter_kind kind) +{ + icalerror_check_arg_rz( (p!=0),"prop"); + + p->parameter_iterator = pvl_head(p->parameters); + + if (p->parameter_iterator == 0) { + return 0; + } + + for( p->parameter_iterator = pvl_head(p->parameters); + p->parameter_iterator !=0; + p->parameter_iterator = pvl_next(p->parameter_iterator)){ + + icalparameter *param = (icalparameter*)pvl_data(p->parameter_iterator); + + if(icalparameter_isa(param) == kind || kind == ICAL_ANY_PARAMETER){ + return param; + } + } + + return 0; +} + + +icalparameter* +icalproperty_get_next_parameter (icalproperty* p, icalparameter_kind kind) +{ + icalerror_check_arg_rz( (p!=0),"prop"); + + if (p->parameter_iterator == 0) { + return 0; + } + + for( p->parameter_iterator = pvl_next(p->parameter_iterator); + p->parameter_iterator !=0; + p->parameter_iterator = pvl_next(p->parameter_iterator)){ + + icalparameter *param = (icalparameter*)pvl_data(p->parameter_iterator); + + if(icalparameter_isa(param) == kind || kind == ICAL_ANY_PARAMETER){ + return param; + } + } + + return 0; + +} + +void +icalproperty_set_value (icalproperty* p, icalvalue* value) +{ + icalerror_check_arg_rv((p !=0),"prop"); + icalerror_check_arg_rv((value !=0),"value"); + + if (p->value != 0){ + icalvalue_set_parent(p->value,0); + icalvalue_free(p->value); + p->value = 0; + } + + p->value = value; + + icalvalue_set_parent(value,p); +} + + +void icalproperty_set_value_from_string(icalproperty* prop,const char* str, + const char* type) +{ + icalvalue *oval,*nval; + icalvalue_kind kind = ICAL_NO_VALUE; + + icalerror_check_arg_rv( (prop!=0),"prop"); + icalerror_check_arg_rv( (str!=0),"str"); + icalerror_check_arg_rv( (type!=0),"type"); + + if(strcmp(type,"NO")==0){ + /* Get the type from the value the property already has, if it exists */ + oval = icalproperty_get_value(prop); + if(oval != 0){ + /* Use the existing value kind */ + kind = icalvalue_isa(oval); + } else { + /* Use the default kind for the property */ + kind = icalproperty_kind_to_value_kind(icalproperty_isa(prop)); + } + } else { + /* Use the given kind string */ + kind = icalvalue_string_to_kind(type); + } + + if(kind == ICAL_NO_VALUE){ + icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR); + return; + } + + nval = icalvalue_new_from_string(kind, str); + + if(nval == 0){ + /* icalvalue_new_from_string sets errno */ + assert(icalerrno != ICAL_NO_ERROR); + return; + } + + icalproperty_set_value(prop,nval); + + +} + +icalvalue* +icalproperty_get_value(const icalproperty* prop) +{ + icalerror_check_arg_rz( (prop!=0),"prop"); + + return prop->value; +} + +const char* icalproperty_get_value_as_string(const icalproperty* prop) +{ + icalvalue *value; + + icalerror_check_arg_rz( (prop!=0),"prop"); + + value = prop->value; + + return icalvalue_as_ical_string(value); +} + + +void icalproperty_set_x_name(icalproperty* prop, const char* name) +{ + icalerror_check_arg_rv( (name!=0),"name"); + icalerror_check_arg_rv( (prop!=0),"prop"); + + if (prop->x_name != 0) { + free(prop->x_name); + } + + prop->x_name = icalmemory_strdup(name); + + if(prop->x_name == 0){ + icalerror_set_errno(ICAL_NEWFAILED_ERROR); + } + +} + +const char* icalproperty_get_x_name(icalproperty* prop){ + icalerror_check_arg_rz( (prop!=0),"prop"); + + return prop->x_name; +} + + +const char* icalproperty_get_name(const icalproperty* prop) +{ +#ifndef NO_WARN_DEPRECATED + icalerror_warn("icalproperty_get_name() is DEPRECATED, please use icalproperty_get_property_name() instead."); +#endif + return icalproperty_get_property_name(prop); +} + +const char* icalproperty_get_property_name(const icalproperty* prop) +{ + + const char* property_name = 0; + size_t buf_size = 256; + char* buf = icalmemory_new_buffer(buf_size); + char* buf_ptr = buf; + + icalerror_check_arg_rz( (prop!=0),"prop"); + + if (prop->kind == ICAL_X_PROPERTY && prop->x_name != 0){ + property_name = prop->x_name; + } else { + property_name = icalproperty_kind_to_string(prop->kind); + } + + if (property_name == 0 ) { + icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR); + return 0; + + } else { + /* _append_string will automatically grow the buffer if + property_name is longer than the initial buffer size */ + icalmemory_append_string(&buf, &buf_ptr, &buf_size, property_name); + } + + /* Add the buffer to the temporary buffer ring -- the caller will + not have to free the memory. */ + icalmemory_add_tmp_buffer(buf); + + return buf; +} + + + + +void icalproperty_set_parent(icalproperty* property, + icalcomponent* component) +{ + icalerror_check_arg_rv( (property!=0),"property"); + + property->parent = component; +} + +icalcomponent* icalproperty_get_parent(const icalproperty* property) +{ + icalerror_check_arg_rz( (property!=0),"property"); + + return property->parent; +} diff --git a/libkcal/libical/src/libical/icalproperty.h b/libkcal/libical/src/libical/icalproperty.h new file mode 100644 index 000000000..b7db33169 --- /dev/null +++ b/libkcal/libical/src/libical/icalproperty.h @@ -0,0 +1,133 @@ +/* -*- Mode: C -*- */ +/*====================================================================== + FILE: icalproperty.h + CREATOR: eric 20 March 1999 + + + + + + (C) COPYRIGHT 2000, Eric Busboom <eric@softwarestudio.org> + http://www.softwarestudio.org + + This program is free software; you can redistribute it and/or modify + it under the terms of either: + + The LGPL as published by the Free Software Foundation, version + 2.1, available at: http://www.fsf.org/copyleft/lesser.html + + Or: + + The Mozilla Public License Version 1.0. You may obtain a copy of + the License at http://www.mozilla.org/MPL/ + + The original code is icalparam.h + + ======================================================================*/ + + +#ifndef ICALPROPERTY_H +#define ICALPROPERTY_H + +#include <time.h> +#include <stdarg.h> /* for va_... */ + +#include "icalderivedparameter.h" + +#include "icalvalue.h" +#include "icalrecur.h" + +/* Actually in icalderivedproperty.h: + typedef struct icalproperty_impl icalproperty; */ + +#include "icalderivedproperty.h" /* To get icalproperty_kind enumerations */ + +icalproperty* icalproperty_new(icalproperty_kind kind); + +icalproperty* icalproperty_new_clone(icalproperty * prop); + +icalproperty* icalproperty_new_from_string(const char* str); + +const char* icalproperty_as_ical_string(icalproperty* prop); + +void icalproperty_free(icalproperty* prop); + +icalproperty_kind icalproperty_isa(icalproperty* property); +int icalproperty_isa_property(void* property); + +void icalproperty_add_parameters(struct icalproperty_impl *prop,va_list args); +void icalproperty_add_parameter(icalproperty* prop,icalparameter* parameter); +void icalproperty_set_parameter(icalproperty* prop,icalparameter* parameter); +void icalproperty_set_parameter_from_string(icalproperty* prop, + const char* name, const char* value); +const char* icalproperty_get_parameter_as_string(icalproperty* prop, + const char* name); + +void icalproperty_remove_parameter(icalproperty* prop, + icalparameter_kind kind); + +void icalproperty_remove_parameter_by_kind(icalproperty* prop, + icalparameter_kind kind); + +void icalproperty_remove_parameter_by_name(icalproperty* prop, + const char *name); + +void icalproperty_remove_parameter_by_ref(icalproperty* prop, + icalparameter *param); + + + +int icalproperty_count_parameters(const icalproperty* prop); + +/* Iterate through the parameters */ +icalparameter* icalproperty_get_first_parameter(icalproperty* prop, + icalparameter_kind kind); +icalparameter* icalproperty_get_next_parameter(icalproperty* prop, + icalparameter_kind kind); +/* Access the value of the property */ +void icalproperty_set_value(icalproperty* prop, icalvalue* value); +void icalproperty_set_value_from_string(icalproperty* prop,const char* value, const char* kind); + +icalvalue* icalproperty_get_value(const icalproperty* prop); +const char* icalproperty_get_value_as_string(const icalproperty* prop); + +/* Deal with X properties */ + +void icalproperty_set_x_name(icalproperty* prop, const char* name); +const char* icalproperty_get_x_name(icalproperty* prop); + +/** Return the name of the property -- the type name converted to a + * string, or the value of _get_x_name if the type is and X + * property + */ +const char* icalproperty_get_property_name (const icalproperty* prop); + +icalvalue_kind icalparameter_value_to_value_kind(icalparameter_value value); + +/* Convert kinds to string and get default value type */ + +icalvalue_kind icalproperty_kind_to_value_kind(icalproperty_kind kind); +icalproperty_kind icalproperty_value_kind_to_kind(icalvalue_kind kind); +const char* icalproperty_kind_to_string(icalproperty_kind kind); +icalproperty_kind icalproperty_string_to_kind(const char* string); + +/** Check validity of a specific icalproperty_kind **/ +int icalproperty_kind_is_valid(const icalproperty_kind kind); + +icalproperty_method icalproperty_string_to_method(const char* str); +const char* icalproperty_method_to_string(icalproperty_method method); + + +const char* icalproperty_enum_to_string(int e); +int icalproperty_string_to_enum(const char* str); +int icalproperty_kind_and_string_to_enum(const int kind, const char* str); + +const char* icalproperty_status_to_string(icalproperty_status); +icalproperty_status icalproperty_string_to_status(const char* string); + +int icalproperty_enum_belongs_to_property(icalproperty_kind kind, int e); + + + + +#endif /*ICALPROPERTY_H*/ diff --git a/libkcal/libical/src/libical/icalrecur.c b/libkcal/libical/src/libical/icalrecur.c new file mode 100644 index 000000000..acd2ccced --- /dev/null +++ b/libkcal/libical/src/libical/icalrecur.c @@ -0,0 +1,2400 @@ +/* -*- Mode: C -*- + ====================================================================== + FILE: icalrecur.c + CREATOR: eric 16 May 2000 + + + + (C) COPYRIGHT 2000, Eric Busboom <eric@softwarestudio.org> + http://www.softwarestudio.org + + This program is free software; you can redistribute it and/or modify + it under the terms of either: + + The LGPL as published by the Free Software Foundation, version + 2.1, available at: http://www.fsf.org/copyleft/lesser.html + + Or: + + The Mozilla Public License Version 1.0. You may obtain a copy of + the License at http://www.mozilla.org/MPL/ +*/ + +/** + @file icalrecur.c + @brief Implementation of routines for dealing with recurring time + + How this code works: + + Processing starts when the caller generates a new recurrence + iterator via icalrecur_iterator_new(). This routine copies the + recurrence rule into the iterator and extracts things like start and + end dates. Then, it checks if the rule is legal, using some logic + from RFC2445 and some logic that probably should be in RFC2445. + + Then, icalrecur_iterator_new() re-writes some of the BY* + arrays. This involves ( via a call to setup_defaults() ) : + + 1) For BY rule parts with no data ( ie BYSECOND was not specified ) + copy the corresponding time part from DTSTART into the BY array. ( + So impl->by_ptrs[BY_SECOND] will then have one element if is + originally had none ) This only happens if the BY* rule part data + would expand the number of occurrences in the occurrence set. This + lets the code ignore DTSTART later on and still use it to get the + time parts that were not specified in any other way. + + 2) For the by rule part that are not the same interval as the + frequency -- for HOURLY anything but BYHOUR, for instance -- copy the + first data element from the rule part into the first occurrence. For + example, for "INTERVAL=MONTHLY and BYHOUR=10,30", initialize the + first time to be returned to have an hour of 10. + + Finally, for INTERVAL=YEARLY, the routine expands the rule to get + all of the days specified in the rule. The code will do this for + each new year, and this is the first expansion. This is a special + case for the yearly interval; no other frequency gets expanded this + way. The yearly interval is the most complex, so some special + processing is required. + + After creating a new iterator, the caller will make successive calls + to icalrecur_iterator_next() to get the next time specified by the + rule. The main part of this routine is a switch on the frequency of + the rule. Each different frequency is handled by a different + routine. + + For example, next_hour handles the case of INTERVAL=HOURLY, and it + is called by other routines to get the next hour. First, the routine + tries to get the next minute part of a time with a call to + next_minute(). If next_minute() returns 1, it has reached the end of + its data, usually the last element of the BYMINUTE array. Then, if + there is data in the BYHOUR array, the routine changes the hour to + the next one in the array. If INTERVAL=HOURLY, the routine advances + the hour by the interval. + + If the routine used the last hour in the BYHOUR array, and the + INTERVAL=HOURLY, then the routine calls increment_monthday() to set + the next month day. The increment_* routines may call higher routine + to increment the month or year also. + + The code for INTERVAL=DAILY is handled by next_day(). First, the + routine tries to get the next hour part of a time with a call to + next_hour. If next_hour() returns 1, it has reached the end of its + data, usually the last element of the BYHOUR array. This means that + next_day() should increment the time to the next day. If FREQUENCY==DAILY, + the routine increments the day by the interval; otherwise, it + increments the day by 1. + + Next_day() differs from next_hour because it does not use the BYDAY + array to select an appropriate day. Instead, it returns every day ( + incrementing by 1 if the frequency is not DAILY with INTERVAL!=1) + Any days that are not specified in an non-empty BYDAY array are + filtered out later. + + Generally, the flow of these routine is for a next_* call a next_* + routine of a lower interval ( next_day calls next_hour) and then to + possibly call an increment_* routine of an equal or higher + interval. ( next_day calls increment_monthday() ) + + When the call to the original next_* routine returns, + icalrecur_iterator_next() will check the returned data against other + BYrule parts to determine if is should be excluded by calling + check_contracting_rules. Generally, a contracting rule is any with a + larger time span than the interval. For instance, if + INTERVAL=DAILY, BYMONTH is a contracting rule part. + + Check_contracting_rules() uses icalrecur_check_rulepart() to do its + work. icalrecur_check_rulepart() uses expand_map[] to determine if a rule + is contracting, and if it is, and if the BY rule part has some data, + then the routine checks if the value of a component of the time is + part of the byrule part. For instance, for "INTERVAL=DAILY; + BYMONTH=6,10", icalrecur_check_rulepart() would check that the time value + given to it has a month of either 6 or 10. + + Finally, icalrecur_iterator_next() does a few other checks on the + time value, and if it passes, it returns the time. + + A note about the end_of_data flag. The flag indicates that the + routine is at the end of its data -- the last BY rule if the routine + is using by rules, or the last day of the week/month/year/etc if + not. + + This flag is usually set early in a next_* routine and returned in + the end. The way it is used allows the next_* routine to set the + last time back to the first element in a BYxx rule, and then signal + to the higer level routine to increment the next higher level. For + instance. WITH FREQ=MONTHLY;BYDAY=TU,FR, After next_weekday_by_month + runs though both TU and FR, it sets the week day back to TU and sets + end_of_data to 1x. This signals next_month to increment the month. + + + ======================================================================*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDINT_H +#include <stdint.h> +#endif + +#include "icalrecur.h" + +#include "icalerror.h" +#include "icalmemory.h" + +#include <stdlib.h> /* for malloc */ +#include <errno.h> /* for errno */ +#include <string.h> /* for strdup and strchr*/ +#include <assert.h> +#include <stddef.h> /* For offsetof() macro */ + +#include "pvl.h" + +/** This is the last year we will go up to, since 32-bit time_t values + only go up to the start of 2038. */ +#define MAX_TIME_T_YEAR 2037 + +#define TEMP_MAX 1024 + + +#define BYDAYIDX impl->by_indices[BY_DAY] +#define BYDAYPTR impl->by_ptrs[BY_DAY] + +#define BYMONIDX impl->by_indices[BY_MONTH] +#define BYMONPTR impl->by_ptrs[BY_MONTH] + +#define BYMDIDX impl->by_indices[BY_MONTH_DAY] +#define BYMDPTR impl->by_ptrs[BY_MONTH_DAY] + +#define BYWEEKIDX impl->by_indices[BY_WEEK_NO] +#define BYWEEKPTR impl->by_ptrs[BY_WEEK_NO] + +const char* icalrecur_freq_to_string(icalrecurrencetype_frequency kind); +icalrecurrencetype_frequency icalrecur_string_to_freq(const char* str); + +const char* icalrecur_weekday_to_string(icalrecurrencetype_weekday kind); +icalrecurrencetype_weekday icalrecur_string_to_weekday(const char* str); + + +/*********************** Rule parsing routines ************************/ + +struct icalrecur_parser { + const char* rule; + char* copy; + char* this_clause; + char* next_clause; + + struct icalrecurrencetype rt; +}; + +const char* icalrecur_first_clause(struct icalrecur_parser *parser) +{ + char *idx; + parser->this_clause = parser->copy; + + idx = strchr(parser->this_clause,';'); + + if (idx == 0){ + parser->next_clause = 0; + return 0; + } + + *idx = 0; + idx++; + parser->next_clause = idx; + + return parser->this_clause; + +} + +const char* icalrecur_next_clause(struct icalrecur_parser *parser) +{ + char* idx; + + parser->this_clause = parser->next_clause; + + if(parser->this_clause == 0){ + return 0; + } + + idx = strchr(parser->this_clause,';'); + + if (idx == 0){ + parser->next_clause = 0; + } else { + + *idx = 0; + idx++; + parser->next_clause = idx; + } + + return parser->this_clause; + +} + +void icalrecur_clause_name_and_value(struct icalrecur_parser *parser, + char** name, char** value) +{ + char *idx; + + *name = parser->this_clause; + + idx = strchr(parser->this_clause,'='); + + if (idx == 0){ + *name = 0; + *value = 0; + return; + } + + *idx = 0; + idx++; + *value = idx; +} + +void icalrecur_add_byrules(struct icalrecur_parser *parser, short *array, + int size, char* vals) +{ + char *t, *n; + int i=0; + int sign = 1; + int v; + (void)parser; + + n = vals; + + while(n != 0){ + + if(i == size){ + return; + } + + t = n; + + n = strchr(t,','); + + if(n != 0){ + *n = 0; + n++; + } + + /* Get optional sign. HACK. sign is not allowed for all BYxxx + rule parts */ + if( *t == '-'){ + sign = -1; + t++; + } else if (*t == '+'){ + sign = 1; + t++; + } else { + sign = 1; + } + + v = atoi(t) * sign ; + + + array[i++] = (short)v; + array[i] = ICAL_RECURRENCE_ARRAY_MAX; + + } + +} + +void icalrecur_add_bydayrules(struct icalrecur_parser *parser, const char* vals) +{ + + char *t, *n; + int i=0; + int sign = 1; + int weekno = 0; + icalrecurrencetype_weekday wd; + short *array = parser->rt.by_day; + char* end; + char* vals_copy; + + vals_copy = icalmemory_strdup(vals); + + end = (char*)vals_copy+strlen(vals_copy); + n = vals_copy; + + while(n != 0){ + + + t = n; + + n = strchr(t,','); + + if(n != 0){ + *n = 0; + n++; + } + + /* Get optional sign. */ + if( *t == '-'){ + sign = -1; + t++; + } else if (*t == '+'){ + sign = 1; + t++; + } else { + sign = 1; + } + + /* Get Optional weekno */ + weekno = strtol(t,&t,10); + + /* Outlook/Exchange generate "BYDAY=MO, FR" and "BYDAY=2 TH". + * Cope with that. + */ + if (*t == ' ') + t++; + + wd = icalrecur_string_to_weekday(t); + + array[i++] = (short)(sign* (wd + 8*weekno)); + array[i] = ICAL_RECURRENCE_ARRAY_MAX; + + } + + free(vals_copy); + +} + + +struct icalrecurrencetype icalrecurrencetype_from_string(const char* str) +{ + struct icalrecur_parser parser; + + memset(&parser,0,sizeof(parser)); + icalrecurrencetype_clear(&parser.rt); + + icalerror_check_arg_re(str!=0,"str",parser.rt); + + + /* Set up the parser struct */ + parser.rule = str; + parser.copy = icalmemory_strdup(parser.rule); + parser.this_clause = parser.copy; + + if(parser.copy == 0){ + icalerror_set_errno(ICAL_NEWFAILED_ERROR); + return parser.rt; + } + + /* Loop through all of the clauses */ + for(icalrecur_first_clause(&parser); + parser.this_clause != 0; + icalrecur_next_clause(&parser)) + { + char *name, *value; + icalrecur_clause_name_and_value(&parser,&name,&value); + + if(name == 0){ + icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR); + icalrecurrencetype_clear(&parser.rt); + free(parser.copy); + return parser.rt; + } + + if (strcasecmp(name,"FREQ") == 0){ + parser.rt.freq = icalrecur_string_to_freq(value); + } else if (strcasecmp(name,"COUNT") == 0){ + parser.rt.count = atoi(value); + } else if (strcasecmp(name,"UNTIL") == 0){ + parser.rt.until = icaltime_from_string(value); + } else if (strcasecmp(name,"INTERVAL") == 0){ + parser.rt.interval = (short)atoi(value); + } else if (strcasecmp(name,"WKST") == 0){ + parser.rt.week_start = icalrecur_string_to_weekday(value); + } else if (strcasecmp(name,"BYSECOND") == 0){ + icalrecur_add_byrules(&parser,parser.rt.by_second, + ICAL_BY_SECOND_SIZE,value); + } else if (strcasecmp(name,"BYMINUTE") == 0){ + icalrecur_add_byrules(&parser,parser.rt.by_minute, + ICAL_BY_MINUTE_SIZE,value); + } else if (strcasecmp(name,"BYHOUR") == 0){ + icalrecur_add_byrules(&parser,parser.rt.by_hour, + ICAL_BY_HOUR_SIZE,value); + } else if (strcasecmp(name,"BYDAY") == 0){ + icalrecur_add_bydayrules(&parser,value); + } else if (strcasecmp(name,"BYMONTHDAY") == 0){ + icalrecur_add_byrules(&parser,parser.rt.by_month_day, + ICAL_BY_MONTHDAY_SIZE,value); + } else if (strcasecmp(name,"BYYEARDAY") == 0){ + icalrecur_add_byrules(&parser,parser.rt.by_year_day, + ICAL_BY_YEARDAY_SIZE,value); + } else if (strcasecmp(name,"BYWEEKNO") == 0){ + icalrecur_add_byrules(&parser,parser.rt.by_week_no, + ICAL_BY_WEEKNO_SIZE,value); + } else if (strcasecmp(name,"BYMONTH") == 0){ + icalrecur_add_byrules(&parser,parser.rt.by_month, + ICAL_BY_MONTH_SIZE,value); + } else if (strcasecmp(name,"BYSETPOS") == 0){ + icalrecur_add_byrules(&parser,parser.rt.by_set_pos, + ICAL_BY_SETPOS_SIZE,value); + } else { + icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR); + icalrecurrencetype_clear(&parser.rt); + free(parser.copy); + return parser.rt; + } + + } + + free(parser.copy); + + return parser.rt; + +} + +static struct { const char* str;size_t offset; int limit; } recurmap[] = +{ + {";BYSECOND=",offsetof(struct icalrecurrencetype,by_second),60}, + {";BYMINUTE=",offsetof(struct icalrecurrencetype,by_minute),60}, + {";BYHOUR=",offsetof(struct icalrecurrencetype,by_hour),24}, + {";BYDAY=",offsetof(struct icalrecurrencetype,by_day),7}, + {";BYMONTHDAY=",offsetof(struct icalrecurrencetype,by_month_day),31}, + {";BYYEARDAY=",offsetof(struct icalrecurrencetype,by_year_day),366}, + {";BYWEEKNO=",offsetof(struct icalrecurrencetype,by_week_no),52}, + {";BYMONTH=",offsetof(struct icalrecurrencetype,by_month),12}, + {";BYSETPOS=",offsetof(struct icalrecurrencetype,by_set_pos),366}, + {0,0,0}, +}; + +/* A private routine in icalvalue.c */ +void print_date_to_string(char* str, struct icaltimetype *data); +void print_datetime_to_string(char* str, struct icaltimetype *data); + +char* icalrecurrencetype_as_string(struct icalrecurrencetype *recur) +{ + char* str; + char *str_p; + size_t buf_sz = 200; + char temp[20]; + int i,j; + + if(recur->freq == ICAL_NO_RECURRENCE){ + return 0; + } + + str = (char*)icalmemory_tmp_buffer(buf_sz); + str_p = str; + + icalmemory_append_string(&str,&str_p,&buf_sz,"FREQ="); + icalmemory_append_string(&str,&str_p,&buf_sz, + icalrecur_freq_to_string(recur->freq)); + + if(recur->until.year != 0){ + + temp[0] = 0; + if (recur->until.is_date) + print_date_to_string(temp,&(recur->until)); + else + print_datetime_to_string(temp,&(recur->until)); + + icalmemory_append_string(&str,&str_p,&buf_sz,";UNTIL="); + icalmemory_append_string(&str,&str_p,&buf_sz, temp); + } + + if(recur->count != 0){ + snprintf(temp,sizeof(temp),"%d",recur->count); + icalmemory_append_string(&str,&str_p,&buf_sz,";COUNT="); + icalmemory_append_string(&str,&str_p,&buf_sz, temp); + } + + if(recur->interval != 1){ + snprintf(temp,sizeof(temp),"%d",recur->interval); + icalmemory_append_string(&str,&str_p,&buf_sz,";INTERVAL="); + icalmemory_append_string(&str,&str_p,&buf_sz, temp); + } + + for(j =0; recurmap[j].str != 0; j++){ + short* array = (short*)(recurmap[j].offset+ (size_t)recur); + int limit = recurmap[j].limit; + + /* Skip unused arrays */ + if( array[0] != ICAL_RECURRENCE_ARRAY_MAX ) { + + icalmemory_append_string(&str,&str_p,&buf_sz,recurmap[j].str); + + for(i=0; + i< limit && array[i] != ICAL_RECURRENCE_ARRAY_MAX; + i++){ + if (j == 3) { /* BYDAY */ + const char *daystr = icalrecur_weekday_to_string( + icalrecurrencetype_day_day_of_week(array[i])); + int pos; + + pos = icalrecurrencetype_day_position(array[i]); + + if (pos == 0) + icalmemory_append_string(&str,&str_p,&buf_sz,daystr); + else { + snprintf(temp,sizeof(temp),"%d%s",pos,daystr); + icalmemory_append_string(&str,&str_p,&buf_sz,temp); + } + + } else { + snprintf(temp,sizeof(temp),"%d",array[i]); + icalmemory_append_string(&str,&str_p,&buf_sz, temp); + } + + if( (i+1)<limit &&array[i+1] + != ICAL_RECURRENCE_ARRAY_MAX){ + icalmemory_append_char(&str,&str_p,&buf_sz,','); + } + } + } + } + + /* Monday is the default, so no need to write that out */ + if ( recur->week_start != ICAL_MONDAY_WEEKDAY ) { + const char *daystr = icalrecur_weekday_to_string( + icalrecurrencetype_day_day_of_week( recur->week_start )); + icalmemory_append_string(&str,&str_p,&buf_sz,";WKST="); + icalmemory_append_string(&str,&str_p,&buf_sz,daystr); + } + + return str; +} + + +/************************* occurrence iteration routiens ******************/ + +enum byrule { + NO_CONTRACTION = -1, + BY_SECOND = 0, + BY_MINUTE = 1, + BY_HOUR = 2, + BY_DAY = 3, + BY_MONTH_DAY = 4, + BY_YEAR_DAY = 5, + BY_WEEK_NO = 6, + BY_MONTH = 7, + BY_SET_POS +}; + + + +struct icalrecur_iterator_impl { + + struct icaltimetype dtstart; /* Hack. Make into time_t */ + struct icaltimetype last; /* last time return from _iterator_next*/ + int occurrence_no; /* number of step made on t iterator */ + struct icalrecurrencetype rule; + + short days[366]; + short days_index; + + enum byrule byrule; + short by_indices[9]; + short orig_data[9]; /**< 1 if there was data in the byrule */ + + + short *by_ptrs[9]; /**< Pointers into the by_* array elements of the rule */ + +}; + +static void increment_year(icalrecur_iterator* impl, int inc); + +int icalrecur_iterator_sizeof_byarray(short* byarray) +{ + int array_itr; + + for(array_itr = 0; + byarray[array_itr] != ICAL_RECURRENCE_ARRAY_MAX; + array_itr++){ + } + + return array_itr; +} + +enum expand_table { + UNKNOWN = 0, + CONTRACT = 1, + EXPAND =2, + ILLEGAL=3 +}; + +/** + * The split map indicates, for a particular interval, wether a BY_* + * rule part expands the number of instances in the occcurrence set or + * contracts it. 1=> contract, 2=>expand, and 3 means the pairing is + * not allowed. + */ + +struct expand_split_map_struct +{ + icalrecurrencetype_frequency frequency; + + /* Elements of the 'map' array correspond to the BYxxx rules: + Second,Minute,Hour,Day,Month Day,Year Day,Week No,Month*/ + + short map[8]; +}; + +static struct expand_split_map_struct expand_map[] = +{ + {ICAL_SECONDLY_RECURRENCE,{1,1,1,1,1,1,1,1}}, + {ICAL_MINUTELY_RECURRENCE,{2,1,1,1,1,1,1,1}}, + {ICAL_HOURLY_RECURRENCE, {2,2,1,1,1,1,1,1}}, + {ICAL_DAILY_RECURRENCE, {2,2,2,1,1,1,1,1}}, + {ICAL_WEEKLY_RECURRENCE, {2,2,2,2,3,3,1,1}}, + {ICAL_MONTHLY_RECURRENCE, {2,2,2,2,2,3,3,1}}, + {ICAL_YEARLY_RECURRENCE, {2,2,2,2,2,2,2,2}}, + {ICAL_NO_RECURRENCE, {0,0,0,0,0,0,0,0}} + +}; + + + +/** Check that the rule has only the two given interday byrule parts. */ +static +int icalrecur_two_byrule(icalrecur_iterator* impl, + enum byrule one,enum byrule two) +{ + short test_array[9]; + enum byrule itr; + int passes = 0; + + memset(test_array,0,sizeof(test_array)); + + test_array[one] = 1; + test_array[two] = 1; + + for(itr = BY_DAY; itr != BY_SET_POS; itr++){ + + if( (test_array[itr] == 0 && + impl->by_ptrs[itr][0] != ICAL_RECURRENCE_ARRAY_MAX + ) || + (test_array[itr] == 1 && + impl->by_ptrs[itr][0] == ICAL_RECURRENCE_ARRAY_MAX + ) + ) { + /* test failed */ + passes = 0; + } + } + + return passes; + +} + +/** Check that the rule has only the one given interdat byrule parts. */ +static int icalrecur_one_byrule(icalrecur_iterator* impl,enum byrule one) +{ + int passes = 1; + enum byrule itr; + + for(itr = BY_DAY; itr != BY_SET_POS; itr++){ + + if ((itr==one && impl->by_ptrs[itr][0] == ICAL_RECURRENCE_ARRAY_MAX) || + (itr!=one && impl->by_ptrs[itr][0] != ICAL_RECURRENCE_ARRAY_MAX)) { + passes = 0; + } + } + + return passes; +} + +/*static int count_byrules(icalrecur_iterator* impl) +{ + int count = 0; + enum byrule itr; + + for(itr = BY_DAY; itr <= BY_SET_POS; itr++){ + if(impl->by_ptrs[itr][0] != ICAL_RECURRENCE_ARRAY_MAX){ + count++; + } + } + + return count; +}*/ + + +static void setup_defaults(icalrecur_iterator* impl, + enum byrule byrule, icalrecurrencetype_frequency req, + int deftime, int *timepart) +{ + + icalrecurrencetype_frequency freq; + freq = impl->rule.freq; + + /* Re-write the BY rule arrays with data from the DTSTART time so + we don't have to explicitly deal with DTSTART */ + + if(impl->by_ptrs[byrule][0] == ICAL_RECURRENCE_ARRAY_MAX && + expand_map[freq].map[byrule] != CONTRACT){ + impl->by_ptrs[byrule][0] = (short)deftime; + } + + /* Initialize the first occurrence */ + if( freq != req && expand_map[freq].map[byrule] != CONTRACT){ + *timepart = impl->by_ptrs[byrule][0]; + } + + +} + +static int has_by_data(icalrecur_iterator* impl, enum byrule byrule){ + + return (impl->orig_data[byrule] == 1); +} + + +static int expand_year_days(icalrecur_iterator* impl, int year); + + +icalrecur_iterator* icalrecur_iterator_new(struct icalrecurrencetype rule, + struct icaltimetype dtstart) +{ + icalrecur_iterator* impl; + icalrecurrencetype_frequency freq; + + if ( ( impl = (icalrecur_iterator*) + malloc(sizeof(icalrecur_iterator))) == 0) { + icalerror_set_errno(ICAL_NEWFAILED_ERROR); + return 0; + } + + memset(impl,0,sizeof(icalrecur_iterator)); + + impl->rule = rule; + impl->last = dtstart; + impl->dtstart = dtstart; + impl->days_index =0; + impl->occurrence_no = 0; + freq = impl->rule.freq; + + /* Set up convienience pointers to make the code simpler. Allows + us to iterate through all of the BY* arrays in the rule. */ + + impl->by_ptrs[BY_MONTH]=impl->rule.by_month; + impl->by_ptrs[BY_WEEK_NO]=impl->rule.by_week_no; + impl->by_ptrs[BY_YEAR_DAY]=impl->rule.by_year_day; + impl->by_ptrs[BY_MONTH_DAY]=impl->rule.by_month_day; + impl->by_ptrs[BY_DAY]=impl->rule.by_day; + impl->by_ptrs[BY_HOUR]=impl->rule.by_hour; + impl->by_ptrs[BY_MINUTE]=impl->rule.by_minute; + impl->by_ptrs[BY_SECOND]=impl->rule.by_second; + impl->by_ptrs[BY_SET_POS]=impl->rule.by_set_pos; + + memset(impl->orig_data,0,9*sizeof(short)); + + /* Note which by rules had data in them when the iterator was + created. We can't use the actuall by_x arrays, because the + empty ones will be given default values later in this + routine. The orig_data array will be used later in has_by_data */ + + impl->orig_data[BY_MONTH] + = (short)(impl->rule.by_month[0]!=ICAL_RECURRENCE_ARRAY_MAX); + impl->orig_data[BY_WEEK_NO] + =(short)(impl->rule.by_week_no[0]!=ICAL_RECURRENCE_ARRAY_MAX); + impl->orig_data[BY_YEAR_DAY] + =(short)(impl->rule.by_year_day[0]!=ICAL_RECURRENCE_ARRAY_MAX); + impl->orig_data[BY_MONTH_DAY] + =(short)(impl->rule.by_month_day[0]!=ICAL_RECURRENCE_ARRAY_MAX); + impl->orig_data[BY_DAY] + = (short)(impl->rule.by_day[0]!=ICAL_RECURRENCE_ARRAY_MAX); + impl->orig_data[BY_HOUR] + = (short)(impl->rule.by_hour[0]!=ICAL_RECURRENCE_ARRAY_MAX); + impl->orig_data[BY_MINUTE] + = (short)(impl->rule.by_minute[0]!=ICAL_RECURRENCE_ARRAY_MAX); + impl->orig_data[BY_SECOND] + = (short)(impl->rule.by_second[0]!=ICAL_RECURRENCE_ARRAY_MAX); + impl->orig_data[BY_SET_POS] + = (short)(impl->rule.by_set_pos[0]!=ICAL_RECURRENCE_ARRAY_MAX); + + + /* Check if the recurrence rule is legal */ + + /* If the BYYEARDAY appears, no other date rule part may appear. */ + + if(icalrecur_two_byrule(impl,BY_YEAR_DAY,BY_MONTH) || + icalrecur_two_byrule(impl,BY_YEAR_DAY,BY_WEEK_NO) || + icalrecur_two_byrule(impl,BY_YEAR_DAY,BY_MONTH_DAY) || + icalrecur_two_byrule(impl,BY_YEAR_DAY,BY_DAY) ){ + + icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR); + + return 0; + } + + /* BYWEEKNO and BYMONTH rule parts may not both appear.*/ + + if(icalrecur_two_byrule(impl,BY_WEEK_NO,BY_MONTH)){ + icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR); + + icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR); + return 0; + } + + /* BYWEEKNO and BYMONTHDAY rule parts may not both appear.*/ + + if(icalrecur_two_byrule(impl,BY_WEEK_NO,BY_MONTH_DAY)){ + icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR); + + icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR); + return 0; + } + + + /*For MONTHLY recurrences (FREQ=MONTHLY) neither BYYEARDAY nor + BYWEEKNO may appear. */ + + if(freq == ICAL_MONTHLY_RECURRENCE && + icalrecur_one_byrule(impl,BY_WEEK_NO)){ + icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR); + return 0; + } + + + /*For WEEKLY recurrences (FREQ=WEEKLY) neither BYMONTHDAY nor + BYYEARDAY may appear. */ + + if(freq == ICAL_WEEKLY_RECURRENCE && + icalrecur_one_byrule(impl,BY_MONTH_DAY )) { + icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR); + return 0; + } + + /* BYYEARDAY may only appear in YEARLY rules */ + if(freq != ICAL_YEARLY_RECURRENCE && + icalrecur_one_byrule(impl,BY_YEAR_DAY )) { + icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR); + return 0; + } + + /* Rewrite some of the rules and set up defaults to make later + processing easier. Primarily, t involves copying an element + from the start time into the corresponding BY_* array when the + BY_* array is empty */ + + + setup_defaults(impl,BY_SECOND,ICAL_SECONDLY_RECURRENCE, + impl->dtstart.second, + &(impl->last.second)); + + setup_defaults(impl,BY_MINUTE,ICAL_MINUTELY_RECURRENCE, + impl->dtstart.minute, + &(impl->last.minute)); + + setup_defaults(impl,BY_HOUR,ICAL_HOURLY_RECURRENCE, + impl->dtstart.hour, + &(impl->last.hour)); + + setup_defaults(impl,BY_MONTH_DAY,ICAL_DAILY_RECURRENCE, + impl->dtstart.day, + &(impl->last.day)); + + setup_defaults(impl,BY_MONTH,ICAL_MONTHLY_RECURRENCE, + impl->dtstart.month, + &(impl->last.month)); + + + if(impl->rule.freq == ICAL_WEEKLY_RECURRENCE ){ + + if(impl->by_ptrs[BY_DAY][0] == ICAL_RECURRENCE_ARRAY_MAX){ + + /* Weekly recurrences with no BY_DAY data should occur on the + same day of the week as the start time . */ + impl->by_ptrs[BY_DAY][0] = (short)icaltime_day_of_week(impl->dtstart); + + } else { + /* If there is BY_DAY data, then we need to move the initial + time to the start of the BY_DAY data. That is if the + start time is on a Wednesday, and the rule has + BYDAY=MO,WE,FR, move the initial time back to + monday. Otherwise, jumping to the next week ( jumping 7 + days ahead ) will skip over some occurrences in the + second week. */ + + /* This is probably a HACK. There should be some more + general way to solve this problem */ + + short dow = (short)(impl->by_ptrs[BY_DAY][0]-icaltime_day_of_week(impl->last)); + + if(dow < 0) { + /* initial time is after first day of BY_DAY data */ + + impl->last.day += dow; + impl->last = icaltime_normalize(impl->last); + } + } + + + } + + /* For YEARLY rule, begin by setting up the year days array . The + YEARLY rules work by expanding one year at a time. */ + + if(impl->rule.freq == ICAL_YEARLY_RECURRENCE){ + struct icaltimetype next; + + for (;;) { + expand_year_days(impl, impl->last.year); + if (impl->days[0] != ICAL_RECURRENCE_ARRAY_MAX) + break; /* break when no days are expanded */ + increment_year(impl,impl->rule.interval); + } + + /* Copy the first day into last. */ + next = icaltime_from_day_of_year(impl->days[0], impl->last.year); + + impl->last.day = next.day; + impl->last.month = next.month; + } + + + /* If this is a monthly interval with by day data, then we need to + set the last value to the appropriate day of the month */ + + if(impl->rule.freq == ICAL_MONTHLY_RECURRENCE && + has_by_data(impl,BY_DAY)) { + + int dow = icalrecurrencetype_day_day_of_week( + impl->by_ptrs[BY_DAY][impl->by_indices[BY_DAY]]); + int pos = icalrecurrencetype_day_position( + impl->by_ptrs[BY_DAY][impl->by_indices[BY_DAY]]); + + int poscount = 0; + int days_in_month = + icaltime_days_in_month(impl->last.month, impl->last.year); + + if(pos >= 0){ + /* Count up from the first day pf the month to find the + pos'th weekday of dow ( like the second monday. ) */ + + for(impl->last.day = 1; + impl->last.day <= days_in_month; + impl->last.day++){ + + if(icaltime_day_of_week(impl->last) == dow){ + if(++poscount == pos || pos == 0){ + break; + } + } + } + } else { + /* Count down from the last day pf the month to find the + pos'th weekday of dow ( like the second to last monday. ) */ + pos = -pos; + for(impl->last.day = days_in_month; + impl->last.day != 0; + impl->last.day--){ + + if(icaltime_day_of_week(impl->last) == dow){ + if(++poscount == pos ){ + break; + } + } + } + } + + + if(impl->last.day > days_in_month || impl->last.day == 0){ + icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR); + return 0; + } + + } + + + + return impl; +} + + +void icalrecur_iterator_free(icalrecur_iterator* i) +{ + icalerror_check_arg_rv((i!=0),"impl"); + + free(i); + +} + +static void increment_year(icalrecur_iterator* impl, int inc) +{ + impl->last.year+=inc; +} + +/** Increment month is different that the other incement_* routines -- + it figures out the interval for itself, and uses BYMONTH data if + available. */ +static void increment_month(icalrecur_iterator* impl) +{ + int years; + + if(has_by_data(impl,BY_MONTH) ){ + /* Ignore the frequency and use the byrule data */ + + impl->by_indices[BY_MONTH]++; + + if (impl->by_ptrs[BY_MONTH][impl->by_indices[BY_MONTH]] + ==ICAL_RECURRENCE_ARRAY_MAX){ + impl->by_indices[BY_MONTH] = 0; + + increment_year(impl,1); + + } + + impl->last.month = + impl->by_ptrs[BY_MONTH][impl->by_indices[BY_MONTH]]; + + } else { + + int inc; + + if(impl->rule.freq == ICAL_MONTHLY_RECURRENCE){ + inc = impl->rule.interval; + } else { + inc = 1; + } + + impl->last.month+=inc; + + /* Months are offset by one */ + impl->last.month--; + + years = impl->last.month / 12; + + impl->last.month = impl->last.month % 12; + + impl->last.month++; + + if (years != 0){ + increment_year(impl,years); + } + } +} + +static void increment_monthday(icalrecur_iterator* impl, int inc) +{ + int i; + + for(i=0; i<inc; i++){ + + int days_in_month = + icaltime_days_in_month(impl->last.month, impl->last.year); + + impl->last.day++; + + if (impl->last.day > days_in_month){ + impl->last.day = impl->last.day-days_in_month; + increment_month(impl); + } + } +} + + +static void increment_hour(icalrecur_iterator* impl, int inc) +{ + int days; + + impl->last.hour+=inc; + + days = impl->last.hour / 24; + impl->last.hour = impl->last.hour % 24; + + if (impl->days != 0){ + increment_monthday(impl,days); + } +} + +static void increment_minute(icalrecur_iterator* impl, int inc) +{ + int hours; + + impl->last.minute+=inc; + + hours = impl->last.minute / 60; + impl->last.minute = impl->last.minute % 60; + + if (hours != 0){ + increment_hour(impl,hours); + } + +} + +static void increment_second(icalrecur_iterator* impl, int inc) +{ + int minutes; + + impl->last.second+=inc; + + minutes = impl->last.second / 60; + impl->last.second = impl->last.second % 60; + + if (minutes != 0) + { + increment_minute(impl, minutes); + } +} + +#if 0 +#include "ical.h" +void test_increment() +{ + icalrecur_iterator impl; + + impl.last = icaltime_from_string("20000101T000000Z"); + + printf("Orig: %s\n",icaltime_as_ctime(impl.last)); + + increment_second(&impl,5); + printf("+ 5 sec : %s\n",icaltime_as_ctime(impl.last)); + + increment_second(&impl,355); + printf("+ 355 sec : %s\n",icaltime_as_ctime(impl.last)); + + increment_minute(&impl,5); + printf("+ 5 min : %s\n",icaltime_as_ctime(impl.last)); + + increment_minute(&impl,360); + printf("+ 360 min : %s\n",icaltime_as_ctime(impl.last)); + increment_hour(&impl,5); + printf("+ 5 hours : %s\n",icaltime_as_ctime(impl.last)); + increment_hour(&impl,43); + printf("+ 43 hours : %s\n",icaltime_as_ctime(impl.last)); + increment_monthday(&impl,3); + printf("+ 3 days : %s\n",icaltime_as_ctime(impl.last)); + increment_monthday(&impl,600); + printf("+ 600 days : %s\n",icaltime_as_ctime(impl.last)); + +} + +#endif + +static int next_second(icalrecur_iterator* impl) +{ + + int has_by_second = (impl->by_ptrs[BY_SECOND][0]!=ICAL_RECURRENCE_ARRAY_MAX); + int this_frequency = (impl->rule.freq == ICAL_SECONDLY_RECURRENCE); + + int end_of_data = 0; + + assert(has_by_second || this_frequency); + + if( has_by_second ){ + /* Ignore the frequency and use the byrule data */ + + impl->by_indices[BY_SECOND]++; + + if (impl->by_ptrs[BY_SECOND][impl->by_indices[BY_SECOND]] + ==ICAL_RECURRENCE_ARRAY_MAX){ + impl->by_indices[BY_SECOND] = 0; + + end_of_data = 1; + } + + + impl->last.second = + impl->by_ptrs[BY_SECOND][impl->by_indices[BY_SECOND]]; + + + } else if( !has_by_second && this_frequency ){ + /* Compute the next value from the last time and the frequency interval*/ + increment_second(impl, impl->rule.interval); + + } + + /* If we have gone through all of the seconds on the BY list, then we + need to move to the next minute */ + + if(has_by_second && end_of_data && this_frequency ){ + increment_minute(impl,1); + } + + return end_of_data; + +} + +static int next_minute(icalrecur_iterator* impl) +{ + + int has_by_minute = (impl->by_ptrs[BY_MINUTE][0]!=ICAL_RECURRENCE_ARRAY_MAX); + int this_frequency = (impl->rule.freq == ICAL_MINUTELY_RECURRENCE); + + int end_of_data = 0; + + assert(has_by_minute || this_frequency); + + + if (next_second(impl) == 0){ + return 0; + } + + if( has_by_minute ){ + /* Ignore the frequency and use the byrule data */ + + impl->by_indices[BY_MINUTE]++; + + if (impl->by_ptrs[BY_MINUTE][impl->by_indices[BY_MINUTE]] + ==ICAL_RECURRENCE_ARRAY_MAX){ + + impl->by_indices[BY_MINUTE] = 0; + + end_of_data = 1; + } + + impl->last.minute = + impl->by_ptrs[BY_MINUTE][impl->by_indices[BY_MINUTE]]; + + } else if( !has_by_minute && this_frequency ){ + /* Compute the next value from the last time and the frequency interval*/ + increment_minute(impl,impl->rule.interval); + } + +/* If we have gone through all of the minutes on the BY list, then we + need to move to the next hour */ + + if(has_by_minute && end_of_data && this_frequency ){ + increment_hour(impl,1); + } + + return end_of_data; +} + +static int next_hour(icalrecur_iterator* impl) +{ + + int has_by_hour = (impl->by_ptrs[BY_HOUR][0]!=ICAL_RECURRENCE_ARRAY_MAX); + int this_frequency = (impl->rule.freq == ICAL_HOURLY_RECURRENCE); + + int end_of_data = 0; + + assert(has_by_hour || this_frequency); + + if (next_minute(impl) == 0){ + return 0; + } + + if( has_by_hour ){ + /* Ignore the frequency and use the byrule data */ + + impl->by_indices[BY_HOUR]++; + + if (impl->by_ptrs[BY_HOUR][impl->by_indices[BY_HOUR]] + ==ICAL_RECURRENCE_ARRAY_MAX){ + impl->by_indices[BY_HOUR] = 0; + + end_of_data = 1; + } + + impl->last.hour = + impl->by_ptrs[BY_HOUR][impl->by_indices[BY_HOUR]]; + + } else if( !has_by_hour && this_frequency ){ + /* Compute the next value from the last time and the frequency interval*/ + increment_hour(impl,impl->rule.interval); + + } + + /* If we have gone through all of the hours on the BY list, then we + need to move to the next day */ + + if(has_by_hour && end_of_data && this_frequency ){ + increment_monthday(impl,1); + } + + return end_of_data; + +} + +static int next_day(icalrecur_iterator* impl) +{ + + int has_by_day = (impl->by_ptrs[BY_DAY][0]!=ICAL_RECURRENCE_ARRAY_MAX); + int this_frequency = (impl->rule.freq == ICAL_DAILY_RECURRENCE); + + assert(has_by_day || this_frequency); + + if (next_hour(impl) == 0){ + return 0; + } + + /* Always increment through the interval, since this routine is not + called by any other next_* routine, and the days that are + excluded will be taken care of by restriction filtering */ + + if(this_frequency){ + increment_monthday(impl,impl->rule.interval); + } else { + increment_monthday(impl,1); + } + + + return 0; + +} + + +/*static int next_yearday(icalrecur_iterator* impl) +{ + + int has_by_yearday = (impl->by_ptrs[BY_YEAR_DAY][0]!=ICAL_RECURRENCE_ARRAY_MAX); + + int end_of_data = 0; + + assert(has_by_yearday ); + + if (next_hour(impl) == 0){ + return 0; + } + + impl->by_indices[BY_YEAR_DAY]++; + + if (impl->by_ptrs[BY_YEAR_DAY][impl->by_indices[BY_YEAR_DAY]] + ==ICAL_RECURRENCE_ARRAY_MAX){ + impl->by_indices[BY_YEAR_DAY] = 0; + + end_of_data = 1; + } + + impl->last.day = + impl->by_ptrs[BY_YEAR_DAY][impl->by_indices[BY_YEAR_DAY]]; + + if(has_by_yearday && end_of_data){ + increment_year(impl,1); + } + + return end_of_data; + +}*/ + + +/* Returns the day of the month for the current month of t that is the + pos'th instance of the day-of-week dow */ + +static int nth_weekday(int dow, int pos, struct icaltimetype t){ + + int days_in_month = icaltime_days_in_month(t.month, t.year); + int end_dow, start_dow; + int wd; + + if(pos >= 0){ + t.day = 1; + start_dow = icaltime_day_of_week(t); + + if (pos != 0) { + pos--; + } + + /* find month day of first occurrence of dow -- such as the + month day of the first monday */ + + wd = dow-start_dow+1; + + if (wd <= 0){ + wd = wd + 7; + } + + wd = wd + pos * 7; + + } else { + t.day = days_in_month; + end_dow = icaltime_day_of_week(t); + + pos++; + + /* find month day of last occurrence of dow -- such as the + month day of the last monday */ + + wd = (end_dow - dow); + + if (wd < 0){ + wd = wd+ 7; + } + + wd = days_in_month - wd; + + wd = wd + pos * 7; + } + + return wd; +} + +static int is_day_in_byday(icalrecur_iterator* impl,struct icaltimetype tt){ + + int idx; + + for(idx = 0; BYDAYPTR[idx] != ICAL_RECURRENCE_ARRAY_MAX; idx++){ + int dow = icalrecurrencetype_day_day_of_week(BYDAYPTR[idx]); + int pos = icalrecurrencetype_day_position(BYDAYPTR[idx]); + int this_dow = icaltime_day_of_week(tt); + + if( (pos == 0 && dow == this_dow ) || /* Just a dow, like "TU" or "FR" */ + (nth_weekday(dow,pos,tt) == tt.day)){ /*pos+wod: "3FR" or -1TU" */ + return 1; + } + } + + return 0; +} + +static int next_month(icalrecur_iterator* impl) +{ + int data_valid = 1; + + int this_frequency = (impl->rule.freq == ICAL_MONTHLY_RECURRENCE); + + assert( has_by_data(impl,BY_MONTH) || this_frequency); + + /* Iterate through the occurrences within a day. If we don't get to + the end of the intra-day data, don't bother going to the next + month */ + + if (next_hour(impl) == 0){ + return data_valid; /* Signal that the data is valid */ + } + + /* Now iterate through the occurrences within a month -- by days, + weeks or weekdays. */ + + /* + * Case 1: + * Rules Like: FREQ=MONTHLY;INTERVAL=1;BYDAY=FR;BYMONTHDAY=13 + */ + + if(has_by_data(impl,BY_DAY) && has_by_data(impl,BY_MONTH_DAY)){ + int day, idx,j; + int days_in_month = icaltime_days_in_month(impl->last.month, + impl->last.year); + /* Iterate through the remaining days in the month and check if + each day is listed in the BY_DAY array and in the BY_MONTHDAY + array. This seems very inneficient, but I think it is the + simplest way to account for both BYDAY=1FR (First friday in + month) and BYDAY=FR ( every friday in month ) */ + + for(day = impl->last.day+1; day <= days_in_month; day++){ + for(idx = 0; BYDAYPTR[idx] != ICAL_RECURRENCE_ARRAY_MAX; idx++){ + for(j = 0; BYMDPTR[j]!=ICAL_RECURRENCE_ARRAY_MAX; j++){ + int dow = + icalrecurrencetype_day_day_of_week(BYDAYPTR[idx]); + int pos = icalrecurrencetype_day_position(BYDAYPTR[idx]); + int mday = BYMDPTR[j]; + int this_dow; + + impl->last.day = day; + this_dow = icaltime_day_of_week(impl->last); + + if( (pos == 0 && dow == this_dow && mday == day) || + (nth_weekday(dow,pos,impl->last) == day && mday==day)){ + goto MDEND; + } + } + } + } + + MDEND: + + if ( day > days_in_month){ + impl->last.day = 1; + increment_month(impl); + data_valid = 0; /* signal that impl->last is invalid */ + } + + + /* + * Case 2: + * Rules Like: FREQ=MONTHLY;INTERVAL=1;BYDAY=FR + */ + + } else if(has_by_data(impl,BY_DAY)){ + /* For this case, the weekdays are relative to the + month. BYDAY=FR -> First Friday in month, etc. */ + + /* This code iterates through the remaining days in the month + and checks if each day is listed in the BY_DAY array. This + seems very inneficient, but I think it is the simplest way to + account for both BYDAY=1FR (First friday in month) and + BYDAY=FR ( every friday in month ) */ + + int day; + int days_in_month = icaltime_days_in_month(impl->last.month, + impl->last.year); + assert( BYDAYPTR[0]!=ICAL_RECURRENCE_ARRAY_MAX); + + for(day = impl->last.day+1; day <= days_in_month; day++){ + impl->last.day = day; + if(is_day_in_byday(impl,impl->last)){ + data_valid = 1; + break; + } + } + + if ( day > days_in_month){ + impl->last.day = 1; + increment_month(impl); + + /* Did moving to the next month put us on a valid date? if + so, note that the new data is valid, if, not, mark it + invalid */ + + if(is_day_in_byday(impl,impl->last)){ + data_valid = 1; + } else { + data_valid = 0; /* signal that impl->last is invalid */ + } + } + + /* + * Case 3 + * Rules Like: FREQ=MONTHLY;COUNT=10;BYMONTHDAY=-3 + */ + + } else if (has_by_data(impl,BY_MONTH_DAY)) { + int day; + + assert( BYMDPTR[0]!=ICAL_RECURRENCE_ARRAY_MAX); + + BYMDIDX++; + + /* Are we at the end of the BYDAY array? */ + if (BYMDPTR[BYMDIDX] ==ICAL_RECURRENCE_ARRAY_MAX){ + + BYMDIDX = 0; /* Reset to 0 */ + increment_month(impl); + } + + day = BYMDPTR[BYMDIDX]; + + if (day < 0) { + day = icaltime_days_in_month(impl->last.month, impl->last.year) + day + 1; + } + + impl->last.day = day; + + } else { + increment_month(impl); + } + + return data_valid; + +} + +static int next_weekday_by_week(icalrecur_iterator* impl) +{ + + int end_of_data = 0; + int start_of_week, dow; + struct icaltimetype next; + + if (next_hour(impl) == 0){ + return 0; + } + + if(!has_by_data(impl,BY_DAY)){ + return 1; + } + + /* If we get here, we need to step to tne next day */ + + for (;;) { + struct icaltimetype tt = icaltime_null_time(); + BYDAYIDX++; /* Look at next elem in BYDAY array */ + + /* Are we at the end of the BYDAY array? */ + if (BYDAYPTR[BYDAYIDX]==ICAL_RECURRENCE_ARRAY_MAX){ + BYDAYIDX = 0; /* Reset to 0 */ + end_of_data = 1; /* Signal that we're at the end */ + } + + /* Add the day of week offset to to the start of this week, and use + that to get the next day */ + /* ignore position of dow ("4FR"), only use dow ("FR")*/ + dow = icalrecurrencetype_day_day_of_week(BYDAYPTR[BYDAYIDX]); + tt.year = impl->last.year; + tt.day = impl->last.day; + tt.month = impl->last.month; + + start_of_week = icaltime_start_doy_of_week(tt); + + dow--; /* Set Sunday to be 0 */ + + if(dow+start_of_week <1){ + /* The selected date is in the previous year. */ + if(!end_of_data){ + continue; + } + } + + next = icaltime_from_day_of_year(start_of_week + dow,impl->last.year); + + impl->last.day = next.day; + impl->last.month = next.month; + impl->last.year = next.year; + + return end_of_data; + } + +} + +static int next_week(icalrecur_iterator* impl) +{ + int end_of_data = 0; + + /* Increment to the next week day, if there is data at a level less than a week */ + if (next_weekday_by_week(impl) == 0){ + return 0; /* Have not reached end of week yet */ + } + + /* If we get here, we have incremented through the entire week, and + can increment to the next week */ + + if( has_by_data(impl,BY_WEEK_NO)){ + /*FREQ=WEEKLY;BYWEEK=20*/ + /* Use the Week Number byrule data */ + int week_no; + struct icaltimetype t; + + impl->by_indices[BY_WEEK_NO]++; + + if (impl->by_ptrs[BY_WEEK_NO][impl->by_indices[BY_WEEK_NO]] + ==ICAL_RECURRENCE_ARRAY_MAX){ + impl->by_indices[BY_WEEK_NO] = 0; + + end_of_data = 1; + } + + t = impl->last; + t.month=1; /* HACK, should be setting to the date of the first week of year*/ + t.day=1; + + week_no = impl->by_ptrs[BY_WEEK_NO][impl->by_indices[BY_WEEK_NO]]; + + impl->last.day += week_no*7; + + impl->last = icaltime_normalize(impl->last); + + } else { + /* Jump to the next week */ + increment_monthday(impl,7*impl->rule.interval); + } + + if( has_by_data(impl,BY_WEEK_NO) && end_of_data){ + increment_year(impl,1); + } + + return end_of_data; + +} + +/** Expand the BYDAY rule part and return a pointer to a newly allocated list of days. */ +static pvl_list expand_by_day(icalrecur_iterator* impl, int year) +{ + /* Try to calculate each of the occurrences. */ + int i; + pvl_list days_list = pvl_newlist(); + + int start_dow, end_dow, end_year_day; + struct icaltimetype tmp = impl->last; + + tmp.year= year; + tmp.month = 1; + tmp.day = 1; + tmp.is_date = 1; + + /* Find the day that 1st Jan falls on, 1 (Sun) to 7 (Sat). */ + start_dow = icaltime_day_of_week(tmp); + + /* Get the last day of the year*/ + tmp.year= year; + tmp.month = 12; + tmp.day = 31; + tmp.is_date = 1; + + end_dow = icaltime_day_of_week(tmp); + end_year_day = icaltime_day_of_year(tmp); + + for(i = 0; BYDAYPTR[i] != ICAL_RECURRENCE_ARRAY_MAX; i++){ + /* This is 1 (Sun) to 7 (Sat). */ + int dow = + icalrecurrencetype_day_day_of_week(BYDAYPTR[i]); + int pos = icalrecurrencetype_day_position(BYDAYPTR[i]); + + if(pos == 0){ + /* The day was specified without a position -- it is just + a bare day of the week ( BYDAY=SU) so add all of the + days of the year with this day-of-week*/ + int doy, tmp_start_doy; + + tmp_start_doy = ((dow + 7 - start_dow) % 7) + 1; + + for (doy = tmp_start_doy; doy <= end_year_day; doy += 7) + pvl_push(days_list,(void*)(ptrdiff_t)doy); + + } else if ( pos > 0) { + int first; + /* First occurrence of dow in year */ + if( dow >= start_dow) { + first = dow - start_dow + 1; + } else { + first = dow - start_dow + 8; + } + + /* Then just multiple the position times 7 to get the pos'th day in the year */ + pvl_push(days_list,(void*)(first+ (pos-1) * 7)); + + } else { /* pos < 0 */ + int last; + pos = -pos; + + /* last occurrence of dow in year */ + if( dow <= end_dow) { + last = end_year_day - end_dow + dow; + } else { + last = end_year_day - end_dow + dow - 7; + } + + pvl_push(days_list,(void*)(last - (pos-1) * 7)); + } + } + return days_list; +} + + +/* For INTERVAL=YEARLY, set up the days[] array in the iterator to + list all of the days of the current year that are specified in this + rule. */ + +static int expand_year_days(icalrecur_iterator* impl, int year) +{ + int j,k; + int days_index=0; + struct icaltimetype t; + int flags; + + t = icaltime_null_date(); + +#define HBD(x) has_by_data(impl,x) + + memset(impl->days,ICAL_RECURRENCE_ARRAY_MAX_BYTE,sizeof(impl->days)); + + /* The flags and the following switch statement select which code + to use to expand the yers days, based on which BY-rules are + present. */ + + flags = (HBD(BY_DAY) ? 1<<BY_DAY : 0) + + (HBD(BY_WEEK_NO) ? 1<<BY_WEEK_NO : 0) + + (HBD(BY_MONTH_DAY) ? 1<<BY_MONTH_DAY : 0) + + (HBD(BY_MONTH) ? 1<<BY_MONTH : 0) + + (HBD(BY_YEAR_DAY) ? 1<<BY_YEAR_DAY : 0); + + + switch(flags) { + + case 0: { + /* FREQ=YEARLY; */ + t = impl->dtstart; + t.year = impl->last.year; + + impl->days[days_index++] = (short)icaltime_day_of_year(t); + + break; + } + case 1<<BY_MONTH: { + /* FREQ=YEARLY; BYMONTH=3,11*/ + + for(j=0;impl->by_ptrs[BY_MONTH][j]!=ICAL_RECURRENCE_ARRAY_MAX;j++){ + int month = impl->by_ptrs[BY_MONTH][j]; + int doy; + + t = impl->dtstart; + t.year = year; + t.month = month; + t.is_date = 1; + + doy = icaltime_day_of_year(t); + + impl->days[days_index++] = (short)doy; + + } + break; + } + + case 1<<BY_MONTH_DAY: { + /* FREQ=YEARLY; BYMONTHDAY=1,15*/ + for(k=0;impl->by_ptrs[BY_MONTH_DAY][k]!=ICAL_RECURRENCE_ARRAY_MAX;k++) + { + int month_day = impl->by_ptrs[BY_MONTH_DAY][k]; + int doy; + + t = impl->dtstart; + t.day = month_day; + t.year = year; + t.is_date = 1; + + doy = icaltime_day_of_year(t); + + impl->days[days_index++] = (short)doy; + + } + break; + } + + case (1<<BY_MONTH_DAY) + (1<<BY_MONTH): { + /* FREQ=YEARLY; BYMONTHDAY=1,15; BYMONTH=10 */ + + for(j=0;impl->by_ptrs[BY_MONTH][j]!=ICAL_RECURRENCE_ARRAY_MAX;j++){ + for(k=0;impl->by_ptrs[BY_MONTH_DAY][k]!=ICAL_RECURRENCE_ARRAY_MAX;k++) + { + int month = impl->by_ptrs[BY_MONTH][j]; + int month_day = impl->by_ptrs[BY_MONTH_DAY][k]; + int doy; + + t.day = month_day; + t.month = month; + t.year = year; + t.is_date = 1; + + doy = icaltime_day_of_year(t); + + impl->days[days_index++] = (short)doy; + + } + } + + break; + } + + case 1<<BY_WEEK_NO: { + /* FREQ=YEARLY; BYWEEKNO=20,50 */ + + int dow; + + t.day = impl->dtstart.day; + t.month = impl->dtstart.month; + t.year = year; + t.is_date = 1; + + dow = icaltime_day_of_week(t); + /* HACK Not finished */ + + icalerror_set_errno(ICAL_UNIMPLEMENTED_ERROR); + + break; + } + + case (1<<BY_WEEK_NO) + (1<<BY_MONTH_DAY): { + /*FREQ=YEARLY; WEEKNO=20,50; BYMONTH= 6,11 */ + icalerror_set_errno(ICAL_UNIMPLEMENTED_ERROR); + break; + } + + case 1<<BY_DAY: { + /*FREQ=YEARLY; BYDAY=TH,20MO,-10FR*/ + pvl_elem i; + pvl_list days = expand_by_day(impl,year); + + + for(i=pvl_head(days);i!=0;i=pvl_next(i)){ + short day = (short)(intptr_t)pvl_data(i); + impl->days[days_index++] = day; + } + + pvl_free(days); + + break; + } + + case (1<<BY_DAY)+(1<<BY_MONTH): { + /*FREQ=YEARLY; BYDAY=TH,20MO,-10FR; BYMONTH = 12*/ + + + for(j=0;impl->by_ptrs[BY_MONTH][j]!=ICAL_RECURRENCE_ARRAY_MAX;j++){ + int month = impl->by_ptrs[BY_MONTH][j]; + int days_in_month = icaltime_days_in_month(month,year); + int first_dow, last_dow, doy_offset; + + t.year = year; + t.month = month; + t.day = 1; + t.is_date = 1; + + first_dow = icaltime_day_of_week(t); + + /* This holds the day offset used to calculate the day of the year + from the month day. Just add the month day to this. */ + doy_offset = icaltime_day_of_year(t) - 1; + + t.day = days_in_month; + last_dow = icaltime_day_of_week(t); + + for(k=0;impl->by_ptrs[BY_DAY][k]!=ICAL_RECURRENCE_ARRAY_MAX;k++){ + short day_coded = impl->by_ptrs[BY_DAY][k]; + enum icalrecurrencetype_weekday dow = + icalrecurrencetype_day_day_of_week(day_coded); + int pos = icalrecurrencetype_day_position(day_coded); + int first_matching_day, last_matching_day, day, month_day; + + /* Calculate the first day in the month with the given weekday, + and the last day. */ + first_matching_day = ((dow + 7 - first_dow) % 7) + 1; + last_matching_day = days_in_month - ((last_dow + 7 - dow) % 7); + + if (pos == 0) { + /* Add all of instances of the weekday within the month. */ + for (day = first_matching_day; day <= days_in_month; day += 7) + impl->days[days_index++] = (short)(doy_offset + day); + + } else if (pos > 0) { + /* Add the nth instance of the weekday within the month. */ + month_day = first_matching_day + (pos - 1) * 7; + + if (month_day <= days_in_month) + impl->days[days_index++] = (short)(doy_offset + month_day); + + } else { + /* Add the -nth instance of the weekday within the month.*/ + month_day = last_matching_day + (pos + 1) * 7; + + if (month_day > 0) + impl->days[days_index++] = (short)(doy_offset + month_day); + } + } + } + break; + } + + case (1<<BY_DAY) + (1<<BY_MONTH_DAY) : { + /*FREQ=YEARLY; BYDAY=TH,20MO,-10FR; BYMONTHDAY=1,15*/ + + pvl_elem itr; + pvl_list days = expand_by_day(impl,year); + + for(itr=pvl_head(days);itr!=0;itr=pvl_next(itr)){ + short day = (short)(intptr_t)pvl_data(itr); + struct icaltimetype tt; + + tt = icaltime_from_day_of_year(day,year); + + for(j = 0; BYMDPTR[j]!=ICAL_RECURRENCE_ARRAY_MAX; j++){ + int mday = BYMDPTR[j]; + + if(tt.day == mday){ + impl->days[days_index++] = day; + } + } + + } + + pvl_free(days); + + break; + } + + case (1<<BY_DAY) + (1<<BY_MONTH_DAY) + (1<<BY_MONTH): { + /*FREQ=YEARLY; BYDAY=TH,20MO,-10FR; BYMONTHDAY=10; MYMONTH=6,11*/ + + pvl_elem itr; + pvl_list days = expand_by_day(impl,year); + + for(itr=pvl_head(days);itr!=0;itr=pvl_next(itr)){ + short day = (short)(intptr_t)pvl_data(itr); + struct icaltimetype tt; + int i; + + tt = icaltime_from_day_of_year(day,year); + + for(i = 0; BYMONPTR[i] != ICAL_RECURRENCE_ARRAY_MAX; i++){ + for(j = 0; BYMDPTR[j]!=ICAL_RECURRENCE_ARRAY_MAX; j++){ + int mday = BYMDPTR[j]; + int month = BYMONPTR[i]; + + if(tt.month == month && tt.day == mday){ + impl->days[days_index++] = day; + } + } + } + + } + + pvl_free(days); + + break; + + } + + case (1<<BY_DAY) + (1<<BY_WEEK_NO) : { + /*FREQ=YEARLY; BYDAY=TH,20MO,-10FR; WEEKNO=20,50*/ + + pvl_elem itr; + pvl_list days = expand_by_day(impl,year); + + for(itr=pvl_head(days);itr!=0;itr=pvl_next(itr)){ + short day = (short)(intptr_t)pvl_data(itr); + struct icaltimetype tt; + int i; + + tt = icaltime_from_day_of_year(day,year); + + for(i = 0; BYWEEKPTR[i] != ICAL_RECURRENCE_ARRAY_MAX; i++){ + int weekno = BYWEEKPTR[i]; + int this_weekno = icaltime_week_number(tt); + if(weekno== this_weekno){ + impl->days[days_index++] = day; + } + } + + } + + pvl_free(days); + break; + } + + case (1<<BY_DAY) + (1<<BY_WEEK_NO) + (1<<BY_MONTH_DAY): { + /*FREQ=YEARLY; BYDAY=TH,20MO,-10FR; WEEKNO=20,50; BYMONTHDAY=1,15*/ + icalerror_set_errno(ICAL_UNIMPLEMENTED_ERROR); + break; + } + + case 1<<BY_YEAR_DAY: { + for(j=0;impl->by_ptrs[BY_YEAR_DAY][j]!=ICAL_RECURRENCE_ARRAY_MAX;j++){ + impl->days[days_index++] = impl->by_ptrs[BY_YEAR_DAY][j]; + } + break; + } + + default: { + icalerror_set_errno(ICAL_UNIMPLEMENTED_ERROR); + break; + } + + } + + return 0; +} + + +static int next_year(icalrecur_iterator* impl) +{ + struct icaltimetype next; + + if (next_hour(impl) == 0){ + return 0; + } + + if (impl->days[++impl->days_index] == ICAL_RECURRENCE_ARRAY_MAX){ + impl->days_index = 0; + + for (;;) { + increment_year(impl,impl->rule.interval); + expand_year_days(impl,impl->last.year); + if (impl->days[0] != ICAL_RECURRENCE_ARRAY_MAX) + break; + } + } + + next = icaltime_from_day_of_year(impl->days[impl->days_index], impl->last.year); + + impl->last.day = next.day; + impl->last.month = next.month; + + return 1; +} + +int icalrecur_check_rulepart(icalrecur_iterator* impl, + int v, enum byrule byrule) +{ + int itr; + + if(impl->by_ptrs[byrule][0]!=ICAL_RECURRENCE_ARRAY_MAX){ + for(itr=0; impl->by_ptrs[byrule][itr]!=ICAL_RECURRENCE_ARRAY_MAX;itr++){ + if(impl->by_ptrs[byrule][itr] == v){ + return 1; + } + } + } + + return 0; +} + +static int check_contract_restriction(icalrecur_iterator* impl, + enum byrule byrule, int v) +{ + int pass = 0; + int itr; + icalrecurrencetype_frequency freq = impl->rule.freq; + + if(impl->by_ptrs[byrule][0]!=ICAL_RECURRENCE_ARRAY_MAX && + expand_map[freq].map[byrule] == CONTRACT){ + for(itr=0; impl->by_ptrs[byrule][itr]!=ICAL_RECURRENCE_ARRAY_MAX;itr++){ + if(impl->by_ptrs[byrule][itr] == v){ + pass=1; + break; + } + } + + return pass; + } else { + /* This is not a contracting byrule, or it has no data, so the + test passes*/ + return 1; + } +} + + +static int check_contracting_rules(icalrecur_iterator* impl) +{ + + int day_of_week = icaltime_day_of_week(impl->last); + int week_no = icaltime_week_number(impl->last); + int year_day = icaltime_day_of_year(impl->last); + + if ( + check_contract_restriction(impl,BY_SECOND, impl->last.second) && + check_contract_restriction(impl,BY_MINUTE, impl->last.minute) && + check_contract_restriction(impl,BY_HOUR, impl->last.hour) && + check_contract_restriction(impl,BY_DAY, day_of_week) && + check_contract_restriction(impl,BY_WEEK_NO, week_no) && + check_contract_restriction(impl,BY_MONTH_DAY, impl->last.day) && + check_contract_restriction(impl,BY_MONTH, impl->last.month) && + check_contract_restriction(impl,BY_YEAR_DAY, year_day) ) + { + + return 1; + } else { + return 0; + } +} + +struct icaltimetype icalrecur_iterator_next(icalrecur_iterator *impl) +{ + int valid = 1; + + if( (impl->rule.count!=0 &&impl->occurrence_no >= impl->rule.count) || + (!icaltime_is_null_time(impl->rule.until) && + icaltime_compare(impl->last,impl->rule.until) > 0)) { + return icaltime_null_time(); + } + + if(impl->occurrence_no == 0 + && icaltime_compare(impl->last,impl->dtstart) >= 0){ + + impl->occurrence_no++; + return impl->last; + } + + do { + valid = 1; + switch(impl->rule.freq){ + + case ICAL_SECONDLY_RECURRENCE: { + next_second(impl); + break; + } + case ICAL_MINUTELY_RECURRENCE: { + next_minute(impl); + break; + } + case ICAL_HOURLY_RECURRENCE: { + next_hour(impl); + break; + } + case ICAL_DAILY_RECURRENCE: { + next_day(impl); + break; + } + case ICAL_WEEKLY_RECURRENCE: { + next_week(impl); + break; + } + case ICAL_MONTHLY_RECURRENCE: { + valid = next_month(impl); + break; + } + case ICAL_YEARLY_RECURRENCE:{ + next_year(impl); + break; + } + default:{ + icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR); + return icaltime_null_time(); + } + } + + if(impl->last.year >= 2038 ){ + /* HACK */ + return icaltime_null_time(); + } + + } while(!check_contracting_rules(impl) + || icaltime_compare(impl->last,impl->dtstart) < 0 + || valid == 0); + + +/* Ignore null times and times that are after the until time */ + if( !icaltime_is_null_time(impl->rule.until) && + icaltime_compare(impl->last,impl->rule.until) > 0 ) { + return icaltime_null_time(); + } + + impl->occurrence_no++; + + return impl->last; +} + + +/************************** Type Routines **********************/ + + +void icalrecurrencetype_clear(struct icalrecurrencetype *recur) +{ + memset(recur,ICAL_RECURRENCE_ARRAY_MAX_BYTE, + sizeof(struct icalrecurrencetype)); + + recur->week_start = ICAL_MONDAY_WEEKDAY; + recur->freq = ICAL_NO_RECURRENCE; + recur->interval = 1; + memset(&(recur->until),0,sizeof(struct icaltimetype)); + recur->count = 0; +} + +/** The 'day' element of icalrecurrencetype_weekday is encoded to + * allow representation of both the day of the week ( Monday, Tueday), + * but also the Nth day of the week ( First tuesday of the month, last + * thursday of the year) These routines decode the day values. + * + * The day's position in the period ( Nth-ness) and the numerical + * value of the day are encoded together as: pos*7 + dow + * + * A position of 0 means 'any' or 'every' + */ + +enum icalrecurrencetype_weekday icalrecurrencetype_day_day_of_week(short day) +{ + return abs(day)%8; +} + +int icalrecurrencetype_day_position(short day) +{ + int wd, pos; + + wd = icalrecurrencetype_day_day_of_week(day); + + pos = (abs(day)-wd)/8 * ((day<0)?-1:1); + + + return pos; +} + + +/****************** Enumeration Routines ******************/ + +static struct {icalrecurrencetype_weekday wd; const char * str; } +wd_map[] = { + {ICAL_SUNDAY_WEEKDAY,"SU"}, + {ICAL_MONDAY_WEEKDAY,"MO"}, + {ICAL_TUESDAY_WEEKDAY,"TU"}, + {ICAL_WEDNESDAY_WEEKDAY,"WE"}, + {ICAL_THURSDAY_WEEKDAY,"TH"}, + {ICAL_FRIDAY_WEEKDAY,"FR"}, + {ICAL_SATURDAY_WEEKDAY,"SA"}, + {ICAL_NO_WEEKDAY,0} +}; + +const char* icalrecur_weekday_to_string(icalrecurrencetype_weekday kind) +{ + int i; + + for (i=0; wd_map[i].wd != ICAL_NO_WEEKDAY; i++) { + if ( wd_map[i].wd == kind) { + return wd_map[i].str; + } + } + + return 0; +} + +icalrecurrencetype_weekday icalrecur_string_to_weekday(const char* str) +{ + int i; + + for (i=0; wd_map[i].wd != ICAL_NO_WEEKDAY; i++) { + if ( strcasecmp(str,wd_map[i].str) == 0){ + return wd_map[i].wd; + } + } + + return ICAL_NO_WEEKDAY; +} + + + +static struct { + icalrecurrencetype_frequency kind; + const char* str; +} freq_map[] = { + {ICAL_SECONDLY_RECURRENCE,"SECONDLY"}, + {ICAL_MINUTELY_RECURRENCE,"MINUTELY"}, + {ICAL_HOURLY_RECURRENCE,"HOURLY"}, + {ICAL_DAILY_RECURRENCE,"DAILY"}, + {ICAL_WEEKLY_RECURRENCE,"WEEKLY"}, + {ICAL_MONTHLY_RECURRENCE,"MONTHLY"}, + {ICAL_YEARLY_RECURRENCE,"YEARLY"}, + {ICAL_NO_RECURRENCE,0} +}; + +const char* icalrecur_freq_to_string(icalrecurrencetype_frequency kind) +{ + int i; + + for (i=0; freq_map[i].kind != ICAL_NO_RECURRENCE ; i++) { + if ( freq_map[i].kind == kind ) { + return freq_map[i].str; + } + } + return 0; +} + +icalrecurrencetype_frequency icalrecur_string_to_freq(const char* str) +{ + int i; + + for (i=0; freq_map[i].kind != ICAL_NO_RECURRENCE ; i++) { + if ( strcasecmp(str,freq_map[i].str) == 0){ + return freq_map[i].kind; + } + } + return ICAL_NO_RECURRENCE; +} + +/** Fill an array with the 'count' number of occurrences generated by + * the rrule. Note that the times are returned in UTC, but the times + * are calculated in local time. YOu will have to convert the results + * back into local time before using them. + */ + +int icalrecur_expand_recurrence(char* rule, time_t start, + int count, time_t* array) +{ + struct icalrecurrencetype recur; + icalrecur_iterator* ritr; + time_t tt; + struct icaltimetype icstart, next; + int i = 0; + + memset(array, 0, count*sizeof(time_t)); + + icstart = icaltime_from_timet_with_zone(start,0,0); + + recur = icalrecurrencetype_from_string(rule); + + for(ritr = icalrecur_iterator_new(recur,icstart), + next = icalrecur_iterator_next(ritr); + !icaltime_is_null_time(next) && i < count; + next = icalrecur_iterator_next(ritr)){ + + tt = icaltime_as_timet(next); + + if (tt >= start ){ + array[i++] = tt; + } + + } + + icalrecur_iterator_free(ritr); + + return 1; +} diff --git a/libkcal/libical/src/libical/icalrecur.h b/libkcal/libical/src/libical/icalrecur.h new file mode 100644 index 000000000..da186f86f --- /dev/null +++ b/libkcal/libical/src/libical/icalrecur.h @@ -0,0 +1,215 @@ +/* -*- Mode: C -*- */ +/*====================================================================== + FILE: icalrecur.h + CREATOR: eric 20 March 2000 + + + (C) COPYRIGHT 2000, Eric Busboom <eric@softwarestudio.org> + http://www.softwarestudio.org + + This program is free software; you can redistribute it and/or modify + it under the terms of either: + + The LGPL as published by the Free Software Foundation, version + 2.1, available at: http://www.fsf.org/copyleft/lesser.html + + Or: + + The Mozilla Public License Version 1.0. You may obtain a copy of + the License at http://www.mozilla.org/MPL/ +*/ + +/** +@file icalrecur.h +@brief Routines for dealing with recurring time + +How to use: + +1) Get a rule and a start time from a component + +@code + icalproperty rrule; + struct icalrecurrencetype recur; + struct icaltimetype dtstart; + + rrule = icalcomponent_get_first_property(comp,ICAL_RRULE_PROPERTY); + recur = icalproperty_get_rrule(rrule); + start = icalproperty_get_dtstart(dtstart); +@endcode + +Or, just make them up: + +@code + recur = icalrecurrencetype_from_string("FREQ=YEARLY;BYDAY=SU,WE"); + dtstart = icaltime_from_string("19970101T123000") +@endcode + +2) Create an iterator + +@code + icalrecur_iterator* ritr; + ritr = icalrecur_iterator_new(recur,start); +@endcode + +3) Iterator over the occurrences + +@code + struct icaltimetype next; + while (next = icalrecur_iterator_next(ritr) + && !icaltime_is_null_time(next){ + Do something with next + } +@endcode + +Note that that the time returned by icalrecur_iterator_next is in +whatever timezone that dtstart is in. + +*/ + +#ifndef ICALRECUR_H +#define ICALRECUR_H + +#include <time.h> +#include "icaltime.h" + +/* + * Recurrance enumerations + */ + +typedef enum icalrecurrencetype_frequency +{ + /* These enums are used to index an array, so don't change the + order or the integers */ + + ICAL_SECONDLY_RECURRENCE=0, + ICAL_MINUTELY_RECURRENCE=1, + ICAL_HOURLY_RECURRENCE=2, + ICAL_DAILY_RECURRENCE=3, + ICAL_WEEKLY_RECURRENCE=4, + ICAL_MONTHLY_RECURRENCE=5, + ICAL_YEARLY_RECURRENCE=6, + ICAL_NO_RECURRENCE=7 + +} icalrecurrencetype_frequency; + +typedef enum icalrecurrencetype_weekday +{ + ICAL_NO_WEEKDAY, + ICAL_SUNDAY_WEEKDAY, + ICAL_MONDAY_WEEKDAY, + ICAL_TUESDAY_WEEKDAY, + ICAL_WEDNESDAY_WEEKDAY, + ICAL_THURSDAY_WEEKDAY, + ICAL_FRIDAY_WEEKDAY, + ICAL_SATURDAY_WEEKDAY +} icalrecurrencetype_weekday; + +enum { + ICAL_RECURRENCE_ARRAY_MAX = 0x7f7f, + ICAL_RECURRENCE_ARRAY_MAX_BYTE = 0x7f +}; + + + +/** + * Recurrence type routines + */ + +/* See RFC 2445 Section 4.3.10, RECUR Value, for an explaination of + the values and fields in struct icalrecurrencetype */ + +#define ICAL_BY_SECOND_SIZE 61 +#define ICAL_BY_MINUTE_SIZE 61 +#define ICAL_BY_HOUR_SIZE 25 +#define ICAL_BY_DAY_SIZE 364 /* 7 days * 52 weeks */ +#define ICAL_BY_MONTHDAY_SIZE 32 +#define ICAL_BY_YEARDAY_SIZE 367 +#define ICAL_BY_WEEKNO_SIZE 54 +#define ICAL_BY_MONTH_SIZE 13 +#define ICAL_BY_SETPOS_SIZE 367 + +/** Main struct for holding digested recurrence rules */ +struct icalrecurrencetype +{ + icalrecurrencetype_frequency freq; + + + /* until and count are mutually exclusive. */ + struct icaltimetype until; + int count; + + short interval; + + icalrecurrencetype_weekday week_start; + + /* The BY* parameters can each take a list of values. Here I + * assume that the list of values will not be larger than the + * range of the value -- that is, the client will not name a + * value more than once. + + * Each of the lists is terminated with the value + * ICAL_RECURRENCE_ARRAY_MAX unless the the list is full. + */ + + short by_second[ICAL_BY_SECOND_SIZE]; + short by_minute[ICAL_BY_MINUTE_SIZE]; + short by_hour[ICAL_BY_HOUR_SIZE]; + short by_day[ICAL_BY_DAY_SIZE]; /* Encoded value, see below */ + short by_month_day[ICAL_BY_MONTHDAY_SIZE]; + short by_year_day[ ICAL_BY_YEARDAY_SIZE]; + short by_week_no[ICAL_BY_WEEKNO_SIZE]; + short by_month[ICAL_BY_MONTH_SIZE]; + short by_set_pos[ICAL_BY_SETPOS_SIZE]; +}; + + +void icalrecurrencetype_clear(struct icalrecurrencetype *r); + +/** + * Array Encoding + * + * The 'day' element of the by_day array is encoded to allow + * representation of both the day of the week ( Monday, Tueday), but also + * the Nth day of the week ( First tuesday of the month, last thursday of + * the year) These routines decode the day values + */ + +/** 1 == Monday, etc. */ +enum icalrecurrencetype_weekday icalrecurrencetype_day_day_of_week(short day); + +/** 0 == any of day of week. 1 == first, 2 = second, -2 == second to last, etc */ +int icalrecurrencetype_day_position(short day); + + +/** Recurrance rule parser */ + +/** Convert between strings and recurrencetype structures. */ +struct icalrecurrencetype icalrecurrencetype_from_string(const char* str); +char* icalrecurrencetype_as_string(struct icalrecurrencetype *recur); + + +/** Recurrence iteration routines */ + +typedef struct icalrecur_iterator_impl icalrecur_iterator; + +/** Create a new recurrence rule iterator */ +icalrecur_iterator* icalrecur_iterator_new(struct icalrecurrencetype rule, + struct icaltimetype dtstart); + +/** Get the next occurrence from an iterator */ +struct icaltimetype icalrecur_iterator_next(icalrecur_iterator*); + +void icalrecur_iterator_decrement_count(icalrecur_iterator*); + +/** Free the iterator */ +void icalrecur_iterator_free(icalrecur_iterator*); + +/** + * Fills array up with at most 'count' time_t values, each + * representing an occurrence time in seconds past the POSIX epoch + */ +int icalrecur_expand_recurrence(char* rule, time_t start, + int count, time_t* array); + + +#endif diff --git a/libkcal/libical/src/libical/icalrestriction.c.in b/libkcal/libical/src/libical/icalrestriction.c.in new file mode 100644 index 000000000..b75e562cf --- /dev/null +++ b/libkcal/libical/src/libical/icalrestriction.c.in @@ -0,0 +1,503 @@ +/* -*- Mode: C -*- */ +/* ====================================================================== + File: icalrestriction.c + + (C) COPYRIGHT 2000, Eric Busboom, http://www.softwarestudio.org + ======================================================================*/ +/*#line 7 "icalrestriction.c.in"*/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "icalrestriction.h" +#include "icalenums.h" +#include "icalerror.h" + +#include <assert.h> +#include <stdio.h> /* For snprintf */ + +#define TMP_BUF_SIZE 1024 + +#ifdef WIN32 +#define snprintf _snprintf +#define strcasecmp stricmp +#endif + + +/* Define the structs for the restrictions. these data are filled out +in machine generated code below */ + +struct icalrestriction_property_record; + +typedef const char* (*restriction_func)(struct icalrestriction_property_record* rec,icalcomponent* comp,icalproperty* prop); + + +typedef struct icalrestriction_property_record { + icalproperty_method method; + icalcomponent_kind component; + icalproperty_kind property; + icalrestriction_kind restriction; + restriction_func function; +} icalrestriction_property_record; + + +typedef struct icalrestriction_component_record { + icalproperty_method method; + icalcomponent_kind component; + icalcomponent_kind subcomponent; + icalrestriction_kind restriction; + restriction_func function; +} icalrestriction_component_record; + +icalrestriction_property_record* +icalrestriction_get_property_restriction(icalproperty_method method, + icalcomponent_kind component, + icalproperty_kind property); +icalrestriction_component_record* +icalrestriction_get_component_restriction(icalproperty_method method, + icalcomponent_kind component, + icalcomponent_kind subcomponent); + +icalrestriction_component_record icalrestriction_component_records[]; +icalrestriction_property_record icalrestriction_property_records[]; + +icalrestriction_property_record null_prop_record = {ICAL_METHOD_NONE,ICAL_NO_COMPONENT,ICAL_NO_PROPERTY,ICAL_RESTRICTION_UNKNOWN,0}; +icalrestriction_component_record null_comp_record = {ICAL_METHOD_NONE,ICAL_NO_COMPONENT,ICAL_NO_COMPONENT,ICAL_RESTRICTION_UNKNOWN,0}; + + +/** Each row gives the result of comparing a restriction against a count. + The columns in each row represent 0,1,2+. '-1' indicates + 'invalid, 'don't care' or 'needs more analysis' So, for + ICAL_RESTRICTION_ONE, if there is 1 of a property with that + restriction, it passes, but if there are 0 or 2+, it fails. */ + +char compare_map[ICAL_RESTRICTION_UNKNOWN+1][3] = { + { 1, 1, 1},/*ICAL_RESTRICTION_NONE*/ + { 1, 0, 0},/*ICAL_RESTRICTION_ZERO*/ + { 0, 1, 0},/*ICAL_RESTRICTION_ONE*/ + { 1, 1, 1},/*ICAL_RESTRICTION_ZEROPLUS*/ + { 0, 1, 1},/*ICAL_RESTRICTION_ONEPLUS*/ + { 1, 1, 0},/*ICAL_RESTRICTION_ZEROORONE*/ + { 1, 1, 0},/*ICAL_RESTRICTION_ONEEXCLUSIVE*/ + { 1, 1, 0},/*ICAL_RESTRICTION_ONEMUTUAL*/ + { 1, 1, 1} /*ICAL_RESTRICTION_UNKNOWN*/ +}; + +char restr_string_map[ICAL_RESTRICTION_UNKNOWN+1][60] = { + "unknown number",/*ICAL_RESTRICTION_NONE*/ + "0",/*ICAL_RESTRICTION_ZERO*/ + "1",/*ICAL_RESTRICTION_ONE*/ + "zero or more",/*ICAL_RESTRICTION_ZEROPLUS*/ + "one or more" ,/*ICAL_RESTRICTION_ONEPLUS*/ + "zero or one",/*ICAL_RESTRICTION_ZEROORONE*/ + "zero or one, exclusive with another property",/*ICAL_RESTRICTION_ONEEXCLUSIVE*/ + "zero or one, mutual with another property",/*ICAL_RESTRICTION_ONEMUTUAL*/ + "unknown number" /*ICAL_RESTRICTION_UNKNOWN*/ +}; + + +int +icalrestriction_compare(icalrestriction_kind restr, int count){ + + /* restr is an unsigned int, ICAL_RESTRICTION_NONE == 0, so the check will always return false */ + if ( /*restr < ICAL_RESTRICTION_NONE ||*/ restr > ICAL_RESTRICTION_UNKNOWN + || count < 0){ + return -1; + } + + if (count > 2) { + count = 2; + } + + return compare_map[restr][count]; + +} + +/* Special case routines */ + +const char* icalrestriction_may_be_draft_final_canceled( + icalrestriction_property_record *rec, + icalcomponent* comp, + icalproperty* prop) +{ + icalproperty_status stat = icalproperty_get_status(prop); + + (void)rec; + (void)comp; + + if( !( stat == ICAL_STATUS_DRAFT || + stat == ICAL_STATUS_FINAL || + stat == ICAL_STATUS_CANCELLED )){ + + return "Failed iTIP restrictions for STATUS property. Value must be one of DRAFT, FINAL, or CANCELED"; + + } + + return 0; +} + +const char* icalrestriction_may_be_comp_need_process( + icalrestriction_property_record *rec, + icalcomponent* comp, + icalproperty* prop) +{ + icalproperty_status stat = icalproperty_get_status(prop); + + (void)rec; + (void)comp; + + if( !( stat == ICAL_STATUS_COMPLETED || + stat == ICAL_STATUS_NEEDSACTION || + stat == ICAL_STATUS_INPROCESS )){ + + return "Failed iTIP restrictions for STATUS property. Value must be one of COMPLETED, NEEDS-ACTION or IN-PROCESS"; + + } + + return 0; +} +const char* icalrestriction_may_be_tent_conf(icalrestriction_property_record *rec, + icalcomponent* comp, + icalproperty* prop) +{ + icalproperty_status stat = icalproperty_get_status(prop); + + (void)rec; + (void)comp; + + if( !( stat == ICAL_STATUS_TENTATIVE || + stat == ICAL_STATUS_CONFIRMED )){ + + return "Failed iTIP restrictions for STATUS property. Value must be one of TENTATIVE or CONFIRMED"; + + } + + return 0; +} +const char* icalrestriction_may_be_tent_conf_cancel( + icalrestriction_property_record *rec, + icalcomponent* comp, + icalproperty* prop) +{ + icalproperty_status stat = icalproperty_get_status(prop); + + (void)rec; + (void)comp; + + if( !( stat == ICAL_STATUS_TENTATIVE || + stat == ICAL_STATUS_CONFIRMED || + stat == ICAL_STATUS_CANCELLED )){ + + return "Failed iTIP restrictions for STATUS property. Value must be one of TENTATIVE, CONFIRMED or CANCELED"; + + } + + return 0; +} + +const char* icalrestriction_must_be_cancel_if_present( + icalrestriction_property_record *rec, + icalcomponent* comp, + icalproperty* prop) +{ + /* This routine will not be called if prop == 0 */ + icalproperty_status stat = icalproperty_get_status(prop); + + (void)rec; + (void)comp; + + if( stat != ICAL_STATUS_CANCELLED) + { + return "Failed iTIP restrictions for STATUS property. Value must be CANCELLED"; + + } + + + return 0; +} + +const char* icalrestriction_must_be_canceled_no_attendee( + icalrestriction_property_record *rec, + icalcomponent* comp, + icalproperty* prop) +{ + (void)rec; + (void)comp; + (void)prop; + + /* Hack. see rfc2446, 3.2.5 CANCEL for porperty STATUS. I don't + understand the note */ + + return 0; +} +const char* icalrestriction_must_be_recurring(icalrestriction_property_record *rec, + icalcomponent* comp, + icalproperty* prop){ + /* Hack */ + (void)rec; + (void)comp; + (void)prop; + return 0; +} +const char* icalrestriction_must_have_duration(icalrestriction_property_record *rec, + icalcomponent* comp, + icalproperty* prop){ + + (void)rec; + (void)prop; + + if( !icalcomponent_get_first_property(comp,ICAL_DURATION_PROPERTY)){ + + return "Failed iTIP restrictions for DURATION property. This component must have a DURATION property"; + + } + + return 0; +} +const char* icalrestriction_must_have_repeat(icalrestriction_property_record *rec, + icalcomponent* comp, + icalproperty* prop){ + (void)rec; + (void)prop; + if( !icalcomponent_get_first_property(comp,ICAL_REPEAT_PROPERTY)){ + + return "Failed iTIP restrictions for REPEAT property. This component must have a REPEAT property"; + + } + + return 0; +} +const char* icalrestriction_must_if_tz_ref(icalrestriction_property_record *rec, + icalcomponent* comp, + icalproperty* prop){ + + /* Hack */ + (void)rec; + (void)comp; + (void)prop; + return 0; +} +const char* icalrestriction_no_dtend(icalrestriction_property_record *rec, + icalcomponent* comp, + icalproperty* prop){ + + (void)rec; + (void)prop; + if( !icalcomponent_get_first_property(comp,ICAL_DTEND_PROPERTY)){ + + return "Failed iTIP restrictions for DTEND property. The component must not have both DURATION and DTEND"; + + } + + return 0; +} +const char* icalrestriction_no_duration(icalrestriction_property_record *rec, + icalcomponent* comp, + icalproperty* prop){ + (void)rec; + (void)comp; + (void)prop; + + /* _no_dtend takes care of this one */ + return 0; +} +const char* icalrestriction_must_be_email( + icalrestriction_property_record *rec, + icalcomponent* comp, + icalproperty* prop) +{ + icalproperty_status stat = icalproperty_get_action(prop); + + (void)rec; + (void)comp; + + if( !( stat == ICAL_ACTION_EMAIL)){ + + return "Failed iTIP restrictions for ACTION property. Value must be EMAIL."; + + } + + return 0; +} + +int icalrestriction_check_component(icalproperty_method method, + icalcomponent* comp) +{ + icalproperty_kind kind; + icalcomponent_kind comp_kind; + icalrestriction_kind restr; + icalrestriction_property_record *prop_record; + const char* funcr = 0; + icalproperty *prop; + + int count; + int compare; + int valid = 1; + + comp_kind = icalcomponent_isa(comp); + + /* Check all of the properties in this component */ + + for(kind = ICAL_ANY_PROPERTY+1; kind != ICAL_NO_PROPERTY; kind++){ + count = icalcomponent_count_properties(comp, kind); + + prop_record = icalrestriction_get_property_restriction(method, + comp_kind, + kind); + + restr = prop_record->restriction; + + if(restr == ICAL_RESTRICTION_ONEEXCLUSIVE || + restr == ICAL_RESTRICTION_ONEMUTUAL) { + + /* First treat is as a 0/1 restriction */ + restr = ICAL_RESTRICTION_ZEROORONE; + compare = icalrestriction_compare(restr,count); + + } else { + + compare = icalrestriction_compare(restr,count); + } + + assert(compare != -1); + + if (compare == 0){ + char temp[TMP_BUF_SIZE]; + + snprintf(temp, TMP_BUF_SIZE,"Failed iTIP restrictions for %s property. Expected %s instances of the property and got %d", + icalenum_property_kind_to_string(kind), + restr_string_map[restr], count); + + icalcomponent_add_property + (comp, + icalproperty_vanew_xlicerror( + temp, + icalparameter_new_xlicerrortype(ICAL_XLICERRORTYPE_INVALIDITIP), + 0)); + } + + + prop = icalcomponent_get_first_property(comp, kind); + + if (prop != 0 && prop_record->function !=0 ){ + funcr = prop_record->function(prop_record,comp,prop); + } + + if(funcr !=0){ + icalcomponent_add_property + (comp, + icalproperty_vanew_xlicerror( + funcr, + icalparameter_new_xlicerrortype( + ICAL_XLICERRORTYPE_INVALIDITIP), + 0)); + + compare = 0; + } + + valid = valid && compare; + } + + + + return valid; + + +} + +int icalrestriction_check(icalcomponent* outer_comp) +{ + icalcomponent_kind comp_kind; + icalproperty_method method; + icalcomponent* inner_comp; + icalproperty *method_prop; + int valid; + + icalerror_check_arg_rz( (outer_comp!=0), "outer comp"); + + + /* Get the Method value from the outer component */ + + comp_kind = icalcomponent_isa(outer_comp); + + if (comp_kind != ICAL_VCALENDAR_COMPONENT){ + icalerror_set_errno(ICAL_BADARG_ERROR); + return 0; + } + + method_prop = icalcomponent_get_first_property(outer_comp, + ICAL_METHOD_PROPERTY); + + if (method_prop == 0){ + method = ICAL_METHOD_NONE; + } else { + method = icalproperty_get_method(method_prop); + } + + + /* Check the VCALENDAR wrapper */ + valid = icalrestriction_check_component(ICAL_METHOD_NONE,outer_comp); + + + /* Now check the inner components */ + + for(inner_comp= icalcomponent_get_first_component(outer_comp, + ICAL_ANY_COMPONENT); + inner_comp != 0; + inner_comp= icalcomponent_get_next_component(outer_comp, + ICAL_ANY_COMPONENT)){ + + valid = valid && icalrestriction_check_component(method,inner_comp); + + } + + + return valid; + +} + +icalrestriction_property_record* +icalrestriction_get_property_restriction(icalproperty_method method, + icalcomponent_kind component, + icalproperty_kind property) +{ + int i; + + for(i = 0; + icalrestriction_property_records[i].restriction != ICAL_RESTRICTION_NONE; + i++){ + + if (method == icalrestriction_property_records[i].method && + component == icalrestriction_property_records[i].component && + property == icalrestriction_property_records[i].property ){ + return &icalrestriction_property_records[i]; + } + } + + return &null_prop_record; +} + + +icalrestriction_component_record* +icalrestriction_get_component_restriction(icalproperty_method method, + icalcomponent_kind component, + icalcomponent_kind subcomponent) +{ + + int i; + + for(i = 0; + icalrestriction_component_records[i].restriction != ICAL_RESTRICTION_NONE; + i++){ + + if (method == icalrestriction_component_records[i].method && + component == icalrestriction_component_records[i].component && + subcomponent == icalrestriction_component_records[i].subcomponent ){ + return &icalrestriction_component_records[i]; + } + } + + return &null_comp_record; +} + diff --git a/libkcal/libical/src/libical/icalrestriction.h b/libkcal/libical/src/libical/icalrestriction.h new file mode 100644 index 000000000..b5281215a --- /dev/null +++ b/libkcal/libical/src/libical/icalrestriction.h @@ -0,0 +1,63 @@ +/* -*- Mode: C -*- */ +/*====================================================================== + FILE: icalrestriction.h + CREATOR: eric 24 April 1999 + + + + (C) COPYRIGHT 2000, Eric Busboom <eric@softwarestudio.org> + http://www.softwarestudio.org + + This program is free software; you can redistribute it and/or modify + it under the terms of either: + + The LGPL as published by the Free Software Foundation, version + 2.1, available at: http://www.fsf.org/copyleft/lesser.html + + Or: + + The Mozilla Public License Version 1.0. You may obtain a copy of + the License at http://www.mozilla.org/MPL/ + + The original code is icalrestriction.h + + Contributions from: + Graham Davison (g.m.davison@computer.org) + + +======================================================================*/ + +#include "icalproperty.h" +#include "icalcomponent.h" + +#ifndef ICALRESTRICTION_H +#define ICALRESTRICTION_H + +/* These must stay in this order for icalrestriction_compare to work */ +typedef enum icalrestriction_kind { + ICAL_RESTRICTION_NONE=0, /* 0 */ + ICAL_RESTRICTION_ZERO, /* 1 */ + ICAL_RESTRICTION_ONE, /* 2 */ + ICAL_RESTRICTION_ZEROPLUS, /* 3 */ + ICAL_RESTRICTION_ONEPLUS, /* 4 */ + ICAL_RESTRICTION_ZEROORONE, /* 5 */ + ICAL_RESTRICTION_ONEEXCLUSIVE, /* 6 */ + ICAL_RESTRICTION_ONEMUTUAL, /* 7 */ + ICAL_RESTRICTION_UNKNOWN /* 8 */ +} icalrestriction_kind; + +int +icalrestriction_compare(icalrestriction_kind restr, int count); + + +int +icalrestriction_is_parameter_allowed(icalproperty_kind property, + icalparameter_kind parameter); + +int icalrestriction_check(icalcomponent* comp); + + +#endif /* !ICALRESTRICTION_H */ + + + diff --git a/libkcal/libical/src/libical/icaltime.c b/libkcal/libical/src/libical/icaltime.c new file mode 100644 index 000000000..514322a36 --- /dev/null +++ b/libkcal/libical/src/libical/icaltime.c @@ -0,0 +1,1014 @@ +/* -*- Mode: C -*- + ====================================================================== + FILE: icaltime.c + CREATOR: eric 02 June 2000 + + + (C) COPYRIGHT 2000, Eric Busboom <eric@softwarestudio.org> + http://www.softwarestudio.org + + This program is free software; you can redistribute it and/or modify + it under the terms of either: + + The LGPL as published by the Free Software Foundation, version + 2.1, available at: http://www.fsf.org/copyleft/lesser.html + + Or: + + The Mozilla Public License Version 1.0. You may obtain a copy of + the License at http://www.mozilla.org/MPL/ + + The Original Code is eric. The Initial Developer of the Original + Code is Eric Busboom + + + ======================================================================*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "icaltime.h" +#include <assert.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <time.h> + +#include "astime.h" /* Julian data handling routines */ + +#include "icalerror.h" +#include "icalmemory.h" + +#include "icaltimezone.h" +#include "icalvalue.h" + +#ifdef WIN32 +#include <Windows.h> + +#define snprintf _snprintf +#define strcasecmp stricmp +#endif + +/* + * Function to convert a struct tm time specification + * to an ANSI time_t using the specified time zone. + * This is different from the standard mktime() function + * in that we don't want the automatic adjustments for + * local daylight savings time applied to the result. + * This function expects well-formed input. + */ +static time_t make_time(struct tm *tm, int tzm) +{ + time_t tim; + + static int days[] = { -1, 30, 58, 89, 119, 150, 180, 211, 242, 272, 303, 333, 364 }; + + /* check that year specification within range */ + + if (tm->tm_year < 70 || tm->tm_year > 138) + return((time_t) -1); + + /* check that month specification within range */ + + if (tm->tm_mon < 0 || tm->tm_mon > 11) + return((time_t) -1); + + /* check for upper bound of Jan 17, 2038 (to avoid possibility of + 32-bit arithmetic overflow) */ + + if (tm->tm_year == 138) { + if (tm->tm_mon > 0) + return((time_t) -1); + else if (tm->tm_mday > 17) + return((time_t) -1); + } + + /* + * calculate elapsed days since start of the epoch (midnight Jan + * 1st, 1970 UTC) 17 = number of leap years between 1900 and 1970 + * (number of leap days to subtract) + */ + + tim = (tm->tm_year - 70) * 365 + ((tm->tm_year - 1) / 4) - 17; + + /* add number of days elapsed in the current year */ + + tim += days[tm->tm_mon]; + + /* check and adjust for leap years (the leap year check only valid + during the 32-bit era */ + + if ((tm->tm_year & 3) == 0 && tm->tm_mon > 1) + tim += 1; + + /* elapsed days to current date */ + + tim += tm->tm_mday; + + + /* calculate elapsed hours since start of the epoch */ + + tim = tim * 24 + tm->tm_hour; + + /* calculate elapsed minutes since start of the epoch */ + + tim = tim * 60 + tm->tm_min; + + /* adjust per time zone specification */ + + tim -= tzm; + + /* calculate elapsed seconds since start of the epoch */ + + tim = tim * 60 + tm->tm_sec; + + /* return number of seconds since start of the epoch */ + + return(tim); +} + +/** @brief Constructor (deprecated). + * + * Convert seconds past UNIX epoch to a timetype. + * + * @deprecated This constructor is deprecated and shouldn't be used in + * new software. Use icaltime_from_timet_with_zone(time_t, int, + * icaltimezone *) instead. In the meantime, calls to this method + * return a floating time, which can always be converted to a local + * time with an appropriate call to icaltime_convert_to_zone(). + */ + +struct icaltimetype +icaltime_from_timet(const time_t tm, const int is_date) +{ +#ifndef NO_WARN_DEPRECATED + icalerror_warn("icaltime_from_timet() is DEPRECATED, use icaltime_from_timet_with_zone() instead"); +#endif + + return icaltime_from_timet_with_zone(tm, is_date, 0); +} + + +/** @brief Constructor. + * + * @param tm The time + * @param is_date Boolean: 1 means we should treat tm as a DATE + * @param zone The timezone tm is in, NULL means to treat tm as a + * floating time + * + * Return a new icaltime instance, initialized to the given time + * expressed as seconds past UNIX epoch, optionally using the given + * timezone. + * + * If the caller specifies the is_date param as TRUE, the returned + * object is of DATE type, otherwise the input is meant to be of + * DATE-TIME type. + * If the zone is not specified (NULL zone param) the time is taken + * to be floating, that is, valid in any timezone. Note that, in + * addition to the uses specified in [RFC2445], this can be used + * when doing simple math on couples of times. + * If the zone is specified (UTC or otherwise), it's stored in the + * object and it's used as the native timezone for this object. + * This means that the caller can convert this time to a different + * target timezone with no need to store the source timezone. + * + */ +struct icaltimetype +icaltime_from_timet_with_zone(const time_t tm, const int is_date, + icaltimezone *zone) +{ + struct icaltimetype tt = icaltime_null_time(); + struct tm t; + icaltimezone *utc_zone; + + /* Convert the time_t to a struct tm. We can trust gmtime for this. */ +#ifdef HAVE_GMTIME_R + gmtime_r(&tm, &t); +#else + { + struct tm *t_ptr = gmtime(&tm); + t = *t_ptr; + } +#endif + + tt.year = t.tm_year + 1900; + tt.month = t.tm_mon + 1; + tt.day = t.tm_mday; + + if (is_date) { + tt.is_date = 1; + return tt; + } + + tt.hour = t.tm_hour; + tt.minute = t.tm_min; + tt.second = t.tm_sec; + + /* If it's a floating time, we don't do any conversion. */ + if (zone == NULL) { + return tt; + } + + utc_zone = icaltimezone_get_utc_timezone (); + tt.is_utc = (zone == utc_zone) ? 1 : 0; + tt.zone = zone; + + return tt; +} + +/** @brief Convenience constructor. + * + * Returns the current time in the given timezone, as an icaltimetype. + */ +struct icaltimetype icaltime_current_time_with_zone(icaltimezone *zone) +{ + return icaltime_from_timet_with_zone (time (NULL), 0, zone); +} + +/** @brief Convenience constructor. + * + * Returns the current day as an icaltimetype, with is_date set. + */ +struct icaltimetype icaltime_today(void) +{ + return icaltime_from_timet_with_zone (time (NULL), 1, NULL); +} + +/** @brief Return the time as seconds past the UNIX epoch + */ +time_t icaltime_as_timet(const struct icaltimetype tt) +{ + struct tm stm; + time_t t; + + /* If the time is the special null time, return 0. */ + if (icaltime_is_null_time(tt)) { + return 0; + } + + /* Copy the icaltimetype to a struct tm. */ + memset (&stm, 0, sizeof (struct tm)); + + stm.tm_sec = tt.second; + stm.tm_min = tt.minute; + stm.tm_hour = tt.hour; + stm.tm_mday = tt.day; + stm.tm_mon = tt.month-1; + stm.tm_year = tt.year-1900; + stm.tm_isdst = -1; + + t = make_time(&stm, 0); + + return t; + +} + +/** @brief Return the time as seconds past the UNIX epoch, using the + * given timezone. + * + * This convenience method combines a call to icaltime_convert() with + * a call to icaltime_as_timet(). + * If the input timezone is null, no conversion is done; that is, the + * time is simply returned as time_t in its native timezone. + */ +time_t icaltime_as_timet_with_zone(const struct icaltimetype _tt, + icaltimezone *zone) +{ + struct icaltimetype tt = _tt; + struct tm stm; + time_t t; + + /* If the time is the special null time, return 0. */ + if (icaltime_is_null_time(tt)) { + return 0; + } + + if (zone != NULL) { + tt = icaltime_convert_to_zone(_tt, zone); + } + + /* Copy the icaltimetype to a struct tm. */ + memset (&stm, 0, sizeof (struct tm)); + + stm.tm_sec = tt.second; + stm.tm_min = tt.minute; + stm.tm_hour = tt.hour; + stm.tm_mday = tt.day; + stm.tm_mon = tt.month-1; + stm.tm_year = tt.year-1900; + stm.tm_isdst = -1; + + t = make_time(&stm, 0); + + return t; +} + +/** + * Return a string represention of the time, in RFC2445 format. The + * string is owned by libical + */ +const char* icaltime_as_ical_string(const struct icaltimetype tt) +{ + size_t size = 17; + char* buf = icalmemory_new_buffer(size); + + if(tt.is_date){ + snprintf(buf, size,"%04d%02d%02d",tt.year,tt.month,tt.day); + } else { + const char* fmt; + if(tt.is_utc){ + fmt = "%04d%02d%02dT%02d%02d%02dZ"; + } else { + fmt = "%04d%02d%02dT%02d%02d%02d"; + } + snprintf(buf, size,fmt,tt.year,tt.month,tt.day, + tt.hour,tt.minute,tt.second); + } + + icalmemory_add_tmp_buffer(buf); + + return buf; + +} + +/** + * Reset all of the time components to be in their normal ranges. For + * instance, given a time with minutes=70, the minutes will be reduces + * to 10, and the hour incremented. This allows the caller to do + * arithmetic on times without worrying about overflow or + * underflow. + * + * Implementation note: we call icaltime_adjust() with no adjustment. + */ +struct icaltimetype icaltime_normalize(const struct icaltimetype tt) +{ + struct icaltimetype ret = tt; + icaltime_adjust(&ret, 0, 0, 0, 0); + return ret; +} + + + +/** @brief Contructor. + * + * Create a time from an ISO format string. + * + * @todo If the given string specifies a DATE-TIME not in UTC, there + * is no way to know if this is a floating time or really refers to a + * timezone. We should probably add a new constructor: + * icaltime_from_string_with_zone() + */ +struct icaltimetype icaltime_from_string(const char* str) +{ + struct icaltimetype tt = icaltime_null_time(); + int size; + + icalerror_check_arg_re(str!=0,"str",icaltime_null_time()); + + size = strlen(str); + + if(size == 15) { /* floating time */ + tt.is_utc = 0; + tt.is_date = 0; + } else if (size == 16) { /* UTC time, ends in 'Z'*/ + if(str[15] != 'Z') + goto errorlabel; + + tt.is_utc = 1; + tt.zone = icaltimezone_get_utc_timezone(); + tt.is_date = 0; + } else if (size == 8) { /* A DATE */ + tt.is_utc = 1; + tt.is_date = 1; + } else { /* error */ + goto errorlabel; + } + + if(tt.is_date == 1){ + if (sscanf(str,"%04d%02d%02d",&tt.year,&tt.month,&tt.day) < 3) + goto errorlabel; + } else { + char tsep; + if (sscanf(str,"%04d%02d%02d%c%02d%02d%02d",&tt.year,&tt.month,&tt.day, + &tsep,&tt.hour,&tt.minute,&tt.second) < 7) + goto errorlabel; + + if(tsep != 'T') + goto errorlabel; + } + + return tt; + +errorlabel: + icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR); + return icaltime_null_time(); +} + + +/* Returns whether the specified year is a leap year. Year is the normal year, + e.g. 2001. */ +int +icaltime_is_leap_year (const int year) +{ + + if (year <= 1752) + return (year % 4 == 0); + else + return ( (year % 4==0) && (year % 100 !=0 )) || (year % 400 == 0); +} + +static int _days_in_month[] = {0,31,28,31,30,31,30,31,31,30,31,30,31}; + +int icaltime_days_in_month(const int month, const int year) +{ + + int days = _days_in_month[month]; + + assert(month > 0); + assert(month <= 12); + + if( month == 2){ + days += icaltime_is_leap_year(year); + } + + return days; +} + +/* 1-> Sunday, 7->Saturday */ +int icaltime_day_of_week(const struct icaltimetype t){ + UTinstant jt; + + memset(&jt,0,sizeof(UTinstant)); + + jt.year = t.year; + jt.month = t.month; + jt.day = t.day; + jt.i_hour = 0; + jt.i_minute = 0; + jt.i_second = 0; + + juldat(&jt); + + return jt.weekday + 1; +} + +/** Day of the year that the first day of the week (Sunday) is on. + * + * @todo Doesn't take into account different week start days. + */ +int icaltime_start_doy_of_week(const struct icaltimetype t){ + UTinstant jt; + + memset(&jt,0,sizeof(UTinstant)); + + jt.year = t.year; + jt.month = t.month; + jt.day = t.day; + jt.i_hour = 0; + jt.i_minute = 0; + jt.i_second = 0; + + juldat(&jt); + caldat(&jt); + + return jt.day_of_year - jt.weekday; +} + +/** + * @todo Doesn't take into account the start day of the + * week. strftime assumes that weeks start on Monday. + */ +int icaltime_week_number(const struct icaltimetype ictt) +{ + UTinstant jt; + + memset(&jt,0,sizeof(UTinstant)); + + jt.year = ictt.year; + jt.month = ictt.month; + jt.day = ictt.day; + jt.i_hour = 0; + jt.i_minute = 0; + jt.i_second = 0; + + juldat(&jt); + caldat(&jt); + + return (jt.day_of_year - jt.weekday) / 7; +} + +/* The first array is for non-leap years, the second for leap years*/ +static const int days_in_year[2][13] = +{ /* jan feb mar apr may jun jul aug sep oct nov dec */ + { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }, + { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 } +}; + +/** + * Returns the day of the year, counting from 1 (Jan 1st). + */ +int icaltime_day_of_year(const struct icaltimetype t){ + int is_leap = icaltime_is_leap_year (t.year); + + return days_in_year[is_leap][t.month - 1] + t.day; +} + +/** @brief Contructor. + * + * Create a new time, given a day of year and a year. + */ +/* Jan 1 is day #1, not 0 */ +struct icaltimetype icaltime_from_day_of_year(const int _doy, const int _year) +{ + struct icaltimetype tt = icaltime_null_date(); + int is_leap; + int month; + int doy = _doy; + int year = _year; + + is_leap = icaltime_is_leap_year(year); + + /* Zero and neg numbers represent days of the previous year */ + if(doy <1){ + year--; + is_leap = icaltime_is_leap_year(year); + doy += days_in_year[is_leap][12]; + } else if(doy > days_in_year[is_leap][12]){ + /* Move on to the next year*/ + is_leap = icaltime_is_leap_year(year); + doy -= days_in_year[is_leap][12]; + year++; + } + + tt.year = year; + + for (month = 11; month >= 0; month--) { + if (doy > days_in_year[is_leap][month]) { + tt.month = month + 1; + tt.day = doy - days_in_year[is_leap][month]; + break; + } + } + + return tt; +} + +/** @brief Constructor. + * + * Return a null time, which indicates no time has been set. + * This time represents the beginning of the epoch. + */ +struct icaltimetype icaltime_null_time(void) +{ + struct icaltimetype t; + memset(&t,0,sizeof(struct icaltimetype)); + + return t; +} + +/** @brief Constructor. + * + * Return a null date, which indicates no time has been set. + */ +struct icaltimetype icaltime_null_date(void) +{ + struct icaltimetype t; + memset(&t,0,sizeof(struct icaltimetype)); + + t.is_date = 1; + + /* + * Init to -1 to match what icalyacc.y used to do. + * Does anything depend on this? + */ + t.hour = -1; + t.minute = -1; + t.second = -1; + + return t; +} + + +/** + * Returns false if the time is clearly invalid, but is not null. This + * is usually the result of creating a new time type buy not clearing + * it, or setting one of the flags to an illegal value. + */ +int icaltime_is_valid_time(const struct icaltimetype t){ + if(t.is_utc > 1 || t.is_utc < 0 || + t.year < 0 || t.year > 3000 || + t.is_date > 1 || t.is_date < 0){ + return 0; + } else { + return 1; + } + +} + +/** @brief Returns true if time is a DATE + */ +int icaltime_is_date(const struct icaltimetype t) { + + return t.is_date; +} + +/** @brief Returns true if time is relative to UTC zone + * + * @todo We should only check the zone + */ +int icaltime_is_utc(const struct icaltimetype t) { + + return t.is_utc; +} + +/** + * Return true if the time is null. + */ +int icaltime_is_null_time(const struct icaltimetype t) +{ + if (t.second +t.minute+t.hour+t.day+t.month+t.year == 0){ + return 1; + } + + return 0; + +} + +/** + * Return -1, 0, or 1 to indicate that a<b, a==b, or a>b. + * This calls icaltime_compare function after converting them to the utc + * timezone. + */ + +int icaltime_compare(const struct icaltimetype a_in, const struct icaltimetype b_in) +{ + int retval = 0; + struct icaltimetype a, b; + + a = icaltime_convert_to_zone(a_in, icaltimezone_get_utc_timezone()); + b = icaltime_convert_to_zone(b_in, icaltimezone_get_utc_timezone()); + + if (a.year > b.year) + retval = 1; + else if (a.year < b.year) + retval = -1; + + else if (a.month > b.month) + retval = 1; + else if (a.month < b.month) + retval = -1; + + else if (a.day > b.day) + retval = 1; + else if (a.day < b.day) + retval = -1; + + /* if both are dates, we are done */ + if (a.is_date && b.is_date) + return retval; + + /* else, if we already found a difference, we are done */ + else if (retval != 0) + return retval; + + /* else, if only one is a date (and we already know the date part is equal), + then the other is greater */ + else if (b.is_date) + retval = 1; + else if (a.is_date) + retval = -1; + + else if (a.hour > b.hour) + retval = 1; + else if (a.hour < b.hour) + retval = -1; + + else if (a.minute > b.minute) + retval = 1; + else if (a.minute < b.minute) + retval = -1; + + else if (a.second > b.second) + retval = 1; + else if (a.second < b.second) + retval = -1; + + return retval; +} + +/** + * like icaltime_compare, but only use the date parts. + */ + +int +icaltime_compare_date_only(const struct icaltimetype a_in, const struct icaltimetype b_in) +{ + int retval; + struct icaltimetype a, b; + + a = icaltime_convert_to_zone(a_in, icaltimezone_get_utc_timezone()); + b = icaltime_convert_to_zone(b_in, icaltimezone_get_utc_timezone()); + + if (a.year > b.year) + retval = 1; + else if (a.year < b.year) + retval = -1; + + else if (a.month > b.month) + retval = 1; + else if (a.month < b.month) + retval = -1; + + else if (a.day > b.day) + retval = 1; + else if (a.day < b.day) + retval = -1; + + else + retval = 0; + + return retval; +} + +/* These are defined in icalduration.c: +struct icaltimetype icaltime_add(struct icaltimetype t, + struct icaldurationtype d) +struct icaldurationtype icaltime_subtract(struct icaltimetype t1, + struct icaltimetype t2) +*/ + + + +/** @brief Internal, shouldn't be part of the public API + * + * Adds (or subtracts) a time from a icaltimetype. + * NOTE: This function is exactly the same as icaltimezone_adjust_change() + * except for the type of the first parameter. + */ +void +icaltime_adjust(struct icaltimetype *tt, const int days, const int hours, + const int minutes, const int seconds) { + + int second, minute, hour, day; + int minutes_overflow, hours_overflow, days_overflow, years_overflow; + int days_in_month; + + /* Add on the seconds. */ + second = tt->second + seconds; + tt->second = second % 60; + minutes_overflow = second / 60; + if (tt->second < 0) { + tt->second += 60; + minutes_overflow--; + } + + /* Add on the minutes. */ + minute = tt->minute + minutes + minutes_overflow; + tt->minute = minute % 60; + hours_overflow = minute / 60; + if (tt->minute < 0) { + tt->minute += 60; + hours_overflow--; + } + + /* Add on the hours. */ + hour = tt->hour + hours + hours_overflow; + tt->hour = hour % 24; + days_overflow = hour / 24; + if (tt->hour < 0) { + tt->hour += 24; + days_overflow--; + } + + /* Normalize the month. We do this before handling the day since we may + need to know what month it is to get the number of days in it. + Note that months are 1 to 12, so we have to be a bit careful. */ + if (tt->month >= 13) { + years_overflow = (tt->month - 1) / 12; + tt->year += years_overflow; + tt->month -= years_overflow * 12; + } else if (tt->month <= 0) { + /* 0 to -11 is -1 year out, -12 to -23 is -2 years. */ + years_overflow = (tt->month / 12) - 1; + tt->year += years_overflow; + tt->month -= years_overflow * 12; + } + + /* Add on the days. */ + day = tt->day + days + days_overflow; + if (day > 0) { + for (;;) { + days_in_month = icaltime_days_in_month (tt->month, tt->year); + if (day <= days_in_month) + break; + + tt->month++; + if (tt->month >= 13) { + tt->year++; + tt->month = 1; + } + + day -= days_in_month; + } + } else { + while (day <= 0) { + if (tt->month == 1) { + tt->year--; + tt->month = 12; + } else { + tt->month--; + } + + day += icaltime_days_in_month (tt->month, tt->year); + } + } + tt->day = day; +} + +/** @brief Convert time to a given timezone + * + * Convert a time from its native timezone to a given timezone. + * + * If tt is a date, the returned time is an exact + * copy of the input. If it's a floating time, the returned object + * represents the same time translated to the given timezone. + * Otherwise the time will be converted to the new + * time zone, and its native timezone set to the right timezone. + */ +struct icaltimetype icaltime_convert_to_zone(const struct icaltimetype tt, + icaltimezone *zone) { + + struct icaltimetype ret = tt; + + /* If it's a date do nothing */ + if (tt.is_date) { + return ret; + } + + if (tt.zone == zone) { + return ret; + } + + /* If it's a floating time we don't want to adjust the time */ + if (tt.zone != NULL) { + icaltimezone_convert_time(&ret, tt.zone, zone); + } + + ret.zone = zone; + if (zone == icaltimezone_get_utc_timezone()) { + ret.is_utc = 1; + } else { + ret.is_utc = 0; + } + + return ret; +} + +icaltimezone * +icaltime_get_timezone(const struct icaltimetype t) { + + return t.zone; +} + +const char * +icaltime_get_tzid(const struct icaltimetype t) { + + if (t.zone != NULL) { + return icaltimezone_get_tzid(t.zone); + } else { + return NULL; + } +} + +/** @brief Set the timezone + * + * Force the icaltime to be interpreted relative to another timezone. + * If you need to do timezone conversion, applying offset adjustments, + * then you should use icaltime_convert_to_timezone instead. + */ +struct icaltimetype +icaltime_set_timezone(struct icaltimetype *t, icaltimezone *zone) { + + /* If it's a date do nothing */ + if (t->is_date) { + return *t; + } + + if (t->zone == zone) { + return *t; + } + + t->zone = zone; + if (zone == icaltimezone_get_utc_timezone()) { + t->is_utc = 1; + } else { + t->is_utc = 0; + } + + return *t; +} + + +/** + * @brief builds an icaltimespan given a start time, end time and busy value. + * + * @param dtstart The beginning time of the span, can be a date-time + * or just a date. + * @param dtend The end time of the span. + * @param is_busy A boolean value, 0/1. + * @return A span using the supplied values. + * + * returned span contains times specified in UTC. + */ + +icaltime_span icaltime_span_new(struct icaltimetype dtstart, + struct icaltimetype dtend, + int is_busy) +{ + icaltime_span span; + + span.is_busy = is_busy; + + span.start = icaltime_as_timet_with_zone(dtstart, + icaltimezone_get_utc_timezone()); + + if (icaltime_is_null_time(dtend)) { + if (!icaltime_is_date(dtstart)) { + /* If dtstart is a DATE-TIME and there is no DTEND nor DURATION + it takes no time */ + span.end = span.start; + return span; + } else { + dtend = dtstart; + } + } + + span.end = icaltime_as_timet_with_zone(dtend, icaltimezone_get_utc_timezone()); + + if (icaltime_is_date(dtstart)) { + /* no time specified, go until the end of the day..*/ + span.end += 60*60*24 - 1; + } + return span; +} + + +/** @brief Returns true if the two spans overlap + * + * @param s1 1st span to test + * @param s2 2nd span to test + * @return boolean value + * + * The result is calculated by testing if the start time of s1 is contained + * by the s2 span, or if the end time of s1 is contained by the s2 span. + * + * Also returns true if the spans are equal. + * + * Note, this will return false if the spans are adjacent. + */ + +int icaltime_span_overlaps(icaltime_span *s1, + icaltime_span *s2) +{ + /* s1->start in s2 */ + if (s1->start > s2->start && s1->start < s2->end) + return 1; + + /* s1->end in s2 */ + if (s1->end > s2->start && s1->end < s2->end) + return 1; + + /* s2->start in s1 */ + if (s2->start > s1->start && s2->start < s1->end) + return 1; + + /* s2->end in s1 */ + if (s2->end > s1->start && s2->end < s1->end) + return 1; + + if (s1->start == s2->start && s1->end == s2->end) + return 1; + + return 0; +} + +/** @brief Returns true if the span is totally within the containing + * span + * + * @param s The span to test for. + * @param container The span to test against. + * @return boolean value. + * + */ + +int icaltime_span_contains(icaltime_span *s, + icaltime_span *container) +{ + + if ((s->start >= container->start && s->start < container->end) && + (s->end <= container->end && s->end > container->start)) + return 1; + + return 0; +} diff --git a/libkcal/libical/src/libical/icaltime.h b/libkcal/libical/src/libical/icaltime.h new file mode 100644 index 000000000..5871529f2 --- /dev/null +++ b/libkcal/libical/src/libical/icaltime.h @@ -0,0 +1,271 @@ +/* -*- Mode: C -*- */ +/*====================================================================== + FILE: icaltime.h + CREATOR: eric 02 June 2000 + + + + (C) COPYRIGHT 2000, Eric Busboom <eric@softwarestudio.org> + http://www.softwarestudio.org + + This program is free software; you can redistribute it and/or modify + it under the terms of either: + + The LGPL as published by the Free Software Foundation, version + 2.1, available at: http://www.fsf.org/copyleft/lesser.html + + Or: + + The Mozilla Public License Version 1.0. You may obtain a copy of + the License at http://www.mozilla.org/MPL/ + + The Original Code is eric. The Initial Developer of the Original + Code is Eric Busboom + + +======================================================================*/ + +/** @file icaltime.h + * @brief struct icaltimetype is a pseudo-object that abstracts time + * handling. + * + * It can represent either a DATE or a DATE-TIME (floating, UTC or in a + * given timezone), and it keeps track internally of its native timezone. + * + * The typical usage is to call the correct constructor specifying the + * desired timezone. If this is not known until a later time, the + * correct behavior is to specify a NULL timezone and call + * icaltime_convert_to_zone() at a later time. + * + * There are several ways to create a new icaltimetype: + * + * - icaltime_null_time() + * - icaltime_null_date() + * - icaltime_current_time_with_zone() + * - icaltime_today() + * - icaltime_from_timet_with_zone(time_t tm, int is_date, + * icaltimezone *zone) + * - icaltime_from_string_with_zone(const char* str, icaltimezone *zone) + * - icaltime_from_day_of_year(int doy, int year) + * - icaltime_from_week_number(int week_number, int year) + * + * italtimetype objects can be converted to different formats: + * + * - icaltime_as_timet(struct icaltimetype tt) + * - icaltime_as_timet_with_zone(struct icaltimetype tt, + * icaltimezone *zone) + * - icaltime_as_ical_string(struct icaltimetype tt) + * + * Accessor methods include: + * + * - icaltime_get_timezone(struct icaltimetype t) + * - icaltime_get_tzid(struct icaltimetype t) + * - icaltime_set_timezone(struct icaltimetype t, icaltimezone *zone) + * - icaltime_day_of_year(struct icaltimetype t) + * - icaltime_day_of_week(struct icaltimetype t) + * - icaltime_start_doy_of_week(struct icaltimetype t) + * - icaltime_week_number(struct icaltimetype t) + * + * Query methods include: + * + * - icaltime_is_null_time(struct icaltimetype t) + * - icaltime_is_valid_time(struct icaltimetype t) + * - icaltime_is_date(struct icaltimetype t) + * - icaltime_is_utc(struct icaltimetype t) + * - icaltime_is_floating(struct icaltimetype t) + * + * Modify, compare and utility methods include: + * + * - icaltime_add(struct icaltimetype t, struct icaldurationtype d) + * - icaltime_subtract(struct icaltimetype t1, struct icaltimetype t2) + * - icaltime_compare_with_zone(struct icaltimetype a,struct icaltimetype b) + * - icaltime_compare(struct icaltimetype a,struct icaltimetype b) + * - icaltime_compare_date_only(struct icaltimetype a, + * struct icaltimetype b) + * - icaltime_adjust(struct icaltimetype *tt, int days, int hours, + * int minutes, int seconds); + * - icaltime_normalize(struct icaltimetype t); + * - icaltime_convert_to_zone(const struct icaltimetype tt, + * icaltimezone *zone); + */ + +#ifndef ICALTIME_H +#define ICALTIME_H + +#include <time.h> +/* An opaque struct representing a timezone. We declare this here to avoid + a circular dependancy. */ +#ifndef ICALTIMEZONE_DEFINED +#define ICALTIMEZONE_DEFINED +typedef struct _icaltimezone icaltimezone; +#endif + +/** icaltime_span is returned by icalcomponent_get_span() */ +struct icaltime_span { + time_t start; /**< in UTC */ + time_t end; /**< in UTC */ + int is_busy; /**< 1->busy time, 0-> free time */ +}; + +typedef struct icaltime_span icaltime_span; + +/* + * FIXME + * + * is_utc is redundant, and might be considered a minor optimization. + * It might be deprecated, so you should use icaltime_is_utc() instead. + */ +struct icaltimetype +{ + int year; /**< Actual year, e.g. 2001. */ + int month; /**< 1 (Jan) to 12 (Dec). */ + int day; + int hour; + int minute; + int second; + + int is_utc; /**< 1-> time is in UTC timezone */ + + int is_date; /**< 1 -> interpret this as date. */ + + int is_daylight; /**< 1 -> time is in daylight savings time. */ + + icaltimezone *zone; /**< timezone */ +}; + +typedef struct icaltimetype icaltimetype; + +/** Return a null time, which indicates no time has been set. + This time represent the beginning of the epoch */ +struct icaltimetype icaltime_null_time(void); + +/** Return a null date */ +struct icaltimetype icaltime_null_date(void); + +/** Returns the current time in the given timezone, as an icaltimetype. */ +struct icaltimetype icaltime_current_time_with_zone(icaltimezone *zone); + +/** Returns the current day as an icaltimetype, with is_date set. */ +struct icaltimetype icaltime_today(void); + +/** Convert seconds past UNIX epoch to a timetype*/ +struct icaltimetype icaltime_from_timet(const time_t v, const int is_date); + +/** Convert seconds past UNIX epoch to a timetype, using timezones. */ +struct icaltimetype icaltime_from_timet_with_zone(const time_t tm, + const int is_date, icaltimezone *zone); + +/** create a time from an ISO format string */ +struct icaltimetype icaltime_from_string(const char* str); + +/** create a time from an ISO format string */ +struct icaltimetype icaltime_from_string_with_zone(const char* str, + icaltimezone *zone); + +/** Create a new time, given a day of year and a year. */ +struct icaltimetype icaltime_from_day_of_year(const int doy, + const int year); + +/** @brief Contructor (TODO). + * Create a new time from a weeknumber and a year. */ +struct icaltimetype icaltime_from_week_number(const int week_number, + const int year); + +/** Return the time as seconds past the UNIX epoch */ +time_t icaltime_as_timet(const struct icaltimetype); + +/** Return the time as seconds past the UNIX epoch, using timezones. */ +time_t icaltime_as_timet_with_zone(const struct icaltimetype tt, + icaltimezone *zone); + +/** Return a string represention of the time, in RFC2445 format. The + string is owned by libical */ +const char* icaltime_as_ical_string(const struct icaltimetype tt); + +/** @brief Return the timezone */ +icaltimezone *icaltime_get_timezone(const struct icaltimetype t); + +/** @brief Return the tzid, or NULL for a floating time */ +const char *icaltime_get_tzid(const struct icaltimetype t); + +/** @brief Set the timezone */ +struct icaltimetype icaltime_set_timezone(struct icaltimetype *t, + icaltimezone *zone); + +/** Return the day of the year of the given time */ +int icaltime_day_of_year(const struct icaltimetype t); + +/** Return the day of the week of the given time. Sunday is 1 */ +int icaltime_day_of_week(const struct icaltimetype t); + +/** Return the day of the year for the Sunday of the week that the + given time is within. */ +int icaltime_start_doy_of_week(const struct icaltimetype t); + +/** Return the week number for the week the given time is within */ +int icaltime_week_number(const struct icaltimetype t); + +/** Return true of the time is null. */ +int icaltime_is_null_time(const struct icaltimetype t); + +/** Returns false if the time is clearly invalid, but is not null. This + is usually the result of creating a new time type buy not clearing + it, or setting one of the flags to an illegal value. */ +int icaltime_is_valid_time(const struct icaltimetype t); + +/** @brief Returns true if time is of DATE type, false if DATE-TIME */ +int icaltime_is_date(const struct icaltimetype t); + +/** @brief Returns true if time is relative to UTC zone */ +int icaltime_is_utc(const struct icaltimetype t); + +/** @brief Returns true if time is a floating time */ +int icaltime_is_floating(const struct icaltimetype t); + +/** Return -1, 0, or 1 to indicate that a<b, a==b or a>b */ +int icaltime_compare_with_zone(const struct icaltimetype a, + const struct icaltimetype b); + +/** Return -1, 0, or 1 to indicate that a<b, a==b or a>b */ +int icaltime_compare(const struct icaltimetype a, + const struct icaltimetype b); + +/** like icaltime_compare, but only use the date parts. */ +int icaltime_compare_date_only(const struct icaltimetype a, + const struct icaltimetype b); + +/** Adds or subtracts a number of days, hours, minutes and seconds. */ +void icaltime_adjust(struct icaltimetype *tt, const int days, + const int hours, const int minutes, const int seconds); + +/** Normalize the icaltime, so that all fields are within the normal range. */ +struct icaltimetype icaltime_normalize(const struct icaltimetype t); + +/** convert tt, of timezone tzid, into a utc time. Does nothing if the + time is already UTC. */ +struct icaltimetype icaltime_convert_to_zone(const struct icaltimetype tt, + icaltimezone *zone); + +/** Return the number of days in the given month */ +int icaltime_days_in_month(const int month, const int year); + + +/** @brief calculate an icaltimespan given a start and end time. */ +struct icaltime_span icaltime_span_new(struct icaltimetype dtstart, + struct icaltimetype dtend, + int is_busy); + +/** @brief Returns true if the two spans overlap **/ +int icaltime_span_overlaps(icaltime_span *s1, + icaltime_span *s2); + +/** @brief Returns true if the span is totally within the containing + * span + */ +int icaltime_span_contains(icaltime_span *s, + icaltime_span *container); + + +#endif /* !ICALTIME_H */ + + diff --git a/libkcal/libical/src/libical/icaltimezone.c b/libkcal/libical/src/libical/icaltimezone.c new file mode 100644 index 000000000..fab228efb --- /dev/null +++ b/libkcal/libical/src/libical/icaltimezone.c @@ -0,0 +1,1670 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/*====================================================================== + FILE: icaltimezone.c + CREATOR: Damon Chaplin 15 March 2001 + + + (C) COPYRIGHT 2001, Damon Chaplin + + This program is free software; you can redistribute it and/or modify + it under the terms of either: + + The LGPL as published by the Free Software Foundation, version + 2.1, available at: http://www.fsf.org/copyleft/lesser.html + + Or: + + The Mozilla Public License Version 1.0. You may obtain a copy of + the License at http://www.mozilla.org/MPL/ + + +======================================================================*/ + +/** @file icaltimezone.c + * @brief implementation of timezone handling routines + **/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "icalproperty.h" +#include "icalarray.h" +#include "icalerror.h" +#include "icalparser.h" +#include "icaltimezone.h" + +#ifdef WIN32 +#define snprintf _snprintf +#define PACKAGE_DATA_DIR "/Projects/libical" +#endif + +/** This is the toplevel directory where the timezone data is installed in. */ +#define ZONEINFO_DIRECTORY PACKAGE_DATA_DIR "/zoneinfo" + +/** The prefix we use to uniquely identify TZIDs. */ +#define TZID_PREFIX "/softwarestudio.org/" +#define TZID_PREFIX_LEN 20 + +/** This is the filename of the file containing the city names and + coordinates of all the builtin timezones. */ +#define ZONES_TAB_FILENAME "zones.tab" + +/** This is the number of years of extra coverage we do when expanding + the timezone changes. */ +#define ICALTIMEZONE_EXTRA_COVERAGE 5 + +/** This is the maximum year we will expand to. time_t values only go up to + somewhere around 2037. */ +#define ICALTIMEZONE_MAX_YEAR 2035 + +struct _icaltimezone { + char *tzid; + /**< The unique ID of this timezone, + e.g. "/softwarestudio.org/Olson_20010601_1/Africa/Banjul". + This should only be used to identify a VTIMEZONE. It is not + meant to be displayed to the user in any form. */ + + char *location; + /**< The location for the timezone, e.g. "Africa/Accra" for the + Olson database. We look for this in the "LOCATION" or + "X-LIC-LOCATION" properties of the VTIMEZONE component. It + isn't a standard property yet. This will be NULL if no location + is found in the VTIMEZONE. */ + + char *tznames; + /**< This will be set to a combination of the TZNAME properties + from the last STANDARD and DAYLIGHT components in the + VTIMEZONE, e.g. "EST/EDT". If they both use the same TZNAME, + or only one type of component is found, then only one TZNAME + will appear, e.g. "AZOT". If no TZNAME is found this will be + NULL. */ + + double latitude; + double longitude; + /**< The coordinates of the city, in degrees. */ + + icalcomponent *component; + /**< The toplevel VTIMEZONE component loaded from the .ics file for this + timezone. If we need to regenerate the changes data we need this. */ + + icaltimezone *builtin_timezone; + /**< If this is not NULL it points to the builtin icaltimezone + that the above TZID refers to. This icaltimezone should be used + instead when accessing the timezone changes data, so that the + expanded timezone changes data is shared between calendar + components. */ + + int end_year; + /**< This is the last year for which we have expanded the data to. + If we need to calculate a date past this we need to expand the + timezone component data from scratch. */ + + icalarray *changes; + /**< A dynamically-allocated array of time zone changes, sorted by the + time of the change in local time. So we can do fast binary-searches + to convert from local time to UTC. */ +}; + +typedef struct _icaltimezonechange icaltimezonechange; + +struct _icaltimezonechange { + int utc_offset; + /**< The offset to add to UTC to get local time, in seconds. */ + + int prev_utc_offset; + /**< The offset to add to UTC, before this change, in seconds. */ + + int year; /**< Actual year, e.g. 2001. */ + int month; /**< 1 (Jan) to 12 (Dec). */ + int day; + int hour; + int minute; + int second; + /**< The time that the change came into effect, in UTC. + Note that the prev_utc_offset applies to this local time, + since we haven't changed to the new offset yet. */ + + int is_daylight; + /**< Whether this is STANDARD or DAYLIGHT time. */ +}; + + +/** An array of icaltimezones for the builtin timezones. */ +static icalarray *builtin_timezones = NULL; + +/** This is the special UTC timezone, which isn't in builtin_timezones. */ +static icaltimezone utc_timezone = { 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + +static char* zone_files_directory = NULL; + +static void icaltimezone_reset (icaltimezone *zone); +static char* icaltimezone_get_location_from_vtimezone (icalcomponent *component); +static char* icaltimezone_get_tznames_from_vtimezone (icalcomponent *component); +static void icaltimezone_expand_changes (icaltimezone *zone, + int end_year); +static void icaltimezone_expand_vtimezone (icalcomponent *comp, + int end_year, + icalarray *changes); +static int icaltimezone_compare_change_fn (const void *elem1, + const void *elem2); + +static int icaltimezone_find_nearby_change (icaltimezone *zone, + icaltimezonechange *change); + +static void icaltimezone_adjust_change (icaltimezonechange *tt, + int days, + int hours, + int minutes, + int seconds); + +static void icaltimezone_init (icaltimezone *zone); + +/** Gets the TZID, LOCATION/X-LIC-LOCATION, and TZNAME properties from the + VTIMEZONE component and places them in the icaltimezone. It returns 1 on + success, or 0 if the TZID can't be found. */ +static int icaltimezone_get_vtimezone_properties (icaltimezone *zone, + icalcomponent *component); + + +static void icaltimezone_load_builtin_timezone (icaltimezone *zone); + +static void icaltimezone_ensure_coverage (icaltimezone *zone, + int end_year); + + +static void icaltimezone_init_builtin_timezones(void); + +static void icaltimezone_parse_zone_tab (void); + +static char* icaltimezone_load_get_line_fn (char *s, + size_t size, + void *data); + +static void format_utc_offset (int utc_offset, + char *buffer); + +static const char* get_zone_directory(void); + + +/** Creates a new icaltimezone. */ +icaltimezone* +icaltimezone_new (void) +{ + icaltimezone *zone; + + zone = (icaltimezone*) malloc (sizeof (icaltimezone)); + if (!zone) { + icalerror_set_errno (ICAL_NEWFAILED_ERROR); + return NULL; + } + + icaltimezone_init (zone); + + return zone; +} + + +/** Frees all memory used for the icaltimezone. */ +void +icaltimezone_free (icaltimezone *zone, + int free_struct) +{ + icaltimezone_reset (zone); + if (free_struct) + free ((void *)zone); +} + + +/** Resets the icaltimezone to the initial state, freeing most of the fields. */ +static void +icaltimezone_reset (icaltimezone *zone) +{ + if (zone->tzid) + free (zone->tzid); + if (zone->location) + free (zone->location); + if (zone->tznames) + free (zone->tznames); + if (zone->component) + icalcomponent_free (zone->component); + if (zone->changes) + icalarray_free (zone->changes); + + icaltimezone_init (zone); +} + + +/** Initializes an icaltimezone. */ +static void +icaltimezone_init (icaltimezone *zone) +{ + zone->tzid = NULL; + zone->location = NULL; + zone->tznames = NULL; + zone->latitude = 0.0; + zone->longitude = 0.0; + zone->component = NULL; + zone->builtin_timezone = NULL; + zone->end_year = 0; + zone->changes = NULL; +} + + +/** Gets the TZID, LOCATION/X-LIC-LOCATION and TZNAME properties of + the VTIMEZONE component and stores them in the icaltimezone. It + returns 1 on success, or 0 if the TZID can't be found. Note that + it expects the zone to be initialized or reset - it doesn't free + any old values. */ +static int +icaltimezone_get_vtimezone_properties (icaltimezone *zone, + icalcomponent *component) +{ + icalproperty *prop; + const char *tzid; + + prop = icalcomponent_get_first_property (component, ICAL_TZID_PROPERTY); + if (!prop) + return 0; + + /* A VTIMEZONE MUST have a TZID, or a lot of our code won't work. */ + tzid = icalproperty_get_tzid (prop); + if (!tzid) + return 0; + + zone->tzid = strdup (tzid); + zone->component = component; + if ( zone->location != 0 ) free ( zone->location ); + zone->location = icaltimezone_get_location_from_vtimezone (component); + zone->tznames = icaltimezone_get_tznames_from_vtimezone (component); + + return 1; +} + +/** Gets the LOCATION or X-LIC-LOCATION property from a VTIMEZONE. */ +static char* +icaltimezone_get_location_from_vtimezone (icalcomponent *component) +{ + icalproperty *prop; + const char *location; + const char *name; + + prop = icalcomponent_get_first_property (component, + ICAL_LOCATION_PROPERTY); + if (prop) { + location = icalproperty_get_location (prop); + if (location) + return strdup (location); + } + + prop = icalcomponent_get_first_property (component, ICAL_X_PROPERTY); + while (prop) { + name = icalproperty_get_x_name (prop); + if (name && !strcasecmp (name, "X-LIC-LOCATION")) { + location = icalproperty_get_x (prop); + if (location) + return strdup (location); + } + prop = icalcomponent_get_next_property (component, + ICAL_X_PROPERTY); + } + + return NULL; +} + + +/** Gets the TZNAMEs used for the last STANDARD & DAYLIGHT components + in a VTIMEZONE. If both STANDARD and DAYLIGHT components use the + same TZNAME, it returns that. If they use different TZNAMEs, it + formats them like "EST/EDT". The returned string should be freed by + the caller. */ +static char* +icaltimezone_get_tznames_from_vtimezone (icalcomponent *component) +{ + icalcomponent *comp; + icalcomponent_kind type; + icalproperty *prop; + struct icaltimetype dtstart; + struct icaldatetimeperiodtype rdate; + const char *current_tzname; + const char *standard_tzname = NULL, *daylight_tzname = NULL; + struct icaltimetype standard_max_date, daylight_max_date; + struct icaltimetype current_max_date; + + standard_max_date = icaltime_null_time(); + daylight_max_date = icaltime_null_time(); + + /* Step through the STANDARD & DAYLIGHT subcomponents. */ + comp = icalcomponent_get_first_component (component, ICAL_ANY_COMPONENT); + while (comp) { + type = icalcomponent_isa (comp); + if (type == ICAL_XSTANDARD_COMPONENT + || type == ICAL_XDAYLIGHT_COMPONENT) { + current_max_date = icaltime_null_time (); + current_tzname = NULL; + + /* Step through the properties. We want to find the TZNAME, and + the largest DTSTART or RDATE. */ + prop = icalcomponent_get_first_property (comp, ICAL_ANY_PROPERTY); + while (prop) { + switch (icalproperty_isa (prop)) { + case ICAL_TZNAME_PROPERTY: + current_tzname = icalproperty_get_tzname (prop); + break; + + case ICAL_DTSTART_PROPERTY: + dtstart = icalproperty_get_dtstart (prop); + if (icaltime_compare (dtstart, current_max_date) > 0) + current_max_date = dtstart; + + break; + + case ICAL_RDATE_PROPERTY: + rdate = icalproperty_get_rdate (prop); + if (icaltime_compare (rdate.time, current_max_date) > 0) + current_max_date = rdate.time; + + break; + + default: + break; + } + + prop = icalcomponent_get_next_property (comp, + ICAL_ANY_PROPERTY); + } + + if (current_tzname) { + if (type == ICAL_XSTANDARD_COMPONENT) { + if (!standard_tzname + || icaltime_compare (current_max_date, + standard_max_date) > 0) { + standard_max_date = current_max_date; + standard_tzname = current_tzname; + } + } else { + if (!daylight_tzname + || icaltime_compare (current_max_date, + daylight_max_date) > 0) { + daylight_max_date = current_max_date; + daylight_tzname = current_tzname; + } + } + } + } + + comp = icalcomponent_get_next_component (component, + ICAL_ANY_COMPONENT); + } + + /* Outlook (2000) places "Standard Time" and "Daylight Time" in the TZNAME + strings, which is totally useless. So we return NULL in that case. */ + if (standard_tzname && !strcmp (standard_tzname, "Standard Time")) + return NULL; + + /* If both standard and daylight TZNAMEs were found, if they are the same + we return just one, else we format them like "EST/EDT". */ + if (standard_tzname && daylight_tzname) { + unsigned int standard_len, daylight_len; + char *tznames; + + if (!strcmp (standard_tzname, daylight_tzname)) + return strdup (standard_tzname); + + standard_len = strlen (standard_tzname); + daylight_len = strlen (daylight_tzname); + tznames = malloc (standard_len + daylight_len + 2); + strcpy (tznames, standard_tzname); + tznames[standard_len] = '/'; + strcpy (tznames + standard_len + 1, daylight_tzname); + return tznames; + } else { + const char *tznames; + + /* If either of the TZNAMEs was found just return that, else NULL. */ + tznames = standard_tzname ? standard_tzname : daylight_tzname; + return tznames ? strdup (tznames) : NULL; + } +} + + +static void +icaltimezone_ensure_coverage (icaltimezone *zone, + int end_year) +{ + /* When we expand timezone changes we always expand at least up to this + year, plus ICALTIMEZONE_EXTRA_COVERAGE. */ + static int icaltimezone_minimum_expansion_year = -1; + + int changes_end_year; + + if (!zone->component) + icaltimezone_load_builtin_timezone (zone); + + if (icaltimezone_minimum_expansion_year == -1) { + struct icaltimetype today = icaltime_today(); + icaltimezone_minimum_expansion_year = today.year; + } + + changes_end_year = end_year; + if (changes_end_year < icaltimezone_minimum_expansion_year) + changes_end_year = icaltimezone_minimum_expansion_year; + + changes_end_year += ICALTIMEZONE_EXTRA_COVERAGE; + + if (changes_end_year > ICALTIMEZONE_MAX_YEAR) + changes_end_year = ICALTIMEZONE_MAX_YEAR; + + if (!zone->changes || zone->end_year < end_year) + icaltimezone_expand_changes (zone, changes_end_year); +} + + +static void +icaltimezone_expand_changes (icaltimezone *zone, + int end_year) +{ + icalarray *changes; + icalcomponent *comp; + +#if 0 + printf ("\nExpanding changes for: %s to year: %i\n", zone->tzid, end_year); +#endif + + changes = icalarray_new (sizeof (icaltimezonechange), 32); + if (!changes) + return; + + /* Scan the STANDARD and DAYLIGHT subcomponents. */ + comp = icalcomponent_get_first_component (zone->component, + ICAL_ANY_COMPONENT); + while (comp) { + icaltimezone_expand_vtimezone (comp, end_year, changes); + comp = icalcomponent_get_next_component (zone->component, + ICAL_ANY_COMPONENT); + } + + /* Sort the changes. We may have duplicates but I don't think it will + matter. */ + icalarray_sort (changes, icaltimezone_compare_change_fn); + + if (zone->changes) + icalarray_free (zone->changes); + + zone->changes = changes; + zone->end_year = end_year; +} + + +static void +icaltimezone_expand_vtimezone (icalcomponent *comp, + int end_year, + icalarray *changes) +{ + icaltimezonechange change; + icalproperty *prop; + struct icaltimetype dtstart, occ; + struct icalrecurrencetype rrule; + icalrecur_iterator* rrule_iterator; + struct icaldatetimeperiodtype rdate; + int found_dtstart = 0, found_tzoffsetto = 0, found_tzoffsetfrom = 0; + int has_recurrence = 0; + + /* First we check if it is a STANDARD or DAYLIGHT component, and + just return if it isn't. */ + if (icalcomponent_isa (comp) == ICAL_XSTANDARD_COMPONENT) + change.is_daylight = 0; + else if (icalcomponent_isa (comp) == ICAL_XDAYLIGHT_COMPONENT) + change.is_daylight = 1; + else + return; + + /* Step through each of the properties to find the DTSTART, + TZOFFSETFROM and TZOFFSETTO. We can't expand recurrences here + since we need these properties before we can do that. */ + prop = icalcomponent_get_first_property (comp, ICAL_ANY_PROPERTY); + while (prop) { + switch (icalproperty_isa (prop)) { + case ICAL_DTSTART_PROPERTY: + dtstart = icalproperty_get_dtstart (prop); + found_dtstart = 1; + break; + case ICAL_TZOFFSETTO_PROPERTY: + change.utc_offset = icalproperty_get_tzoffsetto (prop); + /*printf ("Found TZOFFSETTO: %i\n", change.utc_offset);*/ + found_tzoffsetto = 1; + break; + case ICAL_TZOFFSETFROM_PROPERTY: + change.prev_utc_offset = icalproperty_get_tzoffsetfrom (prop); + /*printf ("Found TZOFFSETFROM: %i\n", change.prev_utc_offset);*/ + found_tzoffsetfrom = 1; + break; + case ICAL_RDATE_PROPERTY: + case ICAL_RRULE_PROPERTY: + has_recurrence = 1; + break; + default: + /* Just ignore any other properties. */ + break; + } + + prop = icalcomponent_get_next_property (comp, ICAL_ANY_PROPERTY); + } + + /* If we didn't find a DTSTART, TZOFFSETTO and TZOFFSETFROM we have to + ignore the component. FIXME: Add an error property? */ + if (!found_dtstart || !found_tzoffsetto || !found_tzoffsetfrom) + return; + +#if 0 + printf ("\n Expanding component DTSTART (Y/M/D): %i/%i/%i %i:%02i:%02i\n", + dtstart.year, dtstart.month, dtstart.day, + dtstart.hour, dtstart.minute, dtstart.second); +#endif + + /* If the STANDARD/DAYLIGHT component has no recurrence data, we just add + a single change for the DTSTART. */ + if (!has_recurrence) { + change.year = dtstart.year; + change.month = dtstart.month; + change.day = dtstart.day; + change.hour = dtstart.hour; + change.minute = dtstart.minute; + change.second = dtstart.second; + + /* Convert to UTC. */ + icaltimezone_adjust_change (&change, 0, 0, 0, -change.prev_utc_offset); + +#if 0 + printf (" Appending single DTSTART (Y/M/D): %i/%02i/%02i %i:%02i:%02i\n", + change.year, change.month, change.day, + change.hour, change.minute, change.second); +#endif + + /* Add the change to the array. */ + icalarray_append (changes, &change); + return; + } + + /* The component has recurrence data, so we expand that now. */ + prop = icalcomponent_get_first_property (comp, ICAL_ANY_PROPERTY); + while (prop) { +#if 0 + printf ("Expanding property...\n"); +#endif + switch (icalproperty_isa (prop)) { + case ICAL_RDATE_PROPERTY: + rdate = icalproperty_get_rdate (prop); + change.year = rdate.time.year; + change.month = rdate.time.month; + change.day = rdate.time.day; + /* RDATEs with a DATE value inherit the time from + the DTSTART. */ + if (icaltime_is_date(rdate.time)) { + change.hour = dtstart.hour; + change.minute = dtstart.minute; + change.second = dtstart.second; + } else { + change.hour = rdate.time.hour; + change.minute = rdate.time.minute; + change.second = rdate.time.second; + + /* The spec was a bit vague about whether RDATEs were in local + time or UTC so we support both to be safe. So if it is in + UTC we have to add the UTC offset to get a local time. */ + if (!icaltime_is_utc(rdate.time)) + icaltimezone_adjust_change (&change, 0, 0, 0, + -change.prev_utc_offset); + } + +#if 0 + printf (" Appending RDATE element (Y/M/D): %i/%02i/%02i %i:%02i:%02i\n", + change.year, change.month, change.day, + change.hour, change.minute, change.second); +#endif + + icalarray_append (changes, &change); + break; + case ICAL_RRULE_PROPERTY: + rrule = icalproperty_get_rrule (prop); + + /* If the rrule UNTIL value is set and is in UTC, we convert it to + a local time, since the recurrence code has no way to convert + it itself. */ + if (!icaltime_is_null_time (rrule.until) && rrule.until.is_utc) { +#if 0 + printf (" Found RRULE UNTIL in UTC.\n"); +#endif + + /* To convert from UTC to a local time, we use the TZOFFSETFROM + since that is the offset from UTC that will be in effect + when each of the RRULE occurrences happens. */ + icaltime_adjust (&rrule.until, 0, 0, 0, + change.prev_utc_offset); + rrule.until.is_utc = 0; + } + + rrule_iterator = icalrecur_iterator_new (rrule, dtstart); + for (;;) { + occ = icalrecur_iterator_next (rrule_iterator); + if (occ.year > end_year || icaltime_is_null_time (occ)) + break; + + change.year = occ.year; + change.month = occ.month; + change.day = occ.day; + change.hour = occ.hour; + change.minute = occ.minute; + change.second = occ.second; + +#if 0 + printf (" Appending RRULE element (Y/M/D): %i/%02i/%02i %i:%02i:%02i\n", + change.year, change.month, change.day, + change.hour, change.minute, change.second); +#endif + + icaltimezone_adjust_change (&change, 0, 0, 0, + -change.prev_utc_offset); + + icalarray_append (changes, &change); + } + + icalrecur_iterator_free (rrule_iterator); + break; + default: + break; + } + + prop = icalcomponent_get_next_property (comp, ICAL_ANY_PROPERTY); + } +} + + +/** A function to compare 2 icaltimezonechange elements, used for qsort(). */ +static int +icaltimezone_compare_change_fn (const void *elem1, + const void *elem2) +{ + icaltimezonechange *change1, *change2; + int retval; + + change1 = (icaltimezonechange *)elem1; + change2 = (icaltimezonechange *)elem2; + + if (change1->year < change2->year) + retval = -1; + else if (change1->year > change2->year) + retval = 1; + + else if (change1->month < change2->month) + retval = -1; + else if (change1->month > change2->month) + retval = 1; + + else if (change1->day < change2->day) + retval = -1; + else if (change1->day > change2->day) + retval = 1; + + else if (change1->hour < change2->hour) + retval = -1; + else if (change1->hour > change2->hour) + retval = 1; + + else if (change1->minute < change2->minute) + retval = -1; + else if (change1->minute > change2->minute) + retval = 1; + + else if (change1->second < change2->second) + retval = -1; + else if (change1->second > change2->second) + retval = 1; + + else + retval = 0; + + return retval; +} + + + +void +icaltimezone_convert_time (struct icaltimetype *tt, + icaltimezone *from_zone, + icaltimezone *to_zone) +{ + int utc_offset, is_daylight; + + /* If the time is a DATE value or both timezones are the same, or we are + converting a floating time, we don't need to do anything. */ + if (icaltime_is_date(*tt) || from_zone == to_zone || from_zone == NULL) + return; + + /* Convert the time to UTC by getting the UTC offset and subtracting it. */ + utc_offset = icaltimezone_get_utc_offset (from_zone, tt, NULL); + icaltime_adjust (tt, 0, 0, 0, -utc_offset); + + /* Now we convert the time to the new timezone by getting the UTC offset + of our UTC time and adding it. */ + utc_offset = icaltimezone_get_utc_offset_of_utc_time (to_zone, tt, + &is_daylight); + tt->is_daylight = is_daylight; + icaltime_adjust (tt, 0, 0, 0, utc_offset); +} + + + + +/** @deprecated This API wasn't updated when we changed icaltimetype to contain its own + timezone. Also, this takes a pointer instead of the struct. */ +/* Calculates the UTC offset of a given local time in the given + timezone. It is the number of seconds to add to UTC to get local + time. The is_daylight flag is set to 1 if the time is in + daylight-savings time. */ +int +icaltimezone_get_utc_offset (icaltimezone *zone, + struct icaltimetype *tt, + int *is_daylight) +{ + icaltimezonechange *zone_change, *prev_zone_change, tt_change, tmp_change; + int change_num, step, utc_offset_change, cmp; + int change_num_to_use; + int want_daylight; + + if (tt == NULL) + return 0; + + if (is_daylight) + *is_daylight = 0; + + /* For local times and UTC return 0. */ + if (zone == NULL || zone == &utc_timezone) + return 0; + + /* Use the builtin icaltimezone if possible. */ + if (zone->builtin_timezone) + zone = zone->builtin_timezone; + + /* Make sure the changes array is expanded up to the given time. */ + icaltimezone_ensure_coverage (zone, tt->year); + + if (!zone->changes || zone->changes->num_elements == 0) + return 0; + + /* Copy the time parts of the icaltimetype to an icaltimezonechange so we + can use our comparison function on it. */ + tt_change.year = tt->year; + tt_change.month = tt->month; + tt_change.day = tt->day; + tt_change.hour = tt->hour; + tt_change.minute = tt->minute; + tt_change.second = tt->second; + + /* This should find a change close to the time, either the change before + it or the change after it. */ + change_num = icaltimezone_find_nearby_change (zone, &tt_change); + + /* Sanity check. */ + icalerror_assert (change_num >= 0, + "Negative timezone change index"); + icalerror_assert (change_num < zone->changes->num_elements, + "Timezone change index out of bounds"); + + /* Now move backwards or forwards to find the timezone change that applies + to tt. It should only have to do 1 or 2 steps. */ + zone_change = icalarray_element_at (zone->changes, change_num); + step = 1; + change_num_to_use = -1; + for (;;) { + /* Copy the change, so we can adjust it. */ + tmp_change = *zone_change; + + /* If the clock is going backward, check if it is in the region of time + that is used twice. If it is, use the change with the daylight + setting which matches tt, or use standard if we don't know. */ + if (tmp_change.utc_offset < tmp_change.prev_utc_offset) { + /* If the time change is at 2:00AM local time and the clock is + going back to 1:00AM we adjust the change to 1:00AM. We may + have the wrong change but we'll figure that out later. */ + icaltimezone_adjust_change (&tmp_change, 0, 0, 0, + tmp_change.utc_offset); + } else { + icaltimezone_adjust_change (&tmp_change, 0, 0, 0, + tmp_change.prev_utc_offset); + } + + cmp = icaltimezone_compare_change_fn (&tt_change, &tmp_change); + + /* If the given time is on or after this change, then this change may + apply, but we continue as a later change may be the right one. + If the given time is before this change, then if we have already + found a change which applies we can use that, else we need to step + backwards. */ + if (cmp >= 0) + change_num_to_use = change_num; + else + step = -1; + + /* If we are stepping backwards through the changes and we have found + a change that applies, then we know this is the change to use so + we exit the loop. */ + if (step == -1 && change_num_to_use != -1) + break; + + change_num += step; + + /* If we go past the start of the changes array, then we have no data + for this time so we return a UTC offset of 0. */ + if (change_num < 0) + return 0; + + if ((unsigned int)change_num >= zone->changes->num_elements) + break; + + zone_change = icalarray_element_at (zone->changes, change_num); + } + + /* If we didn't find a change to use, then we have a bug! */ + icalerror_assert (change_num_to_use != -1, + "No applicable timezone change found"); + + /* Now we just need to check if the time is in the overlapped region of + time when clocks go back. */ + zone_change = icalarray_element_at (zone->changes, change_num_to_use); + + utc_offset_change = zone_change->utc_offset - zone_change->prev_utc_offset; + if (utc_offset_change < 0 && change_num_to_use > 0) { + tmp_change = *zone_change; + icaltimezone_adjust_change (&tmp_change, 0, 0, 0, + tmp_change.prev_utc_offset); + + if (icaltimezone_compare_change_fn (&tt_change, &tmp_change) < 0) { + /* The time is in the overlapped region, so we may need to use + either the current zone_change or the previous one. If the + time has the is_daylight field set we use the matching change, + else we use the change with standard time. */ + prev_zone_change = icalarray_element_at (zone->changes, + change_num_to_use - 1); + + /* I was going to add an is_daylight flag to struct icaltimetype, + but iCalendar doesn't let us distinguish between standard and + daylight time anyway, so there's no point. So we just use the + standard time instead. */ + want_daylight = (tt->is_daylight == 1) ? 1 : 0; + +#if 0 + if (zone_change->is_daylight == prev_zone_change->is_daylight) + printf (" **** Same is_daylight setting\n"); +#endif + + if (zone_change->is_daylight != want_daylight + && prev_zone_change->is_daylight == want_daylight) + zone_change = prev_zone_change; + } + } + + /* Now we know exactly which timezone change applies to the time, so + we can return the UTC offset and whether it is a daylight time. */ + if (is_daylight) + *is_daylight = zone_change->is_daylight; + return zone_change->utc_offset; +} + + +/** @deprecated This API wasn't updated when we changed icaltimetype to contain its own + timezone. Also, this takes a pointer instead of the struct. */ +/** Calculates the UTC offset of a given UTC time in the given + timezone. It is the number of seconds to add to UTC to get local + time. The is_daylight flag is set to 1 if the time is in + daylight-savings time. */ +int +icaltimezone_get_utc_offset_of_utc_time (icaltimezone *zone, + struct icaltimetype *tt, + int *is_daylight) +{ + icaltimezonechange *zone_change, tt_change, tmp_change; + int change_num, step, change_num_to_use; + + if (is_daylight) + *is_daylight = 0; + + /* For local times and UTC return 0. */ + if (zone == NULL || zone == &utc_timezone) + return 0; + + /* Use the builtin icaltimezone if possible. */ + if (zone->builtin_timezone) + zone = zone->builtin_timezone; + + /* Make sure the changes array is expanded up to the given time. */ + icaltimezone_ensure_coverage (zone, tt->year); + + if (!zone->changes || zone->changes->num_elements == 0) + return 0; + + /* Copy the time parts of the icaltimetype to an icaltimezonechange so we + can use our comparison function on it. */ + tt_change.year = tt->year; + tt_change.month = tt->month; + tt_change.day = tt->day; + tt_change.hour = tt->hour; + tt_change.minute = tt->minute; + tt_change.second = tt->second; + + /* This should find a change close to the time, either the change before + it or the change after it. */ + change_num = icaltimezone_find_nearby_change (zone, &tt_change); + + /* Sanity check. */ + icalerror_assert (change_num >= 0, + "Negative timezone change index"); + icalerror_assert (change_num < zone->changes->num_elements, + "Timezone change index out of bounds"); + + /* Now move backwards or forwards to find the timezone change that applies + to tt. It should only have to do 1 or 2 steps. */ + zone_change = icalarray_element_at (zone->changes, change_num); + step = 1; + change_num_to_use = -1; + for (;;) { + /* Copy the change and adjust it to UTC. */ + tmp_change = *zone_change; + + /* If the given time is on or after this change, then this change may + apply, but we continue as a later change may be the right one. + If the given time is before this change, then if we have already + found a change which applies we can use that, else we need to step + backwards. */ + if (icaltimezone_compare_change_fn (&tt_change, &tmp_change) >= 0) + change_num_to_use = change_num; + else + step = -1; + + /* If we are stepping backwards through the changes and we have found + a change that applies, then we know this is the change to use so + we exit the loop. */ + if (step == -1 && change_num_to_use != -1) + break; + + change_num += step; + + /* If we go past the start of the changes array, then we have no data + for this time so we return a UTC offset of 0. */ + if (change_num < 0) + return 0; + + if ((unsigned int)change_num >= zone->changes->num_elements) + break; + + zone_change = icalarray_element_at (zone->changes, change_num); + } + + /* If we didn't find a change to use, then we have a bug! */ + icalerror_assert (change_num_to_use != -1, + "No applicable timezone change found"); + + /* Now we know exactly which timezone change applies to the time, so + we can return the UTC offset and whether it is a daylight time. */ + zone_change = icalarray_element_at (zone->changes, change_num_to_use); + if (is_daylight) + *is_daylight = zone_change->is_daylight; + + return zone_change->utc_offset; +} + + +/** Returns the index of a timezone change which is close to the time + given in change. */ +static int +icaltimezone_find_nearby_change (icaltimezone *zone, + icaltimezonechange *change) +{ + icaltimezonechange *zone_change; + int lower, upper, middle, cmp; + + /* Do a simple binary search. */ + lower = middle = 0; + upper = zone->changes->num_elements; + + while (lower < upper) { + middle = (lower + upper) / 2; + zone_change = icalarray_element_at (zone->changes, middle); + cmp = icaltimezone_compare_change_fn (change, zone_change); + if (cmp == 0) + break; + else if (cmp < 0) + upper = middle; + else + lower = middle + 1; + } + + return middle; +} + + + + +/** Adds (or subtracts) a time from a icaltimezonechange. NOTE: This + function is exactly the same as icaltime_adjust() except for the + type of the first parameter. */ +static void +icaltimezone_adjust_change (icaltimezonechange *tt, + int days, + int hours, + int minutes, + int seconds) +{ + int second, minute, hour, day; + int minutes_overflow, hours_overflow, days_overflow; + int days_in_month; + + /* Add on the seconds. */ + second = tt->second + seconds; + tt->second = second % 60; + minutes_overflow = second / 60; + if (tt->second < 0) { + tt->second += 60; + minutes_overflow--; + } + + /* Add on the minutes. */ + minute = tt->minute + minutes + minutes_overflow; + tt->minute = minute % 60; + hours_overflow = minute / 60; + if (tt->minute < 0) { + tt->minute += 60; + hours_overflow--; + } + + /* Add on the hours. */ + hour = tt->hour + hours + hours_overflow; + tt->hour = hour % 24; + days_overflow = hour / 24; + if (tt->hour < 0) { + tt->hour += 24; + days_overflow--; + } + + /* Add on the days. */ + day = tt->day + days + days_overflow; + if (day > 0) { + for (;;) { + days_in_month = icaltime_days_in_month (tt->month, tt->year); + if (day <= days_in_month) + break; + + tt->month++; + if (tt->month >= 13) { + tt->year++; + tt->month = 1; + } + + day -= days_in_month; + } + } else { + while (day <= 0) { + if (tt->month == 1) { + tt->year--; + tt->month = 12; + } else { + tt->month--; + } + + day += icaltime_days_in_month (tt->month, tt->year); + } + } + tt->day = day; +} + + +const char* +icaltimezone_get_tzid (icaltimezone *zone) +{ + /* If this is a floating time, without a timezone, return NULL. */ + if (!zone) + return NULL; + + if (!zone->tzid) + icaltimezone_load_builtin_timezone (zone); + + return zone->tzid; +} + + +const char* +icaltimezone_get_location (icaltimezone *zone) +{ + /* If this is a floating time, without a timezone, return NULL. */ + if (!zone) + return NULL; + + /* Note that for builtin timezones this comes from zones.tab so we don't + need to check the timezone is loaded here. */ + return zone->location; +} + + +const char* +icaltimezone_get_tznames (icaltimezone *zone) +{ + /* If this is a floating time, without a timezone, return NULL. */ + if (!zone) + return NULL; + + if (!zone->component) + icaltimezone_load_builtin_timezone (zone); + + return zone->tznames; +} + + +/** Returns the latitude of a builtin timezone. */ +double +icaltimezone_get_latitude (icaltimezone *zone) +{ + /* If this is a floating time, without a timezone, return 0. */ + if (!zone) + return 0.0; + + /* Note that for builtin timezones this comes from zones.tab so we don't + need to check the timezone is loaded here. */ + return zone->latitude; +} + + +/** Returns the longitude of a builtin timezone. */ +double +icaltimezone_get_longitude (icaltimezone *zone) +{ + /* If this is a floating time, without a timezone, return 0. */ + if (!zone) + return 0.0; + + /* Note that for builtin timezones this comes from zones.tab so we don't + need to check the timezone is loaded here. */ + return zone->longitude; +} + + +/** Returns the VTIMEZONE component of a timezone. */ +icalcomponent* +icaltimezone_get_component (icaltimezone *zone) +{ + /* If this is a floating time, without a timezone, return NULL. */ + if (!zone) + return NULL; + + if (!zone->component) + icaltimezone_load_builtin_timezone (zone); + + return zone->component; +} + + +/** Sets the VTIMEZONE component of an icaltimezone, initializing the + tzid, location & tzname fields. It returns 1 on success or 0 on + failure, i.e. no TZID was found. */ +int +icaltimezone_set_component (icaltimezone *zone, + icalcomponent *comp) +{ + icaltimezone_reset (zone); + return icaltimezone_get_vtimezone_properties (zone, comp); +} + + +icalarray* +icaltimezone_array_new (void) +{ + return icalarray_new (sizeof (icaltimezone), 16); +} + + +void +icaltimezone_array_append_from_vtimezone (icalarray *timezones, + icalcomponent *child) +{ + icaltimezone zone; + + icaltimezone_init (&zone); + if (icaltimezone_get_vtimezone_properties (&zone, child)) + icalarray_append (timezones, &zone); +} + + +void +icaltimezone_array_free (icalarray *timezones) +{ + icaltimezone *zone; + int i; + + if ( timezones ) + { + for (i = 0; (unsigned int)i < timezones->num_elements; i++) { + zone = icalarray_element_at (timezones, i); + icaltimezone_free (zone, 0); + } + + icalarray_free (timezones); + } +} + + +/* + * BUILTIN TIMEZONE HANDLING + */ + + +/** Returns an icalarray of icaltimezone structs, one for each builtin + timezone. This will load and parse the zones.tab file to get the + timezone names and their coordinates. It will not load the + VTIMEZONE data for any timezones. */ +icalarray* +icaltimezone_get_builtin_timezones (void) +{ + if (!builtin_timezones) + icaltimezone_init_builtin_timezones (); + + return builtin_timezones; +} + +/** Release builtin timezone memory */ +void +icaltimezone_free_builtin_timezones(void) +{ + icaltimezone_array_free(builtin_timezones); +} + + +/** Returns a single builtin timezone, given its Olson city name. */ +icaltimezone* +icaltimezone_get_builtin_timezone (const char *location) +{ + icaltimezone *zone; + int lower, upper, middle, cmp; + const char *zone_location; + + if (!location || !location[0]) + return NULL; + + if (!strcmp (location, "UTC")) + return &utc_timezone; + + if (!builtin_timezones) + icaltimezone_init_builtin_timezones (); + + /* Do a simple binary search. */ + lower = middle = 0; + upper = builtin_timezones->num_elements; + + while (lower < upper) { + middle = (lower + upper) / 2; + zone = icalarray_element_at (builtin_timezones, middle); + zone_location = icaltimezone_get_location (zone); + cmp = strcmp (location, zone_location); + if (cmp == 0) + return zone; + else if (cmp < 0) + upper = middle; + else + lower = middle + 1; + } + + return NULL; +} + + +/** Returns a single builtin timezone, given its TZID. */ +icaltimezone* +icaltimezone_get_builtin_timezone_from_tzid (const char *tzid) +{ + int num_slashes = 0; + const char *p, *zone_tzid; + icaltimezone *zone; + + if (!tzid || !tzid[0]) + return NULL; + + /* Check that the TZID starts with our unique prefix. */ + if (strncmp (tzid, TZID_PREFIX, TZID_PREFIX_LEN)) + return NULL; + + /* Get the location, which is after the 3rd '/' character. */ + p = tzid; + for (p = tzid; *p; p++) { + if (*p == '/') { + num_slashes++; + if (num_slashes == 3) + break; + } + } + + if (num_slashes != 3) + return NULL; + + p++; + + /* Now we can use the function to get the builtin timezone from the + location string. */ + zone = icaltimezone_get_builtin_timezone (p); + if (!zone) + return NULL; + + /* Check that the builtin TZID matches exactly. We don't want to return + a different version of the VTIMEZONE. */ + zone_tzid = icaltimezone_get_tzid (zone); + if (!strcmp (zone_tzid, tzid)) + return zone; + else + return NULL; +} + + +/** Returns the special UTC timezone. */ +icaltimezone* +icaltimezone_get_utc_timezone (void) +{ + if (!builtin_timezones) + icaltimezone_init_builtin_timezones (); + + return &utc_timezone; +} + + + +/** This initializes the builtin timezone data, i.e. the + builtin_timezones array and the special UTC timezone. It should be + called before any code that uses the timezone functions. */ +static void +icaltimezone_init_builtin_timezones (void) +{ + /* Initialize the special UTC timezone. */ + utc_timezone.tzid = (char *)"UTC"; + + icaltimezone_parse_zone_tab (); +} + + +/** This parses the zones.tab file containing the names and locations + of the builtin timezones. It creates the builtin_timezones array + which is an icalarray of icaltimezone structs. It only fills in the + location, latitude and longtude fields; the rest are left + blank. The VTIMEZONE component is loaded later if it is needed. The + timezones in the zones.tab file are sorted by their name, which is + useful for binary searches. */ +static void +icaltimezone_parse_zone_tab (void) +{ + char *filename; + FILE *fp; + char buf[1024]; /* Used to store each line of zones.tab as it is read. */ + char location[1024]; /* Stores the city name when parsing buf. */ + unsigned int filename_len; + int latitude_degrees, latitude_minutes, latitude_seconds; + int longitude_degrees, longitude_minutes, longitude_seconds; + icaltimezone zone; + + icalerror_assert (builtin_timezones == NULL, + "Parsing zones.tab file multiple times"); + + builtin_timezones = icalarray_new (sizeof (icaltimezone), 32); + + filename_len = strlen (get_zone_directory()) + strlen (ZONES_TAB_FILENAME) + + 2; + + filename = (char*) malloc (filename_len); + if (!filename) { + icalerror_set_errno(ICAL_NEWFAILED_ERROR); + return; + } + + snprintf (filename, filename_len, "%s/%s", get_zone_directory(), + ZONES_TAB_FILENAME); + + fp = fopen (filename, "r"); + free (filename); + if (!fp) { + icalerror_set_errno(ICAL_FILE_ERROR); + return; + } + + while (fgets (buf, sizeof(buf), fp)) { + if (*buf == '#') continue; + + /* The format of each line is: "latitude longitude location". */ + if (sscanf (buf, "%4d%2d%2d %4d%2d%2d %s", + &latitude_degrees, &latitude_minutes, + &latitude_seconds, + &longitude_degrees, &longitude_minutes, + &longitude_seconds, + location) != 7) { + fprintf (stderr, "Invalid timezone description line: %s\n", buf); + continue; + } + + icaltimezone_init (&zone); + zone.location = strdup (location); + + if (latitude_degrees >= 0) + zone.latitude = (double) latitude_degrees + + (double) latitude_minutes / 60 + + (double) latitude_seconds / 3600; + else + zone.latitude = (double) latitude_degrees + - (double) latitude_minutes / 60 + - (double) latitude_seconds / 3600; + + if (longitude_degrees >= 0) + zone.longitude = (double) longitude_degrees + + (double) longitude_minutes / 60 + + (double) longitude_seconds / 3600; + else + zone.longitude = (double) longitude_degrees + - (double) longitude_minutes / 60 + - (double) longitude_seconds / 3600; + + icalarray_append (builtin_timezones, &zone); + +#if 0 + printf ("Found zone: %s %f %f\n", + location, zone.latitude, zone.longitude); +#endif + } + + fclose (fp); +} + + +/** Loads the builtin VTIMEZONE data for the given timezone. */ +static void +icaltimezone_load_builtin_timezone (icaltimezone *zone) +{ + char *filename; + unsigned int filename_len; + FILE *fp; + icalparser *parser; + icalcomponent *comp, *subcomp; + + /* If the location isn't set, it isn't a builtin timezone. */ + if (!zone->location || !zone->location[0]) + return; + + filename_len = strlen (get_zone_directory()) + strlen (zone->location) + 6; + + filename = (char*) malloc (filename_len); + if (!filename) { + icalerror_set_errno(ICAL_NEWFAILED_ERROR); + return; + } + + snprintf (filename, filename_len, "%s/%s.ics", get_zone_directory(), + zone->location); + + fp = fopen (filename, "r"); + free (filename); + if (!fp) { + icalerror_set_errno(ICAL_FILE_ERROR); + return; + } + + + /* ##### B.# Sun, 11 Nov 2001 04:04:29 +1100 + this is where the MALFORMEDDATA error is being set, after the call to 'icalparser_parse' + fprintf(stderr, "** WARNING ** %s: %d %s\n", __FILE__, __LINE__, icalerror_strerror(icalerrno)); + */ + + parser = icalparser_new (); + icalparser_set_gen_data (parser, fp); + comp = icalparser_parse (parser, icaltimezone_load_get_line_fn); + icalparser_free (parser); + fclose (fp); + + + + /* Find the VTIMEZONE component inside the VCALENDAR. There should be 1. */ + subcomp = icalcomponent_get_first_component (comp, + ICAL_VTIMEZONE_COMPONENT); + if (!subcomp) { + icalerror_set_errno(ICAL_PARSE_ERROR); + return; + } + + icaltimezone_get_vtimezone_properties (zone, subcomp); + + icalcomponent_remove_component(comp,subcomp); + + icalcomponent_free(comp); + +} + + +/** Callback used from icalparser_parse() */ +static char * +icaltimezone_load_get_line_fn (char *s, + size_t size, + void *data) +{ + return fgets (s, (int)size, (FILE*) data); +} + + + + +/* + * DEBUGGING + */ + +/** + * This outputs a list of timezone changes for the given timezone to the + * given file, up to the maximum year given. We compare this output with the + * output from 'vzic --dump-changes' to make sure that we are consistent. + * (vzic is the Olson timezone database to VTIMEZONE converter.) + * + * The output format is: + * + * Zone-Name [tab] Date [tab] Time [tab] UTC-Offset + * + * The Date and Time fields specify the time change in UTC. + * + * The UTC Offset is for local (wall-clock) time. It is the amount of time + * to add to UTC to get local time. + */ +int +icaltimezone_dump_changes (icaltimezone *zone, + int max_year, + FILE *fp) +{ + static const char *months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; + icaltimezonechange *zone_change; + int change_num; + char buffer[8]; + + /* Make sure the changes array is expanded up to the given time. */ + icaltimezone_ensure_coverage (zone, max_year); + +#if 0 + printf ("Num changes: %i\n", zone->changes->num_elements); +#endif + + change_num = 0; + for (change_num = 0; (unsigned int)change_num < zone->changes->num_elements; change_num++) { + zone_change = icalarray_element_at (zone->changes, change_num); + + if (zone_change->year > max_year) + break; + + fprintf (fp, "%s\t%2i %s %04i\t%2i:%02i:%02i", + zone->location, + zone_change->day, months[zone_change->month - 1], + zone_change->year, + zone_change->hour, zone_change->minute, zone_change->second); + + /* Wall Clock Time offset from UTC. */ + format_utc_offset (zone_change->utc_offset, buffer); + fprintf (fp, "\t%s", buffer); + + fprintf (fp, "\n"); + } + return 1; +} + + +/** This formats a UTC offset as "+HHMM" or "+HHMMSS". + buffer should have space for 8 characters. */ +static void +format_utc_offset (int utc_offset, + char *buffer) +{ + const char *sign = "+"; + int hours, minutes, seconds; + + if (utc_offset < 0) { + utc_offset = -utc_offset; + sign = "-"; + } + + hours = utc_offset / 3600; + minutes = (utc_offset % 3600) / 60; + seconds = utc_offset % 60; + + /* Sanity check. Standard timezone offsets shouldn't be much more than 12 + hours, and daylight saving shouldn't change it by more than a few hours. + (The maximum offset is 15 hours 56 minutes at present.) */ + if (hours < 0 || hours >= 24 || minutes < 0 || minutes >= 60 + || seconds < 0 || seconds >= 60) { + fprintf (stderr, "Warning: Strange timezone offset: H:%i M:%i S:%i\n", + hours, minutes, seconds); + } + + if (seconds == 0) + snprintf (buffer, sizeof(buffer), "%s%02i%02i", sign, hours, minutes); + else + snprintf (buffer, sizeof(buffer), "%s%02i%02i%02i", sign, hours, minutes, seconds); +} + +static const char* get_zone_directory(void) +{ + return zone_files_directory == NULL ? ZONEINFO_DIRECTORY : zone_files_directory; +} + +void set_zone_directory(char *path) +{ + zone_files_directory = malloc(strlen(path)+1); + if ( zone_files_directory != NULL ) + { + strcpy(zone_files_directory,path); + } +} + +void free_zone_directory(void) +{ + if ( zone_files_directory != NULL ) + { + free(zone_files_directory); + } +} diff --git a/libkcal/libical/src/libical/icaltimezone.h b/libkcal/libical/src/libical/icaltimezone.h new file mode 100644 index 000000000..1a97d391f --- /dev/null +++ b/libkcal/libical/src/libical/icaltimezone.h @@ -0,0 +1,165 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/*====================================================================== + FILE: icaltimezone.h + CREATOR: Damon Chaplin 15 March 2001 + + + + (C) COPYRIGHT 2001, Damon Chaplin + + This program is free software; you can redistribute it and/or modify + it under the terms of either: + + The LGPL as published by the Free Software Foundation, version + 2.1, available at: http://www.fsf.org/copyleft/lesser.html + + Or: + + The Mozilla Public License Version 1.0. You may obtain a copy of + the License at http://www.mozilla.org/MPL/ + + +======================================================================*/ +/** + * @file icaltimezone.h + * @brief timezone handling routines + */ + +#ifndef ICALTIMEZONE_H +#define ICALTIMEZONE_H + +#include <stdio.h> /* For FILE* */ +#include "icaltime.h" +#include "icalarray.h" +#include "icalcomponent.h" + + +#ifndef ICALTIMEZONE_DEFINED +#define ICALTIMEZONE_DEFINED +/** @brief An opaque struct representing a timezone. + * We declare this here to avoid a circular dependancy. + */ +typedef struct _icaltimezone icaltimezone; +#endif + +/** + * @par Creating/Destroying individual icaltimezones. + */ + +/** Creates a new icaltimezone. */ +icaltimezone *icaltimezone_new (void); + +/** Frees all memory used for the icaltimezone. Set free_struct to free the + icaltimezone struct as well. */ +void icaltimezone_free (icaltimezone *zone, + int free_struct); + + +/** + * @par Accessing timezones. + */ + +/** Free any builtin timezone information **/ +void icaltimezone_free_builtin_timezones(void); + +/** Returns the array of builtin icaltimezones. */ +icalarray* icaltimezone_get_builtin_timezones (void); + +/** Returns a single builtin timezone, given its Olson city name. */ +icaltimezone* icaltimezone_get_builtin_timezone (const char *location); + +/** Returns a single builtin timezone, given its TZID. */ +icaltimezone* icaltimezone_get_builtin_timezone_from_tzid (const char *tzid); + +/** Returns the UTC timezone. */ +icaltimezone* icaltimezone_get_utc_timezone (void); + +/** Returns the TZID of a timezone. */ +const char* icaltimezone_get_tzid (icaltimezone *zone); + +/** Returns the city name of a timezone. */ +const char* icaltimezone_get_location (icaltimezone *zone); + +/** Returns the TZNAME properties used in the latest STANDARD and DAYLIGHT + components. If they are the same it will return just one, e.g. "LMT". + If they are different it will format them like "EST/EDT". Note that this + may also return NULL. */ +const char* icaltimezone_get_tznames (icaltimezone *zone); + +/** Returns the latitude of a builtin timezone. */ +double icaltimezone_get_latitude (icaltimezone *zone); + +/** Returns the longitude of a builtin timezone. */ +double icaltimezone_get_longitude (icaltimezone *zone); + +/** Returns the VTIMEZONE component of a timezone. */ +icalcomponent* icaltimezone_get_component (icaltimezone *zone); + +/** Sets the VTIMEZONE component of an icaltimezone, initializing the tzid, + location & tzname fields. It returns 1 on success or 0 on failure, i.e. + no TZID was found. */ +int icaltimezone_set_component (icaltimezone *zone, + icalcomponent *comp); + +/** + * @par Converting times between timezones. + */ + +void icaltimezone_convert_time (struct icaltimetype *tt, + icaltimezone *from_zone, + icaltimezone *to_zone); + + +/** + * @par Getting offsets from UTC. + */ + +/** Calculates the UTC offset of a given local time in the given + timezone. It is the number of seconds to add to UTC to get local + time. The is_daylight flag is set to 1 if the time is in + daylight-savings time. */ +int icaltimezone_get_utc_offset (icaltimezone *zone, + struct icaltimetype *tt, + int *is_daylight); + +/** Calculates the UTC offset of a given UTC time in the given + timezone. It is the number of seconds to add to UTC to get local + time. The is_daylight flag is set to 1 if the time is in + daylight-savings time. */ +int icaltimezone_get_utc_offset_of_utc_time (icaltimezone *zone, + struct icaltimetype *tt, + int *is_daylight); + + + +/* + * Handling arrays of timezones. Mainly for internal use. + */ +icalarray* icaltimezone_array_new (void); + +void icaltimezone_array_append_from_vtimezone (icalarray *timezones, + icalcomponent *child); +void icaltimezone_array_free (icalarray *timezones); + + +/* + * @par Handling the default location the timezone files + */ + +/** Set the directory to look for the zonefiles */ +void set_zone_directory(char *path); + +/** Free memory dedicated to the zonefile directory */ +void free_zone_directory(void); + +/* + * @par Debugging Output. + */ + +/** Dumps information about changes in the timezone up to and including + max_year. */ +int icaltimezone_dump_changes (icaltimezone *zone, + int max_year, + FILE *fp); + +#endif /* ICALTIMEZONE_H */ diff --git a/libkcal/libical/src/libical/icaltypes.c b/libkcal/libical/src/libical/icaltypes.c new file mode 100644 index 000000000..6797e04dd --- /dev/null +++ b/libkcal/libical/src/libical/icaltypes.c @@ -0,0 +1,190 @@ +/* -*- Mode: C -*- + ====================================================================== + FILE: icaltypes.c + CREATOR: eric 16 May 1999 + + + + (C) COPYRIGHT 2000, Eric Busboom <eric@softwarestudio.org> + http://www.softwarestudio.org + + This program is free software; you can redistribute it and/or modify + it under the terms of either: + + The LGPL as published by the Free Software Foundation, version + 2.1, available at: http://www.fsf.org/copyleft/lesser.html + + Or: + + The Mozilla Public License Version 1.0. You may obtain a copy of + the License at http://www.mozilla.org/MPL/ + + The original code is icaltypes.c + + ======================================================================*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "icaltypes.h" +#include "icalerror.h" +#include "icalmemory.h" +#include <stdlib.h> /* for malloc and abs() */ +#include <errno.h> /* for errno */ +#include <string.h> /* for icalmemory_strdup */ +#include <assert.h> + +#ifdef WIN32 +#define snprintf _snprintf +#define strcasecmp stricmp +#endif + +#define TEMP_MAX 1024 + + +int icaltriggertype_is_null_trigger(struct icaltriggertype tr) +{ + if(icaltime_is_null_time(tr.time) && + icaldurationtype_is_null_duration(tr.duration)){ + return 1; + } + + return 0; +} + +int icaltriggertype_is_bad_trigger(struct icaltriggertype tr) +{ + if(icaldurationtype_is_bad_duration(tr.duration)){ + return 1; + } + + return 0; +} + +struct icaltriggertype icaltriggertype_from_int(const int reltime) +{ + struct icaltriggertype tr; + + tr.time = icaltime_null_time(); + tr.duration = icaldurationtype_from_int(reltime); + + return tr; +} + +struct icaltriggertype icaltriggertype_from_string(const char* str) +{ + + + struct icaltriggertype tr, null_tr; + icalerrorstate es = ICAL_ERROR_UNKNOWN; + icalerrorenum e; + + tr.time= icaltime_null_time(); + tr.duration = icaldurationtype_from_int(0); + + null_tr = tr; + + + /* Suppress errors so a failure in icaltime_from_string() does not cause an abort */ + es = icalerror_get_error_state(ICAL_MALFORMEDDATA_ERROR); + if(str == 0) goto error; + icalerror_set_error_state(ICAL_MALFORMEDDATA_ERROR,ICAL_ERROR_NONFATAL); + e = icalerrno; + icalerror_set_errno(ICAL_NO_ERROR); + + tr.time = icaltime_from_string(str); + + if (icaltime_is_null_time(tr.time)){ + + tr.duration = icaldurationtype_from_string(str); + + if (icaldurationtype_is_bad_duration(tr.duration)) goto error; + } + + icalerror_set_error_state(ICAL_MALFORMEDDATA_ERROR,es); + icalerror_set_errno(e); + return tr; + + error: + icalerror_set_error_state(ICAL_MALFORMEDDATA_ERROR,es); + icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR); + return tr; + +} + + +struct icalreqstattype icalreqstattype_from_string(const char* str) +{ + const char *p1,*p2; + struct icalreqstattype stat; + short major=0, minor=0; + + icalerror_check_arg((str != 0),"str"); + + stat.code = ICAL_UNKNOWN_STATUS; + stat.debug = 0; + stat.desc = 0; + + /* Get the status numbers */ + + sscanf(str, "%hd.%hd",&major, &minor); + + if (major <= 0 || minor < 0){ + icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR); + return stat; + } + + stat.code = icalenum_num_to_reqstat(major, minor); + + if (stat.code == ICAL_UNKNOWN_STATUS){ + icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR); + return stat; + } + + + p1 = strchr(str,';'); + + if (p1 == 0){ +/* icalerror_set_errno(ICAL_BADARG_ERROR);*/ + return stat; + } + + /* Just ignore the second clause; it will be taken from inside the library + */ + + + + p2 = strchr(p1+1,';'); + if (p2 != 0 && *p2 != 0){ + stat.debug = p2+1; + } + + return stat; + +} + +const char* icalreqstattype_as_string(struct icalreqstattype stat) +{ + char *temp; + + temp = (char*)icalmemory_tmp_buffer(TEMP_MAX); + + icalerror_check_arg_rz((stat.code != ICAL_UNKNOWN_STATUS),"Status"); + + if (stat.desc == 0){ + stat.desc = icalenum_reqstat_desc(stat.code); + } + + if(stat.debug != 0){ + snprintf(temp,TEMP_MAX,"%d.%d;%s;%s", icalenum_reqstat_major(stat.code), + icalenum_reqstat_minor(stat.code), + stat.desc, stat.debug); + + } else { + snprintf(temp,TEMP_MAX,"%d.%d;%s", icalenum_reqstat_major(stat.code), + icalenum_reqstat_minor(stat.code), + stat.desc); + } + + return temp; +} diff --git a/libkcal/libical/src/libical/icaltypes.h b/libkcal/libical/src/libical/icaltypes.h new file mode 100644 index 000000000..a89069d5f --- /dev/null +++ b/libkcal/libical/src/libical/icaltypes.h @@ -0,0 +1,108 @@ +/* -*- Mode: C -*- */ +/*====================================================================== + FILE: icaltypes.h + CREATOR: eric 20 March 1999 + + + (C) COPYRIGHT 2000, Eric Busboom <eric@softwarestudio.org> + http://www.softwarestudio.org + + This program is free software; you can redistribute it and/or modify + it under the terms of either: + + The LGPL as published by the Free Software Foundation, version + 2.1, available at: http://www.fsf.org/copyleft/lesser.html + + Or: + + The Mozilla Public License Version 1.0. You may obtain a copy of + the License at http://www.mozilla.org/MPL/ + + The original code is icaltypes.h + +======================================================================*/ + +#ifndef ICALTYPES_H +#define ICALTYPES_H + +#include <time.h> +#include "icalenums.h" +#include "icaltime.h" +#include "icalduration.h" +#include "icalperiod.h" + + +struct icalgeotype +{ + float lat; + float lon; +}; + + +struct icaldatetimeperiodtype +{ + struct icaltimetype time; + struct icalperiodtype period; +}; + + +struct icaltriggertype +{ + struct icaltimetype time; + struct icaldurationtype duration; +}; + +struct icaltriggertype icaltriggertype_from_int(const int reltime); +struct icaltriggertype icaltriggertype_from_string(const char* str); + +int icaltriggertype_is_null_trigger(struct icaltriggertype tr); +int icaltriggertype_is_bad_trigger(struct icaltriggertype tr); + +/* struct icalreqstattype. This struct contains two string pointers, +but don't try to free either of them. The "desc" string is a pointer +to a static table inside the library. Don't try to free it. The +"debug" string is a pointer into the string that the called passed +into to icalreqstattype_from_string. Don't try to free it either, and +don't use it after the original string has been freed. + +BTW, you would get that original string from +*icalproperty_get_requeststatus() or icalvalue_get_text(), when +operating on a the value of a request_status property. */ + +struct icalreqstattype { + + icalrequeststatus code; + const char* desc; + const char* debug; +}; + +struct icalreqstattype icalreqstattype_from_string(const char* str); +const char* icalreqstattype_as_string(struct icalreqstattype); + + + +struct icaltimezonephase { + const char* tzname; + int is_stdandard; /* 1 = standard tme, 0 = daylight savings time */ + struct icaltimetype dtstart; + int offsetto; + int tzoffsetfrom; + const char* comment; + struct icaldatetimeperiodtype rdate; + const char* rrule; +}; + + +struct icaltimezonetype { + const char* tzid; + struct icaltimetype last_mod; + const char* tzurl; + + /* Array of phases. The end of the array is a phase with tzname == 0 */ + struct icaltimezonephase *phases; +}; + +void icaltimezonetype_free(struct icaltimezonetype tzt); + + +#endif /* !ICALTYPES_H */ diff --git a/libkcal/libical/src/libical/icalvalue.c b/libkcal/libical/src/libical/icalvalue.c new file mode 100644 index 000000000..4166d56af --- /dev/null +++ b/libkcal/libical/src/libical/icalvalue.c @@ -0,0 +1,1326 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vi:set ts=4 sts=4 sw=4 expandtab : */ +/*====================================================================== + FILE: icalvalue.c + CREATOR: eric 02 May 1999 + + + + (C) COPYRIGHT 2000, Eric Busboom <eric@softwarestudio.org> + http://www.softwarestudio.org + + This program is free software; you can redistribute it and/or modify + it under the terms of either: + + The LGPL as published by the Free Software Foundation, version + 2.1, available at: http://www.fsf.org/copyleft/lesser.html + + Or: + + The Mozilla Public License Version 1.0. You may obtain a copy of + the License at http://www.mozilla.org/MPL/ + + The original code is icalvalue.c + + Contributions from: + Graham Davison <g.m.davison@computer.org> + + +======================================================================*/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "icalerror.h" +#include "icalmemory.h" +#include "icalparser.h" +#include "icalenums.h" +#include "icalvalueimpl.h" + +#include <stdlib.h> /* for malloc */ +#include <stdio.h> /* for snprintf */ +#include <string.h> /* For memset, others */ +#include <stddef.h> /* For offsetof() macro */ +#include <errno.h> +#include <time.h> /* for mktime */ +#include <stdlib.h> /* for atoi and atof */ +#include <limits.h> /* for SHRT_MAX */ + +#ifdef WIN32 +#define snprintf _snprintf +#define strcasecmp stricmp +#endif + +#ifdef _MAC_OS_ +#include "icalmemory_strdup.h" +#endif + +#define TMP_BUF_SIZE 1024 + +void print_datetime_to_string(char* str, const struct icaltimetype *data); +void print_date_to_string(char* str, const struct icaltimetype *data); +void print_time_to_string(char* str, const struct icaltimetype *data); + + +struct icalvalue_impl* icalvalue_new_impl(icalvalue_kind kind){ + + struct icalvalue_impl* v; + + if (!icalvalue_kind_is_valid(kind)) + return NULL; + + if ( ( v = (struct icalvalue_impl*) + malloc(sizeof(struct icalvalue_impl))) == 0) { + icalerror_set_errno(ICAL_NEWFAILED_ERROR); + return 0; + } + + strcpy(v->id,"val"); + + v->kind = kind; + v->size = 0; + v->parent = 0; + v->x_value = 0; + memset(&(v->data),0,sizeof(v->data)); + + return v; + +} + + + +icalvalue* +icalvalue_new (icalvalue_kind kind) +{ + return (icalvalue*)icalvalue_new_impl(kind); +} + +icalvalue* icalvalue_new_clone(const icalvalue* old) { + struct icalvalue_impl* new; + + new = icalvalue_new_impl(old->kind); + + if (new == 0){ + return 0; + } + + strcpy(new->id, old->id); + new->kind = old->kind; + new->size = old->size; + + switch (new->kind){ + case ICAL_ATTACH_VALUE: + case ICAL_BINARY_VALUE: + { + /* Hmm. We just ref the attach value, which may not be the right + * thing to do. We cannot quite copy the data, anyways, since we + * don't know how long it is. + */ + new->data.v_attach = old->data.v_attach; + if (new->data.v_attach) + icalattach_ref (new->data.v_attach); + + break; + } + case ICAL_QUERY_VALUE: + case ICAL_STRING_VALUE: + case ICAL_TEXT_VALUE: + case ICAL_CALADDRESS_VALUE: + case ICAL_URI_VALUE: + { + if (old->data.v_string != 0) { + new->data.v_string=icalmemory_strdup(old->data.v_string); + + if ( new->data.v_string == 0 ) { + return 0; + } + + } + break; + } + case ICAL_RECUR_VALUE: + { + if(old->data.v_recur != 0){ + new->data.v_recur = malloc(sizeof(struct icalrecurrencetype)); + + if(new->data.v_recur == 0){ + return 0; + } + + memcpy( new->data.v_recur, old->data.v_recur, + sizeof(struct icalrecurrencetype)); + } + break; + } + + case ICAL_X_VALUE: + { + if (old->x_value != 0) { + new->x_value=icalmemory_strdup(old->x_value); + + if (new->x_value == 0) { + return 0; + } + } + + break; + } + + default: + { + /* all of the other types are stored as values, not + pointers, so we can just copy the whole structure. */ + + new->data = old->data; + } + } + + return new; +} + +static char* icalmemory_strdup_and_dequote(const char* str) +{ + const char* p; + char* out = (char*)malloc(sizeof(char) * strlen(str) +1); + char* pout; + + if (out == 0){ + return 0; + } + + pout = out; + + for (p = str; *p!=0; p++){ + + if( *p == '\\') + { + p++; + switch(*p){ + case 0: + { + *pout = '\0'; + break; + + } + case 'n': + case 'N': + { + *pout = '\n'; + break; + } + case 't': + case 'T': + { + *pout = '\t'; + break; + } + case 'r': + case 'R': + { + *pout = '\r'; + break; + } + case 'b': + case 'B': + { + *pout = '\b'; + break; + } + case 'f': + case 'F': + { + *pout = '\f'; + break; + } + case ';': + case ',': + case '"': + case '\\': + { + *pout = *p; + break; + } + default: + { + *pout = ' '; + } + } + } else { + *pout = *p; + } + + pout++; + + } + + *pout = '\0'; + + return out; +} + +/* + * FIXME + * + * This is a bad API, as it forces callers to specify their own X type. + * This function should take care of this by itself. + */ +static +icalvalue* icalvalue_new_enum(icalvalue_kind kind, int x_type, const char* str) +{ + int e = icalproperty_kind_and_string_to_enum(kind, str); + struct icalvalue_impl *value; + + if(e != 0 && icalproperty_enum_belongs_to_property( + icalproperty_value_kind_to_kind(kind),e)) { + + value = icalvalue_new_impl(kind); + value->data.v_enum = e; + } else { + /* Make it an X value */ + value = icalvalue_new_impl(kind); + value->data.v_enum = x_type; + icalvalue_set_x(value,str); + } + + return value; +} + + +icalvalue* icalvalue_new_from_string_with_error(icalvalue_kind kind,const char* str,icalproperty** error) +{ + + struct icalvalue_impl *value = 0; + + icalerror_check_arg_rz(str!=0,"str"); + + if (error != 0){ + *error = 0; + } + + switch (kind){ + + case ICAL_ATTACH_VALUE: + { + icalattach *attach; + + attach = icalattach_new_from_url (str); + if (!attach) + break; + + value = icalvalue_new_attach (attach); + icalattach_unref (attach); + break; + } + + case ICAL_BINARY_VALUE: + { + icalattach *attach; + attach = icalattach_new_from_data (str, 0, 0); + if ( !attach ) + break; + value = icalvalue_new_attach (attach); + icalattach_unref (attach); + break; + } + case ICAL_BOOLEAN_VALUE: + { + /* HACK */ + value = 0; + + if (error != 0){ + char temp[TMP_BUF_SIZE]; + snprintf(temp,sizeof(temp),"%s Values are not implemented", + icalvalue_kind_to_string(kind)); + *error = icalproperty_vanew_xlicerror( + temp, + icalparameter_new_xlicerrortype( + ICAL_XLICERRORTYPE_VALUEPARSEERROR), + 0); + } + break; + } + + + case ICAL_TRANSP_VALUE: + value = icalvalue_new_enum(kind, (int)ICAL_TRANSP_X,str); + break; + case ICAL_METHOD_VALUE: + value = icalvalue_new_enum(kind, (int)ICAL_METHOD_X,str); + break; + case ICAL_STATUS_VALUE: + value = icalvalue_new_enum(kind, (int)ICAL_STATUS_X,str); + break; + case ICAL_ACTION_VALUE: + value = icalvalue_new_enum(kind, (int)ICAL_ACTION_X,str); + break; + + case ICAL_QUERY_VALUE: + value = icalvalue_new_query(str); + break; + + case ICAL_CLASS_VALUE: + value = icalvalue_new_enum(kind, (int)ICAL_CLASS_X,str); + break; + + + case ICAL_INTEGER_VALUE: + value = icalvalue_new_integer(atoi(str)); + break; + + case ICAL_FLOAT_VALUE: + value = icalvalue_new_float((float)atof(str)); + break; + + case ICAL_UTCOFFSET_VALUE: + { + int t,utcoffset, hours, minutes, seconds; + /* treat the UTCOFSET string a a decimal number, disassemble its digits + and reconstruct it as sections */ + t = strtol(str,0,10); + /* add phantom seconds field */ + if(abs(t)<9999){t *= 100; } + hours = (t/10000); + minutes = (t-hours*10000)/100; + seconds = (t-hours*10000-minutes*100); + utcoffset = hours*3600+minutes*60+seconds; + + value = icalvalue_new_utcoffset(utcoffset); + + break; + } + + case ICAL_TEXT_VALUE: + { + char* dequoted_str = icalmemory_strdup_and_dequote(str); + value = icalvalue_new_text(dequoted_str); + free(dequoted_str); + break; + } + + case ICAL_STRING_VALUE: + value = icalvalue_new_string(str); + break; + + case ICAL_CALADDRESS_VALUE: + value = icalvalue_new_caladdress(str); + break; + + case ICAL_URI_VALUE: + value = icalvalue_new_uri(str); + break; + + case ICAL_GEO_VALUE: + value = 0; + /* HACK */ + + if (error != 0){ + char temp[TMP_BUF_SIZE]; + strcpy(temp,"GEO Values are not implemented"); + *error = icalproperty_vanew_xlicerror( + temp, + icalparameter_new_xlicerrortype( + ICAL_XLICERRORTYPE_VALUEPARSEERROR), + 0); + } + + /*icalerror_warn("Parsing GEO properties is unimplmeneted");*/ + + break; + + case ICAL_RECUR_VALUE: + { + struct icalrecurrencetype rt; + rt = icalrecurrencetype_from_string(str); + if(rt.freq != ICAL_NO_RECURRENCE){ + value = icalvalue_new_recur(rt); + } + break; + } + + case ICAL_DATE_VALUE: + case ICAL_DATETIME_VALUE: + { + struct icaltimetype tt; + + tt = icaltime_from_string(str); + if(!icaltime_is_null_time(tt)){ + value = icalvalue_new_impl(kind); + value->data.v_time = tt; + + icalvalue_reset_kind(value); + } + break; + } + + case ICAL_DATETIMEPERIOD_VALUE: + { + struct icaltimetype tt; + struct icalperiodtype p; + tt = icaltime_from_string(str); + + if(!icaltime_is_null_time(tt)){ + value = icalvalue_new_datetime(tt); + break; + } + + p = icalperiodtype_from_string(str); + if (!icalperiodtype_is_null_period(p)){ + value = icalvalue_new_period(p); + } + + break; + } + + case ICAL_DURATION_VALUE: + { + struct icaldurationtype dur = icaldurationtype_from_string(str); + + if (!icaldurationtype_is_bad_duration(dur)) { /* failed to parse */ + value = icalvalue_new_duration(dur); + } + + break; + } + + case ICAL_PERIOD_VALUE: + { + struct icalperiodtype p; + p = icalperiodtype_from_string(str); + + if(!icalperiodtype_is_null_period(p)){ + value = icalvalue_new_period(p); + } + break; + } + + case ICAL_TRIGGER_VALUE: + { + struct icaltriggertype tr = icaltriggertype_from_string(str); + if (!icaltriggertype_is_bad_trigger(tr)) { + value = icalvalue_new_trigger(tr); + } + break; + } + + case ICAL_REQUESTSTATUS_VALUE: + { + struct icalreqstattype rst = icalreqstattype_from_string(str); + if(rst.code != ICAL_UNKNOWN_STATUS){ + value = icalvalue_new_requeststatus(rst); + } + break; + + } + + case ICAL_X_VALUE: + { + char* dequoted_str = icalmemory_strdup_and_dequote(str); + value = icalvalue_new_x(dequoted_str); + free(dequoted_str); + } + break; + + default: + { + if (error != 0 ){ + char temp[TMP_BUF_SIZE]; + + snprintf(temp,TMP_BUF_SIZE,"Unknown type for \'%s\'",str); + + *error = icalproperty_vanew_xlicerror( + temp, + icalparameter_new_xlicerrortype( + ICAL_XLICERRORTYPE_VALUEPARSEERROR), + 0); + } + + icalerror_warn("icalvalue_new_from_string got an unknown value type"); + value=0; + } + } + + + if (error != 0 && *error == 0 && value == 0){ + char temp[TMP_BUF_SIZE]; + + snprintf(temp,TMP_BUF_SIZE,"Failed to parse value: \'%s\'",str); + + *error = icalproperty_vanew_xlicerror( + temp, + icalparameter_new_xlicerrortype( + ICAL_XLICERRORTYPE_VALUEPARSEERROR), + 0); + } + + + return value; + +} + +icalvalue* icalvalue_new_from_string(icalvalue_kind kind,const char* str) +{ + return icalvalue_new_from_string_with_error(kind,str,(icalproperty**)0); +} + + + +void +icalvalue_free (icalvalue* v) +{ + icalerror_check_arg_rv((v != 0),"value"); + +#ifdef ICAL_FREE_ON_LIST_IS_ERROR + icalerror_assert( (v->parent ==0),"This value is still attached to a property"); + +#else + if(v->parent !=0){ + return; + } +#endif + + if(v->x_value != 0){ + free(v->x_value); + } + + switch (v->kind){ + case ICAL_BINARY_VALUE: + case ICAL_ATTACH_VALUE: { + if (v->data.v_attach) { + icalattach_unref (v->data.v_attach); + v->data.v_attach = NULL; + } + + break; + } + case ICAL_TEXT_VALUE: + case ICAL_CALADDRESS_VALUE: + case ICAL_URI_VALUE: + case ICAL_QUERY_VALUE: + { + if (v->data.v_string != 0) { + free((void*)v->data.v_string); + v->data.v_string = 0; + } + break; + } + case ICAL_RECUR_VALUE: + { + if(v->data.v_recur != 0){ + free((void*)v->data.v_recur); + v->data.v_recur = 0; + } + break; + } + + default: + { + /* Nothing to do */ + } + } + + v->kind = ICAL_NO_VALUE; + v->size = 0; + v->parent = 0; + memset(&(v->data),0,sizeof(v->data)); + v->id[0] = 'X'; + free(v); +} + +int +icalvalue_is_valid (const icalvalue* value) +{ + if(value == 0){ + return 0; + } + + return 1; +} + +static char* icalvalue_binary_as_ical_string(const icalvalue* value) { + + const char* data; + char* str; + icalerror_check_arg_rz( (value!=0),"value"); + + data = icalvalue_get_binary(value); + + str = (char*)icalmemory_tmp_buffer(60); + strcpy(str,"icalvalue_binary_as_ical_string is not implemented yet"); + + return str; +} + + +#define MAX_INT_DIGITS 12 /* Enough for 2^32 + sign*/ + +static char* icalvalue_int_as_ical_string(const icalvalue* value) { + int data; + char* str = (char*)icalmemory_tmp_buffer(MAX_INT_DIGITS); + + icalerror_check_arg_rz( (value!=0),"value"); + + data = icalvalue_get_integer(value); + + snprintf(str,MAX_INT_DIGITS,"%d",data); + + return str; +} + +static char* icalvalue_utcoffset_as_ical_string(const icalvalue* value) +{ + int data,h,m,s; + char sign; + char* str = (char*)icalmemory_tmp_buffer(9); + + icalerror_check_arg_rz( (value!=0),"value"); + + data = icalvalue_get_utcoffset(value); + + if (abs(data) == data){ + sign = '+'; + } else { + sign = '-'; + } + + h = data/3600; + m = (data - (h*3600))/ 60; + s = (data - (h*3600) - (m*60)); + + if (s > 0) + snprintf(str,9,"%c%02d%02d%02d",sign,abs(h),abs(m),abs(s)); + else + snprintf(str,9,"%c%02d%02d",sign,abs(h),abs(m)); + + return str; +} + +static char* icalvalue_string_as_ical_string(const icalvalue* value) { + + const char* data; + char* str = 0; + icalerror_check_arg_rz( (value!=0),"value"); + data = value->data.v_string; + + str = (char*)icalmemory_tmp_buffer(strlen(data)+1); + + strcpy(str,data); + + return str; +} + + +static char* icalvalue_recur_as_ical_string(const icalvalue* value) +{ + struct icalrecurrencetype *recur = value->data.v_recur; + + return icalrecurrencetype_as_string(recur); +} + + /* @todo This is not RFC2445 compliant. + * The RFC only allows: + * TSAFE-CHAR = %x20-21 / %x23-2B / %x2D-39 / %x3C-5B / %x5D-7E / NON-US-ASCII + * As such, \t\r\b\f are not allowed, not even escaped + */ + +static char* icalvalue_text_as_ical_string(const icalvalue* value) { + char *str; + char *str_p; + char *rtrn; + const char *p; + size_t buf_sz; + + buf_sz = strlen(value->data.v_string)+1; + + str_p = str = (char*)icalmemory_new_buffer(buf_sz); + + if (str_p == 0){ + return 0; + } + + for(p=value->data.v_string; *p!=0; p++){ + + switch(*p){ + case '\n': { + icalmemory_append_string(&str,&str_p,&buf_sz,"\\n"); + break; + } + + case '\t': { + icalmemory_append_string(&str,&str_p,&buf_sz,"\\t"); + break; + } + case '\r': { + icalmemory_append_string(&str,&str_p,&buf_sz,"\\r"); + break; + } + case '\b': { + icalmemory_append_string(&str,&str_p,&buf_sz,"\\b"); + break; + } + case '\f': { + icalmemory_append_string(&str,&str_p,&buf_sz,"\\f"); + break; + } + + case ';': + case ',': + case '"': + case '\\':{ + icalmemory_append_char(&str,&str_p,&buf_sz,'\\'); + icalmemory_append_char(&str,&str_p,&buf_sz,*p); + break; + } + + default: { + icalmemory_append_char(&str,&str_p,&buf_sz,*p); + } + } + } + + /* Assume the last character is not a '\0' and add one. We could + check *str_p != 0, but that would be an uninitialized memory + read. */ + + + icalmemory_append_char(&str,&str_p,&buf_sz,'\0'); + + rtrn = icalmemory_tmp_copy(str); + + icalmemory_free_buffer(str); + + return rtrn; +} + + +static char* +icalvalue_attach_as_ical_string(const icalvalue* value) +{ + icalattach *a; + char * str; + + icalerror_check_arg_rz( (value!=0),"value"); + + a = icalvalue_get_attach(value); + + if (icalattach_get_is_url (a)) { + const char *url; + + url = icalattach_get_url (a); + str = icalmemory_tmp_buffer (strlen (url) + 1); + strcpy (str, url); + return str; + } else { +/* return icalvalue_binary_as_ical_string (value);*/ + const char *data = 0; + data = (const char*)icalattach_get_data(a); + str = icalmemory_tmp_buffer (strlen (data) + 1); + strcpy (str, data); + return str; +} +} + + +static char* icalvalue_duration_as_ical_string(const icalvalue* value) { + + struct icaldurationtype data; + + icalerror_check_arg_rz( (value!=0),"value"); + data = icalvalue_get_duration(value); + + return icaldurationtype_as_ical_string(data); +} + +void print_time_to_string(char* str, const struct icaltimetype *data) +{ + char temp[20]; + + if (icaltime_is_utc(*data)){ + snprintf(temp,sizeof(temp),"%02d%02d%02dZ",data->hour,data->minute,data->second); + } else { + snprintf(temp,sizeof(temp),"%02d%02d%02d",data->hour,data->minute,data->second); + } + + strcat(str,temp); +} + + +void print_date_to_string(char* str, const struct icaltimetype *data) +{ + char temp[20]; + + snprintf(temp,sizeof(temp),"%04d%02d%02d",data->year,data->month,data->day); + + strcat(str,temp); +} + +static char* icalvalue_date_as_ical_string(const icalvalue* value) { + + struct icaltimetype data; + char* str; + icalerror_check_arg_rz( (value!=0),"value"); + data = icalvalue_get_date(value); + + str = (char*)icalmemory_tmp_buffer(9); + + str[0] = 0; + print_date_to_string(str,&data); + + return str; +} + +void print_datetime_to_string(char* str, const struct icaltimetype *data) +{ + print_date_to_string(str,data); + if ( !data->is_date ) { + strcat(str,"T"); + print_time_to_string(str,data); + } +} + +static const char* icalvalue_datetime_as_ical_string(const icalvalue* value) { + + struct icaltimetype data; + char* str; + icalvalue_kind kind = icalvalue_isa(value); + + icalerror_check_arg_rz( (value!=0),"value"); + + + if( !(kind == ICAL_DATE_VALUE || kind == ICAL_DATETIME_VALUE )) + { + icalerror_set_errno(ICAL_BADARG_ERROR); + return 0; + } + + data = icalvalue_get_datetime(value); + + str = (char*)icalmemory_tmp_buffer(20); + + str[0] = 0; + + print_datetime_to_string(str,&data); + + return str; + +} + +static char* icalvalue_float_as_ical_string(const icalvalue* value) { + + float data; + char* str; + icalerror_check_arg_rz( (value!=0),"value"); + data = icalvalue_get_float(value); + + str = (char*)icalmemory_tmp_buffer(40); + + snprintf(str,40,"%f",data); + + return str; +} + +static char* icalvalue_geo_as_ical_string(const icalvalue* value) { + + struct icalgeotype data; + char* str; + icalerror_check_arg_rz( (value!=0),"value"); + + data = icalvalue_get_geo(value); + + str = (char*)icalmemory_tmp_buffer(80); + + snprintf(str,80,"%f;%f",data.lat,data.lon); + + return str; +} + +static const char* icalvalue_datetimeperiod_as_ical_string(const icalvalue* value) { + struct icaldatetimeperiodtype dtp = icalvalue_get_datetimeperiod(value); + + icalerror_check_arg_rz( (value!=0),"value"); + + if(!icaltime_is_null_time(dtp.time)){ + return icaltime_as_ical_string(dtp.time); + } else { + return icalperiodtype_as_ical_string(dtp.period); + } +} + +static const char* icalvalue_period_as_ical_string(const icalvalue* value) { + struct icalperiodtype data; + icalerror_check_arg_rz( (value!=0),"value"); + data = icalvalue_get_period(value); + + return icalperiodtype_as_ical_string(data); + +} + +static const char* icalvalue_trigger_as_ical_string(const icalvalue* value) { + + struct icaltriggertype data; + + icalerror_check_arg_rz( (value!=0),"value"); + data = icalvalue_get_trigger(value); + + if(!icaltime_is_null_time(data.time)){ + return icaltime_as_ical_string(data.time); + } else { + return icaldurationtype_as_ical_string(data.duration); + } + +} + +const char* +icalvalue_as_ical_string(const icalvalue* value) +{ + if(value == 0){ + return 0; + } + + switch (value->kind){ + + case ICAL_ATTACH_VALUE: + return icalvalue_attach_as_ical_string(value); + + case ICAL_BINARY_VALUE: + return icalvalue_binary_as_ical_string(value); + + case ICAL_BOOLEAN_VALUE: + case ICAL_INTEGER_VALUE: + return icalvalue_int_as_ical_string(value); + + case ICAL_UTCOFFSET_VALUE: + return icalvalue_utcoffset_as_ical_string(value); + + case ICAL_TEXT_VALUE: + return icalvalue_text_as_ical_string(value); + + case ICAL_QUERY_VALUE: + return icalvalue_string_as_ical_string(value); + + case ICAL_STRING_VALUE: + case ICAL_URI_VALUE: + case ICAL_CALADDRESS_VALUE: + return icalvalue_string_as_ical_string(value); + + case ICAL_DATE_VALUE: + return icalvalue_date_as_ical_string(value); + case ICAL_DATETIME_VALUE: + return icalvalue_datetime_as_ical_string(value); + case ICAL_DURATION_VALUE: + return icalvalue_duration_as_ical_string(value); + + case ICAL_PERIOD_VALUE: + return icalvalue_period_as_ical_string(value); + case ICAL_DATETIMEPERIOD_VALUE: + return icalvalue_datetimeperiod_as_ical_string(value); + + case ICAL_FLOAT_VALUE: + return icalvalue_float_as_ical_string(value); + + case ICAL_GEO_VALUE: + return icalvalue_geo_as_ical_string(value); + + case ICAL_RECUR_VALUE: + return icalvalue_recur_as_ical_string(value); + + case ICAL_TRIGGER_VALUE: + return icalvalue_trigger_as_ical_string(value); + + case ICAL_REQUESTSTATUS_VALUE: + return icalreqstattype_as_string(value->data.v_requeststatus); + + case ICAL_ACTION_VALUE: + case ICAL_METHOD_VALUE: + case ICAL_STATUS_VALUE: + case ICAL_TRANSP_VALUE: + case ICAL_CLASS_VALUE: + if(value->x_value !=0){ + return icalmemory_tmp_copy(value->x_value); + } + + return icalproperty_enum_to_string(value->data.v_enum); + + case ICAL_X_VALUE: + if (value->x_value != 0) + return icalmemory_tmp_copy(value->x_value); + + /* FALLTHRU */ + + case ICAL_NO_VALUE: + default: + { + return 0; + } + } +} + + +icalvalue_kind +icalvalue_isa (const icalvalue* value) +{ + if(value == 0){ + return ICAL_NO_VALUE; + } + + return value->kind; +} + + +int +icalvalue_isa_value (void* value) +{ + struct icalvalue_impl *impl = (struct icalvalue_impl *)value; + + icalerror_check_arg_rz( (value!=0), "value"); + + if (strcmp(impl->id,"val") == 0) { + return 1; + } else { + return 0; + } +} + + +static int icalvalue_is_time(const icalvalue* a) { + icalvalue_kind kind = icalvalue_isa(a); + + if(kind == ICAL_DATETIME_VALUE || + kind == ICAL_DATE_VALUE ){ + return 1; + } + + return 0; + +} + +/* + * In case of error, this function returns 0. This is partly bogus, as 0 is + * not part of the returned enum. + * FIXME We should probably add an error value to the enum. + */ +icalparameter_xliccomparetype +icalvalue_compare(const icalvalue* a, const icalvalue *b) +{ + + icalerror_check_arg_rz( (a!=0), "a"); + icalerror_check_arg_rz( (b!=0), "b"); + + /* Not the same type; they can only be unequal */ + if( ! (icalvalue_is_time(a) && icalvalue_is_time(b)) && + icalvalue_isa(a) != icalvalue_isa(b)){ + return ICAL_XLICCOMPARETYPE_NOTEQUAL; + } + + switch (icalvalue_isa(a)){ + + case ICAL_ATTACH_VALUE: + { + if (icalattach_get_is_url(a->data.v_attach) && + icalattach_get_is_url(b->data.v_attach)) { + if (strcasecmp(icalattach_get_url(a->data.v_attach), + icalattach_get_url(b->data.v_attach)) == 0) + return ICAL_XLICCOMPARETYPE_EQUAL; + else + return ICAL_XLICCOMPARETYPE_NOTEQUAL; + } + else { + if (a->data.v_attach == b->data.v_attach) + return ICAL_XLICCOMPARETYPE_EQUAL; + else + return ICAL_XLICCOMPARETYPE_NOTEQUAL; + } + } + case ICAL_BINARY_VALUE: + { + if (a->data.v_attach == b->data.v_attach) + return ICAL_XLICCOMPARETYPE_EQUAL; + else + return ICAL_XLICCOMPARETYPE_NOTEQUAL; + } + + case ICAL_BOOLEAN_VALUE: + { + if (icalvalue_get_boolean(a) == icalvalue_get_boolean(b)){ + return ICAL_XLICCOMPARETYPE_EQUAL; + } else { + return ICAL_XLICCOMPARETYPE_NOTEQUAL; + } + } + + case ICAL_FLOAT_VALUE: + { + if (a->data.v_float > b->data.v_float){ + return ICAL_XLICCOMPARETYPE_GREATER; + } else if (a->data.v_float < b->data.v_float){ + return ICAL_XLICCOMPARETYPE_LESS; + } else { + return ICAL_XLICCOMPARETYPE_EQUAL; + } + } + + case ICAL_INTEGER_VALUE: + case ICAL_UTCOFFSET_VALUE: + { + if (a->data.v_int > b->data.v_int){ + return ICAL_XLICCOMPARETYPE_GREATER; + } else if (a->data.v_int < b->data.v_int){ + return ICAL_XLICCOMPARETYPE_LESS; + } else { + return ICAL_XLICCOMPARETYPE_EQUAL; + } + } + + case ICAL_DURATION_VALUE: + { + int dur_a = icaldurationtype_as_int(a->data.v_duration); + int dur_b = icaldurationtype_as_int(b->data.v_duration); + + if (dur_a > dur_b){ + return ICAL_XLICCOMPARETYPE_GREATER; + } else if (dur_a < dur_b){ + return ICAL_XLICCOMPARETYPE_LESS; + } else { + return ICAL_XLICCOMPARETYPE_EQUAL; + } + } + + + case ICAL_TEXT_VALUE: + case ICAL_URI_VALUE: + case ICAL_CALADDRESS_VALUE: + case ICAL_TRIGGER_VALUE: + case ICAL_DATE_VALUE: + case ICAL_DATETIME_VALUE: + case ICAL_DATETIMEPERIOD_VALUE: + case ICAL_QUERY_VALUE: + case ICAL_RECUR_VALUE: + { + int r; + + r = strcmp(icalvalue_as_ical_string(a), + icalvalue_as_ical_string(b)); + + if (r > 0) { + return ICAL_XLICCOMPARETYPE_GREATER; + } else if (r < 0){ + return ICAL_XLICCOMPARETYPE_LESS; + } else { + return ICAL_XLICCOMPARETYPE_EQUAL; + } + + + } + + case ICAL_METHOD_VALUE: + { + if (icalvalue_get_method(a) == icalvalue_get_method(b)){ + return ICAL_XLICCOMPARETYPE_EQUAL; + } else { + return ICAL_XLICCOMPARETYPE_NOTEQUAL; + } + + } + + case ICAL_STATUS_VALUE: + { + if (icalvalue_get_status(a) == icalvalue_get_status(b)){ + return ICAL_XLICCOMPARETYPE_EQUAL; + } else { + return ICAL_XLICCOMPARETYPE_NOTEQUAL; + } + + } + + case ICAL_TRANSP_VALUE: + { + if (icalvalue_get_transp(a) == icalvalue_get_transp(b)){ + return ICAL_XLICCOMPARETYPE_EQUAL; + } else { + return ICAL_XLICCOMPARETYPE_NOTEQUAL; + } + } + + case ICAL_ACTION_VALUE: + { + if (icalvalue_get_action(a) == icalvalue_get_action(b)){ + return ICAL_XLICCOMPARETYPE_EQUAL; + } else { + return ICAL_XLICCOMPARETYPE_NOTEQUAL; + } + } + + case ICAL_PERIOD_VALUE: + case ICAL_GEO_VALUE: + case ICAL_NO_VALUE: + default: + { + icalerror_warn("Comparison not implemented for value type"); + return 0; + } + } + +} + +/** Examine the value and possibly change the kind to agree with the + * value + */ + +void icalvalue_reset_kind(icalvalue* value) +{ + if( (value->kind==ICAL_DATETIME_VALUE || value->kind==ICAL_DATE_VALUE )&& + !icaltime_is_null_time(value->data.v_time) ) { + + if(icaltime_is_date(value->data.v_time)){ + value->kind = ICAL_DATE_VALUE; + } else { + value->kind = ICAL_DATETIME_VALUE; + } + } + +} + +void icalvalue_set_parent(icalvalue* value, + icalproperty* property) +{ + value->parent = property; +} + +icalproperty* icalvalue_get_parent(icalvalue* value) +{ + return value->parent; +} + + +int icalvalue_encode_ical_string(const char *szText, char *szEncText, int nMaxBufferLen) +{ + char *ptr; + icalvalue *value = 0; + + if ((szText == 0) || (szEncText == 0)) + return 0; + + value = icalvalue_new_from_string(ICAL_STRING_VALUE, szText); + + if (value == 0) + return 0; + + ptr = icalvalue_text_as_ical_string(value); + if (ptr == 0) + return 0; + + if ((int)strlen(ptr) >= nMaxBufferLen) + { + icalvalue_free (value); + return 0; + } + + strcpy(szEncText, ptr); + + icalvalue_free ((icalvalue*)value); + + return 1; +} + +/* The remaining interfaces are 'new', 'set' and 'get' for each of the value + types */ diff --git a/libkcal/libical/src/libical/icalvalue.h b/libkcal/libical/src/libical/icalvalue.h new file mode 100644 index 000000000..96b970430 --- /dev/null +++ b/libkcal/libical/src/libical/icalvalue.h @@ -0,0 +1,87 @@ +/* -*- Mode: C -*- */ +/*====================================================================== + FILE: icalvalue.h + CREATOR: eric 20 March 1999 + + + + (C) COPYRIGHT 2000, Eric Busboom <eric@softwarestudio.org> + http://www.softwarestudio.org + + This program is free software; you can redistribute it and/or modify + it under the terms of either: + + The LGPL as published by the Free Software Foundation, version + 2.1, available at: http://www.fsf.org/copyleft/lesser.html + + Or: + + The Mozilla Public License Version 1.0. You may obtain a copy of + the License at http://www.mozilla.org/MPL/ + + The original code is icalvalue.h + + ======================================================================*/ + +#ifndef ICALVALUE_H +#define ICALVALUE_H + +#include <time.h> +#include "icalenums.h" +#include "icaltypes.h" +#include "icalrecur.h" +#include "icalduration.h" +#include "icalperiod.h" +#include "icalderivedproperty.h" /* For icalproperty_method, etc. */ +#include "icalderivedparameter.h" +#include "icalderivedvalue.h" + +/* Defined in icalderivedvalue.h */ +/*typedef struct icalvalue_impl icalvalue;*/ + +icalvalue* icalvalue_new(icalvalue_kind kind); + +icalvalue* icalvalue_new_clone(const icalvalue* value); + +icalvalue* icalvalue_new_from_string(icalvalue_kind kind, const char* str); + +void icalvalue_free(icalvalue* value); + +int icalvalue_is_valid(const icalvalue* value); + +const char* icalvalue_as_ical_string(const icalvalue* value); + +icalvalue_kind icalvalue_isa(const icalvalue* value); + +int icalvalue_isa_value(void*); + +icalparameter_xliccomparetype icalvalue_compare(const icalvalue* a, const icalvalue *b); + + +/* Special, non autogenerated value accessors */ + +icalvalue* icalvalue_new_recur (struct icalrecurrencetype v); +void icalvalue_set_recur(icalvalue* value, struct icalrecurrencetype v); +struct icalrecurrencetype icalvalue_get_recur(const icalvalue* value); + +icalvalue* icalvalue_new_trigger (struct icaltriggertype v); +void icalvalue_set_trigger(icalvalue* value, struct icaltriggertype v); +struct icaltriggertype icalvalue_get_trigger(const icalvalue* value); + +icalvalue* icalvalue_new_datetimeperiod (struct icaldatetimeperiodtype v); +void icalvalue_set_datetimeperiod(icalvalue* value, + struct icaldatetimeperiodtype v); +struct icaldatetimeperiodtype icalvalue_get_datetimeperiod(const icalvalue* value); + +/* Convert enumerations */ + +icalvalue_kind icalvalue_string_to_kind(const char* str); +const char* icalvalue_kind_to_string(const icalvalue_kind kind); + +/** Check validity of a specific icalvalue_kind **/ +int icalvalue_kind_is_valid(const icalvalue_kind kind); + +/** Encode a character string in ical format, esacpe certain characters, etc. */ +int icalvalue_encode_ical_string(const char *szText, char *szEncText, int MaxBufferLen); + +#endif /*ICALVALUE_H*/ diff --git a/libkcal/libical/src/libical/icalvalueimpl.h b/libkcal/libical/src/libical/icalvalueimpl.h new file mode 100644 index 000000000..545c8fa70 --- /dev/null +++ b/libkcal/libical/src/libical/icalvalueimpl.h @@ -0,0 +1,95 @@ +/* -*- Mode: C -*- */ +/*====================================================================== + FILE: icalvalue.c + CREATOR: eric 02 May 1999 + + + + (C) COPYRIGHT 2000, Eric Busboom <eric@softwarestudio.org> + http://www.softwarestudio.org + + This program is free software; you can redistribute it and/or modify + it under the terms of either: + + The LGPL as published by the Free Software Foundation, version + 2.1, available at: http://www.fsf.org/copyleft/lesser.html + + Or: + + The Mozilla Public License Version 1.0. You may obtain a copy of + the License at http://www.mozilla.org/MPL/ + + The original code is icalvalue.c + + Contributions from: + Graham Davison (g.m.davison@computer.org) + + +======================================================================*/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#ifndef ICALVALUEIMPL_H +#define ICALVALUEIMPL_H + +#include "icalenums.h" +#include "icalproperty.h" +#include "icalderivedvalue.h" + + +struct icalvalue_impl { + icalvalue_kind kind; /*this is the kind that is visible from the outside*/ + + char id[5]; + int size; + icalproperty* parent; + char* x_value; + + union data { + icalattach *v_attach; + /* void *v_binary; */ /* use v_attach */ + const char *v_string; + /*char *v_text;*/ + /*char *v_caladdress;*/ + /*char *v_uri;*/ + float v_float; + int v_int; + /*int v_boolean;*/ + /*int v_integer;*/ + struct icaldurationtype v_duration; + /*int v_utcoffset;*/ + + struct icalperiodtype v_period; + /*struct icalperiodtype v_datetimeperiod;*/ + struct icalgeotype v_geo; + /*time_t v_time;*/ + struct icaltimetype v_time; + /*struct icaltimetype v_date;*/ + /*struct icaltimetype v_datetime;*/ + /*struct icaltimetype v_datetimedate;*/ + + struct icalreqstattype v_requeststatus; + + /* struct icalrecurrencetype was once included + directly ( not referenced ) in this union, but it + contributes 2000 bytes to every value, so now it is + a reference*/ + + struct icalrecurrencetype *v_recur; + struct icaltriggertype v_trigger; + + int v_enum; + /* v_enum takes care of several enumerated types including: + icalproperty_method v_method; + icalproperty_status v_status; + icalproperty_action v_action; + icalproperty_class v_class; + icalproperty_transp v_transp; + */ + + } data; +}; + +#endif diff --git a/libkcal/libical/src/libical/icalversion.h b/libkcal/libical/src/libical/icalversion.h new file mode 100644 index 000000000..c4b1d15ef --- /dev/null +++ b/libkcal/libical/src/libical/icalversion.h @@ -0,0 +1,7 @@ +#ifndef ICAL_VERSION_H +#define ICAL_VERSION_H + +#define ICAL_PACKAGE "libical" +#define ICAL_VERSION "0.24" + +#endif diff --git a/libkcal/libical/src/libical/icalversion.h.in b/libkcal/libical/src/libical/icalversion.h.in new file mode 100644 index 000000000..5d213693e --- /dev/null +++ b/libkcal/libical/src/libical/icalversion.h.in @@ -0,0 +1,7 @@ +#ifndef ICAL_VERSION_H +#define ICAL_VERSION_H + +#define ICAL_PACKAGE "@PACKAGE@" +#define ICAL_VERSION "@VERSION@" + +#endif diff --git a/libkcal/libical/src/libical/pvl.c b/libkcal/libical/src/libical/pvl.c new file mode 100644 index 000000000..d663eaab7 --- /dev/null +++ b/libkcal/libical/src/libical/pvl.c @@ -0,0 +1,585 @@ +/*====================================================================== + FILE: pvl.c + CREATOR: eric November, 1995 + + + (C) COPYRIGHT 2000, Eric Busboom <eric@softwarestudio.org> + http://www.softwarestudio.org +======================================================================*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "pvl.h" +#include <errno.h> +#include <assert.h> +#include <stdlib.h> + +/** + struct pvl_list_t + + The list structure. This is the hanlde for the entire list + + This type is also private. Use pvl_list instead + + */ + +typedef struct pvl_list_t +{ + int MAGIC; /**< Magic Identifier */ + struct pvl_elem_t *head; /**< Head of list */ + struct pvl_elem_t *tail; /**< Tail of list */ + int count; /**< Number of items in the list */ + struct pvl_elem_t *p; /**< Pointer used for iterators */ +} pvl_list_t; + + + + +/** + * This global is incremented for each call to pvl_new_element(); it gives each + * list a unique identifer + */ + +int pvl_elem_count = 0; +int pvl_list_count = 0; + + +/** + * @brief Creates a new list, clears the pointers and assigns a magic number + * + * @return Pointer to the new list, 0 if there is no available memory. + */ + +pvl_list +pvl_newlist() +{ + struct pvl_list_t *L; + + if ( ( L = (struct pvl_list_t*)malloc(sizeof(struct pvl_list_t))) == 0) + { + errno = ENOMEM; + return 0; + } + + L->MAGIC = pvl_list_count; + pvl_list_count++; + L->head = 0; + L->tail = 0; + L->count = 0; + L->p = 0; + + return L; +} + +void +pvl_free(pvl_list l) +{ + struct pvl_list_t *L = (struct pvl_list_t *)l; + + pvl_clear(l); + + free(L); +} + +/** + * @brief Creates a new list element, assigns a magic number, and assigns + * the next and previous pointers. + * + * Passing in the next and previous points may seem odd, but it allos the user + * to set them while keeping the internal data hidden. In nearly all cases, + * the user is the pvl library itself. + * + * @param d The data item to be stored in the list + * @param next Pointer value to assign to the member "next" + * @param prior Pointer value to assign to the member "prior" + * + * @return A pointer to the new element, 0 if there is no memory available. + */ + +pvl_elem +pvl_new_element(void *d, pvl_elem next, pvl_elem prior) +{ + struct pvl_elem_t *E; + + if ( ( E = (struct pvl_elem_t*)malloc(sizeof(struct pvl_elem_t))) == 0) + { + errno = ENOMEM; + return 0; + } + + E->MAGIC = pvl_elem_count++; + E->d = d; + E->next = next; + E->prior = prior; + + return (pvl_elem)E; +} + +/** + * @brief Add a new element to the from of the list + * + * @param L The list to add the item to + * @param d Pointer to the item to add + */ + +void +pvl_unshift(pvl_list L,void *d) +{ + struct pvl_elem_t *E = pvl_new_element(d,L->head,0); + + if (E->next != 0) + { + /* Link the head node to it */ + E->next->prior = E; + } + + /* move the head */ + L->head = E; + + /* maybe move the tail */ + + if (L->tail == 0) + { + L->tail = E; + } + + L->count++; +} + +/** + * @brief Remove an element from the front of the list + * + * @param L The list to operate on + * + * @return the entry on the front of the list + */ + +void* +pvl_shift(pvl_list L) +{ + if (L->head == 0) + { + return 0; + } + + return pvl_remove(L,(void*)L->head); + +} + +/** + * @brief Add a new item to the tail of the list + * + * @param L The list to operate on + * @param d Pointer to the item to add + * + */ + +void +pvl_push(pvl_list L,void *d) +{ + struct pvl_elem_t *E = pvl_new_element(d,0,L->tail); + + /* These are done in pvl_new_element + E->next = 0; + E->prior = L->tail; + */ + + if (L->tail != 0) + { + L->tail->next = E; + } + + if (L->head == 0) + { + L->head = E; + } + + L->tail = E; + + L->count++; + +} + +/** + * @brief Remove an element from the tail of the list + * + * @param L The list to operate on + */ + +void* +pvl_pop(pvl_list L) +{ + if ( L->tail == 0) + { + return 0; + } + + return pvl_remove(L,(void*) L->tail);; + +} + + +/** + * Add a new item to a list that is ordered by a comparison function. + * This routine assumes that the list is properly ordered. + * + * @param L The list to operate on + * @param f Pointer to a comparison function + * @param d Pointer to data to pass to the comparison function + */ + +void +pvl_insert_ordered(pvl_list L,pvl_comparef f,void *d) +{ + struct pvl_elem_t *P; + + L->count++; + + /* Empty list, add to head */ + + if(L->head == 0) + { + pvl_unshift(L,d); + return; + } + + /* smaller than head, add to head */ + + if ( ((*f)(d,L->head->d)) <= 0) + { + pvl_unshift(L,d); + return; + } + + /* larger than tail, add to tail */ + if ( (*f)(d,L->tail->d) >= 0) + { + pvl_push(L,d); + return; + } + + + /* Search for the first element that is smaller, and add before it */ + + for (P=L->head; P != 0; P = P->next) + { + if ( (*f)(P->d,d) >= 0) + { + pvl_insert_before(L,P,d); + return; + } + } + + /* badness, choke */ +#ifndef lint + assert(0); +#endif +} + +/** + * @brief Add a new item after the referenced element. + * @param L The list to operate on + * @param P The list element to add the item after + * @param d Pointer to the item to add. + */ + +void +pvl_insert_after(pvl_list L,pvl_elem P,void *d) +{ + struct pvl_elem_t *E = 0; + + L->count++; + + if (P == 0) + { + pvl_unshift(L,d); + return; + } + + if ( P == L->tail) + { + E = pvl_new_element(d,0,P); + L->tail = E; + E->prior->next = E; + } + else + { + E = pvl_new_element(d,P->next,P); + E->next->prior = E; + E->prior->next = E; + } +} + +/** + * @brief Add an item after a referenced item + * + * @param L The list to operate on + * @param P The list element to add the item before + * @param d Pointer to the data to be added. + */ + +void +pvl_insert_before(pvl_list L,pvl_elem P,void *d) +{ + struct pvl_elem_t *E = 0; + + L->count++; + + if (P == 0) + { + pvl_unshift(L,d); + return; + } + + if ( P == L->head) + { + E = pvl_new_element(d,P,0); + E->next->prior = E; + L->head = E; + } + else + { + E = pvl_new_element(d,P,P->prior); + E->prior->next = E; + E->next->prior = E; + } +} + +/** + * @brief Remove the referenced item from the list. + * + * This routine will free the element, but not the data item that the + * element contains. + * + * @param L The list to operate on + * @param E The element to remove. + */ + +void* +pvl_remove(pvl_list L,pvl_elem E) +{ + void* data; + + if (E == L->head) + { + if (E->next != 0) + { + E->next->prior = 0; + L->head = E->next; + } else { + /* E Also points to tail -> only one element in list */ + L->tail = 0; + L->head = 0; + } + } + else if (E == L->tail) + { + if (E->prior != 0) + { + E->prior->next = 0; + L->tail = E->prior; + } else { + /* E points to the head, so it was the last element */ + /* This case should be taken care of in the previous clause */ + L->head = 0; + L->tail = 0; + } + } + else + { + E->prior->next = E->next; + E->next->prior = E->prior; + } + + + L->count--; + + data = E->d; + + E->prior = 0; + E->next = 0; + E->d = 0; + + free(E); + + return data; + +} + +/** + * @brief Return a pointer to data that satisfies a function. + * + * This routine will interate through the entire list and call the + * find function for each item. It will break and return a pointer to the + * data that causes the find function to return 1. + * + * @param l The list to operate on + * @param f Pointer to the find function + * @param v Pointer to constant data to pass into the function + * + * @return Pointer to the element that the find function found. + */ + +pvl_elem +pvl_find(pvl_list l,pvl_findf f,void* v) +{ + pvl_elem e; + + for (e=pvl_head(l); e!= 0; e = pvl_next(e)) + { + if ( (*f)(((struct pvl_elem_t *)e)->d,v) == 1) + { + /* Save this elem for a call to find_next */ + ((struct pvl_list_t *)l)->p = e; + return e; + } + } + + return 0; + +} + +/** + * @brief Like pvl_find(), but continues the search where the last find() or + * find_next() left off. + * + * @param l The list to operate on + * @param f Pointer to the find function + * @param v Pointer to constant data to pass into the function + * + * @return Pointer to the element that the find function found. + */ + +pvl_elem +pvl_find_next(pvl_list l,pvl_findf f,void* v) +{ + + pvl_elem e; + + for (e=pvl_head(l); e!= 0; e = pvl_next(e)) + { + if ( (*f)(((struct pvl_elem_t *)e)->d,v) == 1) + { + /* Save this elem for a call to find_next */ + ((struct pvl_list_t *)l)->p = e; + return e; + } + } + + return 0; + +} + +/** + * @brief Remove the all the elements in the list. The does not free + * the data items the elements hold. + */ + +void +pvl_clear(pvl_list l) +{ + pvl_elem e = pvl_head(l); + pvl_elem next; + + if (e == 0) { + return; + } + + while(e != 0) + { + next = pvl_next(e); + pvl_remove(l,e); + e = next; + } +} + + +/** + * @brief Returns the number of items in the list. + */ + +int +pvl_count(pvl_list L) +{ + return L->count; +} + + +/** + * @brief Returns a pointer to the given element + */ + +pvl_elem +pvl_next(pvl_elem E) +{ + if (E == 0){ + return 0; + } + + return (pvl_elem)E->next; +} + + +/** + * @brief Returns a pointer to the element previous to the element given. + */ + +pvl_elem +pvl_prior(pvl_elem E) +{ + return (pvl_elem)E->prior; +} + + +/** + * @brief Returns a pointer to the first item in the list. + */ + +pvl_elem +pvl_head(pvl_list L ) +{ + return (pvl_elem)L->head; +} + +/** + * @brief Returns a pointer to the last item in the list. + */ +pvl_elem +pvl_tail(pvl_list L) +{ + return (pvl_elem)L->tail; +} + +#ifndef PVL_USE_MACROS +void* +pvl_data(pvl_elem E) +{ + if ( E == 0){ + return 0; + } + + return E->d; +} +#endif + +/** + * @brief Call a function for every item in the list. + * + * @param l The list to operate on + * @param f Pointer to the function to call + * @param v Data to pass to the function on every iteration + */ + +void +pvl_apply(pvl_list l,pvl_applyf f, void *v) +{ + pvl_elem e; + + for (e=pvl_head(l); e!= 0; e = pvl_next(e)) + { + (*f)(((struct pvl_elem_t *)e)->d,v); + } + +} diff --git a/libkcal/libical/src/libical/pvl.h b/libkcal/libical/src/libical/pvl.h new file mode 100644 index 000000000..cb306ae6f --- /dev/null +++ b/libkcal/libical/src/libical/pvl.h @@ -0,0 +1,98 @@ +/*====================================================================== + FILE: pvl.h + CREATOR: eric November, 1995 + + + (C) COPYRIGHT 2000, Eric Busboom <eric@softwarestudio.org> + http://www.softwarestudio.org +======================================================================*/ + + +#ifndef __PVL_H__ +#define __PVL_H__ + +typedef struct pvl_list_t* pvl_list; +typedef struct pvl_elem_t* pvl_elem; + +/** + * This type is private. Always use pvl_elem instead. The struct would + * not even appear in this header except to make code in the USE_MACROS + * blocks work + */ + +typedef struct pvl_elem_t +{ + int MAGIC; /**< Magic Identifier */ + void *d; /**< Pointer to data user is storing */ + struct pvl_elem_t *next; /**< Next element */ + struct pvl_elem_t *prior; /**< Prior element */ +} pvl_elem_t; + + + +/** + * This global is incremented for each call to pvl_new_element(); it gives each + * list a unique identifer + */ + +extern int pvl_elem_count; +extern int pvl_list_count; + +/* Create new lists or elements */ +pvl_elem pvl_new_element(void* d, pvl_elem next,pvl_elem prior); +pvl_list pvl_newlist(void); +void pvl_free(pvl_list); + +/* Add, remove, or get the head of the list */ +void pvl_unshift(pvl_list l,void *d); +void* pvl_shift(pvl_list l); +pvl_elem pvl_head(pvl_list); + +/* Add, remove or get the tail of the list */ +void pvl_push(pvl_list l,void *d); +void* pvl_pop(pvl_list l); +pvl_elem pvl_tail(pvl_list); + +/* Insert elements in random places */ +typedef int (*pvl_comparef)(void* a, void* b); /* a, b are of the data type*/ +void pvl_insert_ordered(pvl_list l,pvl_comparef f,void *d); +void pvl_insert_after(pvl_list l,pvl_elem e,void *d); +void pvl_insert_before(pvl_list l,pvl_elem e,void *d); + +/* Remove an element, or clear the entire list */ +void* pvl_remove(pvl_list,pvl_elem); /* Remove element, return data */ +void pvl_clear(pvl_list); /* Remove all elements, de-allocate all data */ + +int pvl_count(pvl_list); + +/* Navagate the list */ +pvl_elem pvl_next(pvl_elem e); +pvl_elem pvl_prior(pvl_elem e); + +/* get the data in the list */ +#ifndef PVL_USE_MACROS +void* pvl_data(pvl_elem); +#else +#define pvl_data(x) x==0 ? 0 : ((struct pvl_elem_t *)x)->d; +#endif + + +/* Find an element for which a function returns true */ +typedef int (*pvl_findf)(void* a, void* b); /*a is list elem, b is other data*/ +pvl_elem pvl_find(pvl_list l,pvl_findf f,void* v); +pvl_elem pvl_find_next(pvl_list l,pvl_findf f,void* v); + +/** + * Pass each element in the list to a function + * a is list elem, b is other data + */ +typedef void (*pvl_applyf)(void* a, void* b); +void pvl_apply(pvl_list l,pvl_applyf f, void *v); + + +#endif /* __PVL_H__ */ + + + + + diff --git a/libkcal/libical/src/libical/sspm.c b/libkcal/libical/src/libical/sspm.c new file mode 100644 index 000000000..61237cb31 --- /dev/null +++ b/libkcal/libical/src/libical/sspm.c @@ -0,0 +1,1630 @@ +/* -*- Mode: C -*- + ====================================================================== + FILE: sspm.c Parse Mime + CREATOR: eric 25 June 2000 + + + The contents of this file are subject to the Mozilla Public License + Version 1.0 (the "License"); you may not use this file except in + compliance with the License. You may obtain a copy of the License at + http://www.mozilla.org/MPL/ + + Software distributed under the License is distributed on an "AS IS" + basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + the License for the specific language governing rights and + limitations under the License. + + + This program is free software; you can redistribute it and/or modify + it under the terms of either: + + The LGPL as published by the Free Software Foundation, version + 2.1, available at: http://www.fsf.org/copyleft/lesser.html + + Or: + + The Mozilla Public License Version 1.0. You may obtain a copy of + the License at http://www.mozilla.org/MPL/ + + The Initial Developer of the Original Code is Eric Busboom + + (C) COPYRIGHT 2000, Eric Busboom <eric@softwarestudio.org> + http://www.softwarestudio.org + ======================================================================*/ + +#include <stdio.h> +#include <string.h> +#include "sspm.h" +#include <assert.h> +#include <ctype.h> /* for tolower */ +#include <stdlib.h> /* for malloc, free */ +#include <string.h> /* for strcasecmp */ + +#ifdef DMALLOC +#include "dmalloc.h" +#endif + +#ifdef WIN32 +#define snprintf _snprintf +#define strcasecmp stricmp +#endif + +#define TMP_BUF_SIZE 1024 + + +enum mime_state { + UNKNOWN_STATE, + IN_HEADER, + END_OF_HEADER, + IN_BODY, + OPENING_PART, + END_OF_PART, + TERMINAL_END_OF_PART, + END_OF_INPUT +}; + +struct mime_impl{ + struct sspm_part *parts; + size_t max_parts; + int part_no; + int level; + struct sspm_action_map *actions; + char* (*get_string)(char *s, size_t size, void* data); + void* get_string_data; + char temp[TMP_BUF_SIZE]; + enum mime_state state; +}; + +void sspm_free_header(struct sspm_header *header); +void* sspm_make_multipart_part(struct mime_impl *impl,struct sspm_header *header); +void sspm_read_header(struct mime_impl *impl,struct sspm_header *header); + +char* sspm_strdup(const char* str){ + + char* s; + + s = strdup(str); + + return s; +} + + +static struct major_content_type_map +{ + enum sspm_major_type type; + const char* str; + +} major_content_type_map[] = +{ + {SSPM_MULTIPART_MAJOR_TYPE,"multipart" }, + {SSPM_TEXT_MAJOR_TYPE,"text" }, + {SSPM_TEXT_MAJOR_TYPE,"text" }, + {SSPM_IMAGE_MAJOR_TYPE,"image" }, + {SSPM_AUDIO_MAJOR_TYPE,"audio" }, + {SSPM_VIDEO_MAJOR_TYPE,"video" }, + {SSPM_APPLICATION_MAJOR_TYPE,"application" }, + {SSPM_MULTIPART_MAJOR_TYPE,"multipart" }, + {SSPM_MESSAGE_MAJOR_TYPE,"message" }, + {SSPM_UNKNOWN_MAJOR_TYPE,"" }, +}; + +static struct minor_content_type_map +{ + enum sspm_minor_type type; + const char* str; + +} minor_content_type_map[] = +{ + {SSPM_ANY_MINOR_TYPE,"*" }, + {SSPM_PLAIN_MINOR_TYPE,"plain" }, + {SSPM_RFC822_MINOR_TYPE,"rfc822" }, + {SSPM_DIGEST_MINOR_TYPE,"digest" }, + {SSPM_CALENDAR_MINOR_TYPE,"calendar" }, + {SSPM_MIXED_MINOR_TYPE,"mixed" }, + {SSPM_RELATED_MINOR_TYPE,"related" }, + {SSPM_ALTERNATIVE_MINOR_TYPE,"alternative" }, + {SSPM_PARALLEL_MINOR_TYPE, "parallel" }, + {SSPM_UNKNOWN_MINOR_TYPE,"" } +}; + + + +struct encoding_map { + enum sspm_encoding encoding; + const char* str; +} sspm_encoding_map[] = +{ + {SSPM_NO_ENCODING,""}, + {SSPM_QUOTED_PRINTABLE_ENCODING,"quoted-printable"}, + {SSPM_8BIT_ENCODING,"8bit"}, + {SSPM_7BIT_ENCODING,"7bit"}, + {SSPM_BINARY_ENCODING,"binary"}, + {SSPM_BASE64_ENCODING,"base64"}, + {SSPM_UNKNOWN_ENCODING,""} + +}; + + +char* sspm_get_parameter(const char* line, const char* parameter) +{ + char *p,*s,*q; + static char name[1024]; + + /* Find where the parameter name is in the line */ + p = strstr(line,parameter); + + if( p == 0){ + return 0; + } + + /* skip over the parameter name, the '=' and any blank spaces */ + + p+=strlen(parameter); + + while(*p==' ' || *p == '='){ + p++; + } + + /*now find the next semicolon*/ + + s = strchr(p,';'); + + /* Strip of leading quote */ + q = strchr(p,'\"'); + + if(q !=0){ + p = q+1; + } + + if(s != 0){ + strncpy(name,p,(size_t)s-(size_t)p); + } else { + strcpy(name,p); + } + + /* Strip off trailing quote, if it exists */ + + q = strrchr(name,'\"'); + + if (q != 0){ + *q='\0'; + } + + return name; +} + +char* sspm_property_name(const char* line) +{ + static char name[1024]; + char *c = strchr(line,':'); + + if(c != 0){ + strncpy(name,line,(size_t)c-(size_t)line); + name[(size_t)c-(size_t)line] = '\0'; + return name; + } else { + return 0; + } +} + +char* sspm_value(char* line) +{ + static char value[1024]; + + char *c,*s, *p; + + /* Find the first colon and the next semicolon */ + + value[0] = 0; + c = strchr(line,':'); + if (!c) + return value; + s = strchr(c,';'); + + /* Skip the colon */ + c++; + + if (s == 0){ + s = c+strlen(line); + } + + for(p=value; c != s; c++){ + if(*c!=' ' && *c!='\n'){ + *(p++) = *c; + } + } + + *p='\0'; + + return value; + +} + +static const char *mime_headers[] = { + "Content-Type", + "Content-Transfer-Encoding", + "Content-Disposition", + "Content-Id", + "Mime-Version", + 0 +}; + + +void* sspm_default_new_part() +{ + return 0; +} +void sspm_default_add_line(void *part, struct sspm_header *header, + const char* line, size_t size) +{ + (void)part; + (void)header; + (void)line; + (void)size; +} + +void* sspm_default_end_part(void* part) +{ + (void)part; + return 0; +} + +void sspm_default_free_part(void *part) +{ + (void)part; +} + + + +struct sspm_action_map sspm_action_map[] = +{ + {SSPM_UNKNOWN_MAJOR_TYPE,SSPM_UNKNOWN_MINOR_TYPE,sspm_default_new_part,sspm_default_add_line,sspm_default_end_part,sspm_default_free_part}, +}; + +int sspm_is_mime_header(char *line) +{ + char *name = sspm_property_name(line); + int i; + + if(name == 0){ + return 0; + } + + for(i = 0; mime_headers[i] != 0; i++){ + if(strcasecmp(name, mime_headers[i]) == 0) + return 1; + } + + return 0; +} + +int sspm_is_mail_header(char* line) +{ + char *name = sspm_property_name(line); + + if (name != 0){ + return 1; + } + + return 0; + +} + +int sspm_is_blank(char* line) +{ + char *p; + char c =0; + + for(p=line; *p!=0; p++){ + if( ! (*p == ' '|| *p == '\t' || *p=='\n') ){ + c++; + } + } + + if (c==0){ + return 1; + } + + return 0; + +} + +int sspm_is_continuation_line(char* line) +{ + if (line[0] == ' '|| line[0] == '\t' ) { + return 1; + } + + return 0; +} + +int sspm_is_mime_boundary(char *line) +{ + if( line[0] == '-' && line[1] == '-') { + return 1; + } + + return 0; +} + +int sspm_is_mime_terminating_boundary(char *line) +{ + + + if (sspm_is_mime_boundary(line) && + strstr(line,"--\n")){ + return 1; + } + + return 0; +} + +enum line_type { + EMPTY, + BLANK, + MIME_HEADER, + MAIL_HEADER, + HEADER_CONTINUATION, + BOUNDARY, + TERMINATING_BOUNDARY, + UNKNOWN_TYPE +}; + + +static enum line_type get_line_type(char* line){ + + if (line == 0){ + return EMPTY; + } else if(sspm_is_blank(line)){ + return BLANK; + } else if (sspm_is_mime_header(line)){ + return MIME_HEADER; + } else if (sspm_is_mail_header(line)){ + return MAIL_HEADER; + } else if (sspm_is_continuation_line(line)){ + return HEADER_CONTINUATION; + } else if (sspm_is_mime_terminating_boundary(line)){ + return TERMINATING_BOUNDARY; + } else if (sspm_is_mime_boundary(line)) { + return BOUNDARY; + } else { + return UNKNOWN_TYPE; + } + + +} + + +static struct sspm_action_map get_action(struct mime_impl *impl, + enum sspm_major_type major, + enum sspm_minor_type minor) +{ + int i; + + /* Read caller suppled action map */ + + if (impl->actions != 0){ + for(i=0; impl->actions[i].major != SSPM_UNKNOWN_MAJOR_TYPE; i++){ + if((major == impl->actions[i].major && + minor == impl->actions[i].minor) || + (major == impl->actions[i].major && + minor == SSPM_ANY_MINOR_TYPE)){ + return impl->actions[i]; + } + } + } + + /* Else, read default action map */ + + for(i=0; sspm_action_map[i].major != SSPM_UNKNOWN_MAJOR_TYPE; i++){ + if((major == sspm_action_map[i].major && + minor == sspm_action_map[i].minor) || + (major == sspm_action_map[i].major && + minor == SSPM_ANY_MINOR_TYPE)){ + break; + } + } + + return sspm_action_map[i]; +} + + +char* sspm_lowercase(char* str) +{ + char* p = 0; + char* new = sspm_strdup(str); + + if(str ==0){ + return 0; + } + + for(p = new; *p!=0; p++){ + *p = tolower(*p); + } + + return new; +} + +enum sspm_major_type sspm_find_major_content_type(char* type) +{ + int i; + + char* ltype = sspm_lowercase(type); + + for (i=0; major_content_type_map[i].type != SSPM_UNKNOWN_MINOR_TYPE; i++){ + if(strncmp(ltype, major_content_type_map[i].str, + strlen(major_content_type_map[i].str))==0){ + free(ltype); + return major_content_type_map[i].type; + } + } + free(ltype); + return major_content_type_map[i].type; /* Should return SSPM_UNKNOWN_MINOR_TYPE */ +} + +enum sspm_minor_type sspm_find_minor_content_type(char* type) +{ + int i; + char* ltype = sspm_lowercase(type); + + char *p = strchr(ltype,'/'); + + if (p==0){ + return SSPM_UNKNOWN_MINOR_TYPE; + } + + p++; /* Skip the '/' */ + + for (i=0; minor_content_type_map[i].type != SSPM_UNKNOWN_MINOR_TYPE; i++){ + if(strncmp(p, minor_content_type_map[i].str, + strlen(minor_content_type_map[i].str))==0){ + free(ltype); + return minor_content_type_map[i].type; + } + } + + free(ltype); + return minor_content_type_map[i].type; /* Should return SSPM_UNKNOWN_MINOR_TYPE */ +} + +const char* sspm_major_type_string(enum sspm_major_type type) +{ + int i; + + for (i=0; major_content_type_map[i].type != SSPM_UNKNOWN_MINOR_TYPE; + i++){ + + if(type == major_content_type_map[i].type){ + return major_content_type_map[i].str; + } + } + + return major_content_type_map[i].str; /* Should return SSPM_UNKNOWN_MINOR_TYPE */ +} + +const char* sspm_minor_type_string(enum sspm_minor_type type) +{ + int i; + for (i=0; minor_content_type_map[i].type != SSPM_UNKNOWN_MINOR_TYPE; + i++){ + if(type == minor_content_type_map[i].type){ + return minor_content_type_map[i].str; + } + } + + return minor_content_type_map[i].str; /* Should return SSPM_UNKNOWN_MINOR_TYPE */ +} + + +const char* sspm_encoding_string(enum sspm_encoding type) +{ + int i; + for (i=0; sspm_encoding_map[i].encoding != SSPM_UNKNOWN_ENCODING; + i++){ + if(type == sspm_encoding_map[i].encoding){ + return sspm_encoding_map[i].str; + } + } + + return sspm_encoding_map[i].str; /* Should return SSPM_UNKNOWN_MINOR_TYPE */ +} + +/* Interpret a header line and add its data to the header + structure. */ +void sspm_build_header(struct sspm_header *header, char* line) +{ + char *prop; + char *val; + + val = sspm_strdup(sspm_value(line)); + prop = sspm_strdup(sspm_property_name(line)); + + if(strcasecmp(prop,"Content-Type") == 0){ + + /* Create a new mime_header, fill in content-type + and possibly boundary */ + + char* boundary= sspm_get_parameter(line,"boundary"); + + header->def = 0; + header->major = sspm_find_major_content_type(val); + header->minor = sspm_find_minor_content_type(val); + + if(header->minor == SSPM_UNKNOWN_MINOR_TYPE){ + char *p = strchr(val,'/'); + + if (p != 0){ + p++; /* Skip the '/' */ + + header->minor_text = sspm_strdup(p); + } else { + /* Error, malformed content type */ + header->minor_text = sspm_strdup("unknown"); + } + } + if (boundary != 0){ + header->boundary = sspm_strdup(boundary); + } + + } else if(strcasecmp(prop,"Content-Transfer-Encoding")==0){ + char* encoding = sspm_value(line); + char* lencoding = sspm_lowercase(encoding); + + if(strcasecmp(lencoding,"base64")==0){ + header->encoding = SSPM_BASE64_ENCODING; + } else if(strcasecmp(lencoding,"quoted-printable")==0){ + header->encoding = SSPM_QUOTED_PRINTABLE_ENCODING; + } else if(strcasecmp(lencoding,"binary")==0){ + header->encoding = SSPM_BINARY_ENCODING; + } else if(strcasecmp(lencoding,"7bit")==0){ + header->encoding = SSPM_7BIT_ENCODING; + } else if(strcasecmp(lencoding,"8bit")==0){ + header->encoding = SSPM_8BIT_ENCODING; + } else { + header->encoding = SSPM_UNKNOWN_ENCODING; + } + + + free(lencoding); + + header->def = 0; + + } else if(strcasecmp(prop,"Content-Id")==0){ + char* cid = sspm_value(line); + header->content_id = sspm_strdup(cid); + header->def = 0; + + } + free(val); + free(prop); +} + +char* sspm_get_next_line(struct mime_impl *impl) +{ + char* s; + s = impl->get_string(impl->temp,TMP_BUF_SIZE,impl->get_string_data); + + if(s == 0){ + impl->state = END_OF_INPUT; + } + return s; +} + + +void sspm_store_part(struct mime_impl *impl, struct sspm_header header, + int level, void *part, size_t size) +{ + + impl->parts[impl->part_no].header = header; + impl->parts[impl->part_no].level = level; + impl->parts[impl->part_no].data = part; + impl->parts[impl->part_no].data_size = size; + impl->part_no++; +} + +void sspm_set_error(struct sspm_header* header, enum sspm_error error, + char* message) +{ + header->error = error; + + if(header->error_text!=0){ + free(header->error_text); + } + + header->def = 0; + + if(message != 0){ + header->error_text = sspm_strdup(message); + } else { + header->error_text = 0; + } + +} + +void* sspm_make_part(struct mime_impl *impl, + struct sspm_header *header, + struct sspm_header *parent_header, + void **end_part, + size_t *size) +{ + + /* For a single part type, read to the boundary, if there is a + boundary. Otherwise, read until the end of input. This routine + assumes that the caller has read the header and has left the input + at the first blank line */ + + char *line; + void *part; + int end = 0; + + struct sspm_action_map action = get_action( + impl, + header->major, + header->minor); + + *size = 0; + part =action.new_part(); + + impl->state = IN_BODY; + + while(end == 0 && (line = sspm_get_next_line(impl)) != 0){ + + if(sspm_is_mime_boundary(line)){ + + /* If there is a boundary, then this must be a multipart + part, so there must be a parent_header. */ + if(parent_header == 0){ + char* boundary; + end = 1; + *end_part = 0; + + sspm_set_error(header,SSPM_UNEXPECTED_BOUNDARY_ERROR,line); + + /* Read until the paired terminating boundary */ + if((boundary = (char*)malloc(strlen(line)+5)) == 0){ + fprintf(stderr,"Out of memory"); + abort(); + } + strcpy(boundary,line); + strcat(boundary,"--"); + while((line = sspm_get_next_line(impl)) != 0){ + /*printf("Error: %s\n",line);*/ + if(strcmp(boundary,line)==0){ + break; + } + } + free(boundary); + + break; + } + + if(strncmp((line+2),parent_header->boundary, + sizeof(parent_header->boundary)) == 0){ + *end_part = action.end_part(part); + + if(sspm_is_mime_boundary(line)){ + impl->state = END_OF_PART; + } else if ( sspm_is_mime_terminating_boundary(line)){ + impl->state = TERMINAL_END_OF_PART; + } + end = 1; + } else { + /* Error, this is not the correct terminating boundary*/ + + /* read and discard until we get the right boundary. */ + char* boundary; + char msg[256]; + + snprintf(msg,256, + "Expected: %s--. Got: %s", + parent_header->boundary,line); + + sspm_set_error(parent_header, + SSPM_WRONG_BOUNDARY_ERROR,msg); + + /* Read until the paired terminating boundary */ + if((boundary = (char*)malloc(strlen(line)+5)) == 0){ + fprintf(stderr,"Out of memory"); + abort(); + } + strcpy(boundary,line); + strcat(boundary,"--"); + while((line = sspm_get_next_line(impl)) != 0){ + if(strcmp(boundary,line)==0){ + break; + } + } + free(boundary); + + } + } else { + char* data=0; + char* rtrn=0; + *size = strlen(line); + + data = (char*)malloc(*size+2); + assert(data != 0); + if (header->encoding == SSPM_BASE64_ENCODING){ + rtrn = decode_base64(data,line,size); + } else if(header->encoding == SSPM_QUOTED_PRINTABLE_ENCODING){ + rtrn = decode_quoted_printable(data,line,size); + } + + if(rtrn == 0){ + strcpy(data,line); + } + + /* add a end-of-string after the data, just in case binary + data from decode64 gets passed to a tring handling + routine in add_line */ + data[*size+1]='\0'; + + action.add_line(part,header,data,*size); + + free(data); + } + } + + if (end == 0){ + /* End the part if the input is exhausted */ + *end_part = action.end_part(part); + } + + return end_part; +} + + +void* sspm_make_multipart_subpart(struct mime_impl *impl, + struct sspm_header *parent_header) +{ + struct sspm_header header; + char *line; + void* part; + size_t size; + + if(parent_header->boundary == 0){ + /* Error. Multipart headers must have a boundary*/ + + sspm_set_error(parent_header,SSPM_NO_BOUNDARY_ERROR,0); + /* read all of the reamining lines */ + while((line = sspm_get_next_line(impl)) != 0){ + } + + return 0; + } + + + /* Step 1: Read the opening boundary */ + + if(get_line_type(impl->temp) != BOUNDARY){ + while((line=sspm_get_next_line(impl)) != 0 ){ + if(sspm_is_mime_boundary(line)){ + + assert(parent_header != 0); + + /* Check if it is the right boundary */ + if(!sspm_is_mime_terminating_boundary(line) && + strncmp((line+2),parent_header->boundary, + sizeof(parent_header->boundary)) + == 0){ + /* The +2 in strncmp skips over the leading "--" */ + + break; + } else { + /* Got the wrong boundary, so read and discard + until we get the right boundary. */ + char* boundary; + char msg[256]; + + snprintf(msg,256, + "Expected: %s. Got: %s", + parent_header->boundary,line); + + sspm_set_error(parent_header, + SSPM_WRONG_BOUNDARY_ERROR,msg); + + /* Read until the paired terminating boundary */ + if((boundary = (char*)malloc(strlen(line)+5)) == 0){ + fprintf(stderr,"Out of memory"); + abort(); + } + strcpy(boundary,line); + strcat(boundary,"--"); + while((line = sspm_get_next_line(impl)) != 0){ + if(strcmp(boundary,line)==0){ + break; + } + } + free(boundary); + + return 0; + } + } + } + } + + /* Step 2: Get the part header */ + sspm_read_header(impl,&header); + + /* If the header is still listed as default, there was probably an + error */ + if(header.def == 1 && header.error != SSPM_NO_ERROR){ + sspm_set_error(&header,SSPM_NO_HEADER_ERROR,0); + return 0; + } + + if(header.error!= SSPM_NO_ERROR){ + sspm_store_part(impl,header,impl->level,0,0); + return 0; + } + + /* Step 3: read the body */ + + if(header.major == SSPM_MULTIPART_MAJOR_TYPE){ + struct sspm_header *child_header; + child_header = &(impl->parts[impl->part_no].header); + + /* Store the multipart part */ + sspm_store_part(impl,header,impl->level,0,0); + + /* now get all of the sub-parts */ + part = sspm_make_multipart_part(impl,child_header); + + if(get_line_type(impl->temp) != TERMINATING_BOUNDARY){ + + sspm_set_error(child_header,SSPM_NO_BOUNDARY_ERROR,impl->temp); + return 0; + } + + sspm_get_next_line(impl); /* Step past the terminating boundary */ + + } else { + sspm_make_part(impl, &header,parent_header,&part,&size); + + memset(&(impl->parts[impl->part_no]), 0, sizeof(struct sspm_part)); + + sspm_store_part(impl,header,impl->level,part,size); + + } + + return part; +} + +void* sspm_make_multipart_part(struct mime_impl *impl,struct sspm_header *header) +{ + void *part=0; + + /* Now descend a level into each of the children of this part */ + impl->level++; + + /* Now we are working on the CHILD */ + memset(&(impl->parts[impl->part_no]), 0, sizeof(struct sspm_part)); + + do{ + part = sspm_make_multipart_subpart(impl,header); + + if (part==0){ + /* Clean up the part in progress */ + impl->parts[impl->part_no].header.major + = SSPM_NO_MAJOR_TYPE; + impl->parts[impl->part_no].header.minor + = SSPM_NO_MINOR_TYPE; + + } + + + } while (get_line_type(impl->temp) != TERMINATING_BOUNDARY && + impl->state != END_OF_INPUT); + + impl->level--; + + return 0; +} + + +void sspm_read_header(struct mime_impl *impl,struct sspm_header *header) +{ +#define BUF_SIZE 1024 +#define MAX_HEADER_LINES 25 + + char *buf; + char header_lines[MAX_HEADER_LINES][BUF_SIZE]; /* HACK, hard limits */ + int current_line = -1; + int end = 0; + + memset(header_lines,0,sizeof(header_lines)); + memset(header,0,sizeof(struct sspm_header)); + + /* Set up default header */ + header->def = 1; + header->major = SSPM_TEXT_MAJOR_TYPE; + header->minor = SSPM_PLAIN_MINOR_TYPE; + header->error = SSPM_NO_ERROR; + header->error_text = 0; + + /* Read all of the lines into memory */ + while(end==0&& (buf=sspm_get_next_line(impl)) != 0){ + + enum line_type line_type = get_line_type(buf); + + switch(line_type){ + case BLANK: { + end = 1; + impl->state = END_OF_HEADER; + break; + } + + case MAIL_HEADER: + case MIME_HEADER: { + impl->state = IN_HEADER; + current_line++; + + assert(strlen(buf) < BUF_SIZE); + + strncpy(header_lines[current_line],buf,BUF_SIZE); + header_lines[current_line][BUF_SIZE-1] = '\0'; + + break; + } + + case HEADER_CONTINUATION: { + char* last_line, *end; + char *buf_start; + + if(current_line < 0){ + /* This is not really a continuation line, since + we have not see any header line yet */ + sspm_set_error(header,SSPM_MALFORMED_HEADER_ERROR,buf); + return; + } + + last_line = header_lines[current_line]; + end = (char*) ( (size_t)strlen(last_line)+ + (size_t)last_line); + + impl->state = IN_HEADER; + + + /* skip over the spaces in buf start, and remove the new + line at the end of the lat line */ + if (last_line[strlen(last_line)-1] == '\n'){ + last_line[strlen(last_line)-1] = '\0'; + } + buf_start = buf; + while(*buf_start == ' ' ||*buf_start == '\t' ){ + buf_start++; + } + + assert( strlen(buf_start) + strlen(last_line) < BUF_SIZE); + + strncat(last_line,buf_start, BUF_SIZE-strlen(last_line)-1); + + break; + } + + default: { + sspm_set_error(header,SSPM_MALFORMED_HEADER_ERROR,buf); + return; + } + } + } + + + for(current_line = 0; + current_line < MAX_HEADER_LINES && header_lines[current_line][0] != 0; + current_line++){ + + sspm_build_header(header,header_lines[current_line]); + } + + +} + +/* Root routine for parsing mime entries*/ +int sspm_parse_mime(struct sspm_part *parts, + size_t max_parts, + struct sspm_action_map *actions, + char* (*get_string)(char *s, size_t size, void* data), + void *get_string_data, + struct sspm_header *first_header + ) +{ + struct mime_impl impl; + struct sspm_header header; + void *part; + int i; + (void)first_header; + + /* Initialize all of the data */ + memset(&impl,0,sizeof(struct mime_impl)); + memset(&header,0,sizeof(struct sspm_header)); + + for(i = 0; i<(int)max_parts; i++){ + parts[i].header.major = SSPM_NO_MAJOR_TYPE; + parts[i].header.minor = SSPM_NO_MINOR_TYPE; + } + + impl.parts = parts; + impl.max_parts = max_parts; + impl.part_no = 0; + impl.actions = actions; + impl.get_string = get_string; + impl.get_string_data = get_string_data; + + /* Read the header of the message. This will be the email header, + unless first_header is specified. But ( HACK) that var is not + currently being used */ + sspm_read_header(&impl,&header); + + if(header.major == SSPM_MULTIPART_MAJOR_TYPE){ + struct sspm_header *child_header; + child_header = &(impl.parts[impl.part_no].header); + + sspm_store_part(&impl,header,impl.level,0,0); + + part = sspm_make_multipart_part(&impl,child_header); + + } else { + void *part; + size_t size; + sspm_make_part(&impl, &header, 0,&part,&size); + + memset(&(impl.parts[impl.part_no]), 0, sizeof(struct sspm_part)); + + sspm_store_part(&impl,header,impl.level,part,size); + } + + return 0; +} + +void sspm_free_parts(struct sspm_part *parts, size_t max_parts) +{ + int i; + + for(i = 0; i<(int)max_parts && parts[i].header.major != SSPM_NO_MAJOR_TYPE; + i++){ + sspm_free_header(&(parts[i].header)); + } +} + +void sspm_free_header(struct sspm_header *header) +{ + if(header->boundary!=0){ + free(header->boundary); + } + if(header->minor_text!=0){ + free(header->minor_text); + } + if(header->charset!=0){ + free(header->charset); + } + if(header->filename!=0){ + free(header->filename); + } + if(header->content_id!=0){ + free(header->content_id); + } + if(header->error_text!=0){ + free(header->error_text); + } +} + +/*********************************************************************** +The remaining code is beased on code from the mimelite distribution, +which has the following notice: + +| Authorship: +| Copyright (c) 1994 Gisle Hannemyr. +| Permission is granted to hack, make and distribute copies of this +| program as long as this copyright notice is not removed. +| Flames, bug reports, comments and improvements to: +| snail: Gisle Hannemyr, Brageveien 3A, 0452 Oslo, Norway +| email: Inet: gisle@oslonett.no + +The code is heavily modified by Eric Busboom. + +***********************************************************************/ + +char *decode_quoted_printable(char *dest, + char *src, + size_t *size) +{ + int cc; + size_t i=0; + + while (*src != 0 && i < *size) { + if (*src == '=') { + + src++; + if (!*src) { + break; + } + + /* remove soft line breaks*/ + if ((*src == '\n') || (*src == '\r')){ + src++; + if ((*src == '\n') || (*src == '\r')){ + src++; + } + continue; + } + + cc = isdigit(*src) ? (*src - '0') : (*src - 55); + cc *= 0x10; + src++; + if (!*src) { + break; + } + cc += isdigit(*src) ? (*src - '0') : (*src - 55); + + *dest = cc; + + } else { + *dest = *src; + } + + dest++; + src++; + i++; + } + + *dest = '\0'; + + *size = i; + return(dest); +} + +char *decode_base64(char *dest, + char *src, + size_t *size) +{ + int cc = 0; + char buf[4] = {0,0,0,0}; + int p = 0; + int valid_data = 0; + size_t size_out=0; + + while (*src && p<(int)*size && (cc!= -1)) { + + /* convert a character into the Base64 alphabet */ + cc = *src++; + + if ((cc >= 'A') && (cc <= 'Z')) cc = cc - 'A'; + else if ((cc >= 'a') && (cc <= 'z')) cc = cc - 'a' + 26; + else if ((cc >= '0') && (cc <= '9')) cc = cc - '0' + 52; + else if (cc == '/') cc = 63; + else if (cc == '+') cc = 62; + else cc = -1; + + assert(cc<64); + + /* If we've reached the end, fill the remaining slots in + the bucket and do a final conversion */ + if(cc== -1){ + if(valid_data == 0){ + return 0; + } + + while(p%4!=3){ + p++; + buf[p%4] = 0; + } + } else { + buf[p%4] = cc; + size_out++; + valid_data = 1; + } + + + /* When we have 4 base64 letters, convert them into three + bytes */ + if (p%4 == 3) { + *dest++ =(buf[0]<< 2)|((buf[1] & 0x30) >> 4); + *dest++ =((buf[1] & 0x0F) << 4)|((buf[2] & 0x3C) >> 2); + *dest++ =((buf[2] & 0x03) << 6)|(buf[3] & 0x3F); + + memset(buf,0,4); + } + + p++; + + } + /* Calculate the size of the converted data*/ + *size = ((int)(size_out/4))*3; + if(size_out%4 == 2) *size+=1; + if(size_out%4 == 3) *size+=2; + + return(dest); +} + + +/*********************************************************************** + + Routines to output MIME + +**********************************************************************/ + + +struct sspm_buffer { + char* buffer; + char* pos; + size_t buf_size; + int line_pos; +}; + +void sspm_append_string(struct sspm_buffer* buf, const char* string); +void sspm_write_part(struct sspm_buffer *buf,struct sspm_part *part, int *part_num); + +void sspm_append_hex(struct sspm_buffer* buf, char ch) +{ + char tmp[4]; + + snprintf(tmp,sizeof(tmp),"=%02X",ch); + + sspm_append_string(buf,tmp); +} + +/* a copy of icalmemory_append_char */ +void sspm_append_char(struct sspm_buffer* buf, char ch) +{ + char *new_buf; + char *new_pos; + + size_t data_length, final_length; + + data_length = (size_t)buf->pos - (size_t)buf->buffer; + + final_length = data_length + 2; + + if ( final_length > (size_t) buf->buf_size ) { + + buf->buf_size = (buf->buf_size) * 2 + final_length +1; + + new_buf = realloc(buf->buffer,buf->buf_size); + + new_pos = (void*)((size_t)new_buf + data_length); + + buf->pos = new_pos; + buf->buffer = new_buf; + } + + *(buf->pos) = ch; + buf->pos += 1; + *(buf->pos) = 0; +} +/* A copy of icalmemory_append_string */ +void sspm_append_string(struct sspm_buffer* buf, const char* string) +{ + char *new_buf; + char *new_pos; + + size_t data_length, final_length, string_length; + + string_length = strlen(string); + data_length = (size_t)buf->pos - (size_t)buf->buffer; + final_length = data_length + string_length; + + if ( final_length >= (size_t) buf->buf_size) { + + + buf->buf_size = (buf->buf_size) * 2 + final_length; + + new_buf = realloc(buf->buffer,buf->buf_size); + + new_pos = (void*)((size_t)new_buf + data_length); + + buf->pos = new_pos; + buf->buffer = new_buf; + } + + strcpy(buf->pos, string); + + buf->pos += string_length; +} + + + +static int sspm_is_printable(char c) +{ + return (c >= 33) && (c <= 126) && (c != '='); + +} + + +void sspm_encode_quoted_printable(struct sspm_buffer *buf, char* data) +{ + char *p; + int lpos = 0; + + for(p = data; *p != 0; p++){ + + if(sspm_is_printable(*p)){ + /* plain characters can represent themselves */ + /* RFC2045 Rule #2 */ + sspm_append_char(buf,*p); + lpos++; + } else if ( *p == '\t' || *p == ' ' ) { + + /* For tabs and spaces, only encode if they appear at the + end of the line */ + /* RFC2045 Rule #3 */ + + char n = *(p+1); + + if( n == '\n' || n == '\r'){ + sspm_append_hex(buf,*p); + lpos += 3; + } else { + sspm_append_char(buf,*p); + lpos++; + } + + } else if( *p == '\n' || *p == '\r'){ + sspm_append_char(buf,*p); + + lpos=0; + + } else { + /* All others need to be encoded */ + sspm_append_hex(buf,*p); + lpos+=3; + } + + + /* Add line breaks */ + if (lpos > 72){ + lpos = 0; + sspm_append_string(buf,"=\n"); + } + } +} + +static char BaseTable[64] = { + 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P', + 'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f', + 'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v', + 'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/' +}; + +void sspm_write_base64(struct sspm_buffer *buf, char* inbuf,int size ) +{ + + char outbuf[4]; + int i; + + outbuf[0] = outbuf[1] = outbuf[2] = outbuf[3] = 65; + + switch(size){ + + case 4: + outbuf[3] = inbuf[2] & 0x3F; + + case 3: + outbuf[2] = ((inbuf[1] & 0x0F) << 2) | ((inbuf[2] & 0xC0) >> 6); + + case 2: + outbuf[0] = (inbuf[0] & 0xFC) >> 2; + outbuf[1] = ((inbuf[0] & 0x03) << 4) | ((inbuf[1] & 0xF0) >> 4); + break; + + default: + assert(0); + } + + for(i = 0; i < 4; i++){ + + if(outbuf[i] == 65){ + sspm_append_char(buf,'='); + } else { + sspm_append_char(buf,BaseTable[(int)outbuf[i]]); + } + } +} + +void sspm_encode_base64(struct sspm_buffer *buf, char* data, size_t size) +{ + char *p; + char inbuf[3]; + int i = 0; + int first = 1; + int lpos = 0; + (void)size; + + inbuf[0] = inbuf[1] = inbuf[2] = 0; + + for (p = data; *p !=0; p++){ + + if (i%3 == 0 && first == 0){ + + sspm_write_base64(buf, inbuf, 4); + lpos+=4; + + inbuf[0] = inbuf[1] = inbuf[2] = 0; + } + + assert(lpos%4 == 0); + + if (lpos == 72){ + sspm_append_string(buf,"\n"); + lpos = 0; + } + + inbuf[i%3] = *p; + + i++; + first = 0; + + } + + + /* If the inbuf was not exactly filled on the last byte, we need + to spit out the odd bytes that did get in -- either one or + two. This will result in an output of two bytes and '==' or + three bytes and '=', respectively */ + + if (i%3 == 1 && first == 0){ + sspm_write_base64(buf, inbuf, 2); + } else if (i%3 == 2 && first == 0){ + sspm_write_base64(buf, inbuf, 3); + } + +} + +void sspm_write_header(struct sspm_buffer *buf,struct sspm_header *header) +{ + + int i; + char temp[TMP_BUF_SIZE]; + const char* major; + const char* minor; + + /* Content-type */ + + major = sspm_major_type_string(header->major); + minor = sspm_minor_type_string(header->minor); + + if(header->minor == SSPM_UNKNOWN_MINOR_TYPE ){ + assert(header->minor_text !=0); + minor = header->minor_text; + } + + snprintf(temp,sizeof(temp),"Content-Type: %s/%s",major,minor); + + sspm_append_string(buf,temp); + + if(header->boundary != 0){ + snprintf(temp,sizeof(temp),";boundary=\"%s\"",header->boundary); + sspm_append_string(buf,temp); + } + + /* Append any content type parameters */ + if(header->content_type_params != 0){ + for(i=0; *(header->content_type_params[i])!= 0;i++){ + strncpy(temp,header->content_type_params[i],sizeof(temp)); + sspm_append_char(buf,';'); + sspm_append_string(buf,temp); + } + } + + sspm_append_char(buf,'\n'); + + /*Content-Transfer-Encoding */ + + if(header->encoding != SSPM_UNKNOWN_ENCODING && + header->encoding != SSPM_NO_ENCODING){ + snprintf(temp,sizeof(temp),"Content-Transfer-Encoding: %s\n", + sspm_encoding_string(header->encoding)); + } + + sspm_append_char(buf,'\n'); + +} + +void sspm_write_multipart_part(struct sspm_buffer *buf, + struct sspm_part *parts, + int* part_num) +{ + + int parent_level, level; + struct sspm_header *header = &(parts[*part_num].header); + /* Write the header for the multipart part */ + sspm_write_header(buf,header); + + parent_level = parts[*part_num].level; + + (*part_num)++; + + level = parts[*part_num].level; + + while(parts[*part_num].header.major != SSPM_NO_MAJOR_TYPE && + level == parent_level+1){ + + assert(header->boundary); + sspm_append_string(buf,header->boundary); + sspm_append_char(buf,'\n'); + + if (parts[*part_num].header.major == SSPM_MULTIPART_MAJOR_TYPE){ + sspm_write_multipart_part(buf,parts,part_num); + } else { + sspm_write_part(buf, &(parts[*part_num]), part_num); + } + + (*part_num)++; + level = parts[*part_num].level; + } + + sspm_append_string(buf,"\n\n--"); + sspm_append_string(buf,header->boundary); + sspm_append_string(buf,"\n"); + + (*part_num)--; /* undo last, spurious, increment */ +} + +void sspm_write_part(struct sspm_buffer *buf,struct sspm_part *part,int *part_num) +{ + (void)part_num; + + /* Write header */ + sspm_write_header(buf,&(part->header)); + + /* Write part data */ + + if(part->data == 0){ + return; + } + + if(part->header.encoding == SSPM_BASE64_ENCODING) { + assert(part->data_size != 0); + sspm_encode_base64(buf,part->data,part->data_size); + } else if(part->header.encoding == SSPM_QUOTED_PRINTABLE_ENCODING) { + sspm_encode_quoted_printable(buf,part->data); + } else { + sspm_append_string(buf,part->data); + } + + sspm_append_string(buf,"\n\n"); +} + +int sspm_write_mime(struct sspm_part *parts,size_t num_parts, + char **output_string, const char* header) +{ + struct sspm_buffer buf; + int part_num =0; + (void)num_parts; + + buf.buffer = malloc(4096); + buf.pos = buf.buffer; + buf.buf_size = 10; + buf.line_pos = 0; + + /* write caller's header */ + if(header != 0){ + sspm_append_string(&buf,header); + } + + if(buf.buffer[strlen(buf.buffer)-1] != '\n'){ + sspm_append_char(&buf,'\n'); + } + + /* write mime-version header */ + sspm_append_string(&buf,"Mime-Version: 1.0\n"); + + /* End of header */ + + /* Write body parts */ + while(parts[part_num].header.major != SSPM_NO_MAJOR_TYPE){ + if (parts[part_num].header.major == SSPM_MULTIPART_MAJOR_TYPE){ + sspm_write_multipart_part(&buf,parts,&part_num); + } else { + sspm_write_part(&buf, &(parts[part_num]), &part_num); + } + + part_num++; + } + + + *output_string = buf.buffer; + + return 0; +} + diff --git a/libkcal/libical/src/libical/sspm.h b/libkcal/libical/src/libical/sspm.h new file mode 100644 index 000000000..00be08214 --- /dev/null +++ b/libkcal/libical/src/libical/sspm.h @@ -0,0 +1,144 @@ +/* -*- Mode: C -*- + ====================================================================== + FILE: sspm.h Mime Parser + CREATOR: eric 25 June 2000 + + + (C) COPYRIGHT 2000, Eric Busboom <eric@softwarestudio.org> + http://www.softwarestudio.org + + The contents of this file are subject to the Mozilla Public License + Version 1.0 (the "License"); you may not use this file except in + compliance with the License. You may obtain a copy of the License at + http://www.mozilla.org/MPL/ + + Software distributed under the License is distributed on an "AS IS" + basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + the License for the specific language governing rights and + limitations under the License. + + + This program is free software; you can redistribute it and/or modify + it under the terms of either: + + The LGPL as published by the Free Software Foundation, version + 2.1, available at: http://www.fsf.org/copyleft/lesser.html + + Or: + + The Mozilla Public License Version 1.0. You may obtain a copy of + the License at http://www.mozilla.org/MPL/ + + The Initial Developer of the Original Code is Eric Busboom + + (C) COPYRIGHT 2000, Eric Busboom, http://www.softwarestudio.org + ======================================================================*/ + +#ifndef SSPM_H +#define SSPM_H + +enum sspm_major_type { + SSPM_NO_MAJOR_TYPE, + SSPM_TEXT_MAJOR_TYPE, + SSPM_IMAGE_MAJOR_TYPE, + SSPM_AUDIO_MAJOR_TYPE, + SSPM_VIDEO_MAJOR_TYPE, + SSPM_APPLICATION_MAJOR_TYPE, + SSPM_MULTIPART_MAJOR_TYPE, + SSPM_MESSAGE_MAJOR_TYPE, + SSPM_UNKNOWN_MAJOR_TYPE +}; + +enum sspm_minor_type { + SSPM_NO_MINOR_TYPE, + SSPM_ANY_MINOR_TYPE, + SSPM_PLAIN_MINOR_TYPE, + SSPM_RFC822_MINOR_TYPE, + SSPM_DIGEST_MINOR_TYPE, + SSPM_CALENDAR_MINOR_TYPE, + SSPM_MIXED_MINOR_TYPE, + SSPM_RELATED_MINOR_TYPE, + SSPM_ALTERNATIVE_MINOR_TYPE, + SSPM_PARALLEL_MINOR_TYPE, + SSPM_UNKNOWN_MINOR_TYPE +}; + +enum sspm_encoding { + SSPM_NO_ENCODING, + SSPM_QUOTED_PRINTABLE_ENCODING, + SSPM_8BIT_ENCODING, + SSPM_7BIT_ENCODING, + SSPM_BINARY_ENCODING, + SSPM_BASE64_ENCODING, + SSPM_UNKNOWN_ENCODING +}; + +enum sspm_error{ + SSPM_NO_ERROR, + SSPM_UNEXPECTED_BOUNDARY_ERROR, + SSPM_WRONG_BOUNDARY_ERROR, + SSPM_NO_BOUNDARY_ERROR, + SSPM_NO_HEADER_ERROR, + SSPM_MALFORMED_HEADER_ERROR +}; + + +struct sspm_header +{ + int def; + char* boundary; + enum sspm_major_type major; + enum sspm_minor_type minor; + char *minor_text; + char ** content_type_params; + char* charset; + enum sspm_encoding encoding; + char* filename; + char* content_id; + enum sspm_error error; + char* error_text; +}; + +struct sspm_part { + struct sspm_header header; + int level; + size_t data_size; + void *data; +}; + +struct sspm_action_map { + enum sspm_major_type major; + enum sspm_minor_type minor; + void* (*new_part)(); + void (*add_line)(void *part, struct sspm_header *header, + const char* line, size_t size); + void* (*end_part)(void* part); + void (*free_part)(void *part); +}; + +const char* sspm_major_type_string(enum sspm_major_type type); +const char* sspm_minor_type_string(enum sspm_minor_type type); +const char* sspm_encoding_string(enum sspm_encoding type); + +int sspm_parse_mime(struct sspm_part *parts, + size_t max_parts, + struct sspm_action_map *actions, + char* (*get_string)(char *s, size_t size, void* data), + void *get_string_data, + struct sspm_header *first_header + ); + +void sspm_free_parts(struct sspm_part *parts, size_t max_parts); + +char *decode_quoted_printable(char *dest, + char *src, + size_t *size); +char *decode_base64(char *dest, + char *src, + size_t *size); + + +int sspm_write_mime(struct sspm_part *parts,size_t num_parts, + char **output_string, const char* header); + +#endif /*SSPM_H*/ diff --git a/libkcal/libical/src/libical/vsnprintf.c b/libkcal/libical/src/libical/vsnprintf.c new file mode 100644 index 000000000..982920b21 --- /dev/null +++ b/libkcal/libical/src/libical/vsnprintf.c @@ -0,0 +1,172 @@ +#ifndef WIN32 +#include "config.h" +#endif +#ifndef HAVE_SNPRINTF +/* + * Revision 12: http://theos.com/~deraadt/snprintf.c + * + * Copyright (c) 1997 Theo de Raadt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef WIN32 +#include <sys/param.h> +#include <sys/mman.h> +#include <unistd.h> +#endif +#include <sys/types.h> +#include <signal.h> +#include <stdio.h> +#include <string.h> +#if __STDC__ +#include <stdarg.h> +#include <stdlib.h> +#else +#include <varargs.h> +#endif +#include <setjmp.h> + +#ifndef roundup +#define roundup(x, y) ((((x)+((y)-1))/(y))*(y)) +#endif + +static int pgsize; +static char *curobj; +static sigjmp_buf bail; + +#define EXTRABYTES 2 /* XXX: why 2? you don't want to know */ + +static char * +msetup(str, n) + char *str; + size_t n; +{ + char *e; + + if (n == 0) + return NULL; + if (pgsize == 0) + pgsize = getpagesize(); + curobj = (char *)malloc(n + EXTRABYTES + pgsize * 2); + if (curobj == NULL) + return NULL; + e = curobj + n + EXTRABYTES; + e = (char *)roundup((unsigned long)e, pgsize); + if (mprotect(e, pgsize, PROT_NONE) == -1) { + free(curobj); + curobj = NULL; + return NULL; + } + e = e - n - EXTRABYTES; + *e = '\0'; + return (e); +} + +static void +mcatch(int i) +{ + siglongjmp(bail, 1); +} + +static void +mcleanup(str, n, p) + char *str; + size_t n; + char *p; +{ + strncpy(str, p, n-1); + str[n-1] = '\0'; + if (mprotect((caddr_t)(p + n + EXTRABYTES), pgsize, + PROT_READ|PROT_WRITE|PROT_EXEC) == -1) + mprotect((caddr_t)(p + n + EXTRABYTES), pgsize, + PROT_READ|PROT_WRITE); + free(curobj); +} + +int +#if __STDC__ +vsnprintf(char *str, size_t n, char const *fmt, va_list ap) +#else +vsnprintf(str, n, fmt, ap) + char *str; + size_t n; + char *fmt; + char *ap; +#endif +{ + struct sigaction osa, nsa; + char *p; + int ret = n + 1; /* if we bail, indicated we overflowed */ + + memset(&nsa, 0, sizeof nsa); + nsa.sa_handler = mcatch; + sigemptyset(&nsa.sa_mask); + + p = msetup(str, n); + if (p == NULL) { + *str = '\0'; + return 0; + } + if (sigsetjmp(bail, 1) == 0) { + if (sigaction(SIGSEGV, &nsa, &osa) == -1) { + mcleanup(str, n, p); + return (0); + } + ret = vsprintf(p, fmt, ap); + } + mcleanup(str, n, p); + (void) sigaction(SIGSEGV, &osa, NULL); + return (ret); +} + +int +#if __STDC__ +snprintf(char *str, size_t n, char const *fmt, ...) +#else +snprintf(str, n, fmt, va_alist) + char *str; + size_t n; + char *fmt; + va_dcl +#endif +{ + va_list ap; +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + + return (vsnprintf(str, n, fmt, ap)); + va_end(ap); +} + + +#else + +/* ANSI C forbids an empty source file... */ + +static void vsnprintf_dummy_func() { + vsnprintf_dummy_func(); +} + +#endif diff --git a/libkcal/libical/src/libicalss/Makefile.am b/libkcal/libical/src/libicalss/Makefile.am new file mode 100644 index 000000000..d2c1396b6 --- /dev/null +++ b/libkcal/libical/src/libicalss/Makefile.am @@ -0,0 +1,35 @@ + +noinst_LTLIBRARIES = libicalss.la + +AM_YFLAGS =-d -v -p ss +AM_LFLAGS = -Pss +LEX_OUTPUT_ROOT = lex.ss + +INCLUDES = -I../libical -I$(srcdir)/../libical + +libicalss_la_SOURCES = \ + icalclassify.c \ + icalclassify.h + +# We don't need icalss.h, but it needs to be built... +libicalss_la_COMPILE_FIRST = ../libical/ical.h icalss.h + +COMBINEDHEADERS = \ + $(srcdir)/icalclassify.h + +icalss.h: $(COMBINEDHEADERS) + echo '#ifdef __cplusplus' > icalss.h + echo 'extern "C" {' >> icalss.h + echo '#endif' >> icalss.h + cat $(COMBINEDHEADERS) \ + | egrep -v "#include.*\"ical" \ + | egrep -v "#include.*\"pvl\.h\"" \ + | egrep -v '\$$(Id|Locker): .+\$$'>> icalss.h + echo '#ifdef __cplusplus' >> icalss.h + echo '}' >> icalss.h + echo '#endif' >> icalss.h + +noinst_HEADERS = icalss.h icalclassify.h + +CLEANFILES = icalss.h + diff --git a/libkcal/libical/src/libicalss/icalclassify.c b/libkcal/libical/src/libicalss/icalclassify.c new file mode 100644 index 000000000..1cc848a1d --- /dev/null +++ b/libkcal/libical/src/libicalss/icalclassify.c @@ -0,0 +1,752 @@ +/* -*- Mode: C -*- + ====================================================================== + FILE: icalclassify.c + CREATOR: ebusboom 23 aug 2000 + + + (C) COPYRIGHT 2000, Eric Busboom <eric@softwarestudio.org> + http://www.softwarestudio.org + + This program is free software; you can redistribute it and/or modify + it under the terms of either: + + The LGPL as published by the Free Software Foundation, version + 2.1, available at: http://www.fsf.org/copyleft/lesser.html + + Or: + + The Mozilla Public License Version 1.0. You may obtain a copy of + the License at http://www.mozilla.org/MPL/ + + + ======================================================================*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "ical.h" +#include "icalclassify.h" +#include "icalmemory.h" + +#include <ctype.h> /* For tolower() */ +#include <string.h> /* for index() */ +#include <stdlib.h> /* for malloc and free */ + + + +struct icalclassify_parts { + icalcomponent *c; + icalcomponent_kind inner_kind; + icalproperty_method method; + char* organizer; + icalparameter_partstat reply_partstat; + char* reply_attendee; + char* uid; + int sequence; + struct icaltimetype dtstamp; + struct icaltimetype recurrence_id; +}; + + +char* icalclassify_lowercase(const char* str) +{ + char* p = 0; + char *xnew; + + if(str ==0){ + return 0; + } + + xnew = icalmemory_strdup(str); + for(p = xnew; *p!=0; p++){ + *p = tolower(*p); + } + + return xnew; +} + + + +icalproperty* icalclassify_find_attendee(icalcomponent *c, + const char* attendee) +{ + icalproperty *p; + icalcomponent* inner; + char* lattendee; + char* upn; + + if(attendee == 0){ + return 0; + } + + lattendee = icalclassify_lowercase(attendee); + upn = strchr(lattendee,':'); + + if (upn== 0){ + upn = lattendee; + } else { + upn++; /* skip the ";"*/ + } + + inner = icalcomponent_get_first_real_component(c); + + for(p = icalcomponent_get_first_property(inner,ICAL_ATTENDEE_PROPERTY); + p != 0; + p = icalcomponent_get_next_property(inner,ICAL_ATTENDEE_PROPERTY)) + { + char* this_upn; + char* this_attendee + = icalclassify_lowercase(icalproperty_get_attendee(p)); + if ( !this_attendee ) continue; + this_upn = strchr(this_attendee,':'); + + if(this_upn == 0){ + continue; + } else { + this_upn++; + } + + if(strcmp(this_upn,upn)==0){ + free(lattendee); + free(this_attendee); + return p; + } + + free(this_attendee); + } + free(lattendee); + + return 0; + +} + +void icalssutil_free_parts(struct icalclassify_parts *parts) +{ + if(parts == 0){ + return; + } + + if(parts->organizer != 0){ + free(parts->organizer); + } + + if(parts->uid != 0){ + free(parts->uid); + } + + if(parts->reply_attendee){ + free(parts->reply_attendee); + } +} + +void icalssutil_get_parts(icalcomponent* c, + struct icalclassify_parts* parts) +{ + icalproperty *p; + icalcomponent *inner; + + memset(parts,0,sizeof(struct icalclassify_parts)); + + parts->method = ICAL_METHOD_NONE; + parts->sequence = 0; + parts->reply_partstat = ICAL_PARTSTAT_NONE; + + if(c == 0){ + return; + } + + parts->c = c; + + p = icalcomponent_get_first_property(c,ICAL_METHOD_PROPERTY); + if(p!=0){ + parts->method = icalproperty_get_method(p); + } + + inner = icalcomponent_get_first_real_component(c); + + parts->inner_kind = icalcomponent_isa(inner); + + p = icalcomponent_get_first_property(inner,ICAL_ORGANIZER_PROPERTY); + if(p!=0){ + const char *p_organizer = icalproperty_get_organizer(p); + if (p_organizer!=0) { + parts->organizer = strdup(p_organizer); + } + } + + p = icalcomponent_get_first_property(inner,ICAL_SEQUENCE_PROPERTY); + if(p!=0){ + parts->sequence = icalproperty_get_sequence(p); + } + + p = icalcomponent_get_first_property(inner,ICAL_UID_PROPERTY); + if(p!=0){ + const char *p_uid = icalproperty_get_uid(p); + if (p_uid!=0) { + parts->uid = strdup(p_uid); + } + } + + p = icalcomponent_get_first_property(inner,ICAL_RECURRENCEID_PROPERTY); + if(p!=0){ + parts->recurrence_id = icalproperty_get_recurrenceid(p); + } + + p = icalcomponent_get_first_property(inner,ICAL_DTSTAMP_PROPERTY); + if(p!=0){ + parts->dtstamp = icalproperty_get_dtstamp(p); + } + + if(parts->method==ICAL_METHOD_REPLY){ + icalparameter *param; + p = icalcomponent_get_first_property(inner,ICAL_ATTENDEE_PROPERTY); + + if(p!=0){ + const char *attendee = 0; + param = icalproperty_get_first_parameter(p,ICAL_PARTSTAT_PARAMETER); + + if(param != 0){ + parts->reply_partstat = + icalparameter_get_partstat(param); + } + attendee = icalproperty_get_attendee(p); + if ( attendee ) + parts->reply_attendee = strdup( attendee ); + } + + } + + +} + + +int icalssutil_is_rescheduled(icalcomponent* a,icalcomponent* b) +{ + icalproperty *p1,*p2; + icalcomponent *i1,*i2; + int i; + + icalproperty_kind kind_array[] = { + ICAL_DTSTART_PROPERTY, + ICAL_DTEND_PROPERTY, + ICAL_DURATION_PROPERTY, + ICAL_DUE_PROPERTY, + ICAL_RRULE_PROPERTY, + ICAL_RDATE_PROPERTY, + ICAL_EXRULE_PROPERTY, + ICAL_EXDATE_PROPERTY, + ICAL_NO_PROPERTY + }; + + i1 = icalcomponent_get_first_real_component(a); + i2 = icalcomponent_get_first_real_component(b); + + for(i =0; kind_array[i] != ICAL_NO_PROPERTY; i++){ + p1 = icalcomponent_get_first_property(i1,kind_array[i]); + p2 = icalcomponent_get_first_property(i2,kind_array[i]); + + if( (p1!=0)^(p2!=0) ){ + /* Return true if the property exists in one component and not + the other */ + return 1; + } + + if(p1 && strcmp(icalproperty_as_ical_string(p1), + icalproperty_as_ical_string(p2)) != 0){ + return 1; + } + } + + return 0; + +} + +#define icalclassify_pre \ + int rtrn =0; + +#define icalclassify_post \ + return rtrn; + + +int icalclassify_publish_new(struct icalclassify_parts *comp, + struct icalclassify_parts *match, + const char* user) +{ + icalclassify_pre; + (void)user; + + if(comp->method == ICAL_METHOD_PUBLISH && + match == 0 && comp->inner_kind != ICAL_VFREEBUSY_COMPONENT){ + rtrn = 1; + } + + icalclassify_post; + +} + +int icalclassify_publish_update(struct icalclassify_parts *comp, + struct icalclassify_parts *match, + const char* user) +{ + icalclassify_pre; + (void)user; + + if(comp->method == ICAL_METHOD_PUBLISH && + match !=0 && comp->inner_kind != ICAL_VFREEBUSY_COMPONENT){ + rtrn = 1; + } + + icalclassify_post; + +} + +int icalclassify_publish_freebusy(struct icalclassify_parts *comp, + struct icalclassify_parts *match, + const char* user) +{ + icalclassify_pre; + + (void)match; + (void)user; + if(comp->method == ICAL_METHOD_PUBLISH && + comp->inner_kind == ICAL_VFREEBUSY_COMPONENT){ + rtrn = 1; + } + + icalclassify_post; + +} + + +int icalclassify_request_new(struct icalclassify_parts *comp, + struct icalclassify_parts *match, + const char* user) +{ + /* Method is REQUEST, and there is no match */ + + icalclassify_pre + (void)user; + + if(match->c==0 && comp->method == ICAL_METHOD_REQUEST){ + rtrn = 1; + } + + icalclassify_post + +} + +int icalclassify_request_update( + struct icalclassify_parts *comp, + struct icalclassify_parts *match, + const char* user) +{ + /* REQUEST method, Higher SEQUENCE than match, and all + time-related properties are unchanged */ + + icalclassify_pre + (void)user; + + if (match != 0 && + comp->sequence >= match->sequence && + !icalssutil_is_rescheduled(comp->c,match->c)){ + rtrn = 1; + } + + icalclassify_post + +} + +int icalclassify_request_reschedule( + struct icalclassify_parts *comp, + struct icalclassify_parts *match, + const char* user) +{ + /* REQUEST method, Higher SEQUENCE than match, and one or more + time-related properties are changed */ + icalclassify_pre + (void)user; + + if (match->c != 0 && + comp->sequence > match->sequence && + icalssutil_is_rescheduled(comp->c,match->c)){ + rtrn = 1; + } + + icalclassify_post + +} + +int icalclassify_request_delegate( + struct icalclassify_parts *comp, + struct icalclassify_parts *match, + const char* user) +{ + icalproperty* attendee; + icalparameter* param; + icalclassify_pre; + (void)match; + + attendee = icalclassify_find_attendee(comp->c,user); + + if(attendee == 0){ + return 0; + } + + param = icalproperty_get_first_parameter(attendee,ICAL_DELEGATEDFROM_PARAMETER); + + if (param != 0){ + rtrn = 1; + } + + icalclassify_post + +} + +int icalclassify_request_new_organizer( + struct icalclassify_parts *comp, + struct icalclassify_parts *match, + const char* user) +{ + /* Organizer has changed between match and component */ + icalclassify_pre + icalerror_set_errno(ICAL_UNIMPLEMENTED_ERROR); + icalclassify_post + (void)comp; + (void)match; + (void)user; + +} + +int icalclassify_request_status( + struct icalclassify_parts *comp, + struct icalclassify_parts *match, + const char* user) +{ + icalclassify_pre + icalerror_set_errno(ICAL_UNIMPLEMENTED_ERROR); + icalclassify_post + (void)comp; + (void)match; + (void)user; +} + +int icalclassify_request_forward( + struct icalclassify_parts *comp, + struct icalclassify_parts *match, + const char* user) +{ + icalclassify_pre + icalerror_set_errno(ICAL_UNIMPLEMENTED_ERROR); + icalclassify_post + (void)comp; + (void)match; + (void)user; +} + +int icalclassify_request_freebusy( + struct icalclassify_parts *comp, + struct icalclassify_parts *match, + const char* user) +{ + icalclassify_pre + icalerror_set_errno(ICAL_UNIMPLEMENTED_ERROR); + icalclassify_post + (void)comp; + (void)match; + (void)user; +} + +int icalclassify_reply_accept( + struct icalclassify_parts *comp, + struct icalclassify_parts *match, + const char* user) +{ + icalproperty* attendee; + icalclassify_pre; + (void)user; + + attendee = icalclassify_find_attendee(match->c,comp->reply_attendee); + + if(attendee != 0&& + comp->reply_partstat == ICAL_PARTSTAT_ACCEPTED){ + rtrn = 1; + } + + icalclassify_post +} +int icalclassify_reply_decline( + struct icalclassify_parts *comp, + struct icalclassify_parts *match, + const char* user) +{ + icalproperty* attendee; + icalclassify_pre; + (void)user; + + attendee = icalclassify_find_attendee(match->c,comp->reply_attendee); + + + if( attendee != 0 && + comp->reply_partstat == ICAL_PARTSTAT_DECLINED){ + rtrn = 1; + } + icalclassify_post +} +int icalclassify_reply_delegate( + struct icalclassify_parts *comp, + struct icalclassify_parts *match, + const char* user) +{ + icalproperty* attendee; + icalclassify_pre; + (void)user; + + attendee = icalclassify_find_attendee(match->c,comp->reply_attendee); + + if( attendee != 0 && + comp->reply_partstat == ICAL_PARTSTAT_DELEGATED){ + rtrn = 1; + } + icalclassify_post +} +int icalclassify_reply_crasher_accept( + struct icalclassify_parts *comp, + struct icalclassify_parts *match, + const char* user) +{ + icalproperty* attendee; + icalclassify_pre; + (void)user; + + attendee= icalclassify_find_attendee(match->c,comp->reply_attendee); + + if(attendee == 0 && + comp->reply_partstat == ICAL_PARTSTAT_ACCEPTED){ + rtrn = 1; + } + icalclassify_post +} +int icalclassify_reply_crasher_decline( + struct icalclassify_parts *comp, + struct icalclassify_parts *match, + const char* user) +{ + icalproperty* attendee; + icalclassify_pre; + (void)user; + + + attendee = icalclassify_find_attendee(match->c,comp->reply_attendee); + + if(attendee == 0 && + comp->reply_partstat == ICAL_PARTSTAT_DECLINED){ + rtrn = 1; + } + icalclassify_post +} +int icalclassify_add_instance( + struct icalclassify_parts *comp, + struct icalclassify_parts *match, + const char* user) +{ + icalclassify_pre + (void)match; + (void)user; + + if(comp->method == ICAL_METHOD_ADD){ + rtrn = 1; + } + icalclassify_post +} +int icalclassify_cancel_event( + struct icalclassify_parts *comp, + struct icalclassify_parts *match, + const char* user) +{ + icalclassify_pre + (void)match; + (void)user; + if(comp->method == ICAL_METHOD_CANCEL){ + rtrn = 1; + } + icalclassify_post +} +int icalclassify_cancel_instance( + struct icalclassify_parts *comp, + struct icalclassify_parts *match, + const char* user) +{ + icalclassify_pre + (void)match; + (void)user; + if(comp->method == ICAL_METHOD_CANCEL){ + rtrn = 1; + } + icalclassify_post +} +int icalclassify_cancel_all( + struct icalclassify_parts *comp, + struct icalclassify_parts *match, + const char* user) +{ + icalclassify_pre + (void)match; + (void)user; + if(comp->method == ICAL_METHOD_CANCEL){ + rtrn = 1; + } + icalclassify_post +} +int icalclassify_refesh( + struct icalclassify_parts *comp, + struct icalclassify_parts *match, + const char* user) +{ + icalclassify_pre + (void)match; + (void)user; + if(comp->method == ICAL_METHOD_REFRESH){ + rtrn = 1; + } + icalclassify_post +} +int icalclassify_counter( + struct icalclassify_parts *comp, + struct icalclassify_parts *match, + const char* user) +{ + icalclassify_pre + (void)match; + (void)user; + if(comp->method == ICAL_METHOD_COUNTER){ + rtrn = 1; + } + icalclassify_post +} +int icalclassify_delinecounter( + struct icalclassify_parts *comp, + struct icalclassify_parts *match, + const char* user) +{ + icalclassify_pre + (void)match; + (void)user; + + if(comp->method == ICAL_METHOD_DECLINECOUNTER){ + rtrn = 1; + } + + icalclassify_post +} + +struct icalclassify_map { + icalproperty_method method; + int (*fn)(struct icalclassify_parts *comp,struct icalclassify_parts *match, const char* user); + icalproperty_xlicclass class; +} icalclassify_map[] = +{ {ICAL_METHOD_PUBLISH,icalclassify_publish_new,ICAL_XLICCLASS_PUBLISHNEW}, + {ICAL_METHOD_PUBLISH,icalclassify_publish_update,ICAL_XLICCLASS_PUBLISHUPDATE}, + {ICAL_METHOD_PUBLISH,icalclassify_publish_freebusy,ICAL_XLICCLASS_PUBLISHFREEBUSY}, + {ICAL_METHOD_REQUEST,icalclassify_request_delegate,ICAL_XLICCLASS_REQUESTDELEGATE}, + {ICAL_METHOD_REQUEST,icalclassify_request_new,ICAL_XLICCLASS_REQUESTNEW}, + {ICAL_METHOD_REQUEST,icalclassify_request_update,ICAL_XLICCLASS_REQUESTUPDATE}, + {ICAL_METHOD_REQUEST,icalclassify_request_reschedule,ICAL_XLICCLASS_REQUESTRESCHEDULE}, + + {ICAL_METHOD_REQUEST,icalclassify_request_new_organizer,ICAL_XLICCLASS_REQUESTNEWORGANIZER}, + {ICAL_METHOD_REQUEST,icalclassify_request_forward,ICAL_XLICCLASS_REQUESTFORWARD}, + {ICAL_METHOD_REQUEST,icalclassify_request_status,ICAL_XLICCLASS_REQUESTSTATUS}, + {ICAL_METHOD_REQUEST,icalclassify_request_freebusy,ICAL_XLICCLASS_REQUESTFREEBUSY}, + + {ICAL_METHOD_REPLY,icalclassify_reply_accept,ICAL_XLICCLASS_REPLYACCEPT}, + {ICAL_METHOD_REPLY,icalclassify_reply_decline,ICAL_XLICCLASS_REPLYDECLINE}, + {ICAL_METHOD_REPLY,icalclassify_reply_delegate,ICAL_XLICCLASS_REPLYDELEGATE}, + {ICAL_METHOD_REPLY,icalclassify_reply_crasher_accept,ICAL_XLICCLASS_REPLYCRASHERACCEPT}, + {ICAL_METHOD_REPLY,icalclassify_reply_crasher_decline,ICAL_XLICCLASS_REPLYCRASHERDECLINE}, + + {ICAL_METHOD_ADD,icalclassify_add_instance,ICAL_XLICCLASS_ADDINSTANCE}, + + {ICAL_METHOD_CANCEL,icalclassify_cancel_event,ICAL_XLICCLASS_CANCELEVENT}, + {ICAL_METHOD_CANCEL,icalclassify_cancel_instance,ICAL_XLICCLASS_CANCELINSTANCE}, + {ICAL_METHOD_CANCEL,icalclassify_cancel_all,ICAL_XLICCLASS_CANCELALL}, + + {ICAL_METHOD_REFRESH,icalclassify_refesh,ICAL_XLICCLASS_REFRESH}, + {ICAL_METHOD_COUNTER,icalclassify_counter,ICAL_XLICCLASS_COUNTER}, + {ICAL_METHOD_DECLINECOUNTER,icalclassify_delinecounter,ICAL_XLICCLASS_DECLINECOUNTER}, + {ICAL_METHOD_NONE,0,ICAL_XLICCLASS_NONE} +}; + + +icalproperty_xlicclass icalclassify(icalcomponent* c,icalcomponent* match, + const char* user) +{ + icalcomponent *inner; + icalproperty *p; + icalproperty_method method; + icalproperty_xlicclass class = ICAL_XLICCLASS_UNKNOWN; + + int i; + + struct icalclassify_parts comp_parts; + struct icalclassify_parts match_parts; + + inner = icalcomponent_get_first_real_component(c); + + if (inner == 0) { + return ICAL_XLICCLASS_NONE; + } + + icalssutil_get_parts(c,&comp_parts); + icalssutil_get_parts(match,&match_parts); + + /* Determine if the incoming component is obsoleted by the match */ + if(match != 0 && ( + comp_parts.method == ICAL_METHOD_REQUEST + )){ + assert ( ! ((comp_parts.dtstamp.is_utc==1)^ + (match_parts.dtstamp.is_utc==1))); + + if( comp_parts.sequence<match_parts.sequence && + icaltime_compare(comp_parts.dtstamp,match_parts.dtstamp)>0) + { + /* comp has a smaller sequence and a later DTSTAMP */ + class = ICAL_XLICCLASS_MISSEQUENCED; + goto CLEANUP; + } + + if( (comp_parts.sequence<match_parts.sequence ) + /*&&icaltime_compare(comp_parts.dtstamp,match_parts.dtstamp)<=0*/ + || + ( comp_parts.sequence == match_parts.sequence && + icaltime_compare(comp_parts.dtstamp,match_parts.dtstamp)<=0)){ + + class = ICAL_XLICCLASS_OBSOLETE; + goto CLEANUP; + } + + } + + p = icalcomponent_get_first_property(c,ICAL_METHOD_PROPERTY); + if (p == 0) { + class = ICAL_XLICCLASS_UNKNOWN; + goto CLEANUP; + } + method = icalproperty_get_method(p); + + for (i =0; icalclassify_map[i].method != ICAL_METHOD_NONE; i++){ + if(icalclassify_map[i].method == method){ + if( (*(icalclassify_map[i].fn))(&comp_parts,&match_parts,user)==1){ + class = icalclassify_map[i].class; + break; + } + } + } + +CLEANUP: + icalssutil_free_parts(&comp_parts); + icalssutil_free_parts(&match_parts); + + return class; + +} + diff --git a/libkcal/libical/src/libicalss/icalclassify.h b/libkcal/libical/src/libicalss/icalclassify.h new file mode 100644 index 000000000..9132a2f5d --- /dev/null +++ b/libkcal/libical/src/libicalss/icalclassify.h @@ -0,0 +1,38 @@ +/* -*- Mode: C -*- */ +/*====================================================================== + FILE: icalclassify.h + CREATOR: eric 21 Aug 2000 + + + + (C) COPYRIGHT 2000, Eric Busboom <eric@softwarestudio.org> + http://www.softwarestudio.org + + This program is free software; you can redistribute it and/or modify + it under the terms of either: + + The LGPL as published by the Free Software Foundation, version + 2.1, available at: http://www.fsf.org/copyleft/lesser.html + + Or: + + The Mozilla Public License Version 1.0. You may obtain a copy of + the License at http://www.mozilla.org/MPL/ + + + =========================================================================*/ + +#ifndef ICALCLASSIFY_H +#define ICALCLASSIFY_H + +#include "ical.h" + +icalproperty_xlicclass icalclassify(icalcomponent* c,icalcomponent* match, + const char* user); + +#endif /* ICALCLASSIFY_H*/ + + + + + |