diff options
| author | Allen Winter <allen.winter@kdab.com> | 2014-05-31 17:22:41 -0400 |
|---|---|---|
| committer | Allen Winter <allen.winter@kdab.com> | 2014-05-31 17:22:41 -0400 |
| commit | bc831ea040913b1069e8eaf99496dc12704b94a9 (patch) | |
| tree | 1502b86977c42dd5e5d202a2ff3807b08309f9ff /src/libical | |
| parent | c1d3761c08651f469a3bd3a8130e6005b8e26136 (diff) | |
| download | libical-git-bc831ea040913b1069e8eaf99496dc12704b94a9.tar.gz | |
Re-arrange after svn to git conversion
Diffstat (limited to 'src/libical')
72 files changed, 27812 insertions, 0 deletions
diff --git a/src/libical/.svnignore b/src/libical/.svnignore new file mode 100644 index 00000000..c32acc82 --- /dev/null +++ b/src/libical/.svnignore @@ -0,0 +1,28 @@ +icalyacc.h +icalyacc.c +icallexer.c +y.output +Makefile +icalversion.h +.deps +.libs +*.lo +*.la +ical.h +icalyacc.output +icalproperty.c +icalproperty.h +icalvalue.h +icalvalue.c +icalparameter.h +icalparameter.c +icalrestriction.c +icalderivedparameter.c +icalderivedparameter.h +icalderivedproperty.c +icalderivedproperty.h +icalderivedvalue.c +icalderivedvalue.h +icallexer.c +y.output +Makefile.in diff --git a/src/libical/CMakeLists.txt b/src/libical/CMakeLists.txt new file mode 100644 index 00000000..eb99e2ab --- /dev/null +++ b/src/libical/CMakeLists.txt @@ -0,0 +1,331 @@ +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/icalversion.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/icalversion.h @ONLY) + +include_directories( + ${CMAKE_BINARY_DIR} + ${CMAKE_BINARY_DIR}/src + ${CMAKE_SOURCE_DIR}/src + ${CMAKE_SOURCE_DIR}/src/libical + ${CMAKE_BINARY_DIR}/src/libical + ${LIBICU_INCLUDE_DIRS} +) + +if(WIN32) + if(MSVC) + add_definitions(-DBUILD_LIBICALDLL) + endif(MSVC) +endif(WIN32) + +configure_file(icalerror.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/icalerror.h) + +set(PACKAGE_DATA_DIR "\\\"${CMAKE_INSTALL_PREFIX}/share/libical\\\"") +add_definitions(-DPACKAGE_DATA_DIR="${PACKAGE_DATA_DIR}") + +set(ICALSCRIPTS ${CMAKE_SOURCE_DIR}/scripts/) + +set(PROPERTYDEPS + ${ICALSCRIPTS}/mkderivedproperties.pl + ${CMAKE_SOURCE_DIR}/design-data/properties.csv + ${CMAKE_SOURCE_DIR}/design-data/value-types.csv + ${CMAKE_SOURCE_DIR}/src/libical/icalderivedproperty.h.in + ${CMAKE_SOURCE_DIR}/src/libical/icalderivedproperty.c.in +) + +add_custom_command( + OUTPUT + ${CMAKE_BINARY_DIR}/src/libical/icalderivedproperty.h + COMMAND + ${PERL_EXECUTABLE} -I ${ICALSCRIPTS} ${ICALSCRIPTS}/mkderivedproperties.pl + -i ${CMAKE_SOURCE_DIR}/src/libical/icalderivedproperty.h.in + -h ${CMAKE_SOURCE_DIR}/design-data/properties.csv + ${CMAKE_SOURCE_DIR}/design-data/value-types.csv > + ${CMAKE_BINARY_DIR}/src/libical/icalderivedproperty.h + DEPENDS ${PROPERTYDEPS} +) + +add_custom_command( + OUTPUT + ${CMAKE_BINARY_DIR}/src/libical/icalderivedproperty.c + COMMAND + ${PERL_EXECUTABLE} -I${ICALSCRIPTS} ${ICALSCRIPTS}/mkderivedproperties.pl + -i ${CMAKE_SOURCE_DIR}/src/libical/icalderivedproperty.c.in + -c ${CMAKE_SOURCE_DIR}/design-data/properties.csv + ${CMAKE_SOURCE_DIR}/design-data/value-types.csv > + ${CMAKE_BINARY_DIR}/src/libical/icalderivedproperty.c + DEPENDS ${PROPERTYDEPS} ${CMAKE_BINARY_DIR}/src/libical/icalderivedproperty.h +) + +set(PARAMETERDEPS + ${ICALSCRIPTS}/mkderivedparameters.pl + ${CMAKE_SOURCE_DIR}/design-data/parameters.csv + ${CMAKE_SOURCE_DIR}/src/libical/icalderivedparameter.c.in + ${CMAKE_SOURCE_DIR}/src/libical/icalderivedparameter.h.in +) + +add_custom_command( + OUTPUT + ${CMAKE_BINARY_DIR}/src/libical/icalderivedparameter.h + COMMAND + ${PERL_EXECUTABLE} -I ${ICALSCRIPTS} ${ICALSCRIPTS}/mkderivedparameters.pl + -i ${CMAKE_SOURCE_DIR}/src/libical/icalderivedparameter.h.in + -h ${CMAKE_SOURCE_DIR}/design-data/parameters.csv > + ${CMAKE_BINARY_DIR}/src/libical/icalderivedparameter.h + DEPENDS ${PARAMETERDEPS} +) + +add_custom_command( + OUTPUT + ${CMAKE_BINARY_DIR}/src/libical/icalderivedparameter.c + COMMAND + ${PERL_EXECUTABLE} -I ${ICALSCRIPTS} ${ICALSCRIPTS}/mkderivedparameters.pl + -i ${CMAKE_SOURCE_DIR}/src/libical/icalderivedparameter.c.in + -c ${CMAKE_SOURCE_DIR}/design-data/parameters.csv > + ${CMAKE_BINARY_DIR}/src/libical/icalderivedparameter.c + DEPENDS ${PARAMETERDEPS} ${CMAKE_BINARY_DIR}/src/libical/icalderivedparameter.h +) + +set(RESTRICTIONDEPS + ${ICALSCRIPTS}/mkrestrictiontable.pl + ${CMAKE_SOURCE_DIR}/design-data/restrictions.csv + ${CMAKE_SOURCE_DIR}/src/libical/icalrestriction.c.in +) + +file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/src/libical/) +add_custom_command( + OUTPUT + ${CMAKE_BINARY_DIR}/src/libical/icalrestriction.c + COMMAND + ${PERL_EXECUTABLE} -I ${ICALSCRIPTS} ${ICALSCRIPTS}/mkrestrictiontable.pl + -i ${CMAKE_SOURCE_DIR}/src/libical/icalrestriction.c.in + ${CMAKE_SOURCE_DIR}/design-data/restrictions.csv > + ${CMAKE_BINARY_DIR}/src/libical/icalrestriction.c + DEPENDS ${RESTRICTIONDEPS} +) + +set(VALUEDEPS + ${ICALSCRIPTS}mkderivedvalues.pl + ${CMAKE_SOURCE_DIR}/design-data/value-types.csv + ${CMAKE_SOURCE_DIR}/src/libical/icalderivedvalue.c.in + ${CMAKE_SOURCE_DIR}/src/libical/icalderivedvalue.h.in +) + +add_custom_command( + OUTPUT + ${CMAKE_BINARY_DIR}/src/libical/icalderivedvalue.h + COMMAND + ${PERL_EXECUTABLE} -I${ICALSCRIPTS} ${ICALSCRIPTS}/mkderivedvalues.pl + -i ${CMAKE_SOURCE_DIR}/src/libical/icalderivedvalue.h.in + -h ${CMAKE_SOURCE_DIR}/design-data/value-types.csv > + ${CMAKE_BINARY_DIR}/src/libical/icalderivedvalue.h + DEPENDS ${VALUEDEPS} +) + +add_custom_command( + OUTPUT + ${CMAKE_BINARY_DIR}/src/libical/icalderivedvalue.c + COMMAND + ${PERL_EXECUTABLE} -I${ICALSCRIPTS} ${ICALSCRIPTS}/mkderivedvalues.pl + -i ${CMAKE_SOURCE_DIR}/src/libical/icalderivedvalue.c.in + -c ${CMAKE_SOURCE_DIR}/design-data/value-types.csv > + ${CMAKE_BINARY_DIR}/src/libical/icalderivedvalue.c + DEPENDS ${VALUEDEPS} ${CMAKE_BINARY_DIR}/src/libical/icalderivedvalue.h +) + +if(WIN32) + set(TOPS "\"${CMAKE_SOURCE_DIR}\"") + set(TOPB "\"${CMAKE_BINARY_DIR}\"") +else(WIN32) + set(TOPS "${CMAKE_SOURCE_DIR}") + set(TOPB "${CMAKE_BINARY_DIR}") +endif(WIN32) + +add_custom_command( + OUTPUT + ${CMAKE_BINARY_DIR}/src/libical/ical.h + COMMAND + ${CMAKE_COMMAND} + -DTOPS:FILEPATH=${TOPS} + -DTOPB:FILEPATH=${TOPB} + -DICAL_FILE_H_FILE:FILEPATH=${CMAKE_BINARY_DIR}/src/libical/ical.h + -P ${CMAKE_CURRENT_SOURCE_DIR}/ical_file.cmake + DEPENDS + ${CMAKE_BINARY_DIR}/src/libical/icalderivedproperty.h + ${CMAKE_BINARY_DIR}/src/libical/icalderivedparameter.h + ${CMAKE_BINARY_DIR}/src/libical/icalderivedvalue.h +) + +add_custom_target(ical-header DEPENDS + ${CMAKE_BINARY_DIR}/src/libical/ical.h +) + +macro(_list_prefix _outvar _listvar _prefix) + set(${_outvar}) + foreach(_item IN LISTS ${_listvar}) + list(APPEND ${_outvar} ${_prefix}${_item}) + endforeach() +endmacro(_list_prefix) + +# GObject Introspection +if(HAVE_INTROSPECTION) + include(GObjectIntrospectionMacros) + set(INTROSPECTION_GIRS) + #set(INTROSPECTION_SCANNER_ARGS "--add-include-path=${CMAKE_CURRENT_SOURCE_DIR}") + #set(INTROSPECTION_COMPILER_ARGS "--includedir=${CMAKE_CURRENT_SOURCE_DIR}") + + set(LIB_VERSION ${LIBICAL_LIB_VERSION_STRING}) + string(REPLACE "." "_" LIB_VERSION "${LIB_VERSION}") + string(REPLACE "-" "_" LIB_VERSION "${LIB_VERSION}") + + set(libical_${LIB_VERSION}_gir "libical") + set(libical_${LIB_VERSION}_gir_VERSION ${LIBICAL_LIB_VERSION_STRING}) + set(INTROSPECTION_SCANNER_ARGS ${CMAKE_BINARY_DIR}/src/libical/ical.h) + set(libical_${LIB_VERSION}_gir_LIBRARY "ical") + set(libical_${LIB_VERSION}_gir_INCLUDES GObject-2.0) + get_directory_property(_tmp_includes ical_LIB_SRCS) + _list_prefix(_includes _tmp_includes "-L") + set(libical_${LIB_VERSION}_gir_CFLAGS ${_includes}) + set(libical_${LIB_VERSION}_gir_LIBS ical) + + list(APPEND INTROSPECTION_GIRS libical-${LIBICAL_LIB_VERSION_STRING}.gir) + + gir_add_introspections(INTROSPECTION_GIRS) +endif() + +########### next target ############### + +set(BUILT_COMBINEDHEADERS + ${CMAKE_BINARY_DIR}/src/libical/icalderivedparameter.h + ${CMAKE_BINARY_DIR}/src/libical/icalderivedproperty.h + ${CMAKE_BINARY_DIR}/src/libical/icalderivedvalue.h +) + +set(BUILT_SOURCES + ${BUILT_COMBINEDHEADERS} + icalderivedparameter.c + icalderivedproperty.c + icalrestriction.c + icalderivedvalue.c +) + +set(ical_LIB_SRCS + ${BUILT_SOURCES} + icalarray.c + icalarray.h + icalattach.h + icalattachimpl.h + icalattach.c + icalcomponent.c + icalcomponent.h + icalenums.c + icalenums.h + icalerror.c + icalmemory.c + icalmemory.h + icalmime.c + icalmime.h + icalparameter.c + icalparameter.h + icalparameterimpl.h + icalparser.c + icalparser.h + icalproperty.c + icalproperty.h + icalrecur.c + icalrecur.h + icalrestriction.h + icaltime.c + icaltime.h + icaltz-util.c + icaltz-util.h + icaltimezone.c + icaltimezone.h + icalduration.h + icalduration.c + icalperiod.h + icalperiod.c + icaltypes.c + icaltypes.h + icalvalue.c + icalvalue.h + icalvalueimpl.h + pvl.c + pvl.h + sspm.c + sspm.h + icallangbind.h + icallangbind.c + caldate.c + astime.h +) +if(WIN32) + if(MSVC) + list(APPEND ical_LIB_SRCS ../ical.def) + endif(MSVC) +endif(WIN32) + +add_library(ical ${LIBRARY_TYPE} ${ical_LIB_SRCS}) +add_library(ical-static STATIC ${ical_LIB_SRCS}) + +add_dependencies(ical ical-header) + +target_link_libraries(ical ${CMAKE_THREAD_LIBS_INIT}) + +if(ICU_FOUND) + target_link_libraries(ical ${ICU_LIBRARIES}) +endif(ICU_FOUND) +if(ICU_I18N_FOUND) + target_link_libraries(ical ${ICU_I18N_LIBRARIES}) +endif(ICU_I18N_FOUND) + +if(WINCE) + target_link_libraries(ical ${WCECOMPAT_LIBRARIES}) +endif(WINCE) + +if(MSVC) + set_target_properties(ical PROPERTIES OUTPUT_NAME "libical") + set_target_properties(ical-static PROPERTIES OUTPUT_NAME "libical-static") +else(MSVC) + set_target_properties(ical-static PROPERTIES OUTPUT_NAME "ical") +endif(MSVC) +set_target_properties(ical PROPERTIES + VERSION ${LIBICAL_LIB_VERSION_STRING} + SOVERSION ${LIBICAL_LIB_MAJOR_VERSION} +) +set_target_properties(ical PROPERTIES CLEAN_DIRECT_OUTPUT 1) +set_target_properties(ical-static PROPERTIES CLEAN_DIRECT_OUTPUT 1) + +install(TARGETS ical ical-static ${INSTALL_TARGETS_DEFAULT_ARGS}) + +########### install files ############### + +install(FILES ${CMAKE_SOURCE_DIR}/src/ical.h DESTINATION ${INCLUDE_INSTALL_DIR}) + +install(FILES + ${CMAKE_BINARY_DIR}/src/libical/ical.h + icalarray.h + icalattach.h + icalcomponent.h + ${CMAKE_BINARY_DIR}/src/libical/icalderivedparameter.h + ${CMAKE_BINARY_DIR}/src/libical/icalderivedproperty.h + ${CMAKE_BINARY_DIR}/src/libical/icalderivedvalue.h + icalduration.h + icalenums.h + ${CMAKE_BINARY_DIR}/src/libical/icalerror.h + icallangbind.h + icalmemory.h + icalmime.h + icalparameter.h + icalparser.h + icalperiod.h + icalproperty.h + icalrecur.h + icalrestriction.h + icaltime.h + icaltz-util.h + icaltimezone.h + icaltypes.h + icalvalue.h + pvl.h + sspm.h + DESTINATION + ${INCLUDE_INSTALL_DIR}/libical +) diff --git a/src/libical/astime.h b/src/libical/astime.h new file mode 100644 index 00000000..6962c060 --- /dev/null +++ b/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/src/libical/caldate.c b/src/libical/caldate.c new file mode 100644 index 00000000..1ce91786 --- /dev/null +++ b/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 = (long) (((double) jd) - 1867216.25 ) / 36524.25; + ka = jd + 1L + ialp - ( ialp >> 2 ); + } + kb = ka + 1524L; + kc = (long) ((double) kb - 122.1 ) / 365.25; + kd = (long) (double) kc * 365.25; + ke = (long) (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 = (long) + ( date->d_hour - (double) date->i_hour ) * 60.0; /* minute */ + date->i_second = date->d_second = (long) + ( 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/src/libical/ical_file.cmake b/src/libical/ical_file.cmake new file mode 100644 index 00000000..21da3508 --- /dev/null +++ b/src/libical/ical_file.cmake @@ -0,0 +1,49 @@ +# ORDERING OF HEADERS IS SIGNIFICANT. Don't change this ordering. +# It is required to make the combined header ical.h properly. +set(COMBINEDHEADERSICAL + ${TOPB}/src/libical/icalversion.h + ${TOPS}/src/libical/icaltime.h + ${TOPS}/src/libical/icalduration.h + ${TOPS}/src/libical/icalperiod.h + ${TOPS}/src/libical/icalenums.h + ${TOPS}/src/libical/icaltypes.h + ${TOPS}/src/libical/icalrecur.h + ${TOPS}/src/libical/icalattach.h + ${TOPB}/src/libical/icalderivedvalue.h + ${TOPB}/src/libical/icalderivedparameter.h + ${TOPS}/src/libical/icalvalue.h + ${TOPS}/src/libical/icalparameter.h + ${TOPB}/src/libical/icalderivedproperty.h + ${TOPS}/src/libical/icalproperty.h + ${TOPS}/src/libical/pvl.h + ${TOPS}/src/libical/icalarray.h + ${TOPS}/src/libical/icalcomponent.h + ${TOPS}/src/libical/icaltimezone.h + ${TOPS}/src/libical/icalparser.h + ${TOPS}/src/libical/icalmemory.h + ${TOPB}/src/libical/icalerror.h + ${TOPS}/src/libical/icalrestriction.h + ${TOPS}/src/libical/sspm.h + ${TOPS}/src/libical/icalmime.h + ${TOPS}/src/libical/icallangbind.h +) + +FILE(WRITE ${ICAL_FILE_H_FILE} "#ifndef LIBICAL_ICAL_H\n") +FILE(APPEND ${ICAL_FILE_H_FILE} "#define LIBICAL_ICAL_H\n") +FILE(APPEND ${ICAL_FILE_H_FILE} "#ifdef __cplusplus\n") +FILE(APPEND ${ICAL_FILE_H_FILE} "extern \"C\" {\n") +FILE(APPEND ${ICAL_FILE_H_FILE} "#endif\n") + +foreach (_current_FILE ${COMBINEDHEADERSICAL}) + FILE(READ ${_current_FILE} _contents) + STRING(REGEX REPLACE "#include *\"ical.*\\.h\"" "" _contents "${_contents}") + STRING(REGEX REPLACE "#include *\"config.*\\.h\"" "" _contents "${_contents}") + STRING(REGEX REPLACE "#include *\"pvl\\.h\"" "" _contents "${_contents}" ) + FILE(APPEND ${ICAL_FILE_H_FILE} "${_contents}") +endforeach (_current_FILE) + +FILE(APPEND ${ICAL_FILE_H_FILE} "\n") +FILE(APPEND ${ICAL_FILE_H_FILE} "#ifdef __cplusplus\n") +FILE(APPEND ${ICAL_FILE_H_FILE} "}\n") +FILE(APPEND ${ICAL_FILE_H_FILE} "#endif\n") +FILE(APPEND ${ICAL_FILE_H_FILE} "#endif\n") diff --git a/src/libical/icalarray.c b/src/libical/icalarray.c new file mode 100644 index 00000000..f382a2a9 --- /dev/null +++ b/src/libical/icalarray.c @@ -0,0 +1,227 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- + ====================================================================== + FILE: icalarray.c + CREATOR: Damon Chaplin 07 March 2001 + + $Id: icalarray.c,v 1.7 2008-01-15 23:17:40 dothebart Exp $ + $Locker: $ + + (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->chunks = NULL; + + return array; +} + +static void * +icalarray_alloc_chunk(icalarray *array) +{ + void *chunk = malloc(array->element_size * array->increment_size); + if (!chunk) + icalerror_set_errno(ICAL_NEWFAILED_ERROR); + return chunk; +} + +icalarray *icalarray_copy (icalarray *originalarray) +{ + icalarray *array = icalarray_new(originalarray->element_size, originalarray->increment_size); + int chunks = originalarray->space_allocated / originalarray->increment_size; + int chunk; + + if (!array) + return NULL; + + array->num_elements = originalarray->num_elements; + array->space_allocated = originalarray->space_allocated; + + array->chunks = malloc(chunks * sizeof (void *)); + if (array->chunks) { + for (chunk = 0; chunk < chunks; chunk++) { + array->chunks[chunk] = icalarray_alloc_chunk(array); + if (array->chunks[chunk]) + memcpy(array->chunks[chunk], originalarray->chunks[chunk], + array->increment_size * array->element_size); + } + + } else { + icalerror_set_errno(ICAL_ALLOCATION_ERROR); + } + + return array; +} + + +/** @brief Destructor + */ + +void +icalarray_free (icalarray *array) +{ + if (array->chunks) { + int chunks = array->space_allocated / array->increment_size; + int chunk; + for (chunk = 0; chunk < chunks; chunk++) + free(array->chunks[chunk]); + free (array->chunks); + array->chunks = 0; + } + free (array); + array = 0; +} + + +void +icalarray_append (icalarray *array, + const void *element) +{ + int pos; + if (array->num_elements >= array->space_allocated) + icalarray_expand (array, 1); + + pos = array->num_elements++; + memcpy (icalarray_element_at(array, pos), element, array->element_size); +} + + +void* +icalarray_element_at (icalarray *array, + int position) +{ + int chunk = position / array->increment_size; + int offset = position % array->increment_size; + + assert (position >= 0); + assert ((unsigned int)position < array->num_elements); + return (char *)(array->chunks[chunk]) + (offset * 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); + + while (position < array->num_elements - 1) { + memmove(icalarray_element_at(array, position), + icalarray_element_at(array, position + 1), + array->element_size); + position++; + } + + array->num_elements--; +} + + +void +icalarray_sort (icalarray *array, + int (*compare) (const void *, + const void *)) +{ + if (array->num_elements == 0) { + return; + } + + if (array->num_elements <= array->increment_size) { + qsort(array->chunks[0], array->num_elements, array->element_size, compare); + } else { + int pos; + void *tmp = malloc (array->num_elements * array->element_size); + if (!tmp) + return; + for (pos = 0; pos < array->num_elements; pos++) + memcpy((char *) tmp + array->element_size * pos, icalarray_element_at(array, pos), array->element_size); + + qsort (tmp, array->num_elements, array->element_size, compare); + + for (pos = 0; pos < array->num_elements; pos++) + memcpy(icalarray_element_at(array, pos), (char *) tmp + array->element_size * pos, array->element_size); + free (tmp); + } +} + + +static void +icalarray_expand (icalarray *array, + int space_needed) +{ + int num_chunks = array->space_allocated / array->increment_size; + int num_new_chunks; + int c; + void **new_chunks; + + num_new_chunks = (space_needed + array->increment_size - 1) / array->increment_size; + if (!num_new_chunks) + num_new_chunks = 1; + + new_chunks = malloc ((num_chunks + num_new_chunks) * sizeof (void *)); + + if (new_chunks) { + memcpy(new_chunks, array->chunks, num_chunks * sizeof (void *)); + for (c = 0; c < num_new_chunks; c++) + new_chunks[c + num_chunks] = icalarray_alloc_chunk(array); + if (array->chunks) + free (array->chunks); + array->chunks = new_chunks; + array->space_allocated = array->space_allocated + num_new_chunks * array->increment_size; + } else + icalerror_set_errno(ICAL_ALLOCATION_ERROR); +} + + diff --git a/src/libical/icalarray.h b/src/libical/icalarray.h new file mode 100644 index 00000000..93620de6 --- /dev/null +++ b/src/libical/icalarray.h @@ -0,0 +1,64 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/*====================================================================== + FILE: icalarray.h + CREATOR: Damon Chaplin 07 March 2001 + + + $Id: icalarray.h,v 1.5 2008-01-15 23:17:40 dothebart Exp $ + $Locker: $ + + (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 **chunks; +}; + + + +icalarray *icalarray_new (int element_size, + int increment_size); +icalarray *icalarray_copy (icalarray *array); +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/src/libical/icalattach.c b/src/libical/icalattach.c new file mode 100644 index 00000000..6c6ff070 --- /dev/null +++ b/src/libical/icalattach.c @@ -0,0 +1,151 @@ +/* -*- Mode: C -*- + ====================================================================== + FILE: icalattach.c + CREATOR: acampi 28 May 02 + + $Id: icalattach.c,v 1.3 2007-04-30 13:57:47 artcancro Exp $ + $Locker: $ + + + (C) COPYRIGHT 2000, Andrea Campi + + 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 (const char *data, icalattach_free_fn_t free_fn, + void *free_fn_data) +{ + icalattach *attach; + char *data_copy; + + icalerror_check_arg_rz ((data != NULL), "data"); + + if ((attach = malloc (sizeof (icalattach))) == NULL) { + errno = ENOMEM; + return NULL; + } + + if ((data_copy = strdup (data)) == NULL) { + free (attach); + errno = ENOMEM; + return NULL; + } + + attach->refcount = 1; + attach->is_url = 0; + attach->u.data.data = data_copy; + 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 { + free (attach->u.data.data); +/* unused for now + 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/src/libical/icalattach.h b/src/libical/icalattach.h new file mode 100644 index 00000000..5a82ab88 --- /dev/null +++ b/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 (const 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/src/libical/icalattachimpl.h b/src/libical/icalattachimpl.h new file mode 100644 index 00000000..952b8496 --- /dev/null +++ b/src/libical/icalattachimpl.h @@ -0,0 +1,59 @@ +/* -*- Mode: C -*- */ +/*====================================================================== + FILE: icalattachimpl.h + CREATOR: acampi 28 May 02 + + $Id: icalattachimpl.h,v 1.3 2008-01-15 23:17:40 dothebart Exp $ + + + (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 { + 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/src/libical/icalcomponent.c b/src/libical/icalcomponent.c new file mode 100644 index 00000000..0ceb695a --- /dev/null +++ b/src/libical/icalcomponent.c @@ -0,0 +1,2692 @@ +/*====================================================================== + FILE: icalcomponent.c + CREATOR: eric 28 April 1999 + + $Id: icalcomponent.c,v 1.64 2008-01-30 20:28:42 dothebart Exp $ + + (C) COPYRIGHT 2000, Eric Busboom, http://www.softwarestudio.org + + This program is free software; you can redistribute it and/or modify + it under the terms of either: + + The LGPL as published by the Free Software Foundation, version + 2.1, available at: http://www.fsf.org/copyleft/lesser.html + + Or: + + The Mozilla Public License Version 1.0. You may obtain a copy of + the License at http://www.mozilla.org/MPL/ + + The original code is 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 */ + +#ifdef _MSC_VER +#define snprintf _snprintf +#define strncasecmp strnicmp +#endif + +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 size_t 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); +static struct icaltimetype +icalcomponent_get_datetime(icalcomponent *comp, icalproperty *prop); + + +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(const 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 Constructor + */ +icalcomponent* +icalcomponent_new_x (const char* x_name) +{ + icalcomponent* comp = icalcomponent_new_impl(ICAL_X_COMPONENT); + if (!comp) { + return 0; + } + comp->x_name = icalmemory_strdup(x_name); + return comp; +} + +/*** @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){ + icalproperty_set_parent(prop,0); + icalproperty_free(prop); + } + pvl_free(c->properties); + } + + + while( (comp=pvl_data(pvl_head(c->components))) != 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; + buf = icalcomponent_as_ical_string_r(impl); + icalmemory_add_tmp_buffer(buf); + return buf; +} + + +char* +icalcomponent_as_ical_string_r (icalcomponent* impl) +{ + char* buf; + 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)!!!! */ + const char newline[] = "\r\n"; + + icalcomponent *c; + icalproperty *p; + icalcomponent_kind kind = icalcomponent_isa(impl); + + const char* kind_string; + + icalerror_check_arg_rz( (impl!=0), "component"); + icalerror_check_arg_rz( (kind!=ICAL_NO_COMPONENT), "component kind is ICAL_NO_COMPONENT"); + + if (kind != ICAL_X_COMPONENT) { + kind_string = icalcomponent_kind_to_string(kind); + } else { + kind_string = impl->x_name; + } + + icalerror_check_arg_rz( (kind_string!=0),"Unknown kind of component"); + + buf = icalmemory_new_buffer(buf_size); + buf_ptr = buf; + + 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_r(p); + + icalmemory_append_string(&buf, &buf_ptr, &buf_size, tmp_buf); + free(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_r(c); + + icalmemory_append_string(&buf, &buf_ptr, &buf_size, tmp_buf); + free(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); + + return 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"); + +#ifdef ICAL_REMOVE_NONMEMBER_COMPONENT_IS_ERROR + icalerror_assert( (icalproperty_get_parent(property)),"The property is not a member of a component"); +#else + if(icalproperty_get_parent(property) == 0){ + return; + } +#endif + + 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; + + /* Fix for Mozilla - bug 327602 */ + if (child->kind != ICAL_VTIMEZONE_COMPONENT) { + pvl_push(parent->components, child); + } else { + /* VTIMEZONES should be first in the resulting VCALENDAR. */ + pvl_unshift(parent->components, child); + + /* Add the VTIMEZONE to our array. */ + /* 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_VAVAILABILITY_COMPONENT || + kind == ICAL_VPOLL_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; + pvl_elem property_iterator; + + if (comp == NULL || + dtstart == NULL || + recurtime == NULL || + icaltime_is_null_time(*recurtime)) + /* BAD DATA */ + return 1; + + property_iterator = comp->property_iterator; + + /** 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 = icalcomponent_get_datetime(comp, exdate); + + if ((icaltime_is_date(exdatetime) + && icaltime_compare_date_only(*recurtime, exdatetime) == 0) || + (icaltime_compare(*recurtime, exdatetime) == 0)) { + /** MATCHED **/ + + comp->property_iterator = property_iterator; + 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 (exrule_itr) { + 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); + comp->property_iterator = property_iterator; + return 1; /** MATCH **/ + } + if (result == 1) + break; /** exrule_time > recurtime **/ + } + + icalrecur_iterator_free(exrule_itr); + } + comp->property_iterator = property_iterator; + + 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; + time_t 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; + if(rrule_itr) + rrule_time = icalrecur_iterator_next(rrule_itr); + /** note that icalrecur_iterator_next always returns dtstart + the first time.. **/ + + while (rrule_itr) { + rrule_time = icalrecur_iterator_next(rrule_itr); + + if (icaltime_is_null_time(rrule_time)) + break; + + /* if we have iterated past end time, then no need to check any further */ + if (icaltime_compare(rrule_time, end) > 0) + 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 **/ + if (icaltime_span_overlaps(&recurspan, &limit_span)) + (*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); + icalproperty_free(p); + p = NULL; + } + } + + 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); + icalproperty_free(p); + p = NULL; + } + } + } + + 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 const 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_VCAR_COMPONENT, "VCAR" }, + { ICAL_VCOMMAND_COMPONENT, "VCOMMAND" }, + { ICAL_VQUERY_COMPONENT, "VQUERY" }, + { ICAL_VREPLY_COMPONENT, "VREPLY" }, + + /* libical private components */ + { ICAL_XLICINVALID_COMPONENT, "X-LIC-UNKNOWN" }, + { ICAL_XLICMIMEPART_COMPONENT, "X-LIC-MIME-PART" }, + { ICAL_ANY_COMPONENT, "ANY" }, + { ICAL_XROOT_COMPONENT, "XROOT" }, + + /* Calendar Availability components */ + { ICAL_VAVAILABILITY_COMPONENT, "VAVAILABILITY" }, + { ICAL_XAVAILABLE_COMPONENT, "AVAILABLE" }, + + /* Consensus Scheduling component */ + { ICAL_VPOLL_COMPONENT, "VPOLL" }, + + /* 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 (strncasecmp(string, component_map[i].name, strlen(component_map[i].name)) == 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) { + + icalcomponent *c; + 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 = NULL; + + for (c = comp; c != NULL; c = icalcomponent_get_parent(c)) { + tz = icalcomponent_get_timezone(c, tzid); + if (tz != NULL) + break; + } + + if (tz == NULL) + tz = icaltimezone_get_builtin_timezone_from_tzid(tzid); + + if (tz != NULL) + ret = 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; + + //extra check to prevent empty durations from crashing + if (icalproperty_get_value(dur_prop)) { + duration = icalproperty_get_duration(dur_prop); + } else { + duration = icaldurationtype_null_duration(); + } + + ret = icaltime_add(start,duration); + } + + 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(void) +{ + return icalcomponent_new(ICAL_VCALENDAR_COMPONENT); +} +icalcomponent* icalcomponent_new_vevent(void) +{ + return icalcomponent_new(ICAL_VEVENT_COMPONENT); +} +icalcomponent* icalcomponent_new_vtodo(void) +{ + return icalcomponent_new(ICAL_VTODO_COMPONENT); +} +icalcomponent* icalcomponent_new_vjournal(void) +{ + return icalcomponent_new(ICAL_VJOURNAL_COMPONENT); +} +icalcomponent* icalcomponent_new_valarm(void) +{ + return icalcomponent_new(ICAL_VALARM_COMPONENT); +} +icalcomponent* icalcomponent_new_vfreebusy(void) +{ + return icalcomponent_new(ICAL_VFREEBUSY_COMPONENT); +} +icalcomponent* icalcomponent_new_vtimezone(void) +{ + return icalcomponent_new(ICAL_VTIMEZONE_COMPONENT); +} +icalcomponent* icalcomponent_new_xstandard(void) +{ + return icalcomponent_new(ICAL_XSTANDARD_COMPONENT); +} +icalcomponent* icalcomponent_new_xdaylight(void) +{ + return icalcomponent_new(ICAL_XDAYLIGHT_COMPONENT); +} +icalcomponent* icalcomponent_new_vagenda(void) +{ + return icalcomponent_new(ICAL_VAGENDA_COMPONENT); +} +icalcomponent* icalcomponent_new_vquery(void) +{ + return icalcomponent_new(ICAL_VQUERY_COMPONENT); +} +icalcomponent* icalcomponent_new_vreply(void) +{ + return icalcomponent_new(ICAL_VREPLY_COMPONENT); +} +icalcomponent* icalcomponent_new_vavailability(void) +{ + return icalcomponent_new(ICAL_VAVAILABILITY_COMPONENT); +} +icalcomponent* icalcomponent_new_xavailable(void) +{ + return icalcomponent_new(ICAL_XAVAILABLE_COMPONENT); +} +icalcomponent* icalcomponent_new_vpoll(void) +{ + return icalcomponent_new(ICAL_VPOLL_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; + size_t 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; + size_t 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); + if(!tzid_copy) { + icalerror_set_errno(ICAL_NEWFAILED_ERROR); + return; + } + existing_tzid_copy = strdup (existing_tzid); + if (!existing_tzid_copy) { + icalerror_set_errno(ICAL_NEWFAILED_ERROR); + free(tzid_copy); + } else { + icalarray_append (tzids_to_rename, tzid_copy); + free(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); + if(!tzid_copy) { + icalerror_set_errno(ICAL_NEWFAILED_ERROR); + return; + } + + snprintf (suffix_buf, sizeof(suffix_buf), "%i", max_suffix + 1); + new_tzid = malloc (tzid_len + strlen (suffix_buf) + 1); + if (!new_tzid) { + icalerror_set_errno(ICAL_NEWFAILED_ERROR); + free(tzid_copy); + 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); + free(tzid_copy); + free(new_tzid); +} + + +/* Returns the length of the TZID, without any trailing digits. */ +static size_t icalcomponent_get_tzid_prefix_len (const char *tzid) +{ + size_t 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); + if (zone_tzid != NULL) { + 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_r (vtimezone1); + if (!string1) { + free (tzid2_copy); + return -1; + } + + string2 = icalcomponent_as_ical_string_r (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) { + 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; + + } + 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); + + } +} diff --git a/src/libical/icalcomponent.h b/src/libical/icalcomponent.h new file mode 100644 index 00000000..6883bffc --- /dev/null +++ b/src/libical/icalcomponent.h @@ -0,0 +1,287 @@ +/* -*- 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(const char* str); +icalcomponent* icalcomponent_vanew(icalcomponent_kind kind, ...); +icalcomponent* icalcomponent_new_x(const char* x_name); +void icalcomponent_free(icalcomponent* component); + +char* icalcomponent_as_ical_string(icalcomponent* component); +char* icalcomponent_as_ical_string_r(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(void); +icalcomponent* icalcomponent_new_vevent(void); +icalcomponent* icalcomponent_new_vtodo(void); +icalcomponent* icalcomponent_new_vjournal(void); +icalcomponent* icalcomponent_new_valarm(void); +icalcomponent* icalcomponent_new_vfreebusy(void); +icalcomponent* icalcomponent_new_vtimezone(void); +icalcomponent* icalcomponent_new_xstandard(void); +icalcomponent* icalcomponent_new_xdaylight(void); +icalcomponent* icalcomponent_new_vagenda(void); +icalcomponent* icalcomponent_new_vavailability(void); +icalcomponent* icalcomponent_new_xavailable(void); +icalcomponent* icalcomponent_new_vpoll(void); + +#endif /* !ICALCOMPONENT_H */ diff --git a/src/libical/icalderivedparameter.c.in b/src/libical/icalderivedparameter.c.in new file mode 100644 index 00000000..2a665391 --- /dev/null +++ b/src/libical/icalderivedparameter.c.in @@ -0,0 +1,216 @@ +/* -*- Mode: C -*- + ====================================================================== + FILE: icalderivedparameters.{c,h} + CREATOR: eric 09 May 1999 + + $Id: icalderivedparameter.c.in,v 1.9 2008-01-15 23:17:40 dothebart Exp $ + $Locker: $ + + + (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 "icaltypes.h" + +#include <stdlib.h> /* for malloc() */ +#include <errno.h> +#include <string.h> /* for memset() */ + +#ifdef _MSC_VER +#define strcasecmp stricmp +#endif + +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; + } + + if (ical_get_unknown_token_handling_setting() == ICAL_TREAT_AS_ERROR) { + return ICAL_NO_PARAMETER; + } else { + return ICAL_IANA_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/src/libical/icalderivedparameter.h.in b/src/libical/icalderivedparameter.h.in new file mode 100644 index 00000000..a4f9efb9 --- /dev/null +++ b/src/libical/icalderivedparameter.h.in @@ -0,0 +1,38 @@ +/* -*- Mode: C -*- */ +/*====================================================================== + FILE: icalparam.h + CREATOR: eric 20 March 1999 + + + $Id: icalderivedparameter.h.in,v 1.4 2007-04-30 13:57:48 artcancro Exp $ + $Locker: $ + + + + (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/src/libical/icalderivedproperty.c.in b/src/libical/icalderivedproperty.c.in new file mode 100644 index 00000000..f74c6f06 --- /dev/null +++ b/src/libical/icalderivedproperty.c.in @@ -0,0 +1,298 @@ +/* -*- Mode: C -*- */ + +/*====================================================================== + FILE: icalderivedproperty.c + CREATOR: eric 15 Feb 2001 + + $Id: icalderivedproperty.c.in,v 1.15 2008-01-28 22:34:37 artcancro Exp $ + + + (C) COPYRIGHT 2000, Eric Busboom, http://www.softwarestudio.org + + This program is free software; you can redistribute it and/or modify + it under the terms of either: + + The LGPL as published by the Free Software Foundation, version + 2.1, available at: http://www.fsf.org/copyleft/lesser.html + + Or: + + The Mozilla Public License Version 1.0. You may obtain a copy of + the License at http://www.mozilla.org/MPL/ + + The original code is 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 "icaltimezone.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 _MSC_VER +#define strcasecmp stricmp +#endif + +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; +} + + +char *icalproperty_enum_to_string_r(int e) +{ + return icalmemory_strdup(icalproperty_enum_to_string(e)); +} + + +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_PROPERTY) + 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(enum_map[i-ICALPROPERTY_FIRST_ENUM].prop == pkind && + 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/src/libical/icalderivedproperty.h.in b/src/libical/icalderivedproperty.h.in new file mode 100644 index 00000000..44f6f311 --- /dev/null +++ b/src/libical/icalderivedproperty.h.in @@ -0,0 +1,33 @@ +/* -*- Mode: C -*- + ====================================================================== + FILE: icalderivedproperties.{c,h} + CREATOR: eric 09 May 1999 + + $Id: icalderivedproperty.h.in,v 1.7 2007-04-30 13:57:48 artcancro Exp $ + + 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/ + + (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/src/libical/icalderivedvalue.c.in b/src/libical/icalderivedvalue.c.in new file mode 100644 index 00000000..c0515adb --- /dev/null +++ b/src/libical/icalderivedvalue.c.in @@ -0,0 +1,459 @@ +/* -*- Mode: C -*- */ +/*====================================================================== + FILE: icalvalue.c + CREATOR: eric 02 May 1999 + + $Id: icalderivedvalue.c.in,v 1.15 2007-04-30 13:57:48 artcancro Exp $ + + + (C) COPYRIGHT 2000, Eric Busboom, http://www.softwarestudio.org + + This program is free software; you can redistribute it and/or modify + it under the terms of either: + + The LGPL as published by the Free Software Foundation, version + 2.1, available at: http://www.fsf.org/copyleft/lesser.html + + Or: + + The Mozilla Public License Version 1.0. You may obtain a copy of + the License at http://www.mozilla.org/MPL/ + + The original code is 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 _MSC_VER +#define strcasecmp stricmp +#endif + +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; + icalerror_check_arg_rz( (v!=0),"v"); + + impl = icalvalue_new_impl(ICAL_X_VALUE); + + 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_rz( (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) +{ + struct icalrecurrencetype rt; + icalrecurrencetype_clear(&rt); + + icalerror_check_arg_rx( (value!=0),"value", rt); + 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; + + tr.duration = icaldurationtype_from_int(0); + tr.time = icaltime_null_time(); + + icalerror_check_arg_rx( (impl!=0),"value", tr); + + if(impl) { + 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); + } + } else { + tr.duration = icaldurationtype_from_int(0); + tr.time = icaltime_null_time(); + icalerror_set_errno(ICAL_BADARG_ERROR); + } + + return tr; +} + +icalvalue* +icalvalue_new_datetime (struct icaltimetype v){ + + struct icalvalue_impl* impl = icalvalue_new_impl(ICAL_DATETIME_VALUE); + icalvalue_set_datetime((icalvalue*)impl,v); + return (icalvalue*)impl; +} + +void +icalvalue_set_datetime(icalvalue* value, struct icaltimetype v) { + struct icalvalue_impl* impl; + icalerror_check_arg_rv( (value!=0),"value"); + + icalerror_check_value_type(value, ICAL_DATETIME_VALUE); + impl = (struct icalvalue_impl*)value; + + + impl->data.v_time = v; + + icalvalue_reset_kind(impl); +} + +struct icaltimetype +icalvalue_get_datetime (const icalvalue* value) { + struct icaltimetype dt; + dt = icaltime_null_time(); + + icalerror_check_arg_rx((value!=0),"value", dt); + icalerror_check_value_type (value, ICAL_DATETIME_VALUE); + return ((struct icalvalue_impl*)value)->data.v_time; +} + +/* 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; + + dtp.period = icalperiodtype_null_period(); + dtp.time = icaltime_null_time(); + + icalerror_check_arg_rx( (impl!=0),"value", dtp); + icalerror_check_value_type(value, ICAL_DATETIMEPERIOD_VALUE); + + if(impl) { + 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); + } + } else { + dtp.period = icalperiodtype_null_period(); + dtp.time = icaltime_null_time(); + icalerror_set_errno(ICAL_BADARG_ERROR); + } + + return dtp; +} + +icalvalue* +icalvalue_new_class (enum icalproperty_class v){ + + struct icalvalue_impl* impl = icalvalue_new_impl(ICAL_CLASS_VALUE); + icalvalue_set_class((icalvalue*)impl,v); + return (icalvalue*)impl; +} + +void +icalvalue_set_class(icalvalue* value, enum icalproperty_class v) { + struct icalvalue_impl* impl; + icalerror_check_arg_rv( (value!=0),"value"); + + icalerror_check_value_type(value, ICAL_CLASS_VALUE); + impl = (struct icalvalue_impl*)value; + + impl->data.v_enum = v; + + icalvalue_reset_kind(impl); +} + +enum icalproperty_class +icalvalue_get_class (const icalvalue* value) { + + icalproperty_class pr; + pr = ICAL_CLASS_NONE; + + icalerror_check_arg_rx ((value!=NULL),"value", pr); + icalerror_check_arg ((value!=0),"value"); + icalerror_check_value_type (value, ICAL_CLASS_VALUE); + return ((struct icalvalue_impl*)value)->data.v_enum; +} + +icalvalue* +icalvalue_new_geo (struct icalgeotype v){ + + struct icalvalue_impl* impl = icalvalue_new_impl(ICAL_GEO_VALUE); + icalvalue_set_geo((icalvalue*)impl,v); + return (icalvalue*)impl; +} + +void +icalvalue_set_geo(icalvalue* value, struct icalgeotype v) { + struct icalvalue_impl* impl; + icalerror_check_arg_rv( (value!=0),"value"); + + icalerror_check_value_type(value, ICAL_GEO_VALUE); + impl = (struct icalvalue_impl*)value; + + impl->data.v_geo = v; + + icalvalue_reset_kind(impl); +} + +struct icalgeotype +icalvalue_get_geo (const icalvalue* value) { + struct icalgeotype gt; + gt.lat = 255.0; + gt.lon = 255.0; + + icalerror_check_arg_rx((value!=0),"value", gt); + icalerror_check_value_type (value, ICAL_GEO_VALUE); + return ((struct icalvalue_impl*)value)->data.v_geo; +} + + +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/src/libical/icalderivedvalue.h.in b/src/libical/icalderivedvalue.h.in new file mode 100644 index 00000000..5c6e2b5d --- /dev/null +++ b/src/libical/icalderivedvalue.h.in @@ -0,0 +1,77 @@ +/* -*- Mode: C -*- */ +/*====================================================================== + FILE: icalvalue.h + CREATOR: eric 20 March 1999 + + + $Id: icalderivedvalue.h.in,v 1.10 2007-04-30 13:57:48 artcancro Exp $ + $Locker: $ + + + + (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_datetime(struct icaltimetype v); +struct icaltimetype icalvalue_get_datetime(const icalvalue* value); +void icalvalue_set_datetime(icalvalue* value, struct icaltimetype v); + +icalvalue* icalvalue_new_datetimeperiod (struct icaldatetimeperiodtype v); +void icalvalue_set_datetimeperiod(icalvalue* value, struct icaldatetimeperiodtype v); +struct icaldatetimeperiodtype icalvalue_get_datetimeperiod(const icalvalue* value); + +/* GEO */ +icalvalue* icalvalue_new_geo(struct icalgeotype v); +struct icalgeotype icalvalue_get_geo(const icalvalue* value); +void icalvalue_set_geo(icalvalue* value, struct icalgeotype v); + +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> + + +icalvalue* icalvalue_new_class(enum icalproperty_class v); +enum icalproperty_class icalvalue_get_class(const icalvalue* value); +void icalvalue_set_class(icalvalue* value, enum icalproperty_class v); diff --git a/src/libical/icalduration.c b/src/libical/icalduration.c new file mode 100644 index 00000000..6226c8b1 --- /dev/null +++ b/src/libical/icalduration.c @@ -0,0 +1,350 @@ +/* -*- Mode: C -*- + ====================================================================== + FILE: icaltime.c + CREATOR: eric 02 June 2000 + + $Id: icalduration.c,v 1.21 2008-01-15 23:17:40 dothebart Exp $ + $Locker: $ + + (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 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" + +#if defined(_MSC_VER) +#define snprintf _snprintf +#endif + +/* 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 digits=-1; + int scan_size = -1; + size_t 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; + break; + } + 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||d.hours !=0||digits ==-1) + goto error; + d.hours = digits; digits = -1; + break; + } + case 'M': { + if (time_flag == 0||d.minutes != 0||digits ==-1) + goto error; + d.minutes = digits; digits = -1; + break; + } + case 'S': { + if (time_flag == 0||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; + d.weeks = digits; digits = -1; + break; + } + case 'D': { + if (time_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; + buf = icaldurationtype_as_ical_string_r(d); + icalmemory_add_tmp_buffer(buf); + return buf; +} + + +char* icaldurationtype_as_ical_string_r(struct icaldurationtype d) +{ + + char *buf; + 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"); + } + + return buf; +} + + +/* 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) +{ + if (!d.is_neg) { + t.second += d.seconds; + t.minute += d.minutes; + t.hour += d.hours; + t.day += d.days; + t.day += d.weeks * 7; + } else { + t.second -= d.seconds; + t.minute -= d.minutes; + t.hour -= d.hours; + t.day -= d.days; + t.day -= d.weeks * 7; + } + + 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/src/libical/icalduration.h b/src/libical/icalduration.h new file mode 100644 index 00000000..dd92f060 --- /dev/null +++ b/src/libical/icalduration.h @@ -0,0 +1,64 @@ +/* -*- Mode: C -*- */ +/*====================================================================== + FILE: icalduration.h + CREATOR: eric 26 Jan 2001 + + + $Id: icalduration.h,v 1.5 2008-01-15 23:17:40 dothebart Exp $ + $Locker: $ + + (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); +char* icaldurationtype_as_ical_string_r(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/src/libical/icalenums.c b/src/libical/icalenums.c new file mode 100644 index 00000000..80720b6c --- /dev/null +++ b/src/libical/icalenums.c @@ -0,0 +1,180 @@ +/* -*- Mode: C -*- */ +/*====================================================================== + FILE: icalenum.c + CREATOR: eric 29 April 1999 + + $Id: icalenums.c,v 1.16 2008-01-15 23:17:40 dothebart Exp $ + + + (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" + +#if defined(_MSC_VER) +#define snprintf _snprintf +#endif + +/*** @brief Allowed request status values + */ +static const 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; +} + +char* icalenum_reqstat_code(icalrequeststatus stat) +{ + char *buf; + buf = icalenum_reqstat_code_r(stat); + icalmemory_add_tmp_buffer(buf); + return buf; +} + + +/*** @brief Return the code for a request status + */ +char* icalenum_reqstat_code_r(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_strdup(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/src/libical/icalenums.h b/src/libical/icalenums.h new file mode 100644 index 00000000..5b30bd12 --- /dev/null +++ b/src/libical/icalenums.h @@ -0,0 +1,171 @@ + +/* -*- 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_VREPLY_COMPONENT, + ICAL_VCAR_COMPONENT, + ICAL_VCOMMAND_COMPONENT, + ICAL_XLICINVALID_COMPONENT, + ICAL_XLICMIMEPART_COMPONENT, /* a non-stardard component that mirrors + structure of MIME data */ + ICAL_VAVAILABILITY_COMPONENT, + ICAL_XAVAILABLE_COMPONENT, + ICAL_VPOLL_COMPONENT + +} 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); +char* icalenum_reqstat_code_r(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/src/libical/icalerror.c b/src/libical/icalerror.c new file mode 100644 index 00000000..f2697188 --- /dev/null +++ b/src/libical/icalerror.c @@ -0,0 +1,271 @@ +/* -*- Mode: C -*- + ====================================================================== + FILE: icalerror.c + CREATOR: eric 16 May 1999 + + $Id: icalerror.c,v 1.22 2008-01-15 23:17:40 dothebart Exp $ + $Locker: $ + + + (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 + +#ifdef HAVE_BACKTRACE +#include <execinfo.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)); + ical_bt(); + assert(0); + } + +} +#endif + +void icalerror_clear_errno() { + + icalerrno = ICAL_NO_ERROR; +} + +#if ICAL_ERRORS_ARE_FATAL == 1 +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 const 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){ + int i; + + for( i = 0; string_map[i].error != ICAL_UNKNOWN_ERROR; i++) + if (strcmp(string_map[i].str,str) == 0) + break; + + return string_map[i].error; +} + +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; +} + +const char* icalerror_perror(void) +{ + 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; +} + + + + +const 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*/ + +} + + +void ical_bt(void) +{ +#ifdef HAVE_BACKTRACE + void *stack_frames[50]; + size_t size, i; + char **strings; + + size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*)); + strings = backtrace_symbols(stack_frames, size); + for (i = 0; i < size; i++) { + if (strings != NULL) + fprintf(stderr, "%s\n", strings[i]); + else + fprintf(stderr, "%p\n", stack_frames[i]); + } + free(strings); +#endif +} + diff --git a/src/libical/icalerror.h.cmake b/src/libical/icalerror.h.cmake new file mode 100644 index 00000000..88021f5f --- /dev/null +++ b/src/libical/icalerror.h.cmake @@ -0,0 +1,181 @@ +/* -*- Mode: C -*- */ +/*====================================================================== + FILE: icalerror.h + CREATOR: eric 09 May 1999 + + $Id: icalerror.h,v 1.17 2008-01-15 23:17:40 dothebart Exp $ + + + (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())) + +#cmakedefine LIBICAL_STATIC 1 + +/** 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. + */ +#ifdef _MSC_VER + #if defined(LIBICAL_STATIC) + #define LIBICAL_EXPORT extern + #elif defined(BUILD_LIBICALDLL) + #define LIBICAL_EXPORT __declspec(dllexport) + #else + #define LIBICAL_EXPORT __declspec(dllimport) + #endif +#else + #define LIBICAL_EXPORT extern +#endif + +LIBICAL_EXPORT 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 ; + +const char* icalerror_strerror(icalerrorenum e); +const char* icalerror_perror(void); +void ical_bt(void); +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)); \ + ical_bt(); \ + assert(0); \ +} } +#else +void icalerror_set_errno(icalerrorenum x); +#endif + +#if !defined(ICAL_ERRORS_ARE_FATAL) +#define ICAL_ERRORS_ARE_FATAL 0 +#endif + +#if ICAL_ERRORS_ARE_FATAL == 1 +#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 */ +#if ICAL_ERRORS_ARE_FATAL == 1 + +#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/src/libical/icallangbind.c b/src/libical/icallangbind.c new file mode 100644 index 00000000..82188720 --- /dev/null +++ b/src/libical/icallangbind.c @@ -0,0 +1,333 @@ +/* -*- Mode: C -*- + ====================================================================== + FILE: icallangbind.c + CREATOR: eric 15 dec 2000 + + DESCRIPTION: + + $Id: icallangbind.c,v 1.24 2008-01-02 20:07:31 dothebart Exp $ + $Locker: $ + + (C) COPYRIGHT 1999 Eric Busboom + http://www.softwarestudio.org + + This package is free software and is provided "as is" without + express or implied warranty. It may be used, redistributed and/or + modified under the same terms as perl itself. ( Either the Artistic + License or the GPL. ) + + ======================================================================*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "icalcomponent.h" +#include "icalproperty.h" +#include "icalerror.h" +#include "icalmemory.h" +#include "icalvalue.h" +#include <stdlib.h> +#include <string.h> + +#if defined(_MSC_VER) +#define snprintf _snprintf +#define strcasecmp stricmp +#endif + +int* icallangbind_new_array(int size){ + int* p = (int*)malloc(size*sizeof(int)); + return p; /* Caller handles failures */ +} + +void icallangbind_free_array(int* array){ + free(array); +} + +int icallangbind_access_array(int* array, int index) { + return array[index]; +} + +/** Iterators to fetch parameters given property */ + +icalparameter* icallangbind_get_first_parameter(icalproperty *prop) + +{ + icalparameter_kind kind = ICAL_ANY_PARAMETER; + + return icalproperty_get_first_parameter(prop,kind); +} + +icalparameter* icallangbind_get_next_parameter(icalproperty *prop) +{ + icalparameter_kind kind = ICAL_ANY_PARAMETER; + + return icalproperty_get_next_parameter(prop,kind); +} + + +/** Like icalcomponent_get_first_component(), but takes a string for the + kind and can iterate over X properties as if each X name was a + seperate kind */ + +icalproperty* icallangbind_get_first_property(icalcomponent *c, + const char* prop) +{ + icalproperty_kind kind = icalproperty_string_to_kind(prop); + icalproperty *p; + + if (kind == ICAL_NO_PROPERTY){ + return 0; + } + + if(kind == ICAL_X_PROPERTY){ + for(p = icalcomponent_get_first_property(c,kind); + p !=0; + p = icalcomponent_get_next_property(c,kind)){ + + if(strcmp(icalproperty_get_x_name(p),prop) == 0){ + return p; + } + } + } else { + p=icalcomponent_get_first_property(c,kind); + + return p; + } + + return 0; + +} + +icalproperty* icallangbind_get_next_property(icalcomponent *c, + const char* prop) +{ + icalproperty_kind kind = icalenum_string_to_property_kind(prop); + icalproperty *p; + + if (kind == ICAL_NO_PROPERTY){ + return 0; + } + + if(kind == ICAL_X_PROPERTY){ + for(p = icalcomponent_get_next_property(c,kind); + p !=0; + p = icalcomponent_get_next_property(c,kind)){ + + if(strcmp(icalproperty_get_x_name(p),prop) == 0){ + return p; + } + } + } else { + p=icalcomponent_get_next_property(c,kind); + + return p; + } + + return 0; + +} + + +icalcomponent* icallangbind_get_first_component(icalcomponent *c, + const char* comp) +{ + icalcomponent_kind kind = icalenum_string_to_component_kind(comp); + + if (kind == ICAL_NO_COMPONENT){ + return 0; + } + return icalcomponent_get_first_component(c,kind); +} + +icalcomponent* icallangbind_get_next_component(icalcomponent *c, + const char* comp) +{ + icalcomponent_kind kind = icalenum_string_to_component_kind(comp); + + if (kind == ICAL_NO_COMPONENT){ + return 0; + } + return icalcomponent_get_next_component(c,kind); +} + + +#define APPENDS(x) icalmemory_append_string(&buf, &buf_ptr, &buf_size, x); + +#define APPENDC(x) icalmemory_append_char(&buf, &buf_ptr, &buf_size, x); + +char* icallangbind_property_eval_string_r(icalproperty* prop, char* sep) +{ + char tmp[25]; + size_t buf_size = 1024; + char* buf; + char* buf_ptr; + icalparameter *param; + + icalvalue* value; + + if( prop == 0){ + return 0; + } + + buf = icalmemory_new_buffer(buf_size); + buf_ptr = buf; + + APPENDS("{ "); + + value = icalproperty_get_value(prop); + + APPENDS(" 'name' "); + APPENDS(sep); + APPENDC('\''); + APPENDS(icalproperty_kind_to_string(icalproperty_isa(prop))); + APPENDC('\''); + + if(value){ + APPENDS(", 'value_type' "); + APPENDS(sep); + APPENDC('\''); + APPENDS(icalvalue_kind_to_string(icalvalue_isa(value))); + APPENDC('\''); + } + + APPENDS(", 'pid' "); + APPENDS(sep); + APPENDC('\''); + snprintf(tmp,25,"%p",prop); + APPENDS(tmp); + APPENDC('\''); + + + if(value){ + switch (icalvalue_isa(value)){ + + case ICAL_ATTACH_VALUE: + case ICAL_BINARY_VALUE: + case ICAL_NO_VALUE: { + icalerror_set_errno(ICAL_INTERNAL_ERROR); + break; + } + + default: + { + char* str = icalvalue_as_ical_string_r(value); + char* copy = (char*) malloc(strlen(str)+1); + + const char *i; + char *j; + + if(copy ==0){ + icalerror_set_errno(ICAL_NEWFAILED_ERROR); + break; + } + /* Remove any newlines */ + + for(j=copy, i = str; *i != 0; j++,i++){ + if(*i=='\n'){ + i++; + } + *j = *i; + } + + *j = 0; + + APPENDS(", 'value'"); + APPENDS(sep); + APPENDC('\''); + APPENDS(copy); + APPENDC('\''); + + free(copy); + free(str); + break; + + } + } + } + + /* Add Parameters */ + + for(param = icalproperty_get_first_parameter(prop,ICAL_ANY_PARAMETER); + param != 0; + param = icalproperty_get_next_parameter(prop,ICAL_ANY_PARAMETER)){ + + char *copy = icalparameter_as_ical_string_r(param); + char *v; + + if(copy == 0){ + icalerror_set_errno(ICAL_NEWFAILED_ERROR); + continue; + } + + v = strchr(copy,'='); + + + if(v == 0){ + free(copy); + continue; + } + + *v = 0; + + v++; + + APPENDS(", "); + APPENDC('\''); + APPENDS(copy); + APPENDC('\''); + APPENDS(sep); + APPENDC('\''); + APPENDS(v); + APPENDC('\''); + free(copy); + } + + + APPENDC('}'); + + return buf; + +} + +const char* icallangbind_property_eval_string(icalproperty* prop, char* sep) +{ + char *buf; + buf = icallangbind_property_eval_string_r(prop, sep); + icalmemory_add_tmp_buffer(buf); + return buf; +} + + +#include "fcntl.h" +int icallangbind_string_to_open_flag(const char* str) +{ + if (strcmp(str,"r") == 0) {return O_RDONLY;} + else if (strcmp(str,"r+") == 0) {return O_RDWR;} + else if (strcmp(str,"w") == 0) {return O_WRONLY;} + else if (strcmp(str,"w+") == 0) {return O_RDWR|O_CREAT;} + else if (strcmp(str,"a") == 0) {return O_WRONLY|O_APPEND;} + else return -1; +} + + +char* icallangbind_quote_as_ical_r(const char* str) +{ + size_t buf_size = 2 * strlen(str); + + /* assume every char could be quoted */ + char* buf = icalmemory_new_buffer(buf_size); + (void)icalvalue_encode_ical_string(str, buf, (int)buf_size); + + return buf; +} + + +const char* icallangbind_quote_as_ical(const char* str) +{ + char *buf; + buf = icallangbind_quote_as_ical_r(str); + icalmemory_add_tmp_buffer(buf); + return(buf); +} + diff --git a/src/libical/icallangbind.h b/src/libical/icallangbind.h new file mode 100644 index 00000000..ff5129e1 --- /dev/null +++ b/src/libical/icallangbind.h @@ -0,0 +1,56 @@ +/* -*- Mode: C -*- + ====================================================================== + FILE: icallangbind.h + CREATOR: eric 25 jan 2001 + + DESCRIPTION: + + $Id: icallangbind.h,v 1.8 2008-01-02 20:07:31 dothebart Exp $ + $Locker: $ + + (C) COPYRIGHT 1999 Eric Busboom + http://www.softwarestudio.org + + This package is free software and is provided "as is" without + express or implied warranty. It may be used, redistributed and/or + modified under the same terms as perl itself. ( Either the Artistic + License or the GPL. ) + + ======================================================================*/ + +#ifndef __ICALLANGBIND_H__ +#define __ICALLANGBIND_H__ + +int* icallangbind_new_array(int size); +void icallangbind_free_array(int* array); +int icallangbind_access_array(int* array, int index); +icalproperty* icallangbind_get_property(icalcomponent *c, int n, const char* prop); +const char* icallangbind_get_property_val(icalproperty* p); +const char* icallangbind_get_parameter(icalproperty *p, const char* parameter); +icalcomponent* icallangbind_get_component(icalcomponent *c, const char* comp); + +icalproperty* icallangbind_get_first_property(icalcomponent *c, + const char* prop); + +icalproperty* icallangbind_get_next_property(icalcomponent *c, + const char* prop); + +icalcomponent* icallangbind_get_first_component(icalcomponent *c, + const char* comp); + +icalcomponent* icallangbind_get_next_component(icalcomponent *c, + const char* comp); + +icalparameter* icallangbind_get_first_parameter(icalproperty *prop); + +icalparameter* icallangbind_get_next_parameter(icalproperty *prop); + +const char* icallangbind_property_eval_string(icalproperty* prop, char* sep); +char* icallangbind_property_eval_string_r(icalproperty* prop, char* sep); + + +int icallangbind_string_to_open_flag(const char* str); + +const char* icallangbind_quote_as_ical(const char* str); +char* icallangbind_quote_as_ical_r(const char* str); +#endif /*__ICALLANGBIND_H__*/ diff --git a/src/libical/icalmemory.c b/src/libical/icalmemory.c new file mode 100644 index 00000000..c5234e9d --- /dev/null +++ b/src/libical/icalmemory.c @@ -0,0 +1,375 @@ +/* -*- Mode: C -*- + ====================================================================== + FILE: icalmemory.c + CREATOR: eric 30 June 1999 + + $Id: icalmemory.c,v 1.12 2008-02-03 16:10:46 dothebart Exp $ + $Locker: $ + + 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); + +#ifndef HAVE_PTHREAD +static buffer_ring* global_buffer_ring = 0; +#endif + +#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); +} +#else +/* 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); +} +#endif + +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/src/libical/icalmemory.h b/src/libical/icalmemory.h new file mode 100644 index 00000000..4bd1d92e --- /dev/null +++ b/src/libical/icalmemory.h @@ -0,0 +1,85 @@ +/* -*- Mode: C -*- */ +/*====================================================================== + FILE: icalmemory.h + CREATOR: eric 30 June 1999 + + + $Id: icalmemory.h,v 1.6 2008-01-15 23:17:40 dothebart Exp $ + $Locker: $ + + 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/src/libical/icalmime.c b/src/libical/icalmime.c new file mode 100644 index 00000000..c902a59c --- /dev/null +++ b/src/libical/icalmime.c @@ -0,0 +1,405 @@ +/* -*- Mode: C -*-*/ +/*====================================================================== + FILE: icalmime.c + CREATOR: eric 26 July 2000 + + + $Id: icalmime.c,v 1.14 2008-01-29 22:25:05 dothebart Exp $ + $Locker: $ + + (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 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 + +#if defined(_MSC_VER) +#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(void) +{ + +#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_r(void* part) +{ + char *buf; + struct text_part* impl = ( struct text_part*) part; + + buf = impl->buf; + free(impl); + + return buf; +} + +void* icalmime_text_end_part(void* part) +{ + void *buf; + buf = icalmime_text_end_part_r(part); + icalmemory_add_tmp_buffer(buf); + return buf; +} + + +void icalmime_text_free_part(void *part) +{ + part = part; +} + + +/* Ignore Attachments for now */ + +void* icalmime_attachment_new_part(void) +{ + 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; +} + + + + +static const 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_r,icalmime_text_free_part}, + {SSPM_TEXT_MAJOR_TYPE,SSPM_PLAIN_MINOR_TYPE,icalmime_text_new_part,icalmime_text_add_line,icalmime_text_end_part_r,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(NUM_PARTS*sizeof(struct sspm_part))); + + 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_MALFORMED_HEADER_ERROR){ + str = "Malformed header, possibly due to input not in MIME format"; + } + + 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); + + } else if (parts[i].level < last_level){ + + if (parent) + parent = icalcomponent_get_parent(parent); + icalcomponent_add_component(parent,comp); + } 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(NUM_PARTS*sizeof(struct sspm_part))); + + 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 = + icalcomponent_as_ical_string_r((icalcomponent*)parts[i].data); + } + } + + sspm_write_mime(parts,NUM_PARTS,&out,"To: bob@bob.org"); + + printf("%s\n",out); + free(out); + + return 0; + +} + + diff --git a/src/libical/icalmime.h b/src/libical/icalmime.h new file mode 100644 index 00000000..012b14da --- /dev/null +++ b/src/libical/icalmime.h @@ -0,0 +1,44 @@ +/* -*- Mode: C -*- */ +/*====================================================================== + FILE: icalmime.h + CREATOR: eric 26 July 2000 + + + $Id: icalmime.h,v 1.3 2008-01-15 23:17:40 dothebart Exp $ + $Locker: $ + + (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/src/libical/icalparameter.c b/src/libical/icalparameter.c new file mode 100644 index 00000000..aa7520ad --- /dev/null +++ b/src/libical/icalparameter.c @@ -0,0 +1,445 @@ +/* -*- Mode: C -*- + ====================================================================== + FILE: icalderivedparameters.{c,h} + CREATOR: eric 09 May 1999 + + $Id: icalparameter.c,v 1.15 2008-01-15 23:17:40 dothebart Exp $ + $Locker: $ + + + (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 + + +#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() */ + +#if defined(_MSC_VER) +#define snprintf _snprintf +#define strcasecmp stricmp +#endif + +/* 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(icalparameter)); + + 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); + free(cpy); + return 0; + } + + *eq = '\0'; + + eq++; + + kind = icalparameter_string_to_kind(cpy); + + if(kind == ICAL_NO_PARAMETER){ + icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR); + free(cpy); + return 0; + } + + param = icalparameter_new_from_value_string(kind,eq); + + if(kind == ICAL_X_PARAMETER){ + icalparameter_set_xname(param,cpy); + } else if(kind == ICAL_IANA_PARAMETER) { + icalparameter_set_iana_name(param, cpy); + } + + free(cpy); + + return param; + +} + +char* +icalparameter_as_ical_string(icalparameter* param) +{ + char *buf; + buf = icalparameter_as_ical_string_r(param); + icalmemory_add_tmp_buffer(buf); + return buf; +} + + +/** + * 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_r(icalparameter* param) +{ + size_t buf_size = 1024; + char* buf; + char* buf_ptr; + 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. + */ + + 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 if (param->kind == ICAL_IANA_PARAMETER) { + icalmemory_append_string(&buf, &buf_ptr, &buf_size, + icalparameter_get_iana_name(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); + free(buf); + 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); + free(buf); + return 0; + } + + return 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_iana_value (icalparameter* param, const char* v) +{ + icalparameter_set_xvalue(param, v); +} + +const char* icalparameter_get_iana_value(icalparameter* param) +{ + return icalparameter_get_xvalue(param); +} + +void icalparameter_set_iana_name (icalparameter* param, const char* v) +{ + icalparameter_set_xname(param, v); +} + +const char* icalparameter_get_iana_name (icalparameter* param) +{ + return icalparameter_get_xname(param); +} + +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; +} + +/* returns 1 if parameters have same name in ICAL, otherwise 0 */ +int icalparameter_has_same_name(icalparameter* param1, icalparameter* param2) +{ + icalparameter_kind kind1; + icalparameter_kind kind2; + const char *name1; + const char *name2; + + icalerror_check_arg_rz( (param1!=0),"param1"); + icalerror_check_arg_rz( (param2!=0),"param2"); + + kind1 = icalparameter_isa(param1); + kind2 = icalparameter_isa(param2); + + if (kind1 != kind2) + return 0; + + if (kind1 == ICAL_X_PARAMETER) { + name1 = icalparameter_get_xname(param1); + name2 = icalparameter_get_xname(param2); + if (strcasecmp(name1, name2) != 0) + return 0; + } else if (kind1 == ICAL_IANA_PARAMETER) { + name1 = icalparameter_get_iana_name(param1); + name2 = icalparameter_get_iana_name(param2); + if (strcasecmp(name1, name2) != 0) + return 0; + } + return 1; +} + +/* Everything below this line is machine generated. Do not edit. */ +/* ALTREP */ diff --git a/src/libical/icalparameter.h b/src/libical/icalparameter.h new file mode 100644 index 00000000..cc159539 --- /dev/null +++ b/src/libical/icalparameter.h @@ -0,0 +1,80 @@ +/* -*- Mode: C -*- */ +/*====================================================================== + FILE: icalparam.h + CREATOR: eric 20 March 1999 + + + $Id: icalparameter.h,v 1.5 2008-01-15 23:17:40 dothebart Exp $ + $Locker: $ + + + + (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); +char* icalparameter_as_ical_string_r(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 parameter */ +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); + +/* Access the name of an IANA parameter */ +void icalparameter_set_iana_name (icalparameter* param, const char* v); +const char* icalparameter_get_iana_name(icalparameter* param); +void icalparameter_set_iana_value (icalparameter* param, const char* v); +const char* icalparameter_get_iana_value(icalparameter* param); + +/* returns 1 if parameters have same name in ICAL, otherwise 0 */ +int icalparameter_has_same_name(icalparameter* param1, icalparameter* param2); + +/* Convert enumerations */ + +const char* icalparameter_kind_to_string(icalparameter_kind kind); +icalparameter_kind icalparameter_string_to_kind(const char* string); + + + +#endif diff --git a/src/libical/icalparameter_cxx.cpp b/src/libical/icalparameter_cxx.cpp new file mode 100644 index 00000000..ae605023 --- /dev/null +++ b/src/libical/icalparameter_cxx.cpp @@ -0,0 +1,300 @@ +/* -*- Mode: C++ -*- */ + +/** + * @file icalparameter_cxx.cpp + * @author fnguyen (12/10/01) + * @brief Implementation of C++ Wrapper for icalparameter.c + * + * (C) COPYRIGHT 2001, Critical Path + + 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 ICALPARAMETER_CXX_H +#include "icalparameter_cxx.h" +#endif + +#ifndef ICALVALUE_CXX_H +#include "icalvalue_cxx.h" +#endif + +typedef char* string; // Will use the string library from STL + +ICalParameter::ICalParameter() throw(icalerrorenum) : imp(icalparameter_new(ICAL_ANY_PARAMETER)){ +} + +ICalParameter::ICalParameter(const ICalParameter& v) throw(icalerrorenum) { + imp = icalparameter_new_clone(v.imp); + if (!imp) throw icalerrno; +} + +ICalParameter& ICalParameter::operator=(const ICalParameter& v) throw(icalerrorenum) { + if (this == &v) return *this; + + if (imp != NULL) + { + icalparameter_free(imp); + imp = icalparameter_new_clone(v.imp); + if (!imp) throw icalerrno; + } + + return *this; +} + +ICalParameter::~ICalParameter(){ + if (imp != NULL) icalparameter_free(imp); +} + +ICalParameter::ICalParameter(icalparameter* v) throw(icalerrorenum) : imp(v){ +} + +/// Create from string of form "PARAMNAME=VALUE" +ICalParameter::ICalParameter(string str) throw(icalerrorenum) { + imp = icalparameter_new_from_string(str); + if (!imp) throw icalerrno; +} + +/// Create from just the value, the part after the "=" +ICalParameter::ICalParameter(icalparameter_kind kind, string str) throw(icalerrorenum) { + imp = icalparameter_new_from_value_string(kind, str); + if (!imp) throw icalerrno; +} + +ICalParameter::ICalParameter(icalparameter_kind kind) throw(icalerrorenum) { + imp = icalparameter_new(kind); + if (!imp) throw icalerrno; +} + +string ICalParameter::as_ical_string() throw(icalerrorenum) { + char *str = icalparameter_as_ical_string(imp); + + if (!str) throw icalerrno; + + return str; +} + +bool ICalParameter::is_valid(){ + if (imp == NULL) return false; + return (icalparameter_isa_parameter((void*)imp) ? true : false); +} +icalparameter_kind ICalParameter::isa(){ + return icalparameter_isa(imp); +} +int ICalParameter::isa_parameter(void* param){ + return icalparameter_isa_parameter(param); +} + +/* Acess the name of an X parameer */ +void ICalParameter::set_xname(ICalParameter ¶m, string v){ + icalparameter_set_xname(param, v); +} +string ICalParameter::get_xname(ICalParameter ¶m){ + return (string)icalparameter_get_xname(param); +} +void ICalParameter::set_xvalue (ICalParameter ¶m, string v){ + icalparameter_set_xvalue(param, v); +} +string ICalParameter::get_xvalue(ICalParameter ¶m){ + return (string)icalparameter_get_xvalue(param); +} + +/* Convert enumerations */ +string ICalParameter::kind_to_string(icalparameter_kind kind){ + return (string)icalparameter_kind_to_string(kind); +} +icalparameter_kind ICalParameter::string_to_kind(string str){ + return icalparameter_string_to_kind(str); +} + +/* DELEGATED-FROM */ +string ICalParameter::get_delegatedfrom(){ + return (string)icalparameter_get_delegatedfrom(imp); +} +void ICalParameter::set_delegatedfrom(string v){ + icalparameter_set_delegatedfrom(imp, v); +} + +/* RELATED */ +icalparameter_related ICalParameter::get_related(){ + return icalparameter_get_related(imp); +} +void ICalParameter::set_related(icalparameter_related v){ + icalparameter_set_related(imp, v); +} + +/* SENT-BY */ +string ICalParameter::get_sentby(){ + return (string)icalparameter_get_sentby(imp); +} +void ICalParameter::set_sentby(string v){ + icalparameter_set_sentby(imp, v); +} + +/* LANGUAGE */ +string ICalParameter::get_language(){ + return (string)icalparameter_get_language(imp); +} +void ICalParameter::set_language(string v){ + icalparameter_set_language(imp, v); +} + +/* RELTYPE */ +icalparameter_reltype ICalParameter::get_reltype(){ + return icalparameter_get_reltype(imp); +} +void ICalParameter::set_reltype(icalparameter_reltype v){ + icalparameter_set_reltype(imp, v); +} + +/* ENCODING */ +icalparameter_encoding ICalParameter::get_encoding(){ + return icalparameter_get_encoding(imp); +} +void ICalParameter::set_encoding(icalparameter_encoding v){ + icalparameter_set_encoding(imp, v); +} + +/* ALTREP */ +string ICalParameter::get_altrep(){ + return (string)icalparameter_get_language(imp); +} +void ICalParameter::set_altrep(string v){ + icalparameter_set_altrep(imp, v); +} + +/* FMTTYPE */ +string ICalParameter::get_fmttype(){ + return (string)icalparameter_get_fmttype(imp); +} +void ICalParameter::set_fmttype(string v){ + icalparameter_set_fmttype(imp, v); +} + +/* FBTYPE */ +icalparameter_fbtype ICalParameter::get_fbtype(){ + return icalparameter_get_fbtype(imp); +} +void ICalParameter::set_fbtype(icalparameter_fbtype v){ + icalparameter_set_fbtype(imp, v); +} + +/* RSVP */ +icalparameter_rsvp ICalParameter::get_rsvp(){ + return icalparameter_get_rsvp(imp); +} +void ICalParameter::set_rsvp(icalparameter_rsvp v){ + icalparameter_set_rsvp(imp, v); +} + +/* RANGE */ +icalparameter_range ICalParameter::get_range(){ + return icalparameter_get_range(imp); +} +void ICalParameter::set_range(icalparameter_range v){ + icalparameter_set_range(imp, v); +} + +/* DELEGATED-TO */ +string ICalParameter::get_delegatedto(){ + return (string)icalparameter_get_delegatedto(imp); +} +void ICalParameter::set_delegatedto(string v){ + icalparameter_set_delegatedto(imp, v); +} + +/* CN */ +string ICalParameter::get_cn(){ + return (string)icalparameter_get_cn(imp); +} +void ICalParameter::set_cn(string v){ + icalparameter_set_cn(imp, v); +} + +/* ROLE */ +icalparameter_role ICalParameter::get_role(){ + return icalparameter_get_role(imp); +} +void ICalParameter::set_role(icalparameter_role v){ + icalparameter_set_role(imp, v); +} + +/* X-LIC-COMPARETYPE */ +icalparameter_xliccomparetype ICalParameter::get_xliccomparetype(){ + return icalparameter_get_xliccomparetype(imp); +} +void ICalParameter::set_xliccomparetype(icalparameter_xliccomparetype v){ + icalparameter_set_xliccomparetype(imp, v); +} + +/* PARTSTAT */ +icalparameter_partstat ICalParameter::get_partstat(){ + return icalparameter_get_partstat(imp); +} +void ICalParameter::set_partstat(icalparameter_partstat v){ + icalparameter_set_partstat(imp, v); +} + +/* X-LIC-ERRORTYPE */ +icalparameter_xlicerrortype ICalParameter::get_xlicerrortype(){ + return icalparameter_get_xlicerrortype(imp); +} +void ICalParameter::set_xlicerrortype(icalparameter_xlicerrortype v){ + icalparameter_set_xlicerrortype(imp, v); +} + +/* MEMBER */ +string ICalParameter::get_member(){ + return (string)icalparameter_get_member(imp); +} +void ICalParameter::set_member(string v){ + icalparameter_set_member(imp, v); +} + +/* X */ +string ICalParameter::get_x(){ + return (string)icalparameter_get_x(imp); +} +void ICalParameter::set_x(string v){ + icalparameter_set_x(imp, v); +} + +/* CUTYPE */ +icalparameter_cutype ICalParameter::get_cutype(){ + return icalparameter_get_cutype(imp); +} +void ICalParameter::set_cutype(icalparameter_cutype v){ + icalparameter_set_cutype(imp, v); +} + +/* TZID */ +string ICalParameter::get_tzid(){ + return (string)icalparameter_get_tzid(imp); +} +void ICalParameter::set_tzid(string v){ + icalparameter_set_tzid(imp, v); +} + +/* VALUE */ +icalparameter_value ICalParameter::get_value(){ + return icalparameter_get_value(imp); +} +void ICalParameter::set_value(icalparameter_value v){ + icalparameter_set_value(imp, v); +} + +/* DIR */ +string ICalParameter::get_dir(){ + return (string)icalparameter_get_dir(imp); +} +void ICalParameter::set_dir(string v){ + icalparameter_set_dir(imp, v); +} diff --git a/src/libical/icalparameter_cxx.h b/src/libical/icalparameter_cxx.h new file mode 100644 index 00000000..ab3893bc --- /dev/null +++ b/src/libical/icalparameter_cxx.h @@ -0,0 +1,170 @@ +/* -*- Mode: C++ -*- */ + +/** + * @file icalparameter_cxx.h + * @author fnguyen (12/10/01) + * @brief Definition of C++ Wrapper for icalparameter.c + * + * (C) COPYRIGHT 2001, Critical Path + + 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 ICALPARAMETER_CXX_H +#define ICALPARAMETER_CXX_H + +extern "C" { +#include "ical.h" +}; + +#include "icptrholder.h" + +typedef char* string; // Will use the string library from STL + +class ICalParameter { +public: + ICalParameter() throw(icalerrorenum); + ICalParameter(const ICalParameter&) throw(icalerrorenum); + ICalParameter& operator=(const ICalParameter&) throw(icalerrorenum); + ~ICalParameter(); + + ICalParameter(icalparameter* v) throw(icalerrorenum); + + // Create from string of form "PARAMNAME=VALUE" + ICalParameter(string str) throw(icalerrorenum); + + // Create from just the value, the part after the "=" + ICalParameter(icalparameter_kind kind, string str) throw(icalerrorenum); + ICalParameter(icalparameter_kind kind) throw(icalerrorenum); + + operator icalparameter*() { return imp; } + + void detach() { + imp = NULL; + } + +public: + string as_ical_string() throw(icalerrorenum); + bool is_valid(); + icalparameter_kind isa( ); + int isa_parameter(void* param); + +public: + /* Acess the name of an X parameer */ + static void set_xname (ICalParameter ¶m, string v); + static string get_xname(ICalParameter ¶m); + static void set_xvalue (ICalParameter ¶m, string v); + static string get_xvalue(ICalParameter ¶m); + + /* Convert enumerations */ + static string kind_to_string(icalparameter_kind kind); + static icalparameter_kind string_to_kind(string str); + +public: + /* DELEGATED-FROM */ + string get_delegatedfrom(); + void set_delegatedfrom(string v); + + /* RELATED */ + icalparameter_related get_related(); + void set_related(icalparameter_related v); + + /* SENT-BY */ + string get_sentby(); + void set_sentby(string v); + + /* LANGUAGE */ + string get_language(); + void set_language(string v); + + /* RELTYPE */ + icalparameter_reltype get_reltype(); + void set_reltype(icalparameter_reltype v); + + /* ENCODING */ + icalparameter_encoding get_encoding(); + void set_encoding(icalparameter_encoding v); + + /* ALTREP */ + string get_altrep(); + void set_altrep(string v); + + /* FMTTYPE */ + string get_fmttype(); + void set_fmttype(string v); + + /* FBTYPE */ + icalparameter_fbtype get_fbtype(); + void set_fbtype(icalparameter_fbtype v); + + /* RSVP */ + icalparameter_rsvp get_rsvp(); + void set_rsvp(icalparameter_rsvp v); + + /* RANGE */ + icalparameter_range get_range(); + void set_range(icalparameter_range v); + + /* DELEGATED-TO */ + string get_delegatedto(); + void set_delegatedto(string v); + + /* CN */ + string get_cn(); + void set_cn(string v); + + /* ROLE */ + icalparameter_role get_role(); + void set_role(icalparameter_role v); + + /* X-LIC-COMPARETYPE */ + icalparameter_xliccomparetype get_xliccomparetype(); + void set_xliccomparetype(icalparameter_xliccomparetype v); + + /* PARTSTAT */ + icalparameter_partstat get_partstat(); + void set_partstat(icalparameter_partstat v); + + /* X-LIC-ERRORTYPE */ + icalparameter_xlicerrortype get_xlicerrortype(); + void set_xlicerrortype(icalparameter_xlicerrortype v); + + /* MEMBER */ + string get_member(); + void set_member(string v); + + /* X */ + string get_x(); + void set_x(string v); + + /* CUTYPE */ + icalparameter_cutype get_cutype(); + void set_cutype(icalparameter_cutype v); + + /* TZID */ + string get_tzid(); + void set_tzid(string v); + + /* VALUE */ + icalparameter_value get_value(); + void set_value(icalparameter_value v); + + /* DIR */ + string get_dir(); + void set_dir(string v); + +private: + icalparameter* imp; +}; + +#endif diff --git a/src/libical/icalparameterimpl.h b/src/libical/icalparameterimpl.h new file mode 100644 index 00000000..3a5d1f57 --- /dev/null +++ b/src/libical/icalparameterimpl.h @@ -0,0 +1,53 @@ +/* -*- Mode: C -*- + ====================================================================== + FILE: icalparameterimpl.h + CREATOR: eric 09 May 1999 + + $Id: icalparameterimpl.h,v 1.4 2008-01-15 23:17:40 dothebart Exp $ + $Locker: $ + + + (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/src/libical/icalparser.c b/src/libical/icalparser.c new file mode 100644 index 00000000..43ea9035 --- /dev/null +++ b/src/libical/icalparser.c @@ -0,0 +1,1331 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 8; -*- + ====================================================================== + FILE: icalparser.c + CREATOR: eric 04 August 1999 + + $Id: icalparser.c,v 1.53 2008-01-15 23:17:40 dothebart Exp $ + $Locker: $ + + 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 +# if !defined HAVE_ISWSPACE && !defined WIN32 +# define iswspace isspace +# endif +#endif + +#if defined(_MSC_VER) +#define snprintf _snprintf +#define strcasecmp stricmp +#endif + +static char* parser_get_next_char(char c, char *str, int qm); +static char* parser_get_next_parameter(char* line,char** end); +static char* parser_get_next_value(char* line, char **end, icalvalue_kind kind); +static char* parser_get_prop_name(char* line, char** end); +static char* parser_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; + +}; + + +/* + * New version of strstrip() that does not move the pointer. + */ +void strstriplt(char *buf) +{ + size_t len; + int a; + + if (buf==NULL) { + return; + } + if (buf[0] == 0) { + return; + } + len = strlen(buf); + while ((buf[0] != 0) && (isspace((unsigned char)buf[len - 1]))) { + buf[--len] = 0; + } + if (buf[0] == 0) { + return; + } + a = 0; + while ((buf[0]!=0) && (isspace((unsigned char)buf[a]))) { + a++; + } + if (a > 0) { + memmove(buf, &buf[a], len - a + 1); + } +} + + + +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); + + +static +char* parser_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>str && *(p-1) != '\\' ){ + quote_mode =1; + continue; + } + + if ( quote_mode == 1 && *p=='"' && p>str && *(p-1) != '\\' ){ + quote_mode =0; + continue; + } + } + + if (quote_mode == 0 && *p== c && p>str && *(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_new_buffer(size+1); + strncpy(buf,start,size); + *(buf+size) = 0; + + tmp = (buf+size); + while ((tmp >= buf) && + ((*tmp == '\0') || iswspace(*tmp))) + { + *tmp = 0; + tmp--; + } + + return buf; +} + +static +char* parser_get_prop_name(char* line, char** end) +{ + char* p; + char* v; + char *str; + + p = parser_get_next_char(';',line,1); + v = parser_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; +} + +static +char* parser_get_param_name(char* line, char **end) +{ + char* next; + char *str; + + next = parser_get_next_char('=',line,1); + + if (next == 0) { + return 0; + } + + str = make_segment(line,next); + *end = next+1; + if (**end == '"') { + *end = *end+1; + next = parser_get_next_char('"',*end,0); + if (next == 0) { + free(str); + return 0; + } + + *end = make_segment(*end,next); + } else { + *end = make_segment(*end, *end + strlen(*end)); + } + + return str; +} + +#if 0 +static +char* parser_get_next_paramvalue(char* line, char **end) +{ + char* next; + char *str; + + next = parser_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; + } +} +#endif + +char* icalparser_get_value(char* line, char **end, icalvalue_kind kind) +{ + char *str; + size_t length = strlen(line); + + if (length == 0){ + return 0; + } + + *end = line+length; + str = make_segment(line, *end); + + return str; +} + +/** + A property may have multiple values, if the values are seperated 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. */ + +static +char* parser_get_next_value(char* line, char **end, icalvalue_kind kind) +{ + + char* next = 0; + char *p; + char *str; + size_t length = strlen(line); + int quoted = 0; + + if( line[0] == '\"' && line[length - 1] == '\"' ) { + /* This line is quoted, don't split into multiple values */ + quoted = 1; + } + + p = line; + while(!quoted){ + + next = parser_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; + +} + +static +char* parser_get_next_parameter(char* line,char** end) +{ + char *next; + char *v; + char *str; + + v = parser_get_next_char(':',line,1); + next = parser_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 = parser_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 ' ' or tab, then the buffer holds a continuation + line, so keep reading. RFC 2445, section 4.1 */ + + 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){ + icalmemory_free_buffer(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 = parser_get_prop_name(line, &end); + + if (str == 0 || *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; + icalmemory_free_buffer(str); + str = NULL; + 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; + + icalmemory_free_buffer(str); + str = NULL; + + parser->level++; + str = parser_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; + + icalmemory_free_buffer(str); + str = NULL; + return 0; + + } else if (strcasecmp(str,"END") == 0 ) { + icalcomponent* tail; + + icalmemory_free_buffer(str); + str = NULL; + parser->level--; + + str = parser_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; + icalmemory_free_buffer(str); + str = NULL; + + /* 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; + icalmemory_free_buffer(str); + str = NULL; + 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; + icalmemory_free_buffer(str); + str = NULL; + return 0; + } + + icalmemory_free_buffer(str); + str = NULL; + + /********************************************************************** + * 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 = parser_get_next_parameter(end,&end); + strstriplt(str); + if (str != 0){ + char* name = 0; + char* pvalue = 0; + + icalparameter *param = 0; + icalparameter_kind kind; + icalcomponent *tail = pvl_data(pvl_tail(parser->components)); + + name = parser_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_IANA_PARAMETER){ + ical_unknown_token_handling tokHandlingSetting = + ical_get_unknown_token_handling_setting(); + if (tokHandlingSetting == ICAL_DISCARD_TOKEN) + continue; + param = icalparameter_new(ICAL_IANA_PARAMETER); + + if(param != 0){ + icalparameter_set_xname(param,name); + icalparameter_set_xvalue(param,pvalue); + } + } else if (kind == ICAL_TZID_PARAMETER){ + /* + Special case handling for TZID to workaround invalid incoming data. + For example, Google Calendar will send back stuff like this: + DTSTART;TZID=GMT+05:30:20120904T020000 + + In this case we read to the last colon rather than the first colon. + This way the TZID will become GMT+05:30 rather than trying to parse + the date-time as 30:20120904T020000. + */ + char *lastColon = 0; + char *nextColon = end; + + /* Find the last colon in the line */ + do { + nextColon = parser_get_next_char(':', nextColon, 1); + + if (nextColon) { + lastColon = nextColon; + } + } while (nextColon); + + /* + Rebuild str so that it includes everything up to the last colon. + So given the above example, str will go from + "TZID=GMT+05" to "TZID=GMT+05:30" + */ + if (lastColon && *(lastColon + 1) != 0) { + char *strStart = line + sizeof(str); + + end = lastColon + 1; + + icalmemory_free_buffer(str); + str = make_segment(strStart, end - 1); + } + + icalmemory_free_buffer(name); + icalmemory_free_buffer(pvalue); + + /* Reparse the parameter name and value with the new segment */ + name = parser_get_param_name(str,&pvalue); + param = icalparameter_new_from_value_string(kind,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 */ + + /* Change for mozilla */ + /* have the option of being flexible towards unsupported parameters */ +#if ICAL_ERRORS_ARE_FATAL == 1 + insert_error(tail, str, "Cant parse parameter name", + ICAL_XLICERRORTYPE_PARAMETERNAMEPARSEERROR); + tail = 0; + parser->state = ICALPARSER_ERROR; + if (pvalue) { + free(pvalue); + pvalue = 0; + } + if (name) { + free(name); + name = 0; + } + icalmemory_free_buffer(str); + str = NULL; + return 0; +#else + if (name) { + free(name); + name = 0; + } + icalmemory_free_buffer(str); + str = NULL; + continue; +#endif + } + + if (pvalue) { + free(pvalue); + pvalue = 0; + } + + if (name) { + free(name); + name = 0; + } + + if (param == 0){ + /* 'tail' defined above */ + insert_error(tail,str,"Cant parse parameter value", + ICAL_XLICERRORTYPE_PARAMETERVALUEPARSEERROR); + + tail = 0; + parser->state = ICALPARSER_ERROR; + + icalmemory_free_buffer(name); + name = NULL; + icalmemory_free_buffer(str); + str = NULL; + + 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); + + value_kind = + icalproperty_kind_to_value_kind( + icalproperty_isa(prop)); + + icalparameter_free(param); + tail = 0; + parser->state = ICALPARSER_ERROR; + + icalmemory_free_buffer(name); + name = NULL; + icalmemory_free_buffer(str); + str = NULL; + continue; + } + } + icalmemory_free_buffer(name); + name = NULL; + + /* Everything is OK, so add the parameter */ + icalproperty_add_parameter(prop,param); + tail = 0; + icalmemory_free_buffer(str); + str = NULL; + + } else { /* if ( str != 0) */ + /* If we did not get a param string, go on to looking for a value */ + if (str != NULL) { + icalmemory_free_buffer(str); + str = NULL; + } + 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) { + /* Only some properies can have multiple values. This list was taken + from rfc2445. Also added the x-properties, because the spec actually + says that commas should be escaped. For x-properties, other apps may + depend on that behaviour + */ + switch (prop_kind) { + case ICAL_X_PROPERTY: + case ICAL_CATEGORIES_PROPERTY: + case ICAL_RESOURCES_PROPERTY: + /* Referring to RFC 2445, section 4.8.5.3 and section 4.8.5.1: + RDATE and EXDATE can specify a list of dates/date-times/periods. + */ + case ICAL_RDATE_PROPERTY: + case ICAL_EXDATE_PROPERTY: + /* Referring to RFC 2445, section 4.8.2.6 Free/Busy Time: + The "FREEBUSY" property can specify more than one value, separated by + the COMMA character (US-ASCII decimal 44). + */ + case ICAL_FREEBUSY_PROPERTY: + str = parser_get_next_value(end,&end, value_kind); + strstriplt (str); + break; + default: + str = icalparser_get_value(end, &end, value_kind); + strstriplt (str); + break; + } + + 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; + } + + /* If this is a URI value for an ATTACH property, then change + the value to an ATTACH kind as well. + Now we can support ATTACH;TYPE=URI:http://my.fav.url + */ + if(value_kind == ICAL_URI_VALUE && prop_kind == ICAL_ATTACH_PROPERTY){ + value_kind = ICAL_ATTACH_VALUE; + } + 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; + + icalmemory_free_buffer(str); + str = NULL; + return 0; + + } else { + vcount++; + icalproperty_set_value(prop, value); + } + icalmemory_free_buffer(str); + str = NULL; + + } else { +#if ICAL_ALLOW_EMPTY_PROPERTIES + /* Don't replace empty properties with an error. + Set an empty length string (not null) as the value instead */ + if (vcount == 0) { + icalproperty_set_value(prop, icalvalue_new_text("")); + } + + break; +#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; + } +#endif + } + } + + /**************************************************************** + * 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/src/libical/icalparser.h b/src/libical/icalparser.h new file mode 100644 index 00000000..e1a02405 --- /dev/null +++ b/src/libical/icalparser.h @@ -0,0 +1,97 @@ +/* -*- Mode: C -*- */ +/*====================================================================== + FILE: icalparser.h + CREATOR: eric 20 April 1999 + + $Id: icalparser.h,v 1.9 2008-01-15 23:17:41 dothebart Exp $ + + + (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/src/libical/icalperiod.c b/src/libical/icalperiod.c new file mode 100644 index 00000000..52be357d --- /dev/null +++ b/src/libical/icalperiod.c @@ -0,0 +1,179 @@ +/* -*- Mode: C -*- + ====================================================================== + FILE: icalperiod.c + CREATOR: eric 02 June 2000 + + $Id: icalperiod.c,v 1.13 2008-01-15 23:17:41 dothebart Exp $ + $Locker: $ + + (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; + 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) +{ + char *buf; + buf = icalperiodtype_as_ical_string_r(p); + icalmemory_add_tmp_buffer(buf); + return buf; +} + + +char* icalperiodtype_as_ical_string_r(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_r(p.start); + icalmemory_append_string(&buf, &buf_ptr, &buf_size, start); + icalmemory_free_buffer((void*)start); + + if(!icaltime_is_null_time(p.end)){ + end = icaltime_as_ical_string_r(p.end); + } else { + end = icaldurationtype_as_ical_string_r(p.duration); + } + + icalmemory_append_char(&buf, &buf_ptr, &buf_size, '/'); + + icalmemory_append_string(&buf, &buf_ptr, &buf_size, end); + icalmemory_free_buffer((void*)end); + + 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/src/libical/icalperiod.h b/src/libical/icalperiod.h new file mode 100644 index 00000000..3a541f59 --- /dev/null +++ b/src/libical/icalperiod.h @@ -0,0 +1,57 @@ +/* -*- Mode: C -*- */ +/*====================================================================== + FILE: icalperiod.h + CREATOR: eric 26 Jan 2001 + + + $Id: icalperiod.h,v 1.6 2008-01-15 23:17:41 dothebart Exp $ + $Locker: $ + + (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); +char* icalperiodtype_as_ical_string_r(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/src/libical/icalproperty.c b/src/libical/icalproperty.c new file mode 100644 index 00000000..df71fac6 --- /dev/null +++ b/src/libical/icalproperty.c @@ -0,0 +1,1063 @@ +/* -*- Mode: C -*- */ + +/*====================================================================== + FILE: icalproperty.c + CREATOR: eric 28 April 1999 + + $Id: icalproperty.c,v 1.44 2008-01-30 20:28:42 dothebart Exp $ + + + (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. */ + +#if defined(_MSC_VER) +#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; + char* buf_ptr; + icalproperty *prop; + icalcomponent *comp; + int errors = 0; + + icalerror_check_arg_rz( (str!=0),"str"); + + buf = icalmemory_new_buffer(buf_size); + buf_ptr = buf; + + /* Is this a HACK or a crafty reuse of code? */ + + icalmemory_append_string(&buf, &buf_ptr, &buf_size, "BEGIN:VCALENDAR\r\n"); + icalmemory_append_string(&buf, &buf_ptr, &buf_size, str); + icalmemory_append_string(&buf, &buf_ptr, &buf_size, "\r\n"); + icalmemory_append_string(&buf, &buf_ptr, &buf_size, "END:VCALENDAR\r\n"); + + comp = icalparser_parse_string(buf); + + if(comp == 0){ + icalerror_set_errno(ICAL_PARSE_ERROR); + free(buf); + 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, size_t 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, len, chars_left; + char *buf, *buf_ptr, *line_start, *next_line_start; + int 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; + } + + return 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) +{ + char *buf; + buf = icalproperty_as_ical_string_r(prop); + icalmemory_add_tmp_buffer(buf); + return buf; +} + + +char* +icalproperty_as_ical_string_r(icalproperty* prop) +{ + icalparameter *param; + + /* Create new buffer that we can append names, parameters and a + * value to, and reallocate as needed. + */ + + const char* property_name = 0; + size_t buf_size = 1024; + char* buf; + char* buf_ptr; + icalvalue* value; + char *out_buf; + const char* kind_string = 0; + const char newline[] = "\r\n"; + + + icalerror_check_arg_rz( (prop!=0),"prop"); + + buf = icalmemory_new_buffer(buf_size); + buf_ptr = buf; + + /* 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_r(param); + + if (kind_string == 0 ) { + icalerror_warn("Got a parameter of unknown kind for the following property"); + + icalerror_warn((property_name) ? property_name : "(NULL)"); + continue; + } + + if (kind==ICAL_VALUE_PARAMETER) { + free ((char *) kind_string); + continue; + } + + icalmemory_append_string(&buf, &buf_ptr, &buf_size, ";"); + icalmemory_append_string(&buf, &buf_ptr, &buf_size, kind_string); + free((char *)kind_string); + } + + /* Append value */ + + icalmemory_append_string(&buf, &buf_ptr, &buf_size, ":"); + + value = icalproperty_get_value(prop); + + if (value != 0){ + char *str = icalvalue_as_ical_string_r(value); + if (str != 0) + icalmemory_append_string(&buf, &buf_ptr, &buf_size, str); +#if ICAL_ALLOW_EMPTY_PROPERTIES == 0 + else + icalmemory_append_string(&buf, &buf_ptr, &buf_size,"ERROR: No Value"); +#endif + free(str); + } else { +#if ICAL_ALLOW_EMPTY_PROPERTIES == 0 + icalmemory_append_string(&buf, &buf_ptr, &buf_size,"ERROR: No Value"); +#endif + } + + icalmemory_append_string(&buf, &buf_ptr, &buf_size, newline); + + /* We now use a function to fold the line properly every 75 characters. + That function also adds the newline for us. */ + 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_name(prop, + icalparameter_get_xname(parameter)); + } else if (kind == ICAL_IANA_PARAMETER) { + icalproperty_remove_parameter_by_name(prop, + icalparameter_get_iana_name(parameter)); + } + else + icalproperty_remove_parameter_by_kind(prop,kind); + + 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); + } else if (kind == ICAL_IANA_PARAMETER) { + icalparameter_set_iana_name(param, name); + } + + icalproperty_set_parameter(prop,param); + +} + +const char* icalproperty_get_parameter_as_string(icalproperty* prop, + const char* name) +{ + char *buf; + buf = icalproperty_get_parameter_as_string_r(prop, name); + icalmemory_add_tmp_buffer(buf); + return buf; +} + + +char* icalproperty_get_parameter_as_string_r(icalproperty* prop, + const char* name) +{ + icalparameter_kind kind; + icalparameter *param; + char* str; + char *pv, *t; + char* pvql; + char* pvqr; + + 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) { + if (strcmp(icalparameter_get_xname(param),name)==0) { + break; + } + } else if (kind == ICAL_IANA_PARAMETER) { + if (strcmp(icalparameter_get_iana_name(param),name)==0) { + break; + } + } else { + break; + } + + } + + if (param == 0){ + return 0; + } + + + str = icalparameter_as_ical_string_r(param); + + t = strchr(str,'='); + + if (t == 0) { + icalerror_set_errno(ICAL_INTERNAL_ERROR); + free(str); + return 0; + } + + /* Strip the property name and the equal sign */ + pv = icalmemory_strdup(t+1); + free(str); + + /* Is the string quoted? */ + pvql = strchr(pv, '"'); + if (pvql == 0) { + return(pv); /* No quotes? Return it immediately. */ + } + + /* Strip everything up to the first quote */ + str = icalmemory_strdup(pvql+1); + free(pv); + + /* Search for the end quote */ + pvqr = strrchr(str, '"'); + if (pvqr == 0) { + icalerror_set_errno(ICAL_INTERNAL_ERROR); + free(str); + return 0; + } + + *pvqr = '\0'; + return str; +} + +/** @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 if (icalparameter_isa(param) == ICAL_IANA_PARAMETER) + kind_string = icalparameter_get_iana_name(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); + icalparameter_free(param); + 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; + + icalerror_check_arg_rv((prop!=0),"prop"); + icalerror_check_arg_rv((parameter!=0),"parameter"); + + for (p=pvl_head(prop->parameters);p != 0; p = pvl_next(p)) { + icalparameter* p_param = (icalparameter *)pvl_data (p); + + if (icalparameter_has_same_name(parameter, p_param)) { + 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; + } + + icalerror_clear_errno(); + 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) +{ + char *buf; + buf = icalproperty_get_value_as_string_r(prop); + icalmemory_add_tmp_buffer(buf); + return buf; +} + + +char* icalproperty_get_value_as_string_r(const icalproperty* prop) +{ + icalvalue *value; + + icalerror_check_arg_rz( (prop!=0),"prop"); + + value = prop->value; + + return icalvalue_as_ical_string_r(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_property_name(const icalproperty* prop) +{ + char *buf; + buf = icalproperty_get_property_name_r(prop); + icalmemory_add_tmp_buffer(buf); + return buf; +} + + +char* icalproperty_get_property_name_r(const icalproperty* prop) +{ + + const char* property_name = 0; + size_t buf_size = 256; + char* buf; + char* buf_ptr; + + icalerror_check_arg_rz( (prop!=0),"prop"); + + buf = icalmemory_new_buffer(buf_size); + buf_ptr = buf; + + 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); + icalmemory_free_buffer(buf); + 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); + } + + 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/src/libical/icalproperty.h b/src/libical/icalproperty.h new file mode 100644 index 00000000..d47835a0 --- /dev/null +++ b/src/libical/icalproperty.h @@ -0,0 +1,141 @@ +/* -*- Mode: C -*- */ +/*====================================================================== + FILE: icalproperty.h + CREATOR: eric 20 March 1999 + + + $Id: icalproperty.h,v 1.20 2008-01-15 23:17:41 dothebart Exp $ + $Locker: $ + + + + (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); +char* icalproperty_as_ical_string_r(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); +char* icalproperty_get_parameter_as_string_r(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); +char* icalproperty_get_value_as_string_r(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); +char* icalproperty_get_property_name_r(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); +char* icalproperty_enum_to_string_r(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/src/libical/icalproperty_cxx.cpp b/src/libical/icalproperty_cxx.cpp new file mode 100644 index 00000000..404dfa8d --- /dev/null +++ b/src/libical/icalproperty_cxx.cpp @@ -0,0 +1,715 @@ +/* -*- Mode: C++ -*- */ + +/** + * @file icalproperty_cxx.cpp + * @author fnguyen (12/10/01) + * @brief Implementation of C++ Wrapper for icalproperty.c + * + * (C) COPYRIGHT 2001, Critical Path + + 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 ICALPROPERTY_CXX_H +#include "icalproperty_cxx.h" +#endif + +#ifndef ICALPARAMETER_CXX_H +#include "icalparameter_cxx.h" +#endif + +#ifndef ICALVALUE_CXX_H +#include "icalvalue_cxx.h" +#endif + +ICalProperty::ICalProperty() : imp(icalproperty_new(ICAL_ANY_PROPERTY)){ +} + +ICalProperty::ICalProperty(const ICalProperty& v) throw(icalerrorenum){ + imp = icalproperty_new_clone(v.imp); + if (!imp) throw icalerrno; +} + +ICalProperty& ICalProperty::operator=(const ICalProperty& v) throw(icalerrorenum) { + if (this == &v) return *this; + + if (imp != NULL) + { + icalproperty_free(imp); + imp = icalproperty_new_clone(v.imp); + if (!imp) throw icalerrno; + } + + return *this; +} +ICalProperty::~ICalProperty(){ + if (imp != NULL) icalproperty_free(imp); +} + +ICalProperty::ICalProperty(icalproperty* v) : imp(v) { +} + +ICalProperty::ICalProperty(string str){ + imp = icalproperty_new_from_string(str); +} + +ICalProperty::ICalProperty(icalproperty_kind kind){ + imp = icalproperty_new(kind); +} + +string ICalProperty::as_ical_string(){ + return (string)icalproperty_as_ical_string(imp); +} +icalproperty_kind ICalProperty::isa(){ + return icalproperty_isa(imp); +} +int ICalProperty::isa_property(void* property){ + return icalproperty_isa_property(property); +} + +int ICalProperty::operator==(ICalProperty& rhs){ + icalparameter_xliccomparetype result; + ICalValue* thisPropValue = this->get_value(); + ICalValue* rhsPropValue = rhs.get_value(); + result = icalvalue_compare((icalvalue*)*thisPropValue, (icalvalue*)*rhsPropValue); + return (result == ICAL_XLICCOMPARETYPE_EQUAL); +} + +void ICalProperty::add_parameter(ICalParameter& parameter){ + icalproperty_add_parameter(imp, parameter); +} +void ICalProperty::set_parameter(ICalParameter ¶meter){ + icalproperty_set_parameter(imp, parameter); +} +void ICalProperty::set_parameter_from_string(string name, string val){ + icalproperty_set_parameter_from_string(imp, name, val); +} +string ICalProperty::get_parameter_as_string(string name){ + return (string)icalproperty_get_parameter_as_string(imp, name); +} +void ICalProperty::remove_parameter(icalparameter_kind kind){ + icalproperty_remove_parameter(imp, kind); +} +int ICalProperty::count_parameters(){ + return icalproperty_count_parameters(imp); +} + +/** Iterate through the parameters */ +ICalParameter* ICalProperty::get_first_parameter(icalparameter_kind kind){ + icalparameter* p = icalproperty_get_first_parameter(imp, kind); + return (p != NULL)? new ICalParameter(p): NULL; +} +ICalParameter* ICalProperty::get_next_parameter(icalparameter_kind kind){ + icalparameter* p = icalproperty_get_next_parameter(imp, kind); + return (p != NULL)? new ICalParameter(p): NULL; +} + +/** Access the value of the property */ +void ICalProperty::set_value(const ICalValue& val){ + icalproperty_set_value(imp, (ICalValue&)val); +} +void ICalProperty::set_value_from_string(string val, string kind){ + icalproperty_set_value_from_string(imp, val, kind); +} + +ICalValue* ICalProperty::get_value(){ + return new ICalValue(icalproperty_get_value(imp)); +} +string ICalProperty::get_value_as_string(){ + return (string)icalproperty_get_value_as_string(imp); +} + +/** Return the name of the property -- the type name converted to a + * string, or the value of get_x_name if the type is X property + */ +string ICalProperty::get_name(){ + return (string)icalproperty_get_property_name(imp); +} + +/* Deal with X properties */ +void ICalProperty::set_x_name(ICalProperty &prop, string name){ + icalproperty_set_x_name(prop, name); +} +string ICalProperty::get_x_name(ICalProperty &prop){ + return (string)icalproperty_get_x_name(prop); +} + +icalvalue_kind ICalProperty::icalparameter_value_to_value_kind(icalparameter_value val){ + return icalparameter_value_to_value_kind(val); +} + +/* Convert kinds to string and get default value type */ +icalvalue_kind ICalProperty::kind_to_value_kind(icalproperty_kind kind){ + return icalproperty_kind_to_value_kind(kind); +} +icalproperty_kind ICalProperty::value_kind_to_kind(icalvalue_kind kind){ + return icalproperty_value_kind_to_kind(kind); +} +string ICalProperty::kind_to_string(icalproperty_kind kind){ + return (string)icalproperty_kind_to_string(kind); +} +icalproperty_kind ICalProperty::string_to_kind(string str){ + return icalproperty_string_to_kind(str); +} + +string ICalProperty::method_to_string(icalproperty_method method){ + return (string)icalproperty_method_to_string(method); +} +icalproperty_method ICalProperty::string_to_method(string str){ + return icalproperty_string_to_method(str); +} + +string ICalProperty::enum_to_string(int e){ + return (string)icalproperty_enum_to_string(e); +} +int ICalProperty::string_to_enum(string str){ + return icalproperty_string_to_enum(str); +} + +string ICalProperty::status_to_string(icalproperty_status s){ + return (string)icalproperty_status_to_string(s); +} +icalproperty_status ICalProperty::string_to_status(string str){ + return icalproperty_string_to_status(str); +} + +int ICalProperty::enum_belongs_to_property(icalproperty_kind kind, int e){ + return icalproperty_enum_belongs_to_property(kind, e); +} + +/* ACTION */ +void ICalProperty::set_action(enum icalproperty_action val){ + icalproperty_set_action(imp, val); +} +enum icalproperty_action ICalProperty::get_action(){ + return icalproperty_get_action(imp); +} + +/* ATTACH */ +void ICalProperty::set_attach(icalattach *val){ + icalproperty_set_attach(imp, val); +} +icalattach * ICalProperty::get_attach(){ + return icalproperty_get_attach(imp); +} + +/* ATTENDEE */ +void ICalProperty::set_attendee(string val){ + icalproperty_set_attendee(imp, val); +} +string ICalProperty::get_attendee(){ + return (string)icalproperty_get_attendee(imp); +} + +/* CALSCALE */ +void ICalProperty::set_calscale(string val){ + icalproperty_set_calscale(imp, val); +} +string ICalProperty::get_calscale(){ + return (string)icalproperty_get_calscale(imp); +} + +/* CATEGORIES */ +void ICalProperty::set_categories(string val){ + icalproperty_set_categories(imp, val); +} +string ICalProperty::get_categories(){ + return (string)icalproperty_get_categories(imp); +} + +/* CLASS */ +void ICalProperty::set_class(enum icalproperty_class val){ + icalproperty_set_class(imp, val); +} +enum icalproperty_class ICalProperty::get_class(){ + return (enum icalproperty_class)icalproperty_get_class(imp); +} + +/* COMMENT */ +void ICalProperty::set_comment(string val){ + icalproperty_set_comment(imp, val); +} +string ICalProperty::get_comment(){ + return (string)icalproperty_get_comment(imp); +} + +/* COMPLETED */ +void ICalProperty::set_completed(struct icaltimetype val){ + icalproperty_set_completed(imp, val); +} +struct icaltimetype ICalProperty::get_completed(){ + return icalproperty_get_completed(imp); +} + +/* CONTACT */ +void ICalProperty::set_contact(string val){ + icalproperty_set_contact(imp, val); +} +string ICalProperty::get_contact(){ + return (string)icalproperty_get_contact(imp); +} + +/* CREATED */ +void ICalProperty::set_created(struct icaltimetype val){ + icalproperty_set_created(imp, val); +} +struct icaltimetype ICalProperty::get_created(){ + return icalproperty_get_created(imp); +} + +/* DESCRIPTION */ +void ICalProperty::set_description(string val){ + icalproperty_set_description(imp, val); +} +string ICalProperty::get_description(){ + return (string)icalproperty_get_description(imp); +} + +/* DTEND */ +void ICalProperty::set_dtend(struct icaltimetype val){ + icalproperty_set_dtend(imp, val); +} +struct icaltimetype ICalProperty::get_dtend(){ + return icalproperty_get_dtend(imp); +} + +/* DTSTAMP */ +void ICalProperty::set_dtstamp(struct icaltimetype val){ + icalproperty_set_dtstamp(imp, val); +} +struct icaltimetype ICalProperty::get_dtstamp(){ + return icalproperty_get_dtstamp(imp); +} + +/* DTSTART */ +void ICalProperty::set_dtstart(struct icaltimetype val){ + icalproperty_set_dtstart(imp, val); +} +struct icaltimetype ICalProperty::get_dtstart(){ + return icalproperty_get_dtstart(imp); +} + +/* DUE */ +void ICalProperty::set_due(struct icaltimetype val){ + icalproperty_set_due(imp, val); +} +struct icaltimetype ICalProperty::get_due(){ + return icalproperty_get_due(imp); +} + +/* DURATION */ +void ICalProperty::set_duration(struct icaldurationtype val){ + icalproperty_set_duration(imp, val); +} +struct icaldurationtype ICalProperty::get_duration(){ + return icalproperty_get_duration(imp); +} + +/* EXDATE */ +void ICalProperty::set_exdate(struct icaltimetype val){ + icalproperty_set_exdate(imp, val); +} +struct icaltimetype ICalProperty::get_exdate(){ + return icalproperty_get_exdate(imp); +} + +/* EXPAND */ +void ICalProperty::set_expand(int val){ + icalproperty_set_expand(imp, val); +} +int ICalProperty::get_expand(){ + return icalproperty_get_expand(imp); +} + +/* EXRULE */ +void ICalProperty::set_exrule(struct icalrecurrencetype val){ + icalproperty_set_exrule(imp, val); +} +struct icalrecurrencetype ICalProperty::get_exrule(){ + return icalproperty_get_exrule(imp); +} + +/* FREEBUSY */ +void ICalProperty::set_freebusy(struct icalperiodtype val){ + icalproperty_set_freebusy(imp, val); +} +struct icalperiodtype ICalProperty::get_freebusy(){ + return icalproperty_get_freebusy(imp); +} + +/* GEO */ +void ICalProperty::set_geo(struct icalgeotype val){ + icalproperty_set_geo(imp, val); +} +struct icalgeotype ICalProperty::get_geo(){ + return icalproperty_get_geo(imp); +} + +/* LAST-MODIFIED */ +void ICalProperty::set_lastmodified(struct icaltimetype val){ + icalproperty_set_lastmodified(imp, val); +} +struct icaltimetype ICalProperty::get_lastmodified(){ + return icalproperty_get_lastmodified(imp); +} + +/* LOCATION */ +void ICalProperty::set_location(string val){ + icalproperty_set_location(imp, val); +} +string ICalProperty::get_location(){ + return (string)icalproperty_get_location(imp); +} + +/* MAXRESULTS */ +void ICalProperty::set_maxresults(int val){ + icalproperty_set_maxresults(imp, val); +} +int ICalProperty::get_maxresults(){ + return icalproperty_get_maxresults(imp); +} + +/* MAXRESULTSSIZE */ +void ICalProperty::set_maxresultsize(int val){ + icalproperty_set_maxresultssize(imp, val); +} +int ICalProperty::get_maxresultsize(){ + return icalproperty_get_maxresultssize(imp); +} + +/* METHOD */ +void ICalProperty::set_method(enum icalproperty_method val){ + icalproperty_set_method(imp, val); +} +enum icalproperty_method ICalProperty::get_method(){ + return icalproperty_get_method(imp); +} + +/* ORGANIZER */ +void ICalProperty::set_organizer(string val){ + icalproperty_set_organizer(imp, val); +} +string ICalProperty::get_organizer(){ + return (string)icalproperty_get_organizer(imp); +} + +/* OWNER */ +void ICalProperty::set_owner(string val){ + icalproperty_set_owner(imp, val); +} +string ICalProperty::get_owner(){ + return (string)icalproperty_get_owner(imp); +} + +/* PERCENT-COMPLETE */ +void ICalProperty::set_percentcomplete(int val){ + icalproperty_set_percentcomplete(imp, val); +} +int ICalProperty::get_percentcomplete(){ + return icalproperty_get_percentcomplete(imp); +} + +/* PRIORITY */ +void ICalProperty::set_priority(int val){ + icalproperty_set_priority(imp, val); +} +int ICalProperty::get_priority(){ + return icalproperty_get_priority(imp); +} + +/* PRODID */ +void ICalProperty::set_prodid(string val){ + icalproperty_set_prodid(imp, val); +} +string ICalProperty::get_prodid(){ + return (string)icalproperty_get_prodid(imp); +} + +/* QUERY */ +void ICalProperty::set_query(string val){ + icalproperty_set_query(imp, val); +} +string ICalProperty::get_query(){ + return (string)icalproperty_get_query(imp); +} + +/* QUERYNAME */ +void ICalProperty::set_queryname(string val){ + icalproperty_set_queryname(imp, val); +} +string ICalProperty::get_queryname(){ + return (string)icalproperty_get_queryname(imp); +} + +/* RDATE */ +void ICalProperty::set_rdate(struct icaldatetimeperiodtype val){ + icalproperty_set_rdate(imp, val); +} +struct icaldatetimeperiodtype ICalProperty::get_rdate(){ + return icalproperty_get_rdate(imp); +} + +/* RECURRENCE-ID */ +void ICalProperty::set_recurrenceid(struct icaltimetype val){ + icalproperty_set_recurrenceid(imp, val); +} +struct icaltimetype ICalProperty::get_recurrenceid(){ + return icalproperty_get_recurrenceid(imp); +} + +/* RELATED-TO */ +void ICalProperty::set_relatedto(string val){ + icalproperty_set_relatedto(imp, val); +} +string ICalProperty::get_relatedto(){ + return (string)icalproperty_get_relatedto(imp); +} + +/* RELCALID */ +void ICalProperty::set_relcalid(string val){ + icalproperty_set_relcalid(imp, val); +} +string ICalProperty::get_relcalid(){ + return (string)icalproperty_get_relcalid(imp); +} + +/* REPEAT */ +void ICalProperty::set_repeat(int val){ + icalproperty_set_repeat(imp, val); +} +int ICalProperty::get_repeat(){ + return icalproperty_get_repeat(imp); +} + +/* REQUEST-STATUS */ +void ICalProperty::set_requeststatus(string val){ + icalreqstattype v; + + v = icalreqstattype_from_string((char*)val); + + icalproperty_set_requeststatus(imp, v); +} + +string ICalProperty::get_requeststatus(){ + icalreqstattype v; + v = icalproperty_get_requeststatus(imp); + return (string)(icalreqstattype_as_string(v)); +} + +/* RESOURCES */ +void ICalProperty::set_resources(string val){ + icalproperty_set_resources(imp, val); +} +string ICalProperty::get_resources(){ + return (string)icalproperty_get_resources(imp); +} + +/* RRULE */ +void ICalProperty::set_rrule(struct icalrecurrencetype val){ + icalproperty_set_rrule(imp, val); +} +struct icalrecurrencetype ICalProperty::get_rrule(){ + return icalproperty_get_rrule(imp); +} + +/* SCOPE */ +void ICalProperty::set_scope(string val){ + icalproperty_set_scope(imp, val); +} +string ICalProperty::get_scope(){ + return (string)icalproperty_get_scope(imp); +} + +/* SEQUENCE */ +void ICalProperty::set_sequence(int val){ + icalproperty_set_sequence(imp, val); +} +int ICalProperty::get_sequence(){ + return icalproperty_get_sequence(imp); +} + +/* STATUS */ +void ICalProperty::set_status(enum icalproperty_status val){ + icalproperty_set_status(imp, val); +} +enum icalproperty_status ICalProperty::get_status(){ + return icalproperty_get_status(imp); +} + +/* SUMMARY */ +void ICalProperty::set_summary(string val){ + icalproperty_set_summary(imp, val); +} +string ICalProperty::get_summary(){ + return (string)icalproperty_get_summary(imp); +} + +/* TARGET */ +void ICalProperty::set_target(string val){ + icalproperty_set_target(imp, val); +} +string ICalProperty::get_target(){ + return (string)icalproperty_get_target(imp); +} + +/* TRANSP */ +void ICalProperty::set_transp(enum icalproperty_transp val){ + icalproperty_set_transp(imp, val); +} +enum icalproperty_transp ICalProperty::get_transp(){ + return icalproperty_get_transp(imp); +} + +/* TRIGGER */ +void ICalProperty::set_trigger(struct icaltriggertype val){ + icalproperty_set_trigger(imp, val); +} +struct icaltriggertype ICalProperty::get_trigger(){ + return icalproperty_get_trigger(imp); +} + +/* TZID */ +void ICalProperty::set_tzid(string val){ + icalproperty_set_tzid(imp, val); +} +string ICalProperty::get_tzid(){ + return (string)icalproperty_get_tzid(imp); +} + +/* TZNAME */ +void ICalProperty::set_tzname(string val){ + icalproperty_set_tzname(imp, val); +} +string ICalProperty::get_tzname(){ + return (string)icalproperty_get_tzname(imp); +} + +/* TZOFFSETFROM */ +void ICalProperty::set_tzoffsetfrom(int val){ + icalproperty_set_tzoffsetfrom(imp, val); +} +int ICalProperty::get_tzoffsetfrom(){ + return icalproperty_get_tzoffsetfrom(imp); +} + +/* TZOFFSETTO */ +void ICalProperty::set_tzoffsetto(int val){ + icalproperty_set_tzoffsetto(imp, val); +} +int ICalProperty::get_tzoffsetto(){ + return icalproperty_get_tzoffsetto(imp); +} + +/* TZURL */ +void ICalProperty::set_tzurl(string val){ + icalproperty_set_tzurl(imp, val); +} +string ICalProperty::get_tzurl(){ + return (string)icalproperty_get_tzurl(imp); +} + +/* UID */ +void ICalProperty::set_uid(string val){ + icalproperty_set_uid(imp, val); +} +string ICalProperty::get_uid(){ + return (string)icalproperty_get_uid(imp); +} + +/* URL */ +void ICalProperty::set_url(string val){ + icalproperty_set_url(imp, val); +} +string ICalProperty::get_url(){ + return (string)icalproperty_get_url(imp); +} + +/* VERSION */ +void ICalProperty::set_version(string val){ + icalproperty_set_version(imp, val); +} +string ICalProperty::get_version(){ + return (string)icalproperty_get_version(imp); +} + +/* X */ +void ICalProperty::set_x(string val){ + icalproperty_set_x(imp, val); +} +string ICalProperty::get_x(){ + return (string)icalproperty_get_x(imp); +} + +/* X-LIC-CLUSTERCOUNT */ +void ICalProperty::set_xlicclustercount(string val){ + icalproperty_set_xlicclustercount(imp, val); +} +string ICalProperty::get_xlicclustercount(){ + return (string)icalproperty_get_xlicclustercount(imp); +} + +/* X-LIC-ERROR */ +void ICalProperty::set_xlicerror(string val){ + icalproperty_set_xlicerror(imp, val); +} +string ICalProperty::get_xlicerror(){ + return (string)icalproperty_get_xlicerror(imp); +} + +/* X-LIC-MIMECHARSET */ +void ICalProperty::set_xlicmimecharset(string val){ + icalproperty_set_xlicmimecharset(imp, val); +} +string ICalProperty::get_xlicmimecharset(){ + return (string)icalproperty_get_xlicmimecharset(imp); +} + +/* X-LIC-MIMECID */ +void ICalProperty::set_xlicmimecid(string val){ + icalproperty_set_xlicmimecid(imp, val); +} +string ICalProperty::get_xlicmimecid(){ + return (string)icalproperty_get_xlicmimecid(imp); +} + +/* X-LIC-MIMECONTENTTYPE */ +void ICalProperty::set_xlicmimecontenttype(string val){ + icalproperty_set_xlicmimecontenttype(imp, val); +} +string ICalProperty::get_xlicmimecontenttype(){ + return (string)icalproperty_get_xlicmimecontenttype(imp); +} + +/* X-LIC-MIMEENCODING */ +void ICalProperty::set_xlicmimeencoding(string val){ + icalproperty_set_xlicmimeencoding(imp, val); +} +string ICalProperty::get_xlicmimeencoding(){ + return (string)icalproperty_get_xlicmimeencoding(imp); +} + +/* X-LIC-MIMEFILENAME */ +void ICalProperty::set_xlicmimefilename(string val){ + icalproperty_set_xlicmimefilename(imp, val); +} +string ICalProperty::get_xlicmimefilename(){ + return (string)icalproperty_get_xlicmimefilename(imp); +} + +/* X-LIC-MIMEOPTINFO */ +void ICalProperty::set_xlicmimeoptinfo(string val){ + icalproperty_set_xlicmimeoptinfo(imp, val); +} +string ICalProperty::get_xlicmimeoptinfo(){ + return (string)icalproperty_get_xlicmimeoptinfo(imp); +} diff --git a/src/libical/icalproperty_cxx.h b/src/libical/icalproperty_cxx.h new file mode 100644 index 00000000..d7242431 --- /dev/null +++ b/src/libical/icalproperty_cxx.h @@ -0,0 +1,376 @@ +/* -*- Mode: C++ -*- */ + +/** + * @file icalproperty_cxx.h + * @author fnguyen (12/10/01) + * @brief Definition of C++ Wrapper for icalproperty.c + * + * (C) COPYRIGHT 2001, Critical Path + + 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 ICALPROPERTY_CXX_H +#define ICALPROPERTY_CXX_H + +#include "ical.h" +#include "icptrholder.h" + +typedef char* string; // Will use the string library from STL + +class ICalParameter; +class ICalValue; + +class ICalProperty { +public: + ICalProperty(); + ICalProperty(const ICalProperty&) throw(icalerrorenum); + ICalProperty& operator=(const ICalProperty&) throw(icalerrorenum); + ~ICalProperty(); + + ICalProperty(icalproperty* v); + ICalProperty(string str); + ICalProperty(icalproperty_kind kind); + ICalProperty(icalproperty_kind kind, string str); + + operator icalproperty*() {return imp;} + int operator==(ICalProperty& rhs); + + void detach() { + imp = NULL; + } + +public: + string as_ical_string(); + icalproperty_kind isa(); + int isa_property(void* property); + + void add_parameter(ICalParameter& parameter); + void set_parameter(ICalParameter& parameter); + void set_parameter_from_string(string name, string val); + string get_parameter_as_string(string name); + void remove_parameter(icalparameter_kind kind); + int count_parameters(); + + /** Iterate through the parameters */ + ICalParameter* get_first_parameter(icalparameter_kind kind); + ICalParameter* get_next_parameter(icalparameter_kind kind); + + /** Access the value of the property */ + void set_value(const ICalValue& val); + void set_value_from_string(string val, string kind); + + ICalValue* get_value(); + string get_value_as_string(); + + /** Return the name of the property -- the type name converted + * to a string, or the value of get_x_name if the type is X + * property + */ + string get_name(); + +public: + /* Deal with X properties */ + static void set_x_name(ICalProperty &prop, string name); + static string get_x_name(ICalProperty &prop); + + static icalvalue_kind icalparameter_value_to_value_kind(icalparameter_value val); + + /* Convert kinds to string and get default value type */ + static icalvalue_kind kind_to_value_kind(icalproperty_kind kind); + static icalproperty_kind value_kind_to_kind(icalvalue_kind kind); + static string kind_to_string(icalproperty_kind kind); + static icalproperty_kind string_to_kind(string str); + + static icalproperty_method string_to_method(string str); + static string method_to_string(icalproperty_method method); + + static string enum_to_string(int e); + static int string_to_enum(string str); + + static string status_to_string(icalproperty_status); + static icalproperty_status string_to_status(string str); + + static int enum_belongs_to_property(icalproperty_kind kind, int e); + +public: + /* ACTION */ + void set_action(enum icalproperty_action v); + enum icalproperty_action get_action(); + + /* ATTACH */ + void set_attach(icalattach *v); + icalattach *get_attach(); + + /* ATTENDEE */ + void set_attendee(string val); + string get_attendee(); + + /* CALSCALE */ + void set_calscale(string val); + string get_calscale(); + + /* CATEGORIES */ + void set_categories(string val); + string get_categories(); + + /* CLASS */ + void set_class(enum icalproperty_class val); + enum icalproperty_class get_class(); + + /* COMMENT */ + void set_comment(string val); + string get_comment(); + + /* COMPLETED */ + void set_completed(struct icaltimetype val); + struct icaltimetype get_completed(); + + /* CONTACT */ + void set_contact(string val); + string get_contact(); + + /* CREATED */ + void set_created(struct icaltimetype val); + struct icaltimetype get_created(); + + /* DESCRIPTION */ + void set_description(string val); + string get_description(); + + /* DTEND */ + void set_dtend(struct icaltimetype val); + struct icaltimetype get_dtend(); + + /* DTSTAMP */ + void set_dtstamp(struct icaltimetype val); + struct icaltimetype get_dtstamp(); + + /* DTSTART */ + void set_dtstart(struct icaltimetype val); + struct icaltimetype get_dtstart(); + + /* DUE */ + void set_due(struct icaltimetype val); + struct icaltimetype get_due(); + + /* DURATION */ + void set_duration(struct icaldurationtype val); + struct icaldurationtype get_duration(); + + /* EXDATE */ + void set_exdate(struct icaltimetype val); + struct icaltimetype get_exdate(); + + /* EXPAND */ + void set_expand(int val); + int get_expand(); + + /* EXRULE */ + void set_exrule(struct icalrecurrencetype val); + struct icalrecurrencetype get_exrule(); + + /* FREEBUSY */ + void set_freebusy(struct icalperiodtype val); + struct icalperiodtype get_freebusy(); + + /* GEO */ + void set_geo(struct icalgeotype val); + struct icalgeotype get_geo(); + + /* GRANT */ + void set_grant(string val); + string get_grant(); + + /* LAST-MODIFIED */ + void set_lastmodified(struct icaltimetype val); + struct icaltimetype get_lastmodified(); + + /* LOCATION */ + void set_location(string val); + string get_location(); + + /* MAXRESULTS */ + void set_maxresults(int val); + int get_maxresults(); + + /* MAXRESULTSSIZE */ + void set_maxresultsize(int val); + int get_maxresultsize(); + + /* METHOD */ + void set_method(enum icalproperty_method val); + enum icalproperty_method get_method(); + + /* OWNER */ + void set_owner(string val); + string get_owner(); + + /* ORGANIZER */ + void set_organizer(string val); + string get_organizer(); + + /* PERCENT-COMPLETE */ + void set_percentcomplete(int val); + int get_percentcomplete(); + + /* PRIORITY */ + void set_priority(int val); + int get_priority(); + + /* PRODID */ + void set_prodid(string val); + string get_prodid(); + + /* QUERY */ + void set_query(string val); + string get_query(); + + /* QUERYNAME */ + void set_queryname(string val); + string get_queryname(); + + /* RDATE */ + void set_rdate(struct icaldatetimeperiodtype val); + struct icaldatetimeperiodtype get_rdate(); + + /* RECURRENCE-ID */ + void set_recurrenceid(struct icaltimetype val); + struct icaltimetype get_recurrenceid(); + + /* RELATED-TO */ + void set_relatedto(string val); + string get_relatedto(); + + /* RELCALID */ + void set_relcalid(string val); + string get_relcalid(); + + /* REPEAT */ + void set_repeat(int val); + int get_repeat(); + + /* REQUEST-STATUS */ + void set_requeststatus(string val); + string get_requeststatus(); + + /* RESOURCES */ + void set_resources(string val); + string get_resources(); + + /* RRULE */ + void set_rrule(struct icalrecurrencetype val); + struct icalrecurrencetype get_rrule(); + + /* SCOPE */ + void set_scope(string val); + string get_scope(); + + /* SEQUENCE */ + void set_sequence(int val); + int get_sequence(); + + /* STATUS */ + void set_status(enum icalproperty_status val); + enum icalproperty_status get_status(); + + /* SUMMARY */ + void set_summary(string val); + string get_summary(); + + /* TARGET */ + void set_target(string val); + string get_target(); + + /* TRANSP */ + void set_transp(enum icalproperty_transp val); + enum icalproperty_transp get_transp(); + + /* TRIGGER */ + void set_trigger(struct icaltriggertype val); + struct icaltriggertype get_trigger(); + + /* TZID */ + void set_tzid(string val); + string get_tzid(); + + /* TZNAME */ + void set_tzname(string val); + string get_tzname(); + + /* TZOFFSETFROM */ + void set_tzoffsetfrom(int val); + int get_tzoffsetfrom(); + + /* TZOFFSETTO */ + void set_tzoffsetto(int val); + int get_tzoffsetto(); + + /* TZURL */ + void set_tzurl(string val); + string get_tzurl(); + + /* UID */ + void set_uid(string val); + string get_uid(); + + /* URL */ + void set_url(string val); + string get_url(); + + /* VERSION */ + void set_version(string val); + string get_version(); + + /* X */ + void set_x(string val); + string get_x(); + + /* X-LIC-CLUSTERCOUNT */ + void set_xlicclustercount(string val); + string get_xlicclustercount(); + + /* X-LIC-ERROR */ + void set_xlicerror(string val); + string get_xlicerror(); + + /* X-LIC-MIMECHARSET */ + void set_xlicmimecharset(string val); + string get_xlicmimecharset(); + + /* X-LIC-MIMECID */ + void set_xlicmimecid(string val); + string get_xlicmimecid(); + + /* X-LIC-MIMECONTENTTYPE */ + void set_xlicmimecontenttype(string val); + string get_xlicmimecontenttype(); + + /* X-LIC-MIMEENCODING */ + void set_xlicmimeencoding(string val); + string get_xlicmimeencoding(); + + /* X-LIC-MIMEFILENAME */ + void set_xlicmimefilename(string val); + string get_xlicmimefilename(); + + /* X-LIC-MIMEOPTINFO */ + void set_xlicmimeoptinfo(string val); + string get_xlicmimeoptinfo(); + +private: + icalproperty* imp; /**< The actual C based icalproperty */ +}; + +typedef ICPointerHolder<ICalProperty> ICalPropertyTmpPtr; /* see icptrholder.h for comments */ + +#endif /* ICalProperty_H */ diff --git a/src/libical/icalrecur.c b/src/libical/icalrecur.c new file mode 100644 index 00000000..c0e5eca0 --- /dev/null +++ b/src/libical/icalrecur.c @@ -0,0 +1,2612 @@ +/* -*- Mode: C -*- + ====================================================================== + FILE: icalrecur.c + CREATOR: eric 16 May 2000 + + $Id: icalrecur.c,v 1.71 2008-02-03 16:10:46 dothebart Exp $ + $Locker: $ + + + (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 <stdio.h> +#include <stdarg.h> + +#if defined(_MSC_VER) +#define snprintf _snprintf +#endif + +#include <limits.h> + +#ifndef HAVE_INTPTR_T +#if (defined (WIN32) && !defined (__MINGW32__)) || defined (XP_BEOS) +typedef long intptr_t; +#endif +#endif + +#ifdef _MSC_VER +#define strcasecmp stricmp +#endif + +#include "icalrecur.h" + +#include "icalerror.h" +#include "icalmemory.h" + +#include <stdlib.h> /* for malloc, strtol */ +#include <errno.h> /* for errno */ +#include <string.h> /* for strdup and strchr*/ +#include <assert.h> +#include <stddef.h> /* For offsetof() macro */ +#ifdef HAVE_INTTYPES_H +#include <inttypes.h> +#endif + +#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; + + 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; + + } + +} + +/* + * Days in the BYDAY rule are expected by the code to be sorted, and while + * this may be the common case, the RFC doesn't actually mandate it. This + * function sorts the days taking into account the first day of week. + */ +static void +sort_bydayrules(struct icalrecur_parser *parser) +{ + short *array; + int week_start, one, two, i, j; + + array = parser->rt.by_day; + week_start = parser->rt.week_start; + + for (i=0; + i<ICAL_BY_DAY_SIZE && array[i] != ICAL_RECURRENCE_ARRAY_MAX; + i++) { + for (j=0; j<i; j++) { + one = icalrecurrencetype_day_day_of_week(array[j]) - week_start; + if (one < 0) one += 7; + two = icalrecurrencetype_day_day_of_week(array[i]) - week_start; + if (two < 0) two += 7; + + if (one > two) { + short tmp = array[j]; + array[j] = array[i]; + array[i] = tmp; + } + } + } +} + +void icalrecur_add_bydayrules(struct icalrecur_parser *parser, const char* vals) +{ + + char *t, *n; + int i=0; + int sign = 1; + char weekno = 0; /* note: Novell/Groupwise sends BYDAY=255SU, so we fit in a signed char to get -1 SU for last sunday. */ + 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 = (char)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); + + sort_bydayrules(parser); +} + + +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); + /* don't allow an interval to be less than 1 + (RFC specifies an interval must be a positive integer) */ + if (parser.rt.interval < 1){ + parser.rt.interval = 1; + } + } else if (strcasecmp(name,"WKST") == 0){ + parser.rt.week_start = icalrecur_string_to_weekday(value); + sort_bydayrules(&parser); + } 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),ICAL_BY_SECOND_SIZE - 1}, + {";BYMINUTE=",offsetof(struct icalrecurrencetype,by_minute),ICAL_BY_MINUTE_SIZE - 1}, + {";BYHOUR=",offsetof(struct icalrecurrencetype,by_hour),ICAL_BY_HOUR_SIZE - 1}, + {";BYDAY=",offsetof(struct icalrecurrencetype,by_day),ICAL_BY_DAY_SIZE - 1}, + {";BYMONTHDAY=",offsetof(struct icalrecurrencetype,by_month_day),ICAL_BY_MONTHDAY_SIZE - 1}, + {";BYYEARDAY=",offsetof(struct icalrecurrencetype,by_year_day),ICAL_BY_YEARDAY_SIZE - 1}, + {";BYWEEKNO=",offsetof(struct icalrecurrencetype,by_week_no),ICAL_BY_WEEKNO_SIZE - 1}, + {";BYMONTH=",offsetof(struct icalrecurrencetype,by_month),ICAL_BY_MONTH_SIZE - 1}, + {";BYSETPOS=",offsetof(struct icalrecurrencetype,by_set_pos),ICAL_BY_SETPOS_SIZE - 1}, + {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 *buf; + buf = icalrecurrencetype_as_string_r(recur); + icalmemory_add_tmp_buffer(buf); + return buf; +} + + +char* icalrecurrencetype_as_string_r(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_new_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 && recur->week_start != ICAL_NO_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 const 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; + + icalerror_clear_errno(); + + 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); + free(impl); + 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); + free(impl); + 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); + free(impl); + 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); + free(impl); + 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); + free(impl); + 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 depends on impl->by_ptrs[BY_DAY] being correctly sorted by + * day. This should probably be abstracted to make such assumption + * more explicit. */ + short dow = (short)(impl->by_ptrs[BY_DAY][0]-icaltime_day_of_week(impl->last)); + + if((icaltime_day_of_week(impl->last) < impl->by_ptrs[BY_DAY][0] && dow >= 0) || 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; + icalerror_clear_errno(); + + /* Fail after hitting the year 20000 if no expanded days match */ + while (impl->last.year < 20000) { + expand_year_days(impl, impl->last.year); + if( icalerrno != ICAL_NO_ERROR) { + icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR); + free(impl); + return 0; + } + 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); + free(impl); + return 0; + } + + } else if (has_by_data(impl,BY_MONTH_DAY)) { + impl->last = icaltime_normalize(impl->last); + } + + + + 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 (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; +} + +int check_set_position(icalrecur_iterator* impl, int set_pos) +{ + int i; + int found = 0; + for (i = 0; impl->rule.by_set_pos[i] != ICAL_RECURRENCE_ARRAY_MAX && + i != ICAL_BY_SETPOS_SIZE; i++){ + if (impl->rule.by_set_pos[i] == set_pos) { + found = 1; + break; + } + } + return found; +} + +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); + int set_pos_counter = 0; + int set_pos_total = 0; + + int found = 0; + + assert( BYDAYPTR[0]!=ICAL_RECURRENCE_ARRAY_MAX); + + /* Count the past positions for the BYSETPOS calculation */ + if(has_by_data(impl,BY_SET_POS)){ + int last_day = impl->last.day; + for(day = 1; day <= days_in_month; day++){ + impl->last.day = day; + + if(is_day_in_byday(impl,impl->last)){ + set_pos_total++; + if(day <= last_day) + set_pos_counter++; + } + } + impl->last.day = last_day; + } + + for(day = impl->last.day+1; day <= days_in_month; day++){ + impl->last.day = day; + + if(is_day_in_byday(impl,impl->last)){ + /* If there is no BYSETPOS rule, calculate only by BYDAY + If there is BYSETPOS rule, take into account the occurence + matches with BYDAY */ + if(!has_by_data(impl,BY_SET_POS) || check_set_position(impl, ++set_pos_counter) + || check_set_position(impl, set_pos_counter-set_pos_total-1)) { + found = 1; + break; + } + } + } + + data_valid = found; + + 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)){ + /* If there is no BYSETPOS rule or BYSETPOS=1, new data is valid */ + if(!has_by_data(impl,BY_SET_POS) || check_set_position(impl,1)) + 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, days_in_month; + + 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); + } + + days_in_month = icaltime_days_in_month(impl->last.month, + impl->last.year); + + day = BYMDPTR[BYMDIDX]; + + if (day < 0) { + day = icaltime_days_in_month(impl->last.month, impl->last.year) + day + 1; + } + + if ( day > days_in_month){ + impl->last.day = 1; + + /* 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 */ + } + } + + impl->last.day = day; + + } else { + int days_in_month; + + assert( BYMDPTR[0]!=ICAL_RECURRENCE_ARRAY_MAX); + impl->last.day = BYMDPTR[0]; + + increment_month(impl); + + days_in_month = icaltime_days_in_month(impl->last.month, + impl->last.year); + if (impl->last.day > days_in_month){ + impl->last.day = days_in_month; + } + } + + 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]); + dow -= impl->rule.week_start; /* Set Sunday to be 0 */ + if (dow < 0) { + dow += 7; + } + + tt.year = impl->last.year; + tt.day = impl->last.day; + tt.month = impl->last.month; + + start_of_week = icaltime_start_doy_week(tt, impl->rule.week_start); + + 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 i,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); + + + /* BY_WEEK_NO together with BY_MONTH - may conflict, in this case BY_MONTH wins */ + if( (flags & 1<<BY_MONTH) && (flags & 1<<BY_WEEK_NO) ){ + int valid_weeks[ICAL_BY_WEEKNO_SIZE]; + int valid = 1; + memset(valid_weeks, 0, sizeof(valid_weeks)); + t.year = year; + t.is_date = 1; + + /* calculate valid week numbers */ + for(j=0; impl->by_ptrs[BY_MONTH][j]!=ICAL_RECURRENCE_ARRAY_MAX; j++){ + int month = impl->by_ptrs[BY_MONTH][j]; + int first_week, last_week; + t.month = month; + t.day = 1; + first_week = icaltime_week_number(t); + t.day = icaltime_days_in_month(month,year); + last_week = icaltime_week_number(t); + for(j=first_week; j<last_week; j++) { + valid_weeks[j] = 1; + } + } + + /* check valid weeks */ + for(i = 0; BYWEEKPTR[i] != ICAL_RECURRENCE_ARRAY_MAX && valid; i++){ + int weekno = BYWEEKPTR[i]; + if(weekno < ICAL_BY_WEEKNO_SIZE) + valid &= valid_weeks[i]; /* check if the week number is valid */ + else + valid = 0; /* invalid week number */ + } + + /* let us make the decision which rule to keep */ + if(valid) { /* BYWEEKNO wins */ + flags -= 1<<BY_MONTH; + } + else { /* BYMONTH vins */ + flags -= 1<<BY_WEEK_NO; + } + } + + 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); + + if(has_by_data(impl,BY_SET_POS)) { + /*FREQ=YEARLY; BYDAY=TH,20MO,-10FR; BYMONTH = 12; BYSETPOS=1*/ + int day; + int set_pos_counter = 0; + int set_pos_total = 0; + int by_month_day[ICAL_BY_MONTHDAY_SIZE]; + for(day = 1; day <= days_in_month; day++){ + t.day = day; + if(is_day_in_byday(impl,t)) + by_month_day[set_pos_total++] = day; + } + for(set_pos_counter = 0; set_pos_counter < set_pos_total; set_pos_counter++){ + if(check_set_position(impl, set_pos_counter+1) || + check_set_position(impl, set_pos_counter-set_pos_total)) + impl->days[days_index++] = doy_offset + by_month_day[set_pos_counter]; + } + } + else 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 || (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); + ritr = icalrecur_iterator_new(recur,icstart); + if(ritr) { + for(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/src/libical/icalrecur.h b/src/libical/icalrecur.h new file mode 100644 index 00000000..884bf049 --- /dev/null +++ b/src/libical/icalrecur.h @@ -0,0 +1,217 @@ +/* -*- 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); + +icalrecurrencetype_weekday icalrecur_string_to_weekday(const char* str); + +/** Recurrance rule parser */ + +/** Convert between strings and recurrencetype structures. */ +struct icalrecurrencetype icalrecurrencetype_from_string(const char* str); +char* icalrecurrencetype_as_string(struct icalrecurrencetype *recur); +char* icalrecurrencetype_as_string_r(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/src/libical/icalrestriction.c.in b/src/libical/icalrestriction.c.in new file mode 100644 index 00000000..d8c03ccc --- /dev/null +++ b/src/libical/icalrestriction.c.in @@ -0,0 +1,520 @@ +/* -*- Mode: C -*- */ +/* ====================================================================== + File: icalrestriction.c + + 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/ + + (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> + +#if defined(_MSC_VER) +#define snprintf _snprintf +#endif + +#define TMP_BUF_SIZE 1024 + +/* 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)(const 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; + +static const icalrestriction_property_record* +icalrestriction_get_property_restriction(icalproperty_method method, + icalcomponent_kind component, + icalproperty_kind property); +static const icalrestriction_component_record* +icalrestriction_get_component_restriction(icalproperty_method method, + icalcomponent_kind component, + icalcomponent_kind subcomponent); + +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*/ +}; + +const 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( + const 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( + const 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(const 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( + const 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( + const 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( + const 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( + const icalrestriction_property_record *rec, + icalcomponent* comp, + icalproperty* prop){ + /* Hack */ + (void)rec; + (void)comp; + (void)prop; + return 0; +} + +const char* icalrestriction_must_have_duration( + const icalrestriction_property_record *rec, + icalcomponent* comp, + icalproperty* 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( + const 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( + const icalrestriction_property_record *rec, + icalcomponent* comp, + icalproperty* prop){ + + /* Hack */ + (void)rec; + (void)comp; + (void)prop; + return 0; +} + +const char* icalrestriction_no_dtend( + const 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( + const 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( + const icalrestriction_property_record *rec, + icalcomponent* comp, + icalproperty* prop) +{ + icalproperty_action 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; + const 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; + +} + +<insert_code_here> + +static const 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; +} + + +static const 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/src/libical/icalrestriction.h b/src/libical/icalrestriction.h new file mode 100644 index 00000000..a4914247 --- /dev/null +++ b/src/libical/icalrestriction.h @@ -0,0 +1,64 @@ +/* -*- Mode: C -*- */ +/*====================================================================== + FILE: icalrestriction.h + CREATOR: eric 24 April 1999 + + $Id: icalrestriction.h,v 1.3 2008-01-15 23:17:42 dothebart Exp $ + + + (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/src/libical/icaltime.c b/src/libical/icaltime.c new file mode 100644 index 00000000..b9842568 --- /dev/null +++ b/src/libical/icaltime.c @@ -0,0 +1,1241 @@ +/* -*- Mode: C -*- + ====================================================================== + FILE: icaltime.c + CREATOR: eric 02 June 2000 + + $Id: icaltime.c,v 1.71 2008-01-29 18:31:48 dothebart Exp $ + $Locker: $ + + (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> +#endif + +#if defined(_MSC_VER) +#define snprintf _snprintf +#define strcasecmp stricmp +#endif + +#ifdef WIN32 +/* Undef the similar macro from pthread.h, it doesn't check if + * gmtime() returns NULL. + */ +#undef gmtime_r + +/* The gmtime() in Microsoft's C library is MT-safe */ +#define gmtime_r(tp,tmp) (gmtime(tp)?(*(tmp)=*gmtime(tp),(tmp)):0) +#endif + +#ifdef HAVE_PTHREAD + #include <pthread.h> + static pthread_mutex_t tzid_mutex = PTHREAD_MUTEX_INITIALIZER; +#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, + const icaltimezone *zone) +{ + struct icaltimetype tt; + struct tm t; + icaltimezone *utc_zone; + + utc_zone = icaltimezone_get_utc_timezone (); + + /* Convert the time_t to a struct tm in UTC time. We can trust gmtime + for this. */ +#ifdef HAVE_PTHREAD + gmtime_r (&tm, &t); +#else + t = *(gmtime (&tm)); +#endif + + tt.year = t.tm_year + 1900; + tt.month = t.tm_mon + 1; + tt.day = t.tm_mday; + tt.hour = t.tm_hour; + tt.minute = t.tm_min; + tt.second = t.tm_sec; + tt.is_date = 0; + tt.is_utc = (zone == utc_zone) ? 1 : 0; + tt.is_daylight = 0; + tt.zone = NULL; + + /* Use our timezone functions to convert to the required timezone. */ + icaltimezone_convert_time (&tt, utc_zone, (icaltimezone *)zone); + + tt.is_date = is_date; + + /* If it is a DATE value, make sure hour, minute & second are 0. */ + if (is_date) { + tt.hour = 0; + tt.minute = 0; + tt.second = 0; + } + + return tt; +} + +/** @brief Convenience constructor. + * + * Returns the current time in the given timezone, as an icaltimetype. + */ +struct icaltimetype icaltime_current_time_with_zone(const 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 + * + * While this function is not currently deprecated, it probably won't do + * what you expect, unless you know what you're doing. In particular, you + * should only pass an icaltime in UTC, since no conversion is done. Even + * in that case, it's probably better to just use + * icaltime_as_timet_with_zone(). + */ +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)); + + if (icaltime_is_date(tt)) { + stm.tm_sec = stm.tm_min = stm.tm_hour = 0; + } else { + 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; + +} + + +/* Structure used by set_tz to hold an old value of TZ, and the new + value, which is in memory we will have to free in unset_tz */ +/* This will hold the last "TZ=XXX" string we used with putenv(). After we + call putenv() again to set a new TZ string, we can free the previous one. + As far as I know, no libc implementations actually free the memory used in + the environment variables (how could they know if it is a static string or + a malloc'ed string?), so we have to free it ourselves. */ +static char* saved_tz = NULL; + +/* If you use set_tz(), you must call unset_tz() some time later to restore the + original TZ. Pass unset_tz() the string that set_tz() returns. Call both the functions + locking the tzid mutex as in icaltime_as_timet_with_zone */ +char* set_tz(const char* tzid) +{ + char *old_tz, *old_tz_copy = NULL, *new_tz; + + /* Get the old TZ setting and save a copy of it to return. */ + old_tz = getenv("TZ"); + if(old_tz){ + old_tz_copy = (char*)malloc(strlen (old_tz) + 4); + + if(old_tz_copy == 0){ + icalerror_set_errno(ICAL_NEWFAILED_ERROR); + return 0; + } + + strcpy (old_tz_copy, "TZ="); + strcpy (old_tz_copy + 3, old_tz); + } + + /* Create the new TZ string. */ + new_tz = (char*)malloc(strlen (tzid) + 4); + + if(new_tz == 0){ + icalerror_set_errno(ICAL_NEWFAILED_ERROR); + free(old_tz_copy); + return 0; + } + + strcpy (new_tz, "TZ="); + strcpy (new_tz + 3, tzid); + + /* Add the new TZ to the environment. */ + putenv(new_tz); + + /* Free any previous TZ environment string we have used in a synchronized manner. */ + + free (saved_tz); + + /* Save a pointer to the TZ string we just set, so we can free it later. */ + saved_tz = new_tz; + + return old_tz_copy; /* This will be zero if the TZ env var was not set */ +} + +void unset_tz(char *tzstr) +{ + /* restore the original environment */ + + if(tzstr!=0){ + putenv(tzstr); + } else { + /* Delete from environment. We prefer unsetenv(3) over putenv(3) + because the former is POSIX and behaves consistently. The later + does not unset the variable in some systems (like NetBSD), leaving + it with an empty value. This causes problems later because further + calls to time related functions in libc will treat times in UTC. */ +#ifdef HAVE_UNSETENV + unsetenv("TZ"); +#else +#ifdef _MSC_VER + putenv("TZ="); // The equals is required to remove with MS Visual C++ +#else + putenv("TZ"); +#endif +#endif + } + + /* Free any previous TZ environment string we have used in a synchronized manner */ + free (saved_tz); + + /* Save a pointer to the TZ string we just set, so we can free it later. + (This can possibly be NULL if there was no TZ to restore.) */ + saved_tz = tzstr; +} + +/** Return the time as seconds past the UNIX epoch, using the + * given timezone. + * + * This convenience method combines a call to icaltime_convert_to_zone() + * 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, + const icaltimezone *zone) +{ + icaltimezone *utc_zone; + struct tm stm; + time_t t; + char *old_tz; + struct icaltimetype local_tt; + + utc_zone = icaltimezone_get_utc_timezone (); + + /* If the time is the special null time, return 0. */ + if (icaltime_is_null_time(tt)) { + return 0; + } + + local_tt = tt; + + /* Clear the is_date flag, so we can convert the time. */ + local_tt.is_date = 0; + + /* Use our timezone functions to convert to UTC. */ + icaltimezone_convert_time (&local_tt, (icaltimezone *)zone, utc_zone); + + /* Copy the icaltimetype to a struct tm. */ + memset (&stm, 0, sizeof (struct tm)); + + stm.tm_sec = local_tt.second; + stm.tm_min = local_tt.minute; + stm.tm_hour = local_tt.hour; + stm.tm_mday = local_tt.day; + stm.tm_mon = local_tt.month-1; + stm.tm_year = local_tt.year-1900; + stm.tm_isdst = -1; +/* The functions putenv and mktime are not thread safe, inserting a lock +to prevent any crashes */ + +#ifdef HAVE_PTHREAD + pthread_mutex_lock (&tzid_mutex); +#endif + + /* Set TZ to UTC and use mktime to convert to a time_t. */ + old_tz = set_tz ("UTC"); + tzset (); + + t = mktime (&stm); + unset_tz (old_tz); + tzset (); + +#ifdef HAVE_PTHREAD + pthread_mutex_unlock (&tzid_mutex); +#endif + return t; +} + +const char* icaltime_as_ical_string(const struct icaltimetype tt) +{ + char *buf; + buf = icaltime_as_ical_string_r(tt); + icalmemory_add_tmp_buffer(buf); + return buf; +} + + +/** + * Return a string represention of the time, in RFC2445 format. The + * string is owned by libical + */ +char* icaltime_as_ical_string_r(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); + } + + 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(); + size_t size; + + icalerror_check_arg_re(str!=0,"str",icaltime_null_time()); + + size = strlen(str); + + if ((size == 15) || (size == 19)) { /* floating time with/without separators*/ + tt.is_utc = 0; + tt.is_date = 0; + } else if ((size == 16) || (size == 20)) { /* UTC time, ends in 'Z'*/ + if ((str[15] != 'Z') && (str[19] != 'Z')) + goto FAIL; + + tt.is_utc = 1; + tt.zone = icaltimezone_get_utc_timezone(); + tt.is_date = 0; + } else if ((size == 8) || (size == 10)) { /* A DATE */ + tt.is_utc = 0; + tt.is_date = 1; + } else { /* error */ + goto FAIL; + } + + if (tt.is_date == 1){ + if (size == 10) { + char dsep1, dsep2; + if (sscanf(str,"%04d%c%02d%c%02d",&tt.year,&dsep1,&tt.month,&dsep2,&tt.day) < 5) + goto FAIL; + if ((dsep1 != '-') || (dsep2 != '-')) + goto FAIL; + } else if (sscanf(str,"%04d%02d%02d",&tt.year,&tt.month,&tt.day) < 3) { + goto FAIL; + } + } else { + if (size > 16 ) { + char dsep1, dsep2, tsep, tsep1, tsep2; + if (sscanf(str,"%04d%c%02d%c%02d%c%02d%c%02d%c%02d",&tt.year,&dsep1,&tt.month,&dsep2, + &tt.day,&tsep,&tt.hour,&tsep1,&tt.minute,&tsep2,&tt.second) < 11) + goto FAIL; + + if((tsep != 'T') || (dsep1 != '-') || (dsep2 != '-') || (tsep1 != ':') || (tsep2 != ':')) + goto FAIL; + + } 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 FAIL; + + if(tsep != 'T') + goto FAIL; + } + } + + return tt; + +FAIL: + 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); +} + + +int +ycaltime_days_in_year (const int year) +{ + if (icaltime_is_leap_year (year)) + return 366; + else return 365; +} + +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]; + +/* The old code aborting if it was passed a parameter like BYMONTH=0 + * Unfortunately it's not practical right now to pass an error all + * the way up the stack, so instead of aborting we're going to apply + * the GIGO principle and simply return '30 days' if we get an + * invalid month. Modern applications cannot tolerate crashing. + * assert(month > 0); + * assert(month <= 12); + */ + if ((month < 1) || (month > 12)) { + return 30; + } + + 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. + */ +int icaltime_start_doy_week(const struct icaltimetype t, int fdow){ + UTinstant jt; + int delta; + + 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); + + delta = jt.weekday - (fdow - 1); + if (delta < 0) delta += 7; + return jt.day_of_year - delta; +} + +/** Day of the year that the first day of the week (Sunday) is on. + * + * @deprecated Doesn't take into account different week start days. + */ +int icaltime_start_doy_of_week(const struct icaltimetype t){ + +#ifndef NO_WARN_DEPRECATED + icalerror_warn("icaltime_start_doy_of_week() is DEPRECATED, use\ + icaltime_start_doy_week() instead"); +#endif + + return icaltime_start_doy_week(t, 1); +} + +/** + * @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_passed_month[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_passed_month[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_passed_month[is_leap][12]; + } else if(doy > days_in_year_passed_month[is_leap][12]){ + /* Move on to the next year*/ + is_leap = icaltime_is_leap_year(year); + doy -= days_in_year_passed_month[is_leap][12]; + year++; + } + + tt.year = year; + + for (month = 11; month >= 0; month--) { + if (doy > days_in_year_passed_month[is_leap][month]) { + tt.month = month + 1; + tt.day = doy - days_in_year_passed_month[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) +{ + 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) + return 1; + else if (a.year < b.year) + return -1; + + else if (a.month > b.month) + return 1; + else if (a.month < b.month) + return -1; + + else if (a.day > b.day) + return 1; + else if (a.day < b.day) + return -1; + + /* if both are dates, we are done */ + if (a.is_date && b.is_date) + return 0; + + /* 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) + return 1; + else if (a.is_date) + return -1; + + else if (a.hour > b.hour) + return 1; + else if (a.hour < b.hour) + return -1; + + else if (a.minute > b.minute) + return 1; + else if (a.minute < b.minute) + return -1; + + else if (a.second > b.second) + return 1; + else if (a.second < b.second) + return -1; + + return 0; +} + +/** + * like icaltime_compare, but only use the date parts. + */ + +int +icaltime_compare_date_only(const struct icaltimetype a_in, const struct icaltimetype b_in) +{ + struct icaltimetype a, b; + icaltimezone *tz = icaltimezone_get_utc_timezone(); + + a = icaltime_convert_to_zone(a_in, tz); + b = icaltime_convert_to_zone(b_in, tz); + + if (a.year > b.year) + return 1; + else if (a.year < b.year) + return -1; + + if (a.month > b.month) + return 1; + else if (a.month < b.month) + return -1; + + if (a.day > b.day) + return 1; + else if (a.day < b.day) + return -1; + + return 0; +} + +/** + * like icaltime_compare, but only use the date parts; accepts timezone. + */ + +int +icaltime_compare_date_only_tz(const struct icaltimetype a_in, const struct icaltimetype b_in, icaltimezone *tz) +{ + struct icaltimetype a, b; + + a = icaltime_convert_to_zone(a_in, tz); + b = icaltime_convert_to_zone(b_in, tz); + + if (a.year > b.year) + return 1; + else if (a.year < b.year) + return -1; + + if (a.month > b.month) + return 1; + else if (a.month < b.month) + return -1; + + if (a.day > b.day) + return 1; + else if (a.day < b.day) + return -1; + + return 0; +} + +/* 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 = 0, years_overflow; + int days_in_month; + + /* If we are passed a date make sure to ignore hour minute and second */ + if (tt->is_date) + goto IS_DATE; + + /* 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--; + } + +IS_DATE: + /* 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, (icaltimezone *)tt.zone, zone); + } + + ret.zone = zone; + if (zone == icaltimezone_get_utc_timezone()) { + ret.is_utc = 1; + } else { + ret.is_utc = 0; + } + + return ret; +} + +const 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((icaltimezone *)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, const 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, + dtstart.zone ? dtstart.zone : 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, + dtend.zone ? dtend.zone : 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/src/libical/icaltime.h b/src/libical/icaltime.h new file mode 100644 index 00000000..1919a2a5 --- /dev/null +++ b/src/libical/icaltime.h @@ -0,0 +1,287 @@ +/* -*- Mode: C -*- */ +/*====================================================================== + FILE: icaltime.h + CREATOR: eric 02 June 2000 + + + $Id: icaltime.h,v 1.28 2008-01-15 23:17:42 dothebart Exp $ + $Locker: $ + + (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, const icaltimezone *zone) + * - icaltime_day_of_year(struct icaltimetype t) + * - icaltime_day_of_week(struct icaltimetype t) + * - icaltime_start_doy_of_week(struct icaltimetype t, int fdow) + * - 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. */ + + const 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(const 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, const 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, + const 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, + const icaltimezone *zone); + +/** Return a string represention of the time, in RFC2445 format. */ +const char* icaltime_as_ical_string(const struct icaltimetype tt); +char* icaltime_as_ical_string_r(const struct icaltimetype tt); + +/** @brief Return the timezone */ +const 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, + const 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 day of the year for the first day of the week that the + given time is within. */ +int icaltime_start_doy_week(const struct icaltimetype t, int fdow); + +/** 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); + +/** like icaltime_compare, but only use the date parts. */ +int icaltime_compare_date_only_tz(const struct icaltimetype a, + const struct icaltimetype b, icaltimezone *tz); + +/** 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); + +/** Return whether you've specified a leapyear or not. */ +int icaltime_is_leap_year (const int year); + +/** Return the number of days in this year */ +int icaltime_days_in_year (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/src/libical/icaltimezone.c b/src/libical/icaltimezone.c new file mode 100644 index 00000000..a5791602 --- /dev/null +++ b/src/libical/icaltimezone.c @@ -0,0 +1,2143 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/*====================================================================== + FILE: icaltimezone.c + CREATOR: Damon Chaplin 15 March 2001 + + $Id: icaltimezone.c,v 1.44 2008-02-03 16:10:46 dothebart Exp $ + $Locker: $ + + (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 <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "icalproperty.h" +#include "icalarray.h" +#include "icalerror.h" +#include "icalparser.h" +#include "icaltimezone.h" +#include "icaltimezoneimpl.h" +#include "icaltz-util.h" + +#include <sys/stat.h> + +#ifdef HAVE_PTHREAD +#include <pthread.h> +static pthread_mutex_t builtin_mutex = PTHREAD_MUTEX_INITIALIZER; +#endif + +#ifdef WIN32 +#ifndef _WIN32_WCE +#include <mbstring.h> +#endif +#include <windows.h> +/* Undef the similar macro from pthread.h, it doesn't check if + * gmtime() returns NULL. + */ +#undef gmtime_r + +/* The gmtime() in Microsoft's C library is MT-safe */ +#define gmtime_r(tp,tmp) (gmtime(tp)?(*(tmp)=*gmtime(tp),(tmp)):0) + +// MSVC lacks the POSIX macro S_ISDIR, however it's a trivial one: +#ifndef S_ISDIR +#define S_ISDIR(m) (((m) & _S_IFMT) == _S_IFDIR) +#endif +#endif + +#if defined(_MSC_VER) +#define snprintf _snprintf +#define strcasecmp stricmp +#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. + It must begin and end with forward slashes. + */ +const char *ical_tzid_prefix = "/freeassociation.sourceforge.net/"; + +/** 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 + +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); + +#ifdef USE_BUILTIN_TZDATA +static char* icaltimezone_load_get_line_fn (char *s, + size_t size, + void *data); +#endif + +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; +} + +icaltimezone * +icaltimezone_copy (icaltimezone *originalzone) +{ + icaltimezone *zone; + + zone = (icaltimezone*) malloc (sizeof (icaltimezone)); + if (!zone) { + icalerror_set_errno (ICAL_NEWFAILED_ERROR); + return NULL; + } + + memcpy (zone, originalzone, sizeof (icaltimezone)); + if (zone->tzid != NULL) + zone->tzid = strdup (zone->tzid); + if (zone->location != NULL) + zone->location = strdup (zone->location); + if (zone->tznames != NULL) + zone->tznames = strdup (zone->tznames); + if (zone->changes != NULL) + zone->changes = icalarray_copy(zone->changes); + + /* Let the caller set the component because then they will + know to be careful not to free this reference twice. */ + zone->component = NULL; + + return zone; +} + +/** Frees all memory used for the icaltimezone. */ +void +icaltimezone_free (icaltimezone *zone, + int free_struct) +{ + icaltimezone_reset (zone); + if (free_struct) + free (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; + + if (zone->tzid) free(zone->tzid); + zone->tzid = strdup (tzid); + + if (zone->component) icalcomponent_free(zone->component); + zone->component = component; + + if (zone->location) free(zone->location); + zone->location = icaltimezone_get_location_from_vtimezone (component); + + if (zone->tznames) free(zone->tznames); + 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) { + size_t 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; + + 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_rdate = 0, has_rrule = 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: + has_rdate = 1; + break; + case ICAL_RRULE_PROPERTY: + has_rrule = 1; + break; + default: + /* Just ignore any other properties. */ + break; + } + + prop = icalcomponent_get_next_property (comp, ICAL_ANY_PROPERTY); + } + + /* Microsoft Outlook for Mac (and possibly other versions) will create + timezones without a tzoffsetfrom property if it's a timezone that + doesn't change for DST. */ + if (found_tzoffsetto && !found_tzoffsetfrom) { + change.prev_utc_offset = change.utc_offset; + found_tzoffsetfrom = 1; + } + + /* 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 rule, we add + a single change for the DTSTART. */ + if (!has_rrule) { + 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); + } + + /* The component has recurrence data, so we expand that now. */ + prop = icalcomponent_get_first_property (comp, ICAL_ANY_PROPERTY); + while (prop && (has_rdate || has_rrule)) { +#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; + } + + /* Add the dtstart to changes, otherwise some oddly-defined VTIMEZONE + components can cause the first year to get skipped. */ + change.year = dtstart.year; + change.month = dtstart.month; + change.day = dtstart.day; + change.hour = dtstart.hour; + change.minute = dtstart.minute; + change.second = dtstart.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); + + rrule_iterator = icalrecur_iterator_new (rrule, dtstart); + for (;rrule_iterator;) { + occ = icalrecur_iterator_next (rrule_iterator); + /* Skip dtstart since we just added it */ + if (icaltime_compare(dtstart, occ) == 0) + continue; + 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) +{ + const icaltimezonechange *change1, *change2; + int retval; + + change1 = (const icaltimezonechange *)elem1; + change2 = (const 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 the prev UTC offset. */ + if (change_num < 0) { + if (is_daylight) + *is_daylight = ! tmp_change.is_daylight; + return tmp_change.prev_utc_offset; + } + + 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 the prev UTC offset. */ + if (change_num < 0) { + if (is_daylight) + *is_daylight = ! tmp_change.is_daylight; + return tmp_change.prev_utc_offset; + } + + 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; + + 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; + + 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; + + 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); +} + + +/* Returns the timezone name to display to the user. We prefer to use the + Olson city name, but fall back on the TZNAME, or finally the TZID. We don't + want to use "" as it may be wrongly interpreted as a floating time. + Do not free the returned string. */ +const char* +icaltimezone_get_display_name (icaltimezone *zone) +{ + const char *display_name; + + display_name = icaltimezone_get_location (zone); + if (!display_name) + display_name = icaltimezone_get_tznames (zone); + if (!display_name) { + display_name = icaltimezone_get_tzid (zone); + /* Outlook will strip out X-LIC-LOCATION property and so all + we get back in the iTIP replies is the TZID. So we see if + this is one of our TZIDs and if so we jump to the city name + at the end of it. */ + if (display_name + && !strncmp (display_name, ical_tzid_prefix, strlen(ical_tzid_prefix))) { + /* Get the location, which is after the 3rd '/' char. */ + const char *p; + int num_slashes = 0; + for (p = display_name; *p; p++) { + if (*p == '/') { + num_slashes++; + if (num_slashes == 3) + return p + 1; + } + } + } + } + + return display_name; +} + +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); + builtin_timezones = 0; +} + + +/** Returns a single builtin timezone, given its Olson city name. */ +icaltimezone* +icaltimezone_get_builtin_timezone (const char *location) +{ + icalcomponent *comp; + icaltimezone *zone; + unsigned int lower; + const char *zone_location; + + if (!location || !location[0]) + return NULL; + + if (!builtin_timezones) + icaltimezone_init_builtin_timezones (); + + if (strcmp (location, "UTC") == 0 || strcmp (location, "GMT") == 0) + return &utc_timezone; + +#if 0 + /* 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; + } +#endif + + /* The zones from the system are not stored in alphabetical order, + so we just do a sequential search */ + for (lower = 0; lower < builtin_timezones->num_elements; lower++) { + zone = icalarray_element_at (builtin_timezones, lower); + zone_location = icaltimezone_get_location (zone); + if (strcmp (location, zone_location) == 0) + return zone; + } + + /* Check whether file exists, but is not mentioned in zone.tab. + It means it's a deprecated timezone, but still available. */ + comp = icaltzutil_fetch_timezone (location); + if (comp) { + icaltimezone tz; + icaltimezone_init (&tz); + if (icaltimezone_set_component (&tz, comp)) { + icalarray_append (builtin_timezones, &tz); + return icalarray_element_at (builtin_timezones, builtin_timezones->num_elements - 1); + } else { + icalcomponent_free (comp); + } + } + + return NULL; +} + +static struct icaltimetype +tm_to_icaltimetype (struct tm *tm) +{ + struct icaltimetype itt; + + memset (&itt, 0, sizeof (struct icaltimetype)); + + itt.second = tm->tm_sec; + itt.minute = tm->tm_min; + itt.hour = tm->tm_hour; + + itt.day = tm->tm_mday; + itt.month = tm->tm_mon + 1; + itt.year = tm->tm_year+ 1900; + + itt.is_utc = 0; + itt.is_date = 0; + + return itt; +} + +static int +get_offset (icaltimezone *zone) +{ + struct tm local; + struct icaltimetype tt; + int offset; + time_t now = time(NULL); + + gmtime_r ((const time_t *) &now, &local); + tt = tm_to_icaltimetype (&local); + offset = icaltimezone_get_utc_offset(zone, &tt, NULL); + + return offset; +} + +/** Returns a single builtin timezone, given its offset from UTC */ +icaltimezone* +icaltimezone_get_builtin_timezone_from_offset (int offset, const char *tzname) +{ + icaltimezone *zone=NULL; + int count, i; + + if (!builtin_timezones) + icaltimezone_init_builtin_timezones (); + + if (offset==0) + return &utc_timezone; + + if (!tzname) + return NULL; + + count = builtin_timezones->num_elements; + + for (i=0; i<count; i++) { + int z_offset; + zone = icalarray_element_at (builtin_timezones, i); + icaltimezone_load_builtin_timezone (zone); + + z_offset = get_offset(zone); + + if (z_offset == offset && zone->tznames && !strcmp(tzname, zone->tznames)) + return zone; + } + + 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; + + if (strcmp (tzid, "UTC") == 0 || strcmp (tzid, "GMT") == 0) { + return icaltimezone_get_builtin_timezone(tzid); + } + + /* Check that the TZID starts with our unique prefix. */ + if (strncmp (tzid, ical_tzid_prefix, strlen(ical_tzid_prefix))) + return NULL; + + /* Get the location, which is after the 3rd '/' character. */ + 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"; + +#ifdef HAVE_PTHREAD + pthread_mutex_lock(&builtin_mutex); +#endif + if (!builtin_timezones) + icaltimezone_parse_zone_tab (); +#ifdef HAVE_PTHREAD + pthread_mutex_unlock(&builtin_mutex); +#endif +} + +static int +parse_coord (char *coord, + int len, + int *degrees, + int *minutes, + int *seconds) +{ + if (len == 5) + sscanf (coord + 1, "%2d%2d", degrees, minutes); + else if (len == 6) + sscanf (coord + 1, "%3d%2d", degrees, minutes); + else if (len == 7) + sscanf (coord + 1, "%2d%2d%2d", degrees, minutes, seconds); + else if (len == 8) + sscanf (coord + 1, "%3d%2d%2d", degrees, minutes, seconds); + else { + fprintf (stderr, "Invalid coordinate: %s\n", coord); + return 1; + } + + if (coord [0] == '-') + *degrees = -*degrees; + return 0; +} +static int +fetch_lat_long_from_string (const char *str, int *latitude_degrees, int *latitude_minutes, int *latitude_seconds, + int *longitude_degrees, int *longitude_minutes, int *longitude_seconds, char *location) +{ + size_t len; + char *sptr, *lat, *lon, *loc, *temp; + + /* We need to parse the latitude/longitude co-ordinates and location fields */ + sptr = (char *) str; + while (*sptr != '\t') + sptr++; + temp = ++sptr; + while (*sptr != '\t') + sptr++; + len = sptr-temp; + lat = (char *) malloc (len + 1); + lat = strncpy (lat, temp, len); + lat [len] = '\0'; + while (*sptr != '\t') + sptr++; + + loc = ++sptr; + while (!isspace (*sptr)) + sptr++; + len = sptr - loc; + location = strncpy (location, loc, len); + location [len] = '\0'; + +#if defined(sun) && defined(__SVR4) + /* Handle EET, MET and WET in zone_sun.tab. */ + if (!strcmp (location, "Europe/")) { + while (*sptr != '\t') + sptr++; + loc = ++sptr; + while (!isspace (*sptr)) + sptr++; + len = sptr - loc; + location = strncpy (location, loc, len); + location [len] = '\0'; + } +#endif + + lon = lat + 1; + while (*lon != '+' && *lon != '-') + lon++; + + if (parse_coord (lat, (int)(lon - lat), + latitude_degrees, + latitude_minutes, + latitude_seconds) == 1 || + parse_coord (lon, (int)strlen(lon), + longitude_degrees, + longitude_minutes, + longitude_seconds) == 1) { + free(lat); + return 1; + } + + free (lat); + + return 0; +} + +/** 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. */ + size_t filename_len; + int latitude_degrees = 0, latitude_minutes = 0, latitude_seconds = 0; + int longitude_degrees = 0, longitude_minutes = 0, longitude_seconds = 0; + icaltimezone zone; + + icalerror_assert (builtin_timezones == NULL, + "Parsing zones.tab file multiple times"); + + builtin_timezones = icalarray_new (sizeof (icaltimezone), 32); + +#ifndef USE_BUILTIN_TZDATA + filename_len = strlen ((char *) icaltzutil_get_zone_directory()) + strlen (ZONES_TAB_SYSTEM_FILENAME) + + 2; +#else + filename_len = strlen (get_zone_directory()) + strlen (ZONES_TAB_FILENAME) + + 2; +#endif + + filename = (char*) malloc (filename_len); + if (!filename) { + icalerror_set_errno(ICAL_NEWFAILED_ERROR); + return; + } +#ifndef USE_BUILTIN_TZDATA + snprintf (filename, filename_len, "%s/%s", icaltzutil_get_zone_directory (), + ZONES_TAB_SYSTEM_FILENAME); +#else + snprintf (filename, filename_len, "%s/%s", get_zone_directory(), + ZONES_TAB_FILENAME); +#endif + + fp = fopen (filename, "r"); + free (filename); + if (!fp) { + icalerror_set_errno(ICAL_FILE_ERROR); + return; + } + + while (fgets (buf, sizeof(buf), fp)) { + if (*buf == '#') continue; + +#ifdef USE_BUILTIN_TZDATA + /* 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; + } +#else + if (fetch_lat_long_from_string (buf, &latitude_degrees, &latitude_minutes, + &latitude_seconds, + &longitude_degrees, &longitude_minutes, &longitude_seconds, + location)) { + fprintf (stderr, "Invalid timezone description line: %s\n", buf); + continue; + } +#endif + + 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); +} + +void +icaltimezone_release_zone_tab (void) +{ + unsigned int i; + icalarray *mybuiltin_timezones = builtin_timezones; + + if (builtin_timezones == NULL) + return; + builtin_timezones = NULL; + for (i = 0; i < mybuiltin_timezones->num_elements; i++) + free ( ((icaltimezone*)icalarray_element_at(mybuiltin_timezones, i))->location); + icalarray_free (mybuiltin_timezones); +} + +/** Loads the builtin VTIMEZONE data for the given timezone. */ +static void +icaltimezone_load_builtin_timezone (icaltimezone *zone) +{ + icalcomponent *subcomp; + + /* If the location isn't set, it isn't a builtin timezone. */ + if (!zone->location || !zone->location[0]) + return; + +#ifdef HAVE_PTHREAD + pthread_mutex_lock(&builtin_mutex); + if (zone->component) + goto out; +#else +#ifdef USE_BUILTIN_TZDATA + if (zone->component) + return; +#endif +#endif + +#ifdef USE_BUILTIN_TZDATA + { + char *filename; + icalcomponent *comp; + unsigned int filename_len; + FILE *fp; + icalparser *parser; + + filename_len = strlen (get_zone_directory()) + strlen (zone->location) + 6; + + filename = (char*) malloc (filename_len); + if (!filename) { + icalerror_set_errno(ICAL_NEWFAILED_ERROR); + goto out; + } + + 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); + goto out; + } + + + /* ##### 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); +#else + subcomp = icaltzutil_fetch_timezone (zone->location); +#endif + + if (!subcomp) { + icalerror_set_errno(ICAL_PARSE_ERROR); + goto out; + } + + icaltimezone_get_vtimezone_properties (zone, subcomp); + +#ifdef USE_BUILTIN_TZDATA + icalcomponent_remove_component(comp,subcomp); + icalcomponent_free(comp); + } +#endif + + out: +#ifdef HAVE_PTHREAD + pthread_mutex_unlock(&builtin_mutex); +#endif + return; +} + + +#ifdef USE_BUILTIN_TZDATA +/** 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); +} +#endif + + + +/* + * 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 + + 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) +{ +#ifndef WIN32 + return zone_files_directory == NULL ? ZONEINFO_DIRECTORY : zone_files_directory; +#else + wchar_t wbuffer[1000]; +#ifndef _WIN32_WCE + char buffer[1000], zoneinfodir[1000], dirname[1000]; +#else + wchar_t zoneinfodir[1000], dirname[1000]; +#endif + int used_default; + static char *cache = NULL; +#ifndef _WIN32_WCE + char *dirslash, *zislash; +#else + wchar_t *dirslash, *zislash; +#endif + struct stat st; + + if (zone_files_directory) + return zone_files_directory; + + if (cache) + return cache; + + /* Get the filename of the application */ + if (!GetModuleFileNameW (NULL, wbuffer, sizeof (wbuffer) / sizeof (wbuffer[0]))) + return ZONEINFO_DIRECTORY; + +/*wince supports only unicode*/ +#ifndef _WIN32_WCE + /* Convert to system codepage */ + if (!WideCharToMultiByte (CP_ACP, 0, wbuffer, -1, buffer, sizeof (buffer), + NULL, &used_default) || + used_default) { + /* Failed, try 8.3 format */ + if (!GetShortPathNameW (wbuffer, wbuffer, + sizeof (wbuffer) / sizeof (wbuffer[0])) || + !WideCharToMultiByte (CP_ACP, 0, wbuffer, -1, buffer, sizeof (buffer), + NULL, &used_default) || + used_default) + return ZONEINFO_DIRECTORY; + } +#endif + /* Look for the zoneinfo directory somewhere in the path where + * the app is installed. If the path to the app is + * + * C:\opt\evo-2.6\bin\evolution-2.6.exe + * + * and the compile-time ZONEINFO_DIRECTORY is + * + * C:/devel/target/evo/share/evolution-data-server-1.6/zoneinfo, + * + * we check the pathnames: + * + * C:\opt\evo-2.6/devel/target/evo/share/evolution-data-server-1.6/zoneinfo + * C:\opt\evo-2.6/target/evo/share/evolution-data-server-1.6/zoneinfo + * C:\opt\evo-2.6/evo/share/evolution-data-server-1.6/zoneinfo + * C:\opt\evo-2.6/share/evolution-data-server-1.6/zoneinfo <=== + * C:\opt\evo-2.6/evolution-data-server-1.6/zoneinfo + * C:\opt\evo-2.6/zoneinfo + * C:\opt/devel/target/evo/share/evolution-data-server-1.6/zoneinfo + * C:\opt/target/evo/share/evolution-data-server-1.6/zoneinfo + * C:\opt/evo/share/evolution-data-server-1.6/zoneinfo + * C:\opt/share/evolution-data-server-1.6/zoneinfo + * C:\opt/evolution-data-server-1.6/zoneinfo + * C:\opt/zoneinfo + * C:/devel/target/evo/share/evolution-data-server-1.6/zoneinfo + * C:/target/evo/share/evolution-data-server-1.6/zoneinfo + * C:/evo/share/evolution-data-server-1.6/zoneinfo + * C:/share/evolution-data-server-1.6/zoneinfo + * C:/evolution-data-server-1.6/zoneinfo + * C:/zoneinfo + * + * In Evolution's case, we would get a match already at the + * fourth pathname check. + */ + + /* Strip away basename of app .exe first */ +#ifndef _WIN32_WCE + dirslash = _mbsrchr (buffer, '\\'); +#else + dirslash = wcsrchr (wbuffer, L'\\'); +#endif + if (dirslash) +#ifndef _WIN32_WCE + *dirslash = '\0'; +#else + *dirslash = L'\0'; +#endif + +#ifdef _WIN32_WCE + while ((dirslash = wcsrchr (wbuffer, '\\'))) { + /* Strip one more directory from app .exe location */ + *dirslash = L'\0'; + + MultiByteToWideChar(CP_ACP,0,ZONEINFO_DIRECTORY,-1,zoneinfodir,1000); + + while ((zislash = wcschr (zoneinfodir, L'/'))) { + *zislash = L'.'; + wcscpy (dirname, wbuffer); + wcscat (dirname, "/"); + wcscat (dirname, zislash + 1); + if (stat (dirname, &st) == 0 && + S_ISDIR (st.st_mode)) { + cache = wce_wctomb (dirname); + return cache; + } + } + } +#else + while ((dirslash = _mbsrchr (buffer, '\\'))) { + /* Strip one more directory from app .exe location */ + *dirslash = '\0'; + + strcpy (zoneinfodir, ZONEINFO_DIRECTORY); + while ((zislash = _mbschr (zoneinfodir, '/'))) { + *zislash = '.'; + strcpy (dirname, buffer); + strcat (dirname, "/"); + strcat (dirname, zislash + 1); + if (stat (dirname, &st) == 0 && + S_ISDIR (st.st_mode)) { + cache = strdup (dirname); + return cache; + } + } + } +#endif + return ZONEINFO_DIRECTORY; +#endif +} + +void set_zone_directory(char *path) +{ + if (zone_files_directory) + free_zone_directory(); + 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); + zone_files_directory = NULL; + } +} + +void icaltimezone_set_tzid_prefix(const char *new_prefix) +{ + if (new_prefix) { + ical_tzid_prefix = new_prefix; + } +} diff --git a/src/libical/icaltimezone.h b/src/libical/icaltimezone.h new file mode 100644 index 00000000..0da233d6 --- /dev/null +++ b/src/libical/icaltimezone.h @@ -0,0 +1,180 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/*====================================================================== + FILE: icaltimezone.h + CREATOR: Damon Chaplin 15 March 2001 + + + $Id: icaltimezone.h,v 1.14 2008-01-15 23:17:42 dothebart Exp $ + $Locker: $ + + (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); +icaltimezone *icaltimezone_copy (icaltimezone *originalzone); + +/** 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); + +/** Sets the prefix to be used for tzid's generated from system tzdata. + Must be globally unique (such as a domain name owned by the developer + of the calling application), and begin and end with forward slashes. + Do not change or de-allocate the string buffer after calling this. + */ +void icaltimezone_set_tzid_prefix(const char *new_prefix); + +/** + * @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 offset. */ +icaltimezone* icaltimezone_get_builtin_timezone_from_offset (int offset, const char *tzname); + +/** 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); + +const char* icaltimezone_get_display_name (icaltimezone *zone); + +/** + * @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); +void icaltimezone_release_zone_tab(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/src/libical/icaltimezoneimpl.h b/src/libical/icaltimezoneimpl.h new file mode 100644 index 00000000..354d5bb2 --- /dev/null +++ b/src/libical/icaltimezoneimpl.h @@ -0,0 +1,88 @@ +/* -*- Mode: C -*- + ====================================================================== + FILE: icaltimezoneimpl.h + CREATOR: glenn 07 March 2010 + + $Id: icalparameterimpl.h,v 1.4 2008-01-15 23:17:40 dothebart Exp $ + $Locker: $ + + + (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/ + + Contributions from: + + ======================================================================*/ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#ifndef ICALTIMEZONE_IMPL +#define ICALTIMEZONE_IMPL + +#include "icaltimezone.h" +#include "icalcomponent.h" +#include "icalarray.h" + +struct _icaltimezone { + char *tzid; + /**< The unique ID of this timezone, + e.g. "/citadel.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. */ +}; + + + +#endif /*ICALTIMEZONE_IMPL*/ diff --git a/src/libical/icaltypes.c b/src/libical/icaltypes.c new file mode 100644 index 00000000..8ce5d1d8 --- /dev/null +++ b/src/libical/icaltypes.c @@ -0,0 +1,236 @@ +/* -*- Mode: C -*- + ====================================================================== + FILE: icaltypes.c + CREATOR: eric 16 May 1999 + + $Id: icaltypes.c,v 1.18 2008-01-15 23:17:42 dothebart Exp $ + $Locker: $ + + + (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> + +#if defined(_MSC_VER) +#define snprintf _snprintf +#define strcasecmp stricmp +#endif + +#define TEMP_MAX 1024 + +#ifdef HAVE_PTHREAD + #include <pthread.h> + static pthread_mutex_t unk_token_mutex = PTHREAD_MUTEX_INITIALIZER; +#endif + +static ical_unknown_token_handling unknownTokenHandling = ICAL_TREAT_AS_ERROR; + +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; + icalerrorstate es = ICAL_ERROR_DEFAULT; + icalerrorenum e; + + tr.time= icaltime_null_time(); + tr.duration = icaldurationtype_from_int(0); + + /* 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 *buf; + buf = icalreqstattype_as_string_r(stat); + icalmemory_add_tmp_buffer(buf); + return buf; +} + + +char* icalreqstattype_as_string_r(struct icalreqstattype stat) +{ + char *temp; + + icalerror_check_arg_rz((stat.code != ICAL_UNKNOWN_STATUS),"Status"); + + temp = (char*)icalmemory_new_buffer(TEMP_MAX); + + 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; +} + +ical_unknown_token_handling ical_get_unknown_token_handling_setting(void) +{ + ical_unknown_token_handling myHandling; + +#ifdef HAVE_PTHREAD + pthread_mutex_lock (&unk_token_mutex); +#endif + + myHandling = unknownTokenHandling; + +#ifdef HAVE_PTHREAD + pthread_mutex_unlock (&unk_token_mutex); +#endif + + return myHandling; +} + +void ical_set_unknown_token_handling_setting(ical_unknown_token_handling newSetting) +{ + +#ifdef HAVE_PTHREAD + pthread_mutex_lock (&unk_token_mutex); +#endif + + unknownTokenHandling = newSetting; + +#ifdef HAVE_PTHREAD + pthread_mutex_unlock (&unk_token_mutex); +#endif + +} diff --git a/src/libical/icaltypes.h b/src/libical/icaltypes.h new file mode 100644 index 00000000..b1629c22 --- /dev/null +++ b/src/libical/icaltypes.h @@ -0,0 +1,122 @@ +/* -*- 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 +{ + double lat; + double 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 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); +char* icalreqstattype_as_string_r(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); + +/* ical_unknown_token_handling : + * How should the ICAL library handle components, properties and parameters with + * unknown names? + * FIXME: Currently only affects parameters. Extend to components and properties. + */ +typedef enum ical_unknown_token_handling { + ICAL_ASSUME_IANA_TOKEN = 1, + ICAL_DISCARD_TOKEN = 2, + ICAL_TREAT_AS_ERROR = 3 +} ical_unknown_token_handling; + +ical_unknown_token_handling ical_get_unknown_token_handling_setting(void); +void ical_set_unknown_token_handling_setting(ical_unknown_token_handling newSetting); + +#endif /* !ICALTYPES_H */ diff --git a/src/libical/icaltz-util.c b/src/libical/icaltz-util.c new file mode 100644 index 00000000..adf75982 --- /dev/null +++ b/src/libical/icaltz-util.c @@ -0,0 +1,439 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Authors : + * Chenthill Palanisamy <pchenthill@novell.com> + * + * Copyright 2007, Novell, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU Lesser General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include <string.h> + +#ifdef HAVE_STDINT_H +#include <stdint.h> +#endif + +#if defined(sun) && defined(__SVR4) +#include <sys/types.h> +#include <sys/byteorder.h> +#else +# ifdef HAVE_BYTESWAP_H +# include <byteswap.h> +# endif +# ifdef HAVE_ENDIAN_H +# include <endian.h> +# else +# ifdef HAVE_SYS_ENDIAN_H +# include <sys/endian.h> +# ifdef bswap32 +# define bswap_32 bswap32 +# else +# define bswap_32 swap32 +# endif +# endif +# endif +#endif + +#ifdef _MSC_VER +#if !defined(HAVE_BYTESWAP_H) && !defined(HAVE_SYS_ENDIAN_H) && !defined(HAVE_ENDIAN_H) +#define bswap_16(x) (((x) << 8) & 0xff00) | (((x) >> 8 ) & 0xff) +#define bswap_32(x) (((x) << 24) & 0xff000000) \ + | (((x) << 8) & 0xff0000) \ + | (((x) >> 8) & 0xff00) \ + | (((x) >> 24) & 0xff ) +#define bswap_64(x) ((((x) & 0xff00000000000000ull) >> 56) \ + | (((x) & 0x00ff000000000000ull) >> 40) \ + | (((x) & 0x0000ff0000000000ull) >> 24) \ + | (((x) & 0x000000ff00000000ull) >> 8) \ + | (((x) & 0x00000000ff000000ull) << 8) \ + | (((x) & 0x0000000000ff0000ull) << 24) \ + | (((x) & 0x000000000000ff00ull) << 40) \ + | (((x) & 0x00000000000000ffull) << 56)) +#endif +#include <io.h> +#endif + +#if defined(__APPLE__) +#define bswap_16(x) (((x) << 8) & 0xff00) | (((x) >> 8 ) & 0xff) +#define bswap_32 __builtin_bswap32 +#define bswap_64 __builtin_bswap64 +#endif + +#ifndef PATH_MAX +#define PATH_MAX 512 +#endif + +#ifndef F_OK +#define F_OK 0 +#endif + +#ifndef R_OK +#define R_OK 4 +#endif + +#include <limits.h> +#include <time.h> +#include <stdlib.h> +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#include <libical/icalerror.h> +#include <icaltz-util.h> + +typedef struct +{ + char ttisgmtcnt [4]; + char ttisstdcnt[4]; + char leapcnt[4]; + char timecnt[4]; + char typecnt[4]; + char charcnt[4]; +} tzinfo; + +static int r_pos [] = {1, 2, 3, -2, -1}; + +static char *search_paths [] = {"/usr/share/zoneinfo","/usr/lib/zoneinfo","/etc/zoneinfo","/usr/share/lib/zoneinfo"}; +static char *zdir = NULL; + +#define NUM_SEARCH_PATHS (sizeof (search_paths)/ sizeof (search_paths [0])) +#define EFREAD(buf,size,num,fs) \ + if (fread (buf, size, num, fs) == 0 && ferror (fs)) {\ + icalerror_set_errno (ICAL_FILE_ERROR); \ + goto error; \ + } \ + +typedef struct +{ + long int gmtoff; + unsigned char isdst; + unsigned int abbr; + unsigned char isstd; + unsigned char isgmt; + char *zname; + +} ttinfo; + +typedef struct +{ + time_t transition; + long int change; +} leap; + +extern const char *ical_tzid_prefix; + +static int +decode (const void *ptr) +{ +#if defined(sun) && defined(__SVR4) + if (sizeof (int) == 4) +#ifdef _BIG_ENDIAN + return *(const int *) ptr; +#else + return BSWAP_32 (*(const int *) ptr); +#endif +#else + if ((BYTE_ORDER == BIG_ENDIAN) && sizeof (int) == 4) + return *(const int *) ptr; + else if (BYTE_ORDER == LITTLE_ENDIAN && sizeof (int) == 4) + return bswap_32 (*(const int *) ptr); +#endif + else + { + const unsigned char *p = ptr; + int result = *p & (1 << (CHAR_BIT - 1)) ? ~0 : 0; + + result = (result << 8) | *p++; + result = (result << 8) | *p++; + result = (result << 8) | *p++; + result = (result << 8) | *p++; + + return result; + } +} + +static char * +zname_from_stridx (char *str, long int idx) +{ + int i = 0; + char *ret; + size_t size; + + i = idx; + + while (str [i] != '\0') + i++; + + size = i - idx; + str += idx; + ret = (char *) malloc (size + 1); + ret = strncpy (ret, str, size); + ret [size] = '\0'; + + return ret; +} + +static void +set_zonedir (void) +{ + char file_path[PATH_MAX]; + const char *fname = ZONES_TAB_SYSTEM_FILENAME; + int i; + + for (i = 0;i < NUM_SEARCH_PATHS; i++) { + sprintf (file_path, "%s/%s", search_paths [i], fname); + if (!access (file_path, F_OK|R_OK)) { + zdir = search_paths [i]; + break; + } + } +} + + +const char * +icaltzutil_get_zone_directory (void) +{ + if (!zdir) + set_zonedir (); + + return zdir; +} + +/* Calculate the relative position of the week in a month from a date */ +static int +calculate_pos (icaltimetype icaltime) +{ + int pos; + + pos = (icaltime.day -1) / 7; + + /* Check if pos 3 is the last occurence of the week day in the month */ + if (pos == 3 && ((icaltime.day + 7) > icaltime_days_in_month (icaltime.month, icaltime.year))) + pos = 4; + + return r_pos [pos]; +} + +icalcomponent* +icaltzutil_fetch_timezone (const char *location) +{ + int ret = 0; + FILE *f; + tzinfo type_cnts; + unsigned int i, num_trans, num_types, num_chars, num_leaps, num_isstd, num_isgmt; + time_t *transitions = NULL; + time_t trans, start, end; + int *trans_idx = NULL, pos, sign, zidx, zp_idx, idx, prev_idx; + ttinfo *types = NULL; + char *znames = NULL, *full_path, *tzid, *r_trans, *temp; + leap *leaps = NULL; + icalcomponent *tz_comp = NULL, *comp = NULL; + icalproperty *icalprop; + icaltimetype dtstart, icaltime; + struct icalrecurrencetype ical_recur; + const char *basedir; + + basedir = icaltzutil_get_zone_directory(); + if (!basedir) { + icalerror_set_errno (ICAL_FILE_ERROR); + return NULL; + } + + full_path = (char *) malloc (strlen (basedir) + strlen (location) + 2); + sprintf (full_path,"%s/%s",basedir, location); + + if ((f = fopen (full_path, "rb")) == 0) { + icalerror_set_errno (ICAL_FILE_ERROR); + free (full_path); + return NULL; + } + + if ((ret = fseek (f, 20, SEEK_SET)) != 0) { + icalerror_set_errno (ICAL_FILE_ERROR); + goto error; + } + + EFREAD(&type_cnts, 24, 1, f); + + num_isgmt = decode (type_cnts.ttisgmtcnt); + num_leaps = decode (type_cnts.leapcnt); + num_chars = decode (type_cnts.charcnt); + num_trans = decode (type_cnts.timecnt); + num_isstd = decode (type_cnts.ttisstdcnt); + num_types = decode (type_cnts.typecnt); + + transitions = calloc (num_trans, sizeof (time_t)); + r_trans = calloc (num_trans, 4); + EFREAD(r_trans, 4, num_trans, f); + temp = r_trans; + + if (num_trans) { + trans_idx = calloc (num_trans, sizeof (int)); + for (i = 0; i < num_trans; i++) { + trans_idx [i] = fgetc (f); + transitions [i] = decode (r_trans); + r_trans += 4; + } + } + + free (temp); + + types = calloc (num_types, sizeof (ttinfo)); + for (i = 0; i < num_types; i++) { + unsigned char a [4]; + int c; + + EFREAD(a, 4, 1, f); + c = fgetc (f); + types [i].isdst = c; + if((c = fgetc (f)) < 0) { + c = 0; + break; + } + types [i].abbr = c; + types [i].gmtoff = decode (a); + } + + znames = (char *) malloc (num_chars); + EFREAD(znames, num_chars, 1, f); + + /* We got all the information which we need */ + + leaps = calloc (num_leaps, sizeof (leap)); + for (i = 0; i < num_leaps; i++) { + char c [4]; + + EFREAD (c, 4, 1, f); + leaps [i].transition = decode (c); + + EFREAD (c, 4, 1, f); + leaps [i].change = decode (c); + } + + for (i = 0; i < num_isstd; ++i) { + int c = getc (f); + types [i].isstd = c != 0; + } + + while (i < num_types) + types [i++].isstd = 0; + + for (i = 0; i < num_isgmt; ++i) { + int c = getc (f); + types [i].isgmt = c != 0; + } + + while (i < num_types) + types [i++].isgmt = 0; + + /* Read all the contents now */ + + for (i = 0; i < num_types; i++) + types [i].zname = zname_from_stridx (znames, types [i].abbr); + + tz_comp = icalcomponent_new (ICAL_VTIMEZONE_COMPONENT); + + /* Add tzid property */ + tzid = (char *) malloc (strlen (ical_tzid_prefix) + strlen (location) + 8); + sprintf (tzid, "%sTzfile/%s", ical_tzid_prefix, location); + icalprop = icalproperty_new_tzid (tzid); + icalcomponent_add_property (tz_comp, icalprop); + free (tzid); + + icalprop = icalproperty_new_x (location); + icalproperty_set_x_name (icalprop, "X-LIC-LOCATION"); + icalcomponent_add_property (tz_comp, icalprop); + + prev_idx = 0; + if (num_trans == 0) { + prev_idx = idx = 0; + + } else { + idx = trans_idx[0]; + } + start = 0; + for (i = 1; i < num_trans; i++, start = end) { + prev_idx = idx; + idx = trans_idx [i]; + end = transitions [i] + types [prev_idx].gmtoff; + /* don't bother starting until the epoch */ + if (0 > end) + continue; + + if (types [prev_idx].isdst) + comp = icalcomponent_new (ICAL_XDAYLIGHT_COMPONENT); + else + comp = icalcomponent_new (ICAL_XSTANDARD_COMPONENT); + icalprop = icalproperty_new_tzname (types [prev_idx].zname); + icalcomponent_add_property (comp, icalprop); + dtstart = icaltime_from_timet(start, 0); + icalprop = icalproperty_new_dtstart (dtstart); + icalcomponent_add_property (comp, icalprop); + icalprop = icalproperty_new_tzoffsetfrom (types [idx].gmtoff); + icalcomponent_add_property (comp, icalprop); + icalprop = icalproperty_new_tzoffsetto (types [prev_idx].gmtoff); + icalcomponent_add_property (comp, icalprop); + icalcomponent_add_component (tz_comp, comp); + } + /* finally, add a last zone with no end date */ + if (types [idx].isdst) + comp = icalcomponent_new (ICAL_XDAYLIGHT_COMPONENT); + else + comp = icalcomponent_new (ICAL_XSTANDARD_COMPONENT); + icalprop = icalproperty_new_tzname (types [idx].zname); + icalcomponent_add_property (comp, icalprop); + dtstart = icaltime_from_timet(start, 0); + icalprop = icalproperty_new_dtstart (dtstart); + icalcomponent_add_property (comp, icalprop); + icalprop = icalproperty_new_tzoffsetfrom (types [prev_idx].gmtoff); + icalcomponent_add_property (comp, icalprop); + icalprop = icalproperty_new_tzoffsetto (types [idx].gmtoff); + icalcomponent_add_property (comp, icalprop); + icalcomponent_add_component (tz_comp, comp); + + +error: + if (f) + fclose (f); + + if (transitions) + free (transitions); + if (trans_idx) + free (trans_idx); + if (types) { + for (i = 0; i < num_types; i++) + if (types [i].zname) + free (types [i].zname); + free (types); + } + if (znames) + free (znames); + free (full_path); + if (leaps) + free (leaps); + + return tz_comp; +} + +/* +int +main (int argc, char *argv []) +{ + tzutil_fetch_timezone (argv [1]); + return 0; +}*/ diff --git a/src/libical/icaltz-util.h b/src/libical/icaltz-util.h new file mode 100644 index 00000000..187cc7f2 --- /dev/null +++ b/src/libical/icaltz-util.h @@ -0,0 +1,39 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Authors : + * Chenthill Palanisamy <pchenthill@novell.com> + * + * Copyright 2007, Novell, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU Lesser General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef ICALTZUTIL_H +#define ICALTZUTIL_H + +#include <stdio.h> +#include "icaltime.h" +#include "icalcomponent.h" + +#if defined(sun) && defined(__SVR4) +#define ZONES_TAB_SYSTEM_FILENAME "tab/zone_sun.tab" +#else +#define ZONES_TAB_SYSTEM_FILENAME "zone.tab" +#endif + +const char *icaltzutil_get_zone_directory (void); +icalcomponent *icaltzutil_fetch_timezone (const char *location); + +#endif diff --git a/src/libical/icalvalue.c b/src/libical/icalvalue.c new file mode 100644 index 00000000..8a3d0882 --- /dev/null +++ b/src/libical/icalvalue.c @@ -0,0 +1,1585 @@ +/* -*- 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 + + $Id: icalvalue.c,v 1.44 2008-01-15 23:17:43 dothebart Exp $ + + + (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 */ +#include <locale.h> +#include <ctype.h> /* for isspace and isdigit */ + +#if defined(_MSC_VER) +#define snprintf _snprintf +#define strcasecmp stricmp +#endif + +#if _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 ) { + icalvalue_free(new); + return 0; + } + + } + break; + } + case ICAL_ACTION_VALUE: + { + new->data = old->data; + + if (old->data.v_enum == ICAL_ACTION_X) { + //preserve the custom action string + if (old->x_value != 0) { + new->x_value = icalmemory_strdup(old->x_value); + + if (new->x_value == 0) { + icalvalue_free(new); + 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){ + icalvalue_free(new); + 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) { + icalvalue_free(new); + 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; + int wroteNull = 0; + + if (out == 0){ + return 0; + } + + pout = out; + + /* Stop the loop when encountering a terminator in the source string + or if a null has been written to the destination. This prevents + reading past the end of the source string if the last character + is a backslash. */ + for (p = str; !wroteNull && *p!=0; p++){ + + if( *p == '\\') + { + p++; + switch(*p){ + case 0: + { + wroteNull = 1; //stops iteration so p isn't incremented past the end of str + *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; +} + +/* + * Returns a quoted copy of a string + * @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* icalmemory_strdup_and_quote(const icalvalue* value, + const char* unquoted_str) +{ + char *str; + char *str_p; + const char *p; + size_t buf_sz; + + buf_sz = strlen(unquoted_str)+1; + + str_p = str = (char*)icalmemory_new_buffer(buf_sz); + + if (str_p == 0){ + return 0; + } + + for(p=unquoted_str; *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 ',': + /* unescaped COMMA is allowed in CATEGORIES property as its + considered a list delimiter here, see: + http://tools.ietf.org/html/rfc2445#section-4.3.11 */ + if ((icalproperty_isa(value->parent) == ICAL_CATEGORIES_PROPERTY) || + (icalproperty_isa(value->parent) == ICAL_RESOURCES_PROPERTY) || + (icalproperty_isa(value->parent) == ICAL_POLLPROPERTIES_PROPERTY)) { + icalmemory_append_char(&str,&str_p,&buf_sz,*p); + break; + } + 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'); + return str; +} + +/* + * 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; +} + +/** + * Transforms a simple float number string into a double. + * The decimal separator (if any) of the double has to be '.' + * The code is locale *independant* and does *not* change the locale. + * It should be thread safe. + * If you want a code that that does the same job with a decimal separator + * dependant on the current locale, then use strtof() from libc. + */ +int simple_str_to_double(const char* from, + double *result, + char** to) +{ +#define TMP_NUM_SIZE 100 + char *start=NULL, *end=NULL, *cur=(char*)from ; + char tmp_buf[TMP_NUM_SIZE+1] ; /*hack*/ +#ifndef _WIN32_WCE + struct lconv *loc_data = localeconv () ; +#endif + int i=0 ; + + /*sanity checks*/ + if (!from || !result) { + return 1 ; + } + + /*skip the white spaces at the beginning*/ + while (cur && isspace (*cur)) + cur++ ; + + start = cur ; + /* + * copy the part that looks like a double into tmp_buf + * so that we can call strtof() on it. + * during the copy, we give ourselves a chance to convert the '.' + * into the decimal separator of the current locale. + */ + while (cur && (isdigit (*cur) || + *cur == '.' || + *cur == '+' || + *cur == '-')){ + ++cur ; + } + end = cur ; + if (end - start + 1> 100) { + /*huh hoh, number is too big. getting out*/ + return 1 ; + } + memset(tmp_buf, 0, TMP_NUM_SIZE+1) ; + + /* + * copy the float number string into tmp_buf, and take + * care to have the (optional) decimal separator be the one + * of the current locale. + */ +#ifndef _WIN32_WCE + for (i=0 ; i < end - from ;++i) { + if (start[i] == '.' + && loc_data + && loc_data->decimal_point + && loc_data->decimal_point[0] + && loc_data->decimal_point[0] != '.') { + /*replace '.' by the digit separator of the current locale*/ + tmp_buf[i] = loc_data->decimal_point[0] ; + } else { + tmp_buf[i] = start[i] ; + } + } +#else + GetNumberFormat(LOCALE_SYSTEM_DEFAULT,0,start, NULL, tmp_buf,TMP_NUM_SIZE); +#endif + if (to) + *to = end ; + *result = atof(tmp_buf) ; + return 0 ; +} + +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_CMD_VALUE: + value = icalvalue_new_enum(kind, ICAL_CMD_X,str); + break; + case ICAL_QUERYLEVEL_VALUE: + value = icalvalue_new_enum(kind, ICAL_QUERYLEVEL_X,str); + break; + case ICAL_CARLEVEL_VALUE: + value = icalvalue_new_enum(kind, ICAL_CARLEVEL_X,str); + break; + case ICAL_BUSYTYPE_VALUE: + value = icalvalue_new_enum(kind, ICAL_BUSYTYPE_X,str); + break; + case ICAL_POLLMODE_VALUE: + value = icalvalue_new_enum(kind, ICAL_POLLMODE_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: + { + char *cur=NULL ; + struct icalgeotype geo = {0.0, 0.0}; + + if (simple_str_to_double (str, &geo.lat, &cur)) { + goto geo_parsing_error ; + } + + /*skip white spaces*/ + while (cur && isspace (*cur)) { + ++cur ; + } + + /*there is a ';' between the latitude and longitude parts*/ + if (!cur || *cur != ';') { + goto geo_parsing_error ; + } + + ++cur ; + if (!cur) + goto geo_parsing_error ; + + /*skip white spaces*/ + while (cur && isspace (*cur)) { + ++cur ; + } + + if (simple_str_to_double (cur, &geo.lon, &cur)) { + goto geo_parsing_error ; + } + value = icalvalue_new_geo (geo) ; + break ; + +geo_parsing_error: + if (error != 0){ + char temp[TMP_BUF_SIZE]; + sprintf(temp, "Could not parse %s as a %s property", + str, icalvalue_kind_to_string(kind)); + *error = icalproperty_vanew_xlicerror( + temp, + icalparameter_new_xlicerrortype( + ICAL_XLICERRORTYPE_VALUEPARSEERROR), + 0); + } + } + 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_STRING_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_r(const icalvalue* value) { + + const char* data; + char* str; + icalerror_check_arg_rz( (value!=0),"value"); + + data = icalvalue_get_binary(value); + + str = (char*)icalmemory_new_buffer(60); + snprintf(str, 60,"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_r(const icalvalue* value) { + int data; + char* str; + + icalerror_check_arg_rz( (value!=0),"value"); + str = (char*)icalmemory_new_buffer(MAX_INT_DIGITS); + + data = icalvalue_get_integer(value); + + snprintf(str,MAX_INT_DIGITS,"%d",data); + + return str; +} + + +static char* icalvalue_utcoffset_as_ical_string_r(const icalvalue* value) +{ + int data,h,m,s; + char sign; + char* str; + + icalerror_check_arg_rz( (value!=0),"value"); + + str = (char*)icalmemory_new_buffer(9); + 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_r(const icalvalue* value) { + + const char* data; + char* str = 0; + icalerror_check_arg_rz( (value!=0),"value"); + data = value->data.v_string; + + str = (char*)icalmemory_new_buffer(strlen(data)+1); + + strcpy(str,data); + + return str; +} + + +static char* icalvalue_recur_as_ical_string_r(const icalvalue* value) +{ + struct icalrecurrencetype *recur = value->data.v_recur; + return icalrecurrencetype_as_string_r(recur); +} + +static char* icalvalue_text_as_ical_string_r(const icalvalue* value) { + return icalmemory_strdup_and_quote(value, value->data.v_string); +} + + + +static char* +icalvalue_attach_as_ical_string_r(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_new_buffer (strlen (url) + 1); + strcpy (str, url); + return str; + } else { + const char *data = 0; + data = (const char*)icalattach_get_data(a); + str = icalmemory_new_buffer (strlen (data) + 1); + strcpy (str, data); + return str; +} +} + + +static char* icalvalue_duration_as_ical_string_r(const icalvalue* value) { + + struct icaldurationtype data; + + icalerror_check_arg_rz( (value!=0),"value"); + data = icalvalue_get_duration(value); + + return icaldurationtype_as_ical_string_r(data); +} + + + +void print_time_to_string(char* str, const struct icaltimetype *data) +{ + char temp[20]; + str[0] = '\0'; + + if (data != 0) { + if (icaltime_is_utc(*data)){ + snprintf(temp,sizeof(temp),"%02d%02d%02dZ",data->hour,data->minute,data->second); + strncat(str,temp,7); + } else { + snprintf(temp,sizeof(temp),"%02d%02d%02d",data->hour,data->minute,data->second); + strncat(str,temp,6); + } + } +} + + +void print_date_to_string(char* str, const struct icaltimetype *data) +{ + char temp[20]; + str[0] = '\0'; + + if (data != 0) { + snprintf(temp,sizeof(temp),"%04d%02d%02d",data->year,data->month,data->day); + strncat(str,temp,8); + } +} + +static char* icalvalue_date_as_ical_string_r(const icalvalue* value) { + + struct icaltimetype data; + char* str; + icalerror_check_arg_rz( (value!=0),"value"); + data = icalvalue_get_date(value); + + str = (char*)icalmemory_new_buffer(9); + + str[0] = '\0'; + print_date_to_string(str,&data); + + return str; +} + +void print_datetime_to_string(char* str, const struct icaltimetype *data) +{ + char temp[20]; + str[0] = '\0'; + + if (data != 0) { + print_date_to_string(str,data); + if ( !data->is_date ) { + strncat(str,"T",19); + temp[0] = '\0'; + print_time_to_string(temp,data); + strncat(str,temp,19); + } + } +} + + +static char* icalvalue_datetime_as_ical_string_r(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_new_buffer(20); + + str[0] = 0; + print_datetime_to_string(str,&data); + + return str; + +} + +static char* icalvalue_float_as_ical_string_r(const icalvalue* value) { + + float data; + char* str; + char* old_locale; + icalerror_check_arg_rz( (value!=0),"value"); + data = icalvalue_get_float(value); + + /* bypass current locale in order to make + sure snprintf uses a '.' as a separator + set locate to 'C' and keep old locale */ + old_locale = strdup (setlocale (LC_NUMERIC,NULL)); + setlocale (LC_NUMERIC,"C"); + + str = (char*)icalmemory_new_buffer(40); + + snprintf(str,40,"%f",data); + + /* restore saved locale */ + setlocale (LC_NUMERIC,old_locale); + free (old_locale); + + return str; +} + + +static char* icalvalue_geo_as_ical_string_r(const icalvalue* value) { + + struct icalgeotype data; + char* str; + char* old_locale; + icalerror_check_arg_rz( (value!=0),"value"); + + data = icalvalue_get_geo(value); + + /* bypass current locale in order to make + * sure snprintf uses a '.' as a separator + * set locate to 'C' and keep old locale */ + old_locale = strdup (setlocale (LC_NUMERIC,NULL)); + setlocale (LC_NUMERIC,"C"); + + str = (char*)icalmemory_new_buffer(80); + + snprintf(str,80,"%f;%f",data.lat,data.lon); + + /* restore saved locale */ + setlocale (LC_NUMERIC,old_locale); + free (old_locale); + + return str; +} + + +static char* icalvalue_datetimeperiod_as_ical_string_r(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_r(dtp.time); + } else { + return icalperiodtype_as_ical_string_r(dtp.period); + } +} + + +static char* icalvalue_period_as_ical_string_r(const icalvalue* value) { + struct icalperiodtype data; + icalerror_check_arg_rz( (value!=0),"value"); + data = icalvalue_get_period(value); + + return icalperiodtype_as_ical_string_r(data); + +} + + +static char* icalvalue_trigger_as_ical_string_r(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_r(data.time); + } else { + return icaldurationtype_as_ical_string_r(data.duration); + } + +} + +const char* +icalvalue_as_ical_string(const icalvalue* value) +{ + char *buf; + buf = icalvalue_as_ical_string_r(value); + icalmemory_add_tmp_buffer(buf); + return buf; +} + + +char* +icalvalue_as_ical_string_r(const icalvalue* value) +{ + if(value == 0){ + return 0; + } + + switch (value->kind){ + + case ICAL_ATTACH_VALUE: + return icalvalue_attach_as_ical_string_r(value); + + case ICAL_BINARY_VALUE: + return icalvalue_binary_as_ical_string_r(value); + + case ICAL_BOOLEAN_VALUE: + case ICAL_INTEGER_VALUE: + return icalvalue_int_as_ical_string_r(value); + + case ICAL_UTCOFFSET_VALUE: + return icalvalue_utcoffset_as_ical_string_r(value); + + case ICAL_TEXT_VALUE: + return icalvalue_text_as_ical_string_r(value); + + case ICAL_QUERY_VALUE: + return icalvalue_string_as_ical_string_r(value); + + case ICAL_STRING_VALUE: + case ICAL_URI_VALUE: + case ICAL_CALADDRESS_VALUE: + return icalvalue_string_as_ical_string_r(value); + + case ICAL_DATE_VALUE: + return icalvalue_date_as_ical_string_r(value); + case ICAL_DATETIME_VALUE: + return icalvalue_datetime_as_ical_string_r(value); + case ICAL_DURATION_VALUE: + return icalvalue_duration_as_ical_string_r(value); + + case ICAL_PERIOD_VALUE: + return icalvalue_period_as_ical_string_r(value); + case ICAL_DATETIMEPERIOD_VALUE: + return icalvalue_datetimeperiod_as_ical_string_r(value); + + case ICAL_FLOAT_VALUE: + return icalvalue_float_as_ical_string_r(value); + + case ICAL_GEO_VALUE: + return icalvalue_geo_as_ical_string_r(value); + + case ICAL_RECUR_VALUE: + return icalvalue_recur_as_ical_string_r(value); + + case ICAL_TRIGGER_VALUE: + return icalvalue_trigger_as_ical_string_r(value); + + case ICAL_REQUESTSTATUS_VALUE: + return icalreqstattype_as_string_r(value->data.v_requeststatus); + + case ICAL_ACTION_VALUE: + case ICAL_CMD_VALUE: + case ICAL_QUERYLEVEL_VALUE: + case ICAL_CARLEVEL_VALUE: + case ICAL_METHOD_VALUE: + case ICAL_STATUS_VALUE: + case ICAL_TRANSP_VALUE: + case ICAL_CLASS_VALUE: + case ICAL_BUSYTYPE_VALUE: + case ICAL_POLLMODE_VALUE: + if(value->x_value !=0){ + return icalmemory_strdup(value->x_value); + } + + return icalproperty_enum_to_string_r(value->data.v_enum); + + case ICAL_X_VALUE: + if (value->x_value != 0) + return icalmemory_strdup_and_quote(value,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; + char *temp1, *temp2; + temp1 = icalvalue_as_ical_string_r(a); + temp2 = icalvalue_as_ical_string_r(b); + r = strcmp(temp1, temp2); + free(temp1); + free(temp2); + + 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_r(value); + if (ptr == 0) + return 0; + + if ((int)strlen(ptr) >= nMaxBufferLen) + { + icalvalue_free (value); + free(ptr); + return 0; + } + + strcpy(szEncText, ptr); + free(ptr); + + icalvalue_free ((icalvalue*)value); + + return 1; +} + +int icalvalue_decode_ical_string(const char *szText, char *szDecText, int nMaxBufferLen) +{ + char *str, *str_p; + const char *p; + size_t buf_sz; + + if ((szText == 0) || (szDecText == 0)) + return 0; + + buf_sz = strlen(szText); + str_p = str = (char*)icalmemory_new_buffer(buf_sz + 1); + + if (str_p == 0){ + return 0; + } + + for (p=szText; *p!=0; p++) { + if (*p == '\\') { + icalmemory_append_char (&str,&str_p,&buf_sz,*(p+1)); + p++; + } + else + icalmemory_append_char (&str,&str_p,&buf_sz,*p); + } + + icalmemory_append_char(&str,&str_p,&buf_sz,'\0'); + + if ((int)strlen(str) > nMaxBufferLen) { + icalmemory_free_buffer(str); + return 0; + } + + strcpy(szDecText, str); + + icalmemory_free_buffer(str); + return 1; +} + + +/* The remaining interfaces are 'new', 'set' and 'get' for each of the value + types */ diff --git a/src/libical/icalvalue.h b/src/libical/icalvalue.h new file mode 100644 index 00000000..41057620 --- /dev/null +++ b/src/libical/icalvalue.h @@ -0,0 +1,94 @@ +/* -*- Mode: C -*- */ +/*====================================================================== + FILE: icalvalue.h + CREATOR: eric 20 March 1999 + + + $Id: icalvalue.h,v 1.10 2008-01-15 23:17:43 dothebart Exp $ + $Locker: $ + + (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); +char* icalvalue_as_ical_string_r(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 */ + +/* Defined in icalderivedvalue.h */ +/* 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); + +/** Extract the original character string encoded by the above function **/ +int icalvalue_decode_ical_string(const char *szText, char *szDecText, int nMaxBufferLen); + +#endif /*ICALVALUE_H*/ diff --git a/src/libical/icalvalue_cxx.cpp b/src/libical/icalvalue_cxx.cpp new file mode 100644 index 00000000..48029fa6 --- /dev/null +++ b/src/libical/icalvalue_cxx.cpp @@ -0,0 +1,273 @@ +/* -*- Mode: C -*- */ +/*====================================================================== + FILE: icalvalue_cxx.cpp + CREATOR: fnguyen 12/21/01 + (C) COPYRIGHT 2001, Critical Path + + 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 ICALVALUE_CXX_H +#include "icalvalue_cxx.h" +#endif + +typedef char* string; // Will use the string library from STL + +ICalValue::ICalValue() throw(icalerrorenum) : imp(icalvalue_new(ICAL_ANY_VALUE)){} + +ICalValue::ICalValue(const ICalValue& v) throw (icalerrorenum) { + imp = icalvalue_new_clone(v.imp); + if (!imp) throw icalerrno; +} +ICalValue& ICalValue::operator=(const ICalValue& v) throw(icalerrorenum) { + if (this == &v) return *this; + + if (imp != NULL) + { + icalvalue_free(imp); + imp = icalvalue_new_clone(v.imp); + if (!imp) throw icalerrno; + } + + return *this; +} + +ICalValue::~ICalValue(){ + if (imp != NULL) icalvalue_free(imp); +} + +ICalValue::ICalValue(icalvalue* v) throw(icalerrorenum) : imp(v){ +} + +ICalValue::ICalValue(icalvalue_kind kind) throw(icalerrorenum) { + imp = icalvalue_new(kind); + if (!imp) throw icalerrno; +} + +ICalValue::ICalValue(icalvalue_kind kind, string str) throw(icalerrorenum) { + imp = icalvalue_new_from_string(kind, str); + if (!imp) throw icalerrno; +} + +string ICalValue::as_ical_string(){ + return (string)icalvalue_as_ical_string(imp); +} +bool ICalValue::is_valid(){ + if (imp == NULL) return false; + return (icalvalue_is_valid(imp) ? true : false); +} +icalvalue_kind ICalValue::isa(){ + return icalvalue_isa(imp); +} +int ICalValue::isa_value(void* value){ + return icalvalue_isa_value(value); +} + +/* Special, non autogenerated value accessors */ +void ICalValue::set_recur(struct icalrecurrencetype v){ + icalvalue_set_recur(imp, v); +} +struct icalrecurrencetype ICalValue::get_recur(){ + return icalvalue_get_recur(imp); +} + +void ICalValue::set_trigger(struct icaltriggertype v){} +struct icaltriggertype ICalValue::get_trigger(){ + return icalvalue_get_trigger(imp); +} + +void ICalValue::set_datetimeperiod(struct icaldatetimeperiodtype v){ + icalvalue_set_datetimeperiod(imp, v); +} +struct icaldatetimeperiodtype ICalValue::get_datetimeperiod(){ + return icalvalue_get_datetimeperiod(imp); +} + +icalparameter_xliccomparetype ICalValue::compare(ICalValue& a, ICalValue& b){ + return icalvalue_compare(a, b); +} + +/* Convert enumerations */ +icalvalue_kind ICalValue::string_to_kind(string str){ + return icalvalue_string_to_kind(str); +} +string ICalValue::kind_to_string(icalvalue_kind kind){ + return (string)icalvalue_kind_to_string(kind); +} + +/* BOOLEAN */ +int ICalValue::get_boolean(){ + return icalvalue_get_boolean(imp); +} +void ICalValue::set_boolean(int v){ + icalvalue_set_boolean(imp, v); +} + +/* UTC-OFFSET */ +int ICalValue::get_utcoffset(){ + return icalvalue_get_utcoffset(imp); +} +void ICalValue::set_utcoffset(int v){ + icalvalue_set_utcoffset(imp, v); +} + +/* METHOD */ +enum icalproperty_method ICalValue::get_method(){ + return icalvalue_get_method(imp); +} +void ICalValue::set_method(enum icalproperty_method v){ + icalvalue_set_method(imp, v); +} + +/* CAL-ADDRESS */ +string ICalValue::get_caladdress(){ + return (string)icalvalue_get_caladdress(imp); +} +void ICalValue::set_caladdress(string v){ + icalvalue_set_caladdress(imp, v); +} + +/* PERIOD */ +struct icalperiodtype ICalValue::get_period(){ + return icalvalue_get_period(imp); +} +void ICalValue::set_period(struct icalperiodtype v){ + icalvalue_set_period(imp, v); +} + +/* STATUS */ +enum icalproperty_status ICalValue::get_status(){ + return icalvalue_get_status(imp); +} +void ICalValue::set_status(enum icalproperty_status v){ + icalvalue_set_status(imp, v); +} + +/* BINARY */ +string ICalValue::get_binary(){ + return (string)icalvalue_get_binary(imp); +} +void ICalValue::set_binary(string v){ + icalvalue_set_binary(imp, v); +} + +/* TEXT */ +string ICalValue::get_text(){ + return (string)icalvalue_get_text(imp); +} +void ICalValue::set_text(string v){ + icalvalue_set_text(imp, v); +} + +/* DURATION */ +struct icaldurationtype ICalValue::get_duration(){ + return icalvalue_get_duration(imp); +} +void ICalValue::set_duration(struct icaldurationtype v){ + icalvalue_set_duration(imp, v); +} + +/* INTEGER */ +int ICalValue::get_integer(){ + return icalvalue_get_integer(imp); +} +void ICalValue::set_integer(int v){ + icalvalue_set_integer(imp, v); +} + +/* URI */ +string ICalValue::get_uri(){ + return (string)icalvalue_get_uri(imp); +} +void ICalValue::set_uri(string v){ + icalvalue_set_uri(imp, v); +} + +/* ATTACH */ +icalattach *ICalValue::get_attach(){ + return icalvalue_get_attach(imp); +} +void ICalValue::set_attach(icalattach *v){ + icalvalue_set_attach(imp, v); +} + +/* CLASS */ +enum icalproperty_class ICalValue::get_class(){ + return icalvalue_get_class(imp); +} +void ICalValue::set_class(enum icalproperty_class v){ + icalvalue_set_class(imp, v); +} + +/* FLOAT */ +float ICalValue::get_float(){ + return icalvalue_get_float(imp); +} +void ICalValue::set_float(float v){ + icalvalue_set_float(imp, v); +} + +/* QUERY */ +string ICalValue::get_query(){ + return (string)icalvalue_get_query(imp); +} +void ICalValue::set_query(string v){ + icalvalue_set_query(imp, v); +} + +/* STRING */ +string ICalValue::get_string(){ + return (string)icalvalue_get_string(imp); +} +void ICalValue::set_string(string v){ + icalvalue_set_string(imp, v); +} + +/* TRANSP */ +enum icalproperty_transp ICalValue::get_transp(){ + return icalvalue_get_transp(imp); +} +void ICalValue::set_transp(enum icalproperty_transp v){ + icalvalue_set_transp(imp, v); +} + +/* DATE-TIME */ +struct icaltimetype ICalValue::get_datetime(){ + return icalvalue_get_datetime(imp); +} +void ICalValue::set_datetime(struct icaltimetype v){ + icalvalue_set_datetime(imp, v); +} + +/* GEO */ +struct icalgeotype ICalValue::get_geo(){ + return icalvalue_get_geo(imp); +} +void ICalValue::set_geo(struct icalgeotype v){ + icalvalue_set_geo(imp, v); +} + +/* DATE */ +struct icaltimetype ICalValue::get_date(){ + return icalvalue_get_date(imp); +} +void ICalValue::set_date(struct icaltimetype v){ + icalvalue_set_date(imp, v); +} + +/* ACTION */ +enum icalproperty_action ICalValue::get_action(){ + return icalvalue_get_action(imp); +} +void ICalValue::set_action(enum icalproperty_action v){ + icalvalue_set_action(imp, v); +} diff --git a/src/libical/icalvalue_cxx.h b/src/libical/icalvalue_cxx.h new file mode 100644 index 00000000..229b008d --- /dev/null +++ b/src/libical/icalvalue_cxx.h @@ -0,0 +1,162 @@ +/* -*- Mode: C -*- */ +/*====================================================================== + FILE: icalvalue_cxx.h + CREATOR: fnguyen 12/13/01 + (C) COPYRIGHT 2001, Critical Path + + 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 CP_ICALVALUE_H +#define CP_ICALVALUE_H + +extern "C" { +#include "ical.h" +}; + +#include "icptrholder.h" + +typedef char* string; // Will use the string library from STL + +class ICalValue { +public: + ICalValue() throw(icalerrorenum); + ICalValue(const ICalValue&) throw(icalerrorenum); + ICalValue& operator=(const ICalValue&) throw(icalerrorenum); + ~ICalValue(); + + ICalValue(icalvalue*) throw(icalerrorenum); + ICalValue(icalvalue_kind kind) throw(icalerrorenum); + ICalValue(icalvalue_kind kind, string str) throw(icalerrorenum); + + operator icalvalue* () { return imp; } + + void detach() { + imp = NULL; + } + +public: + string as_ical_string(); + bool is_valid(); + icalvalue_kind isa(); + int isa_value(void*); + + /* Special, non autogenerated value accessors */ + void set_recur(struct icalrecurrencetype v); + struct icalrecurrencetype get_recur(); + + void set_trigger(struct icaltriggertype v); + struct icaltriggertype get_trigger(); + + void set_datetimeperiod(struct icaldatetimeperiodtype v); + struct icaldatetimeperiodtype get_datetimeperiod(); + +public: + static icalparameter_xliccomparetype compare(ICalValue& a, ICalValue& b); + + /* Convert enumerations */ + static icalvalue_kind string_to_kind(string str); + string kind_to_string(icalvalue_kind kind); + +public: + /* BOOLEAN */ + int get_boolean(); + void set_boolean(int v); + + /* UTC-OFFSET */ + int get_utcoffset(); + void set_utcoffset(int v); + + /* METHOD */ + enum icalproperty_method get_method(); + void set_method(enum icalproperty_method v); + + /* CAL-ADDRESS */ + string get_caladdress(); + void set_caladdress(string v); + + /* PERIOD */ + struct icalperiodtype get_period(); + void set_period(struct icalperiodtype v); + + /* STATUS */ + enum icalproperty_status get_status(); + void set_status(enum icalproperty_status v); + + /* BINARY */ + string get_binary(); + void set_binary(string v); + + /* TEXT */ + string get_text(); + void set_text(string v); + + /* DURATION */ + struct icaldurationtype get_duration(); + void set_duration(struct icaldurationtype v); + + /* INTEGER */ + int get_integer(); + void set_integer(int v); + + /* URI */ + string get_uri(); + void set_uri(string v); + + /* ATTACH */ + icalattach *get_attach(); + void set_attach(icalattach *v); + + /* CLASS */ + enum icalproperty_class get_class(); + void set_class(enum icalproperty_class v); + + /* FLOAT */ + float get_float(); + void set_float(float v); + + /* QUERY */ + string get_query(); + void set_query(string v); + + /* STRING */ + string get_string(); + void set_string(string v); + + /* TRANSP */ + enum icalproperty_transp get_transp(); + void set_transp(enum icalproperty_transp v); + + /* DATE-TIME */ + struct icaltimetype get_datetime(); + void set_datetime(struct icaltimetype v); + + /* GEO */ + struct icalgeotype get_geo(); + void set_geo(struct icalgeotype v); + + /* DATE */ + struct icaltimetype get_date(); + void set_date(struct icaltimetype v); + + /* ACTION */ + enum icalproperty_action get_action(); + void set_action(enum icalproperty_action v); + +private: + icalvalue* imp; +}; + + +typedef ICPointerHolder<ICalValue> ICalValueTmpPtr; /* see icptrholder.h for comments */ + +#endif /*ICALVALUE_H*/ diff --git a/src/libical/icalvalueimpl.h b/src/libical/icalvalueimpl.h new file mode 100644 index 00000000..6674428f --- /dev/null +++ b/src/libical/icalvalueimpl.h @@ -0,0 +1,96 @@ +/* -*- Mode: C -*- */ +/*====================================================================== + FILE: icalvalue.c + CREATOR: eric 02 May 1999 + + $Id: icalvalueimpl.h,v 1.7 2008-01-15 23:17:43 dothebart Exp $ + + + (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/src/libical/icalversion.h.cmake b/src/libical/icalversion.h.cmake new file mode 100644 index 00000000..bcc05c6a --- /dev/null +++ b/src/libical/icalversion.h.cmake @@ -0,0 +1,7 @@ +#ifndef ICAL_VERSION_H +#define ICAL_VERSION_H + +#define ICAL_PACKAGE "@CMAKE_PROJECT_NAME@" +#define ICAL_VERSION "@PROJECT_VERSION@" + +#endif diff --git a/src/libical/icptrholder.h b/src/libical/icptrholder.h new file mode 100644 index 00000000..5f697eed --- /dev/null +++ b/src/libical/icptrholder.h @@ -0,0 +1,104 @@ +/* -*- Mode: C++ -*- */ + +/** + * @file icptrholder.h + * @author wyau (08/29/02) + * @brief C++ template classes for managing C++ pointers returned by VComponent::get_..._component, + * VComponent::get_..._property, ICalPropety::get_..._value. + * @remarks VComponent::get... functions returns a C++ oject that wraps the libical implementation. + * It is important to note that the wrapped implementation still belongs to the orginal + * component. To stop memory leak, caller must delete the pointer. However, the destructor + * will call the appropriate free function. eg. ~VComponent calls icalcomponent_free(imp). + * As stated previously, imp stil belongs to the original component. To avoid freeing the + * wrapped "imp", caller must set the "imp" to null before deleting the pointer. + * + * The template class relieves the burden of memory management when used as a stack based + * object. The class holds a pointer to the C++ Wrapper. The destructor set the imp to + * null before deleting the pointer. + * + * Each C++ Wrapper instantiates a template class in it's corresponding .h file. + * + * Usage example: + * VComponentTmpPtr p; // VComponentTmpPtr is an instantiation of this template + * for (p=component.get_first_component; p!= NULL; p=component.get_next_component) { + * + * (C) COPYRIGHT 2001, Critical Path + + 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 __ICPTRHOLDER_H__ +#define __ICPTRHOLDER_H__ + +template<class T> class ICPointerHolder { +public: + ICPointerHolder() { ptr = NULL; } + ICPointerHolder(T* p) { ptr = p; } + + // copy constructor to support assignment + ICPointerHolder(const ICPointerHolder& ip) { + ptr = ip.ptr; + + // We need to transfer ownership of ptr to this object by setting + // ip's ptr to null. Otherwise, ptr will de deleted twice. + // const ugliness requires us to do the const_cast. + ICPointerHolder *ipp = const_cast<ICPointerHolder*>(&ip); + ipp->ptr = NULL; + }; + + ~ICPointerHolder() { + release(); + } + + ICPointerHolder& operator=(T* p) { + this->release(); + ptr = p; + return *this; + } + + ICPointerHolder& operator=(ICPointerHolder& p) { + this->release(); + ptr = p.ptr; // this transfer ownership of the pointer + p.ptr = NULL; // set it to null so the pointer won't get delete twice. + return *this; + } + + int operator!=(T* p) {return (ptr != p);} + int operator==(T* p) {return (ptr == p);} + + operator T*() const { + return ptr; + } + + T* operator->() const { + assert(ptr); + return ptr; + } + + T& operator*() { + assert(ptr); + return *ptr; + } + +private: + void release() { + if (ptr != NULL) { + ptr->detach(); + delete ptr; + ptr = NULL; + } + } + + T* ptr; +}; + +#endif diff --git a/src/libical/libical.dsp b/src/libical/libical.dsp new file mode 100644 index 00000000..c14ba958 --- /dev/null +++ b/src/libical/libical.dsp @@ -0,0 +1,594 @@ +# Microsoft Developer Studio Project File - Name="libical" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=libical - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "libical.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "libical.mak" CFG="libical - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "libical - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "libical - Win32 Debug" (based on "Win32 (x86) Static Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "libical - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "./autogenex" /I "./" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ELSEIF "$(CFG)" == "libical - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "." /I ".." /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FR /YX /FD /GZ /c +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ENDIF + +# Begin Target + +# Name "libical - Win32 Release" +# Name "libical - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\caldate.c +# End Source File +# Begin Source File + +SOURCE=.\icalarray.c +# End Source File +# Begin Source File + +SOURCE=.\icalattach.c +# End Source File +# Begin Source File + +SOURCE=icalcomponent.c +# End Source File +# Begin Source File + +SOURCE=.\icalderivedparameter.c +# End Source File +# Begin Source File + +SOURCE=icalderivedparameter.c.in + +!IF "$(CFG)" == "libical - Win32 Release" + +# PROP Ignore_Default_Tool 1 +USERDEP__ICALD="../../scripts/mkderivedparameters.pl" "../../design-data/parameters.csv" +# Begin Custom Build +InputPath=icalderivedparameter.c.in +InputName=icalderivedparameter.c + +"$(InputName)" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + perl -I ../../scripts ../../scripts/mkderivedparameters.pl -i $(InputName).in -c ../../design-data/parameters.csv > $(InputName) + +# End Custom Build + +!ELSEIF "$(CFG)" == "libical - Win32 Debug" + +USERDEP__ICALD="../../scripts/mkderivedparameters.pl" "../../design-data/parameters.csv" +# Begin Custom Build +InputPath=icalderivedparameter.c.in +InputName=icalderivedparameter.c + +"$(InputName)" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + perl -I ../../scripts ../../scripts/mkderivedparameters.pl -i $(InputName).in -c ../../design-data/parameters.csv > $(InputName) + +# End Custom Build + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\icalderivedproperty.c +# End Source File +# Begin Source File + +SOURCE=icalderivedproperty.c.in + +!IF "$(CFG)" == "libical - Win32 Release" + +# PROP Ignore_Default_Tool 1 +USERDEP__ICALDE="../../scripts/mkderivedproperties.pl" "../../design-data/properties.csv" "../../design-data/value-types.csv" +# Begin Custom Build +InputPath=icalderivedproperty.c.in +InputName=icalderivedproperty.c + +"$(InputName)" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + perl -I ../../scripts ../../scripts/mkderivedproperties.pl -i $(InputName).in -c ../../design-data/properties.csv ../../design-data/value-types.csv > $(InputName) + +# End Custom Build + +!ELSEIF "$(CFG)" == "libical - Win32 Debug" + +USERDEP__ICALDE="../../scripts/mkderivedproperties.pl" "../../design-data/properties.csv" "../../design-data/value-types.csv" +# Begin Custom Build +InputPath=icalderivedproperty.c.in +InputName=icalderivedproperty.c + +"$(InputName)" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + perl -I ../../scripts ../../scripts/mkderivedproperties.pl -i $(InputName).in -c ../../design-data/properties.csv ../../design-data/value-types.csv > $(InputName) + +# End Custom Build + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\icalderivedvalue.c +# End Source File +# Begin Source File + +SOURCE=icalderivedvalue.c.in + +!IF "$(CFG)" == "libical - Win32 Release" + +# PROP Ignore_Default_Tool 1 +USERDEP__ICALDER="../../scripts/mkderivedvalues.pl" "../../design-data/value-types.csv" +# Begin Custom Build +InputPath=icalderivedvalue.c.in +InputName=icalderivedvalue.c + +"$(InputName)" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + perl -I ../../scripts ../../scripts/mkderivedvalues.pl -i $(InputName).in -c ../../design-data/value-types.csv > $(InputName) + +# End Custom Build + +!ELSEIF "$(CFG)" == "libical - Win32 Debug" + +USERDEP__ICALDER="../../scripts/mkderivedvalues.pl" "../../design-data/value-types.csv" +# Begin Custom Build +InputPath=icalderivedvalue.c.in +InputName=icalderivedvalue.c + +"$(InputName)" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + perl -I ../../scripts ../../scripts/mkderivedvalues.pl -i $(InputName).in -c ../../design-data/value-types.csv > $(InputName) + +# End Custom Build + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=icalduration.c +# End Source File +# Begin Source File + +SOURCE=icalenums.c +# End Source File +# Begin Source File + +SOURCE=icalerror.c +# End Source File +# Begin Source File + +SOURCE=icallangbind.c +# End Source File +# Begin Source File + +SOURCE=icalmemory.c +# End Source File +# Begin Source File + +SOURCE=icalmime.c +# End Source File +# Begin Source File + +SOURCE=icalparameter.c +# End Source File +# Begin Source File + +SOURCE=icalparser.c +# End Source File +# Begin Source File + +SOURCE=icalperiod.c +# End Source File +# Begin Source File + +SOURCE=icalproperty.c +# End Source File +# Begin Source File + +SOURCE=icalrecur.c +# End Source File +# Begin Source File + +SOURCE=.\icalrestriction.c +# End Source File +# Begin Source File + +SOURCE=icalrestriction.c.in + +!IF "$(CFG)" == "libical - Win32 Release" + +# PROP Ignore_Default_Tool 1 +USERDEP__ICALR="../../scripts/mkrestrictiontable.pl" "../../design-data/restrictions.csv" +# Begin Custom Build +InputPath=icalrestriction.c.in +InputName=icalrestriction.c + +"$(InputName)" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + perl -I ../../scripts ../../scripts/mkrestrictiontable.pl -i $(InputName).in ../../design-data/restrictions.csv > $(InputName) + +# End Custom Build + +!ELSEIF "$(CFG)" == "libical - Win32 Debug" + +# PROP Ignore_Default_Tool 1 +USERDEP__ICALR="../../scripts/mkrestrictiontable.pl" "../../design-data/restrictions.csv" +# Begin Custom Build +InputPath=icalrestriction.c.in +InputName=icalrestriction.c + +"$(InputName)" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + perl -I ../../scripts ../../scripts/mkrestrictiontable.pl -i $(InputName).in ../../design-data/restrictions.csv > $(InputName) + +# End Custom Build + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=icaltime.c +# End Source File +# Begin Source File + +SOURCE=.\icaltimezone.c +# End Source File +# Begin Source File + +SOURCE=icaltypes.c +# End Source File +# Begin Source File + +SOURCE=icalvalue.c +# End Source File +# Begin Source File + +SOURCE=pvl.c +# End Source File +# Begin Source File + +SOURCE=sspm.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\astime.h +# End Source File +# Begin Source File + +SOURCE=.\ical.h + +!IF "$(CFG)" == "libical - Win32 Release" + +# PROP Ignore_Default_Tool 1 +USERDEP__ICAL_="icalversion.h" "icaltime.h" "icalduration.h" "icalperiod.h" "icalenums.h" "icaltypes.h" "icalrecur.h" "icalattach.h" "icalderivedvalue.h" "icalderivedparameter.h" "icalvalue.h" "icalparameter.h" "icalderivedproperty.h" "icalproperty.h" "pvl.h" "icalarray.h" "icalcomponent.h" "icaltimezone.h" "icalparser.h" "icalmemory.h" "icalerror.h" "icalrestriction.h" "sspm.h" "icalmime.h" "icallangbind.h" +# Begin Custom Build +InputPath=.\ical.h + +"ical.h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + sh ../../scripts/mkinclude.sh -o ical.h -e "#include.*\"ical" -e "#include.*\"config" -e "#include.*\"pvl\.h\"" -e '\$$(Id|Locker): .+\$$' icalversion.h icaltime.h icalduration.h icalperiod.h icalenums.h icaltypes.h icalrecur.h icalattach.h icalderivedvalue.h icalderivedparameter.h icalvalue.h icalparameter.h icalderivedproperty.h icalproperty.h pvl.h icalarray.h icalcomponent.h icaltimezone.h icalparser.h icalmemory.h icalerror.h icalrestriction.h sspm.h icalmime.h icallangbind.h + +# End Custom Build + +!ELSEIF "$(CFG)" == "libical - Win32 Debug" + +USERDEP__ICAL_="icalversion.h" "icaltime.h" "icalduration.h" "icalperiod.h" "icalenums.h" "icaltypes.h" "icalrecur.h" "icalattach.h" "icalderivedvalue.h" "icalderivedparameter.h" "icalvalue.h" "icalparameter.h" "icalderivedproperty.h" "icalproperty.h" "pvl.h" "icalarray.h" "icalcomponent.h" "icaltimezone.h" "icalparser.h" "icalmemory.h" "icalerror.h" "icalrestriction.h" "sspm.h" "icalmime.h" "icallangbind.h" +# Begin Custom Build +InputPath=.\ical.h + +"ical.h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + sh ../../scripts/mkinclude.sh -o ical.h -e "#include.*\"ical" -e "#include.*\"config" -e "#include.*\"pvl\.h\"" -e '\$$(Id|Locker): .+\$$' icalversion.h icaltime.h icalduration.h icalperiod.h icalenums.h icaltypes.h icalrecur.h icalattach.h icalderivedvalue.h icalderivedparameter.h icalvalue.h icalparameter.h icalderivedproperty.h icalproperty.h pvl.h icalarray.h icalcomponent.h icaltimezone.h icalparser.h icalmemory.h icalerror.h icalrestriction.h sspm.h icalmime.h icallangbind.h + +# End Custom Build + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\icalarray.h +# End Source File +# Begin Source File + +SOURCE=.\icalattach.h +# End Source File +# Begin Source File + +SOURCE=.\icalattachimpl.h +# End Source File +# Begin Source File + +SOURCE=icalcomponent.h +# End Source File +# Begin Source File + +SOURCE=.\icalderivedparameter.h +# End Source File +# Begin Source File + +SOURCE=icalderivedparameter.h.in + +!IF "$(CFG)" == "libical - Win32 Release" + +# PROP Ignore_Default_Tool 1 +USERDEP__ICALDERI="../../scripts/mkderivedparameters.pl" "../../design-data/parameters.csv" +# Begin Custom Build +InputPath=icalderivedparameter.h.in +InputName=icalderivedparameter.h + +"$(InputName)" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + perl -I ../../scripts ../../scripts/mkderivedparameters.pl -i $(InputName).in -h ../../design-data/parameters.csv > $(InputName) + +# End Custom Build + +!ELSEIF "$(CFG)" == "libical - Win32 Debug" + +USERDEP__ICALDERI="../../scripts/mkderivedparameters.pl" "../../design-data/parameters.csv" +# Begin Custom Build +InputPath=icalderivedparameter.h.in +InputName=icalderivedparameter.h + +"$(InputName)" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + perl -I ../../scripts ../../scripts/mkderivedparameters.pl -i $(InputName).in -h ../../design-data/parameters.csv > $(InputName) + +# End Custom Build + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\icalderivedproperty.h +# End Source File +# Begin Source File + +SOURCE=icalderivedproperty.h.in + +!IF "$(CFG)" == "libical - Win32 Release" + +# PROP Ignore_Default_Tool 1 +USERDEP__ICALDERIV="../../scripts/mkderivedproperties.pl" "../../design-data/properties.csv" "../../design-data/value-types.csv" +# Begin Custom Build +InputPath=icalderivedproperty.h.in +InputName=icalderivedproperty.h + +"$(InputName)" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + perl -I ../../scripts ../../scripts/mkderivedproperties.pl -i $(InputName).in -h ../../design-data/properties.csv ../../design-data/value-types.csv > $(InputName) + +# End Custom Build + +!ELSEIF "$(CFG)" == "libical - Win32 Debug" + +USERDEP__ICALDERIV="../../scripts/mkderivedproperties.pl" "../../design-data/properties.csv" "../../design-data/value-types.csv" +# Begin Custom Build +InputPath=icalderivedproperty.h.in +InputName=icalderivedproperty.h + +"$(InputName)" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + perl -I ../../scripts ../../scripts/mkderivedproperties.pl -i $(InputName).in -h ../../design-data/properties.csv ../../design-data/value-types.csv > $(InputName) + +# End Custom Build + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\icalderivedvalue.h +# End Source File +# Begin Source File + +SOURCE=icalderivedvalue.h.in + +!IF "$(CFG)" == "libical - Win32 Release" + +# PROP Ignore_Default_Tool 1 +USERDEP__ICALDERIVE="../../scripts/mkderivedvalues.pl" "../../design-data/value-types.csv" +# Begin Custom Build +InputPath=icalderivedvalue.h.in +InputName=icalderivedvalue.h + +"$(InputName)" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + perl -I ../../scripts ../../scripts/mkderivedvalues.pl -i $(InputName).in -h ../../design-data/value-types.csv > $(InputName) + +# End Custom Build + +!ELSEIF "$(CFG)" == "libical - Win32 Debug" + +USERDEP__ICALDERIVE="../../scripts/mkderivedvalues.pl" "../../design-data/value-types.csv" +# Begin Custom Build +InputPath=icalderivedvalue.h.in +InputName=icalderivedvalue.h + +"$(InputName)" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + perl -I ../../scripts ../../scripts/mkderivedvalues.pl -i $(InputName).in -h ../../design-data/value-types.csv > $(InputName) + +# End Custom Build + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=icalduration.h +# End Source File +# Begin Source File + +SOURCE=icalenums.h +# End Source File +# Begin Source File + +SOURCE=icalerror.h +# End Source File +# Begin Source File + +SOURCE=icallangbind.h +# End Source File +# Begin Source File + +SOURCE=icalmemory.h +# End Source File +# Begin Source File + +SOURCE=icalmime.h +# End Source File +# Begin Source File + +SOURCE=icalparameter.h +# End Source File +# Begin Source File + +SOURCE=icalparameterimpl.h +# End Source File +# Begin Source File + +SOURCE=icalparser.h +# End Source File +# Begin Source File + +SOURCE=icalperiod.h +# End Source File +# Begin Source File + +SOURCE=icalproperty.h +# End Source File +# Begin Source File + +SOURCE=icalrecur.h +# End Source File +# Begin Source File + +SOURCE=icalrestriction.h +# End Source File +# Begin Source File + +SOURCE=icaltime.h +# End Source File +# Begin Source File + +SOURCE=.\icaltimezone.h +# End Source File +# Begin Source File + +SOURCE=icaltypes.h +# End Source File +# Begin Source File + +SOURCE=icalvalue.h +# End Source File +# Begin Source File + +SOURCE=icalvalueimpl.h +# End Source File +# Begin Source File + +SOURCE=.\icalversion.h +# End Source File +# Begin Source File + +SOURCE=icalversion.h.in + +!IF "$(CFG)" == "libical - Win32 Release" + +# PROP Ignore_Default_Tool 1 +# Begin Custom Build +InputPath=icalversion.h.in +InputName=icalversion.h + +"$(InputName)" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + sed -e "s/@VERSION@/0.24/g" -e "s/@PACKAGE@/libical/g" $(InputName).in > $(InputName) + +# End Custom Build + +!ELSEIF "$(CFG)" == "libical - Win32 Debug" + +# PROP Ignore_Default_Tool 1 +# Begin Custom Build +InputPath=icalversion.h.in +InputName=icalversion.h + +"$(InputName)" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + sed -e "s/@VERSION@/0.24/g" -e "s/@PACKAGE@/libical/g" $(InputName).in > $(InputName) + +# End Custom Build + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=pvl.h +# End Source File +# Begin Source File + +SOURCE=sspm.h +# End Source File +# End Group +# End Target +# End Project diff --git a/src/libical/libicals_w32_vsnprintf_replacement.c b/src/libical/libicals_w32_vsnprintf_replacement.c new file mode 100644 index 00000000..8cb98176 --- /dev/null +++ b/src/libical/libicals_w32_vsnprintf_replacement.c @@ -0,0 +1,173 @@ +#ifdef WIN32 +#include "config.h" +#ifndef HAVE_SNPRINTF +#error DO NOT USE libical version of vsnprintf. +#endif +/* + * 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/src/libical/pvl.c b/src/libical/pvl.c new file mode 100644 index 00000000..23c69a8b --- /dev/null +++ b/src/libical/pvl.c @@ -0,0 +1,597 @@ +/*====================================================================== + FILE: pvl.c + CREATOR: eric November, 1995 + + + (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 "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/src/libical/pvl.h b/src/libical/pvl.h new file mode 100644 index 00000000..77c0531d --- /dev/null +++ b/src/libical/pvl.h @@ -0,0 +1,110 @@ +/*====================================================================== + FILE: pvl.h + CREATOR: eric November, 1995 + + + (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 __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/src/libical/sspm.c b/src/libical/sspm.c new file mode 100644 index 00000000..b2b1dc23 --- /dev/null +++ b/src/libical/sspm.c @@ -0,0 +1,1639 @@ +/* -*- Mode: C -*- + ====================================================================== + FILE: sspm.c Parse Mime + CREATOR: eric 25 June 2000 + + $Id: sspm.c,v 1.13 2008-01-28 22:34:38 artcancro Exp $ + $Locker: $ + + 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 + +#if defined(_MSC_VER) +#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; + const 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 { + strncpy(name,p,sizeof(name)-1); + name[sizeof(name)-1]='\0'; + } + + /* 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(void) +{ + 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; + + if(str ==0){ + return 0; + } + new = sspm_strdup(str); + 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_MAJOR_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){ + free(ltype); + 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 TODO*/ + 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(current_line<(MAX_HEADER_LINES-2) && + (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, + const 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++){ + snprintf(temp, sizeof(temp),"%s", header->content_type_params[i]); + 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; + size_t slen; + (void)num_parts; + + buf.buffer = malloc(4096); + buf.buffer[0] = '\0'; + buf.pos = buf.buffer; + buf.buf_size = 10; + buf.line_pos = 0; + + /* write caller's header */ + if(header != 0){ + sspm_append_string(&buf,header); + } + + slen = strlen(buf.buffer); + if(slen > 0 && buf.buffer[slen-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/src/libical/sspm.h b/src/libical/sspm.h new file mode 100644 index 00000000..fad0bb65 --- /dev/null +++ b/src/libical/sspm.h @@ -0,0 +1,146 @@ +/* -*- Mode: C -*- + ====================================================================== + FILE: sspm.h Mime Parser + CREATOR: eric 25 June 2000 + + $Id: sspm.h,v 1.5 2008-01-15 23:17:43 dothebart Exp $ + $Locker: $ + + (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); + 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, + const 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/src/libical/vcomponent.cpp b/src/libical/vcomponent.cpp new file mode 100644 index 00000000..aceade9a --- /dev/null +++ b/src/libical/vcomponent.cpp @@ -0,0 +1,828 @@ +/** + * @file vcomponent.cpp + * @author fnguyen (12/10/01) + * @brief Implemenation of C++ Wrapper for icalcomponent.c + * + * (C) COPYRIGHT 2001, Critical Path + + 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 VCOMPONENT_H +#include "vcomponent.h" +#endif + +#ifndef ICALVALUE_CXX_H +#include "icalvalue_cxx.h" +#endif + +#ifndef ICALPROPERTY_CXX_H +#include "icalproperty_cxx.h" +#endif + +#ifndef ICALPARAMETER_CXX_H +#include "icalparameter_cxx.h" +#endif +#include <stdlib.h> +#include <string.h> + +#include <exception> + +VComponent::VComponent() throw(icalerrorenum) : imp(icalcomponent_new(ICAL_ANY_COMPONENT)) { +} +VComponent::VComponent(const VComponent& v) throw(icalerrorenum) { + imp = icalcomponent_new_clone(v.imp); + if (!imp) throw icalerrno; +} + +VComponent& VComponent::operator=(const VComponent& v) throw(icalerrorenum) { + if (this == &v) return *this; + + if (imp != NULL) + { + icalcomponent_free(imp); + imp = icalcomponent_new_clone(v.imp); + if (!imp) throw icalerrno; + } + + return *this; +} + +VComponent::~VComponent(){ + if (imp) + icalcomponent_free(imp); +} + +VComponent::VComponent(icalcomponent* v) throw (icalerrorenum) : imp(v){ +} + +/* char* returned is in the ring buffer. caller doesn't have to free it */ +char* VComponent::quote_ical_string(char *str){ + const char* p; + size_t buf_sz; + buf_sz = strlen(str) * 2; /* assume worse case scenarios. otherwise, we have to parse the string and count \ */ + char* out = (char*)icalmemory_new_buffer(buf_sz); /* memory is from the ring buffer */ + char* pout; + + if (out == 0){ + return 0; + } + + pout = out; + + for (p = str; *p!=0; p++){ + + if( *p == '\\'){ + *pout++ = '\\'; + } + *pout++ = *p; + + } + *pout++ = '\0'; + + return out; +} + +/** + * @brief Constructor + * + * Create a new VComponent from a string. + * + * @exception ICAL_MALFORMEDDATA_ERROR + * Catch this error if you + * + */ +VComponent::VComponent(string str) throw (icalerrorenum) { + // Fix for BUG #15647, but breaks fix for BUG #15596. Attempting a UI fix for cal-4.0. + //char* quoted_str = quote_ical_string((char *)str); + //imp = icalcomponent_new_from_string(quoted_str); + imp = icalcomponent_new_from_string(str); + + if (!imp) throw icalerrno; +} + +VComponent::VComponent(icalcomponent_kind kind) throw(icalerrorenum) { + imp = icalcomponent_new(kind); + + if (!imp) throw icalerrno; +} + + +string VComponent::as_ical_string() throw(icalerrorenum) { + char *str = icalcomponent_as_ical_string(imp); + + if (!str) throw icalerrno; + + return(str); +} + + +bool VComponent::is_valid(){ + if (imp == NULL) return false; + return (icalcomponent_is_valid(imp) ? true : false); +} + +icalcomponent_kind VComponent::isa(){ + return icalcomponent_isa(imp); +} + +int VComponent::isa_component(void* component){ + return icalcomponent_isa_component(component); +} + +void VComponent::new_from_string(string str){ + if (imp != NULL) icalcomponent_free(imp); + imp = icalcomponent_new_from_string(str); +} + +/* Working with properties */ +void VComponent::add_property(ICalProperty* property){ + icalcomponent_add_property(imp, *property); +} +void VComponent::remove_property(ICalProperty* property){ + icalcomponent_remove_property(imp, *property); + icalproperty_free(*property); + property->detach(); // set imp to null, it's free already. +} +int VComponent::count_properties(icalproperty_kind kind){ + return icalcomponent_count_properties(imp, kind); +} + +/* Iterate through the properties */ +ICalProperty* VComponent::get_current_property(){ + icalproperty* t = icalcomponent_get_current_property(imp); + return ((t != NULL) ? new ICalProperty(t) : NULL); +} + +ICalProperty* VComponent::get_first_property(icalproperty_kind kind){ + icalproperty* t = icalcomponent_get_first_property(imp, kind); + return ((t != NULL) ? new ICalProperty(t) : NULL); +} + +ICalProperty* VComponent::get_next_property(icalproperty_kind kind){ + icalproperty* t = icalcomponent_get_next_property(imp, kind); + return ((t != NULL) ? new ICalProperty(t) : NULL); +} + +/* Working with components */ +/* Return the first VEVENT, VTODO or VJOURNAL sub-component if it is one of those types */ +VComponent* VComponent::get_inner(){ + return new VComponent(icalcomponent_get_inner(imp)); +} + +void VComponent::add_component(VComponent* child){ + icalcomponent_add_component(imp, *child); +} +void VComponent::remove_component(VComponent* child){ + icalcomponent_remove_component(imp, *child); +} +int VComponent::count_components(icalcomponent_kind kind){ + return icalcomponent_count_components(imp, kind); +} + +/* 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 */ +VComponent* VComponent::get_current_component(){ + icalcomponent* t = icalcomponent_get_current_component(imp); + return ((t != NULL) ? new VComponent(t) : NULL); +} +VComponent* VComponent::get_first_component(icalcomponent_kind kind){ + VComponent* result = NULL; + icalcomponent* t = icalcomponent_get_first_component(imp, kind); + if (t != NULL) { + switch (kind) { + case ICAL_VALARM_COMPONENT: + result = new VAlarm(t); + break; + case ICAL_VCALENDAR_COMPONENT: + result = new VCalendar(t); + break; + case ICAL_VEVENT_COMPONENT: + result = new VEvent(t); + break; + case ICAL_VQUERY_COMPONENT: + result = new VQuery(t); + break; + case ICAL_VTODO_COMPONENT: + result = new VToDo(t); + break; + case ICAL_VAGENDA_COMPONENT: + result = new VAgenda(t); + break; + default: + result = new VComponent(t); + } + } + + return (result); +} +VComponent* VComponent::get_next_component(icalcomponent_kind kind){ + VComponent* result = NULL; + icalcomponent* t = icalcomponent_get_next_component(imp, kind); + if (t != NULL) { + switch (kind) { + case ICAL_VALARM_COMPONENT: + result = new VAlarm(t); + break; + case ICAL_VCALENDAR_COMPONENT: + result = new VCalendar(t); + break; + case ICAL_VEVENT_COMPONENT: + result = new VEvent(t); + break; + case ICAL_VQUERY_COMPONENT: + result = new VQuery(t); + break; + case ICAL_VTODO_COMPONENT: + result = new VToDo(t); + break; + case ICAL_VAGENDA_COMPONENT: + result = new VAgenda(t); + break; + default: + result = new VComponent(t); + } + } + + return (result); +} + +/* Using external iterators */ +icalcompiter VComponent::begin_component(icalcomponent_kind kind){ + return icalcomponent_begin_component(imp, kind); +} +icalcompiter VComponent::end_component(icalcomponent_kind kind){ + return icalcomponent_end_component(imp, kind); +} +VComponent* VComponent::next(icalcompiter* i){ + return (VComponent*)icalcompiter_next(i); +} +VComponent* VComponent::prev(icalcompiter* i){ + return (VComponent*)icalcompiter_prior(i); +} +VComponent* VComponent::current(icalcompiter* i){ + return (VComponent*)icalcompiter_deref(i); +} + +/* Working with embedded error properties */ +int VComponent::count_errors(){ + return icalcomponent_count_errors(imp); +} + +/* Remove all X-LIC-ERROR properties*/ +void VComponent::strip_errors(){ + icalcomponent_strip_errors(imp); +} + +/* Convert some X-LIC-ERROR properties into RETURN-STATUS properties*/ +void VComponent::convert_errors(){ + icalcomponent_convert_errors(imp); +} + +/* Kind conversion routines */ +icalcomponent_kind VComponent::string_to_kind(string str){ + return icalcomponent_string_to_kind(str); +} +string VComponent::kind_to_string(icalcomponent_kind kind){ + return (string)icalcomponent_kind_to_string(kind); +} + +struct icaltimetype VComponent::get_dtstart(){ + return icalcomponent_get_dtstart(imp); +} +void VComponent::set_dtstart(struct icaltimetype v){ + icalcomponent_set_dtstart(imp, v); +} + +/* 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 VComponent::get_dtend(){ + return icalcomponent_get_dtend(imp); +} + +void VComponent::set_dtend(struct icaltimetype v){ + icalcomponent_set_dtend(imp, v); +} + +struct icaltimetype VComponent::get_due(){ + return icalcomponent_get_due(imp); +} +void VComponent::set_due(struct icaltimetype v){ + icalcomponent_set_due(imp, v); +} + +struct icaldurationtype VComponent::get_duration(){ + return icalcomponent_get_duration(imp); +} +void VComponent::set_duration(struct icaldurationtype v){ + icalcomponent_set_duration(imp, v); +} + +icalproperty_method VComponent::get_method(){ + return icalcomponent_get_method(imp); +} +void VComponent::set_method(icalproperty_method method){ + icalcomponent_set_method(imp, method); +} + +struct icaltimetype VComponent::get_dtstamp(){ + return icalcomponent_get_dtstamp(imp); +} +void VComponent::set_dtstamp(struct icaltimetype v){ + icalcomponent_set_dtstamp(imp, v); +} + +string VComponent::get_summary(){ + return (string)icalcomponent_get_summary(imp); +} +void VComponent::set_summary(string v){ + icalcomponent_set_summary(imp, v); +} + +string VComponent::get_location(){ + return (string)icalcomponent_get_location(imp); +} +void VComponent::set_location(string v){ + icalcomponent_set_location(imp, v); +} + + +string VComponent::get_description(){ + return (string)icalcomponent_get_description(imp); +} +void VComponent::set_description(string v){ + icalcomponent_set_description(imp, v); +} +string VComponent::get_comment(){ + return (string)icalcomponent_get_comment(imp); +} +void VComponent::set_comment(string v){ + icalcomponent_set_comment(imp, v); +} + +string VComponent::get_uid(){ + return (string)icalcomponent_get_uid(imp); +} +void VComponent::set_uid(string v){ + icalcomponent_set_uid(imp, v); +} + +string VComponent::get_relcalid(){ + return (string)icalcomponent_get_relcalid(imp); +} +void VComponent::set_relcalid(string v){ + icalcomponent_set_relcalid(imp, v); +} + +struct icaltimetype VComponent::get_recurrenceid(){ + return icalcomponent_get_recurrenceid(imp); +} +void VComponent::set_recurrenceid(struct icaltimetype v){ + icalcomponent_set_recurrenceid(imp, v); +} + +int VComponent::get_sequence(){ + return (int)icalcomponent_get_sequence(imp); +} +void VComponent::set_sequence(int v){ + icalcomponent_set_sequence(imp, v); +} + +int VComponent::get_status(){ + return (int)icalcomponent_get_status(imp); +} +void VComponent::set_status(enum icalproperty_status v){ + icalcomponent_set_status(imp, v); +} + +/* For VCOMPONENT: Return a reference to the first VEVENT, VTODO, or VJOURNAL */ +VComponent* VComponent::get_first_real_component(){ + return (VComponent*)icalcomponent_get_first_real_component(imp); +} + +/* For VEVENT, VTODO, VJOURNAL and VTIMEZONE: report the start and end + times of an event in UTC */ +struct icaltime_span VComponent::get_span(){ + return icalcomponent_get_span(imp); +} + +int VComponent::recurrence_is_excluded( struct icaltimetype *dtstart, + struct icaltimetype *recurtime){ + return icalproperty_recurrence_is_excluded(imp, dtstart, recurtime); +} + + +/* Internal operations. They are private, and you should not be using them. */ +VComponent* VComponent::get_parent() { + return new VComponent(icalcomponent_get_parent(imp)); +} +void VComponent::set_parent(VComponent *parent){ + icalcomponent_set_parent(imp, *parent); +} + +/* ignoreValue means remove properties even if the data doesn't match */ +bool VComponent::remove(VComponent& fromVC, bool ignoreValue){ + + /* the two components must be the same kind */ + if (this->isa() != fromVC.isa()) return false; + + /* properties first */ + ICalPropertyTmpPtr propToBeRemoved; + for (propToBeRemoved=fromVC.get_first_property(ICAL_ANY_PROPERTY); propToBeRemoved != NULL; + propToBeRemoved=fromVC.get_next_property(ICAL_ANY_PROPERTY)) { + + /* loop through properties from this component */ + ICalPropertyTmpPtr next; + ICalPropertyTmpPtr p; + for (p=this->get_first_property(propToBeRemoved->isa()); p != NULL; p=next) { + next = this->get_next_property(propToBeRemoved->isa()); + if (ignoreValue) + this->remove_property(p); + else { + if (*p == *propToBeRemoved) { + this->remove_property(p); + break; + } + } + } + } + + /* components next - should remove by UID */ + VComponentTmpPtr comp; + for (comp=fromVC.get_first_component(ICAL_ANY_COMPONENT); comp != NULL; + comp=fromVC.get_next_component(ICAL_ANY_COMPONENT)) { + const char* fromCompUid = comp->get_uid(); + VComponentTmpPtr c; + for (c=this->get_first_component(comp->isa()); c != NULL; + c=this->get_next_component(comp->isa())) { + const char* thisCompUid = c->get_uid(); + if (strcmp(fromCompUid, thisCompUid) == 0) { + // recursively go down the components + c->remove(*comp, ignoreValue); + // if all properties are removed and there is no sub-components, then + // remove this compoent + if ((c->count_properties(ICAL_ANY_PROPERTY) == 0) && + (c->count_components(ICAL_ANY_COMPONENT) == 0)) { + this->remove_component(c); + } + break; + } + } + } + + return true; +} +/* removeMissing == true: remove properties that are missing from fromC */ +/* todo: only change the first occurence of the property */ +/* todo: removeMissing is not implemented */ +bool VComponent::update(VComponent& fromC, bool removeMissing){ + + /* make sure they are the same kind */ + if (this->isa() != fromC.isa()) return false; + + /* property first */ + ICalPropertyTmpPtr prop; + for (prop=fromC.get_first_property(ICAL_ANY_PROPERTY); prop != NULL; + prop=fromC.get_next_property(ICAL_ANY_PROPERTY)) { + ICalPropertyTmpPtr thisProp; + thisProp = this->get_first_property(prop->isa()); + if (thisProp == NULL) { + thisProp = new ICalProperty(prop->isa()); + this->add_property(thisProp); + } + ICalValue *value = new ICalValue(*(prop->get_value())); // clone the value + thisProp->set_value(*value); + } + + /* recursively updating sub-components */ + VComponentTmpPtr comp; + for (comp=fromC.get_first_component(ICAL_ANY_COMPONENT); comp != NULL; + comp=fromC.get_next_component(ICAL_ANY_COMPONENT)) { + VComponentTmpPtr thisComp; + thisComp = this->get_first_component(comp->isa()); + if (thisComp == NULL) { + thisComp = new VComponent(comp->isa()); + this->add_component(thisComp); + } + bool err = thisComp->update(*comp, removeMissing); + if (!err) return false; + } + return true; +} +/* add components and property. recursively goes down child components */ +bool VComponent::add(VComponent& fromC){ + /* make sure the kind are the same */ + if (this->isa() != fromC.isa()) return false; + + /* properties first */ + ICalPropertyTmpPtr prop; + for (prop=fromC.get_first_property(ICAL_ANY_PROPERTY); prop != NULL; + prop=fromC.get_next_property(ICAL_ANY_PROPERTY)) { + /* clone another property */ + ICalProperty *p = new ICalProperty(*prop); + add_property(p); + } + + /* sub-components next */ + bool err = false; + VComponentTmpPtr comp; + for (comp=fromC.get_first_component(ICAL_ANY_COMPONENT); comp != NULL; + comp=fromC.get_next_component(ICAL_ANY_COMPONENT)) { + VComponent *c = new VComponent(comp->isa()); + err = c->add(*comp); + add_component(c); + } + + return true; +} + +VCalendar::VCalendar() : VComponent(icalcomponent_new_vcalendar()){ +} +VCalendar::VCalendar(const VCalendar& v) : VComponent(v) {} +VCalendar& VCalendar::operator=(const VCalendar& v){ + if (this == &v) return *this; + VComponent::operator=(v); + + return *this; +} +VCalendar::~VCalendar(){} + +VCalendar::VCalendar(icalcomponent* v) : VComponent(v){} +VCalendar::VCalendar(string str) : VComponent(str){} + + +/* VEvent */ + +VEvent::VEvent() : VComponent(icalcomponent_new_vevent()){} +VEvent::VEvent(const VEvent& v) : VComponent(v) {} +VEvent& VEvent::operator=(const VEvent& v){ + if (this == &v) return *this; + VComponent::operator=(v); + + return *this; +} +VEvent::~VEvent(){} + +VEvent::VEvent(icalcomponent* v) : VComponent(v){} +VEvent::VEvent(string str) : VComponent(str){} + + +/* VTodo */ + +VToDo::VToDo() : VComponent(icalcomponent_new_vtodo()){} +VToDo::VToDo(const VToDo& v) : VComponent(v) {} +VToDo& VToDo::operator=(const VToDo& v){ + if (this == &v) return *this; + VComponent::operator=(v); + + return *this; +} +VToDo::~VToDo(){} + +VToDo::VToDo(icalcomponent* v) : VComponent(v){} +VToDo::VToDo(string str) : VComponent(str){} + + +/* VAgenda */ + +VAgenda::VAgenda() : VComponent(icalcomponent_new_vagenda()){} +VAgenda::VAgenda(const VAgenda& v) : VComponent(v) {} +VAgenda& VAgenda::operator=(const VAgenda& v){ + if (this == &v) return *this; + VComponent::operator=(v); + + return *this; +} +VAgenda::~VAgenda(){} + +VAgenda::VAgenda(icalcomponent* v) : VComponent(v){} +VAgenda::VAgenda(string str) : VComponent(str){} + + +/* VQuery */ + +VQuery::VQuery() : VComponent(icalcomponent_new_vquery()){} +VQuery::VQuery(const VQuery& v) : VComponent(v) {} +VQuery& VQuery::operator=(const VQuery& v){ + if (this == &v) return *this; + VComponent::operator=(v); + + return *this; +} +VQuery::~VQuery(){} + +VQuery::VQuery(icalcomponent* v) : VComponent(v){} +VQuery::VQuery(string str) : VComponent(str){} + + +/* VJournal */ + +VJournal::VJournal() : VComponent(icalcomponent_new_vjournal()){} +VJournal::VJournal(const VJournal& v) : VComponent(v) {} +VJournal& VJournal::operator=(const VJournal& v){ + if (this == &v) return *this; + VComponent::operator=(v); + + return *this; +} +VJournal::~VJournal(){} + +VJournal::VJournal(icalcomponent* v) : VComponent(v){} +VJournal::VJournal(string str) : VComponent(str){} + + +/* VAlarm */ + +VAlarm::VAlarm() : VComponent(icalcomponent_new_valarm()){} +VAlarm::VAlarm(const VAlarm& v) : VComponent(v) {} +VAlarm& VAlarm::operator=(const VAlarm& v){ + if (this == &v) return *this; + VComponent::operator=(v); + + return *this; +} +VAlarm::~VAlarm(){} + +VAlarm::VAlarm(icalcomponent* v) : VComponent(v){} +VAlarm::VAlarm(string str) : VComponent(str){} + +icalrequeststatus +VAlarm::getTriggerTime(VComponent &c, struct icaltriggertype *tr) +{ + struct icaltimetype tt; + ICalParameter *related_param; + + ICalPropertyTmpPtr trigger_prop = this->get_first_property(ICAL_TRIGGER_PROPERTY); + + // all VALARMs must have a TRIGGER + if (trigger_prop == NULL) + return ICAL_3_1_INVPROPVAL_STATUS; + + *tr = trigger_prop->get_trigger(); + + tt = icaltime_null_time(); + + if (icaltime_is_null_time(tr->time)) { + + // relative time trigger + + // TRIGGER;RELATED=END:P5M 5 minutes after END + // TRIGGER;RELATED=START:-P15M 15 minutes before START + // get RELATED parameter + + related_param = trigger_prop->get_first_parameter(ICAL_RELATED_PARAMETER); + + if (related_param && related_param->is_valid()) { + + // get RELATED parameter value + icalparameter_related related = related_param->get_related(); + + if(related) { + switch(related) { + case ICAL_RELATED_END: + if (c.isa() == ICAL_VEVENT_COMPONENT) { + tt = c.get_dtend(); + + // If a recurrenceid exists, use that to calculate the + // dtend from the dtstart. + struct icaltimetype recur_time = c.get_recurrenceid(); + if (!(icaltime_is_null_time(recur_time))) { + struct icaldurationtype dur = icaltime_subtract(c.get_dtstart(), tt); + tt = icaltime_add(recur_time, dur); + } + } + else if (c.isa() == ICAL_VTODO_COMPONENT) { + tt = c.get_due(); + struct icaltimetype recur_time = c.get_recurrenceid(); + if (!(icaltime_is_null_time(recur_time))) { + tt = recur_time; + } + } + // @@@ TODO: if not DTEND or DUE, then DTSTART and DURATION must be present + break; + case ICAL_RELATED_START: + case ICAL_RELATED_X: + case ICAL_RELATED_NONE: + default: + tt = c.get_dtstart(); + struct icaltimetype recur_time = c.get_recurrenceid(); + if (!(icaltime_is_null_time(recur_time))) { + tt = recur_time; + } + break; + } + + } + } + else { // no RELATED explicity specified, the default is + // relative to the start of an event or to-do, rfc2445 + // if no RELATED, we are forced to use dtstart for VEVENT, + // due for VTODO to calculate trigger time. + // If a recur time exists, use that. Recur time trumps dtstart or due. + struct icaltimetype recur_time = c.get_recurrenceid(); + if (!(icaltime_is_null_time(recur_time))) { + tt = recur_time; + } + else if (c.isa() == ICAL_VEVENT_COMPONENT) + tt = c.get_dtstart(); + else if (c.isa() == ICAL_VTODO_COMPONENT) + tt = c.get_due(); + } + + // malformed? encapsulating VEVENT or VTODO MUST have DTSTART/DTEND + if(icaltime_is_null_time(tt)) + return ICAL_3_1_INVPROPVAL_STATUS;; + + // now offset using tr.duration + tr->time = icaltime_add(tt, tr->duration); + } + // else absolute time trigger + + return ICAL_2_0_SUCCESS_STATUS; +} + + +/* VFreeBusy */ + +VFreeBusy::VFreeBusy() : VComponent(icalcomponent_new_vfreebusy()){} +VFreeBusy::VFreeBusy(const VFreeBusy& v) : VComponent(v) {} +VFreeBusy& VFreeBusy::operator=(const VFreeBusy& v){ + if (this == &v) return *this; + VComponent::operator=(v); + + return *this; +} +VFreeBusy::~VFreeBusy(){} + +VFreeBusy::VFreeBusy(icalcomponent* v) : VComponent(v){} +VFreeBusy::VFreeBusy(string str) : VComponent(str){} + + +/* VTimezone */ + +VTimezone::VTimezone() : VComponent(icalcomponent_new_vtimezone()){} +VTimezone::VTimezone(const VTimezone& v) : VComponent(v) {} +VTimezone& VTimezone::operator=(const VTimezone& v){ + if (this == &v) return *this; + VComponent::operator=(v); + + return *this; +} +VTimezone::~VTimezone(){} + +VTimezone::VTimezone(icalcomponent* v) : VComponent(v){} +VTimezone::VTimezone(string str) : VComponent(str){} + + +/* XStandard */ + +XStandard::XStandard() : VComponent(icalcomponent_new_xstandard()){} +XStandard::XStandard(const XStandard& v) : VComponent(v) {} +XStandard& XStandard::operator=(const XStandard& v){ + if (this == &v) return *this; + VComponent::operator=(v); + + return *this; +} +XStandard::~XStandard(){} + +XStandard::XStandard(icalcomponent* v) : VComponent(v){} +XStandard::XStandard(string str) : VComponent(str){} + + +/* XDaylight */ + +XDaylight::XDaylight() : VComponent(icalcomponent_new_xdaylight()){} +XDaylight::XDaylight(const XDaylight& v) : VComponent(v) {} +XDaylight& XDaylight::operator=(const XDaylight& v){ + if (this == &v) return *this; + VComponent::operator=(v); + + return *this; +} +XDaylight::~XDaylight(){} + +XDaylight::XDaylight(icalcomponent* v) : VComponent(v){} +XDaylight::XDaylight(string str) : VComponent(str){} diff --git a/src/libical/vcomponent.h b/src/libical/vcomponent.h new file mode 100644 index 00000000..ec965236 --- /dev/null +++ b/src/libical/vcomponent.h @@ -0,0 +1,351 @@ +/* -*- Mode: C++ -*- */ + +/** + * @file vcomponent.h + * @author fnguyen (12/10/01) + * @brief C++ classes for the icalcomponent wrapper (VToDo VEvent, etc..). + * + * (C) COPYRIGHT 2001, Critical Path + + 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 VCOMPONENT_H +#define VCOMPONENT_H + +#include "ical.h" +#include "icptrholder.h" + +typedef char* string; // Will use the string library from STL + +class ICalProperty; + +/** + * @class VComponent + * @brief A class wrapping the libical icalcomponent functions + * + * @exception icalerrorenum Any errors generated in libical are + * propogated via this exception type. + */ + +class VComponent { +public: + VComponent() throw (icalerrorenum); + VComponent(const VComponent&) throw (icalerrorenum); + VComponent& operator=(const VComponent&) throw (icalerrorenum); + virtual ~VComponent(); + + VComponent(icalcomponent* v) throw (icalerrorenum); + VComponent(string str) throw (icalerrorenum); + VComponent(icalcomponent_kind kind) throw (icalerrorenum);; + + operator icalcomponent* () { return imp; } + + void new_from_string(string str); + + // detach imp to this object. use with caution. it would cause + // memory leak if you are not careful. + void detach() { + imp = NULL; + } + +public: + string as_ical_string() throw (icalerrorenum); + bool is_valid(); + icalcomponent_kind isa(); + int isa_component(void* component); + + /// Working with properties + void add_property(ICalProperty* property); + void remove_property(ICalProperty* property); + int count_properties(icalproperty_kind kind); + + // Iterate through the properties + ICalProperty* get_current_property(); + ICalProperty* get_first_property(icalproperty_kind kind); + ICalProperty* get_next_property(icalproperty_kind kind); + + // Working with components + + /** + * Return the first VEVENT, VTODO or VJOURNAL sub-component if + * it is one of those types + */ + + VComponent* get_inner(); + + void add_component(VComponent* child); + void remove_component(VComponent* child); + int count_components(icalcomponent_kind kind); + + /** + * 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 + VComponent* get_current_component(); + VComponent* get_first_component(icalcomponent_kind kind); + VComponent* get_next_component(icalcomponent_kind kind); + + /// Using external iterators + icalcompiter begin_component(icalcomponent_kind kind); + icalcompiter end_component(icalcomponent_kind kind); + VComponent* next(icalcompiter* i); + VComponent* prev(icalcompiter* i); + VComponent* current(icalcompiter* i); + + /// Working with embedded error properties + int count_errors(); + + /// Remove all X-LIC-ERROR properties + void strip_errors(); + + /// Convert some X-LIC-ERROR properties into RETURN-STATUS properties + void convert_errors(); + + /// Kind conversion routines + static icalcomponent_kind string_to_kind(string str); + static string kind_to_string(icalcomponent_kind kind); + +public: + struct icaltimetype get_dtstart(); + void set_dtstart(struct icaltimetype v); + + /** 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 apropriate property + */ + + struct icaltimetype get_dtend(); + void set_dtend(struct icaltimetype v); + + struct icaltimetype get_due(); + void set_due(struct icaltimetype v); + + struct icaldurationtype get_duration(); + void set_duration(struct icaldurationtype v); + + icalproperty_method get_method(); + void set_method(icalproperty_method method); + + struct icaltimetype get_dtstamp(); + void set_dtstamp(struct icaltimetype v); + + string get_summary(); + void set_summary(string v); + + string get_location(); + void set_location(string v); + + string get_description(); + void set_description(string v); + + string get_comment(); + void set_comment(string v); + + string get_uid(); + void set_uid(string v); + + string get_relcalid(); + void set_relcalid(string v); + + struct icaltimetype get_recurrenceid(); + void set_recurrenceid(struct icaltimetype v); + + int get_sequence(); + void set_sequence(int v); + + int get_status(); + void set_status(enum icalproperty_status v); + + +public: + /** + * For VCOMPONENT: Return a reference to the first VEVENT, + * VTODO, or VJOURNAL + */ + VComponent* get_first_real_component(); + + /** + * For VEVENT, VTODO, VJOURNAL and VTIMEZONE: report the + * start and end times of an event in UTC + */ + virtual struct icaltime_span get_span(); + + int recurrence_is_excluded(struct icaltimetype *dtstart, + struct icaltimetype *recurtime); + + +public: + /** + helper functions for adding/removing/updating property and + sub-components */ + + /// Note: the VComponent kind have to be the same + + bool remove(VComponent&, bool ignoreValue); + bool update(VComponent&, bool removeMissing); + bool add(VComponent&); + +private: + /* Internal operations. They are private, and you should not be using them. */ + VComponent* get_parent(); + void set_parent(VComponent *parent); + + char* quote_ical_string(char* str); + +private: + icalcomponent* imp; +}; + +class VCalendar : public VComponent { +public: + VCalendar(); + VCalendar(const VCalendar&); + VCalendar& operator=(const VCalendar&); + ~VCalendar(); + + VCalendar(icalcomponent* v); + VCalendar(string str); +}; + + +class VEvent : public VComponent { +public: + VEvent(); + VEvent(const VEvent&); + VEvent& operator=(const VEvent&); + ~VEvent(); + + VEvent(icalcomponent* v); + VEvent(string str); +}; + +class VToDo : public VComponent { +public: + VToDo(); + VToDo(const VToDo&); + VToDo& operator=(const VToDo&); + ~VToDo(); + + VToDo(icalcomponent* v); + VToDo(string str); +}; + +class VAgenda : public VComponent { +public: + VAgenda(); + VAgenda(const VAgenda&); + VAgenda& operator=(const VAgenda&); + ~VAgenda(); + + VAgenda(icalcomponent* v); + VAgenda(string str); +}; + +class VQuery : public VComponent { +public: + VQuery(); + VQuery(const VQuery&); + VQuery& operator=(const VQuery&); + ~VQuery(); + + VQuery(icalcomponent* v); + VQuery(string str); +}; + +class VJournal : public VComponent { +public: + VJournal(); + VJournal(const VJournal&); + VJournal& operator=(const VJournal&); + ~VJournal(); + + VJournal(icalcomponent* v); + VJournal(string str); +}; + +class VAlarm : public VComponent { +public: + VAlarm(); + VAlarm(const VAlarm&); + VAlarm& operator=(const VAlarm&); + ~VAlarm(); + + VAlarm(icalcomponent* v); + VAlarm(string str); + + /** + * compute the absolute trigger time for this alarm. trigger + * may be related to the containing component c. the result + * is populated in tr->time + */ + icalrequeststatus getTriggerTime(VComponent &c, struct icaltriggertype *tr); +}; + +class VFreeBusy : public VComponent { +public: + VFreeBusy(); + VFreeBusy(const VFreeBusy&); + VFreeBusy& operator=(const VFreeBusy&); + ~VFreeBusy(); + + VFreeBusy(icalcomponent* v); + VFreeBusy(string str); +}; + +class VTimezone : public VComponent { +public: + VTimezone(); + VTimezone(const VTimezone&); + VTimezone& operator=(const VTimezone&); + ~VTimezone(); + + VTimezone(icalcomponent* v); + VTimezone(string str); +}; + +class XStandard : public VComponent { +public: + XStandard(); + XStandard(const XStandard&); + XStandard& operator=(const XStandard&); + ~XStandard(); + + XStandard(icalcomponent* v); + XStandard(string str); +}; + +class XDaylight : public VComponent { +public: + XDaylight(); + XDaylight(const XDaylight&); + XDaylight& operator=(const XDaylight&); + ~XDaylight(); + + XDaylight(icalcomponent* v); + XDaylight(string str); +}; + +typedef ICPointerHolder<VComponent> VComponentTmpPtr; /* see icptrholder.h for comments */ + +#endif /* VComponent_H */ diff --git a/src/libical/vsnprintf.h b/src/libical/vsnprintf.h new file mode 100644 index 00000000..104a53a9 --- /dev/null +++ b/src/libical/vsnprintf.h @@ -0,0 +1,33 @@ +#ifdef WIN32 + +#ifndef roundup +#define roundup(x, y) ((((x)+((y)-1))/(y))*(y)) +#endif + +#include <stdlib.h> +#include <stdarg.h> + +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 + + +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 + +#endif |
