aboutsummaryrefslogtreecommitdiffstats
path: root/libical
diff options
context:
space:
mode:
authorJP Rosevear <jpr@src.gnome.org>2000-08-25 03:31:03 +0800
committerJP Rosevear <jpr@src.gnome.org>2000-08-25 03:31:03 +0800
commit8357d7b199e26e4d071b267a314447b22f2ddb3c (patch)
treef6ed518cd2056dacdc9833750137db05bb7fb7cb /libical
parent695baf618d363f760ec81d109c6e6185e510b1e7 (diff)
downloadgsoc2013-evolution-8357d7b199e26e4d071b267a314447b22f2ddb3c.tar
gsoc2013-evolution-8357d7b199e26e4d071b267a314447b22f2ddb3c.tar.gz
gsoc2013-evolution-8357d7b199e26e4d071b267a314447b22f2ddb3c.tar.bz2
gsoc2013-evolution-8357d7b199e26e4d071b267a314447b22f2ddb3c.tar.lz
gsoc2013-evolution-8357d7b199e26e4d071b267a314447b22f2ddb3c.tar.xz
gsoc2013-evolution-8357d7b199e26e4d071b267a314447b22f2ddb3c.tar.zst
gsoc2013-evolution-8357d7b199e26e4d071b267a314447b22f2ddb3c.zip
Initial revision
svn path=/trunk/; revision=5011
Diffstat (limited to 'libical')
-rw-r--r--libical/examples/Makefile.am13
-rw-r--r--libical/examples/Makefile.in301
-rw-r--r--libical/examples/access_components.c325
-rw-r--r--libical/examples/access_properties_and_parameters.c144
-rw-r--r--libical/examples/errors.c70
-rw-r--r--libical/examples/main.c9
-rw-r--r--libical/examples/parse_text.c73
-rw-r--r--libical/src/libical/icalmime.c325
-rw-r--r--libical/src/libical/icalmime.h41
-rw-r--r--libical/src/libical/icalrecur.c1174
-rw-r--r--libical/src/libical/icalrecur.h44
-rw-r--r--libical/src/libical/icaltime.c182
-rw-r--r--libical/src/libical/icaltime.h74
-rw-r--r--libical/src/libical/sspm.c1191
-rw-r--r--libical/src/libical/sspm.h138
-rw-r--r--libical/src/libicalss/icaldirset.c718
-rw-r--r--libical/src/libicalss/icaldirset.h84
-rw-r--r--libical/src/libicalss/icalfileset.c427
-rw-r--r--libical/src/libicalss/icalfileset.h90
-rw-r--r--libical/src/libicalss/icalfilesetimpl.h42
-rw-r--r--libical/src/libicalss/icalgauge.c208
-rw-r--r--libical/src/libicalss/icalgauge.h37
-rw-r--r--libical/src/libicalss/icalset.c86
-rw-r--r--libical/src/libicalss/icalset.h100
-rw-r--r--libical/src/libicalvcal/Makefile.am17
-rw-r--r--libical/src/libicalvcal/Makefile.in309
-rw-r--r--libical/src/libicalvcal/README.TXT951
-rw-r--r--libical/src/libicalvcal/icalvcal.c498
-rw-r--r--libical/src/libicalvcal/icalvcal.h39
-rw-r--r--libical/src/libicalvcal/port.h88
-rw-r--r--libical/src/libicalvcal/vcaltest.c118
-rw-r--r--libical/src/libicalvcal/vcaltmp.c337
-rw-r--r--libical/src/libicalvcal/vcaltmp.h128
-rw-r--r--libical/src/libicalvcal/vcc.c2142
-rw-r--r--libical/src/libicalvcal/vcc.h80
-rw-r--r--libical/src/libicalvcal/vcc.y1218
-rw-r--r--libical/src/libicalvcal/vctest.c95
-rw-r--r--libical/src/libicalvcal/vobject.c1452
-rw-r--r--libical/src/libicalvcal/vobject.h366
-rw-r--r--libical/src/test/recur.c96
-rw-r--r--libical/src/test/testmime.c339
-rw-r--r--libical/src/test/testvcal.c56
-rw-r--r--libical/test-data/complex-mime.txt81
-rw-r--r--libical/test-data/recur.txt632
-rw-r--r--libical/test-data/simple-mime.txt26
-rw-r--r--libical/test-data/user-cal.vcf76
46 files changed, 15040 insertions, 0 deletions
diff --git a/libical/examples/Makefile.am b/libical/examples/Makefile.am
new file mode 100644
index 0000000000..f18663723d
--- /dev/null
+++ b/libical/examples/Makefile.am
@@ -0,0 +1,13 @@
+
+noinst_PROGRAMS = doesnothing
+
+LDADD = ../src/libical/libical.a ../src/libicalss/libicalss.a ../src/libicalvcal/libicalvcal.a
+INCLUDES = -I . -I../src/libical -I../src/libicalss -I../src/libicalvcal
+
+doesnothing_SOURCES = \
+ access_components.c \
+ access_properties_and_parameters.c \
+ errors.c \
+ main.c \
+ parse_text.c
+
diff --git a/libical/examples/Makefile.in b/libical/examples/Makefile.in
new file mode 100644
index 0000000000..74aabc1016
--- /dev/null
+++ b/libical/examples/Makefile.in
@@ -0,0 +1,301 @@
+# Makefile.in generated automatically by automake 1.4 from Makefile.am
+
+# Copyright (C) 1994, 1995-8, 1999 Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+
+SHELL = @SHELL@
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+
+bindir = @bindir@
+sbindir = @sbindir@
+libexecdir = @libexecdir@
+datadir = @datadir@
+sysconfdir = @sysconfdir@
+sharedstatedir = @sharedstatedir@
+localstatedir = @localstatedir@
+libdir = @libdir@
+infodir = @infodir@
+mandir = @mandir@
+includedir = @includedir@
+oldincludedir = /usr/include
+
+DESTDIR =
+
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+
+top_builddir = ..
+
+ACLOCAL = @ACLOCAL@
+AUTOCONF = @AUTOCONF@
+AUTOMAKE = @AUTOMAKE@
+AUTOHEADER = @AUTOHEADER@
+
+INSTALL = @INSTALL@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@ $(AM_INSTALL_PROGRAM_FLAGS)
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+transform = @program_transform_name@
+
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+AR = @AR@
+CC = @CC@
+LEX = @LEX@
+LN_S = @LN_S@
+MAKEINFO = @MAKEINFO@
+PACKAGE = @PACKAGE@
+RANLIB = @RANLIB@
+VERSION = @VERSION@
+YACC = @YACC@
+
+noinst_PROGRAMS = doesnothing
+
+LDADD = ../src/libical/libical.a ../src/libicalss/libicalss.a ../src/libicalvcal/libicalvcal.a
+INCLUDES = -I . -I../src/libical -I../src/libicalss -I../src/libicalvcal
+
+doesnothing_SOURCES = access_components.c access_properties_and_parameters.c errors.c main.c parse_text.c
+
+mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
+CONFIG_HEADER = ../config.h
+CONFIG_CLEAN_FILES =
+PROGRAMS = $(noinst_PROGRAMS)
+
+
+DEFS = @DEFS@ -I. -I$(srcdir) -I..
+CPPFLAGS = @CPPFLAGS@
+LDFLAGS = @LDFLAGS@
+LIBS = @LIBS@
+doesnothing_OBJECTS = access_components.o \
+access_properties_and_parameters.o errors.o main.o parse_text.o
+doesnothing_LDADD = $(LDADD)
+doesnothing_DEPENDENCIES = ../src/libical/libical.a \
+../src/libicalss/libicalss.a ../src/libicalvcal/libicalvcal.a
+doesnothing_LDFLAGS =
+CFLAGS = @CFLAGS@
+COMPILE = $(CC) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(LDFLAGS) -o $@
+DIST_COMMON = Makefile.am Makefile.in
+
+
+DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) $(TEXINFOS) $(EXTRA_DIST)
+
+TAR = gtar
+GZIP_ENV = --best
+SOURCES = $(doesnothing_SOURCES)
+OBJECTS = $(doesnothing_OBJECTS)
+
+all: all-redirect
+.SUFFIXES:
+.SUFFIXES: .S .c .o .s
+$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4)
+ cd $(top_srcdir) && $(AUTOMAKE) --gnu --include-deps examples/Makefile
+
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ cd $(top_builddir) \
+ && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= $(SHELL) ./config.status
+
+
+mostlyclean-noinstPROGRAMS:
+
+clean-noinstPROGRAMS:
+ -test -z "$(noinst_PROGRAMS)" || rm -f $(noinst_PROGRAMS)
+
+distclean-noinstPROGRAMS:
+
+maintainer-clean-noinstPROGRAMS:
+
+.c.o:
+ $(COMPILE) -c $<
+
+.s.o:
+ $(COMPILE) -c $<
+
+.S.o:
+ $(COMPILE) -c $<
+
+mostlyclean-compile:
+ -rm -f *.o core *.core
+
+clean-compile:
+
+distclean-compile:
+ -rm -f *.tab.c
+
+maintainer-clean-compile:
+
+doesnothing: $(doesnothing_OBJECTS) $(doesnothing_DEPENDENCIES)
+ @rm -f doesnothing
+ $(LINK) $(doesnothing_LDFLAGS) $(doesnothing_OBJECTS) $(doesnothing_LDADD) $(LIBS)
+
+tags: TAGS
+
+ID: $(HEADERS) $(SOURCES) $(LISP)
+ list='$(SOURCES) $(HEADERS)'; \
+ unique=`for i in $$list; do echo $$i; done | \
+ awk ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ here=`pwd` && cd $(srcdir) \
+ && mkid -f$$here/ID $$unique $(LISP)
+
+TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS)'; \
+ unique=`for i in $$list; do echo $$i; done | \
+ awk ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ test -z "$(ETAGS_ARGS)$$unique$(LISP)$$tags" \
+ || (cd $(srcdir) && etags $(ETAGS_ARGS) $$tags $$unique $(LISP) -o $$here/TAGS)
+
+mostlyclean-tags:
+
+clean-tags:
+
+distclean-tags:
+ -rm -f TAGS ID
+
+maintainer-clean-tags:
+
+distdir = $(top_builddir)/$(PACKAGE)-$(VERSION)/$(subdir)
+
+subdir = examples
+
+distdir: $(DISTFILES)
+ @for file in $(DISTFILES); do \
+ d=$(srcdir); \
+ if test -d $$d/$$file; then \
+ cp -pr $$d/$$file $(distdir)/$$file; \
+ else \
+ test -f $(distdir)/$$file \
+ || ln $$d/$$file $(distdir)/$$file 2> /dev/null \
+ || cp -p $$d/$$file $(distdir)/$$file || :; \
+ fi; \
+ done
+access_components.o: access_components.c ../src/libical/ical.h \
+ ../src/libical/icalversion.h ../src/libical/icalenums.h \
+ ../src/libical/icalvalue.h ../src/libical/icaltypes.h \
+ ../src/libical/icaltime.h ../src/libical/icalparameter.h \
+ ../src/libical/icalproperty.h ../src/libical/icalcomponent.h \
+ ../src/libical/pvl.h ../src/libical/icalparser.h \
+ ../src/libical/icalmemory.h ../src/libical/icalerror.h \
+ ../src/libical/icalrestriction.h ../src/libical/icalrecur.h
+access_properties_and_parameters.o: access_properties_and_parameters.c \
+ ../src/libical/ical.h ../src/libical/icalversion.h \
+ ../src/libical/icalenums.h ../src/libical/icalvalue.h \
+ ../src/libical/icaltypes.h ../src/libical/icaltime.h \
+ ../src/libical/icalparameter.h ../src/libical/icalproperty.h \
+ ../src/libical/icalcomponent.h ../src/libical/pvl.h \
+ ../src/libical/icalparser.h ../src/libical/icalmemory.h \
+ ../src/libical/icalerror.h ../src/libical/icalrestriction.h \
+ ../src/libical/icalrecur.h
+errors.o: errors.c ../src/libical/ical.h ../src/libical/icalversion.h \
+ ../src/libical/icalenums.h ../src/libical/icalvalue.h \
+ ../src/libical/icaltypes.h ../src/libical/icaltime.h \
+ ../src/libical/icalparameter.h ../src/libical/icalproperty.h \
+ ../src/libical/icalcomponent.h ../src/libical/pvl.h \
+ ../src/libical/icalparser.h ../src/libical/icalmemory.h \
+ ../src/libical/icalerror.h ../src/libical/icalrestriction.h \
+ ../src/libical/icalrecur.h
+main.o: main.c
+parse_text.o: parse_text.c ../src/libical/ical.h \
+ ../src/libical/icalversion.h ../src/libical/icalenums.h \
+ ../src/libical/icalvalue.h ../src/libical/icaltypes.h \
+ ../src/libical/icaltime.h ../src/libical/icalparameter.h \
+ ../src/libical/icalproperty.h ../src/libical/icalcomponent.h \
+ ../src/libical/pvl.h ../src/libical/icalparser.h \
+ ../src/libical/icalmemory.h ../src/libical/icalerror.h \
+ ../src/libical/icalrestriction.h ../src/libical/icalrecur.h
+
+info-am:
+info: info-am
+dvi-am:
+dvi: dvi-am
+check-am: all-am
+check: check-am
+installcheck-am:
+installcheck: installcheck-am
+install-exec-am:
+install-exec: install-exec-am
+
+install-data-am:
+install-data: install-data-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+install: install-am
+uninstall-am:
+uninstall: uninstall-am
+all-am: Makefile $(PROGRAMS)
+all-redirect: all-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) AM_INSTALL_PROGRAM_FLAGS=-s install
+installdirs:
+
+
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -rm -f Makefile $(CONFIG_CLEAN_FILES)
+ -rm -f config.cache config.log stamp-h stamp-h[0-9]*
+
+maintainer-clean-generic:
+mostlyclean-am: mostlyclean-noinstPROGRAMS mostlyclean-compile \
+ mostlyclean-tags mostlyclean-generic
+
+mostlyclean: mostlyclean-am
+
+clean-am: clean-noinstPROGRAMS clean-compile clean-tags clean-generic \
+ mostlyclean-am
+
+clean: clean-am
+
+distclean-am: distclean-noinstPROGRAMS distclean-compile distclean-tags \
+ distclean-generic clean-am
+
+distclean: distclean-am
+
+maintainer-clean-am: maintainer-clean-noinstPROGRAMS \
+ maintainer-clean-compile maintainer-clean-tags \
+ maintainer-clean-generic distclean-am
+ @echo "This command is intended for maintainers to use;"
+ @echo "it deletes files that may require special tools to rebuild."
+
+maintainer-clean: maintainer-clean-am
+
+.PHONY: mostlyclean-noinstPROGRAMS distclean-noinstPROGRAMS \
+clean-noinstPROGRAMS maintainer-clean-noinstPROGRAMS \
+mostlyclean-compile distclean-compile clean-compile \
+maintainer-clean-compile tags mostlyclean-tags distclean-tags \
+clean-tags maintainer-clean-tags distdir info-am info dvi-am dvi check \
+check-am installcheck-am installcheck install-exec-am install-exec \
+install-data-am install-data install-am install uninstall-am uninstall \
+all-redirect all-am all installdirs mostlyclean-generic \
+distclean-generic clean-generic maintainer-clean-generic clean \
+mostlyclean distclean maintainer-clean
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/libical/examples/access_components.c b/libical/examples/access_components.c
new file mode 100644
index 0000000000..6b655b42f7
--- /dev/null
+++ b/libical/examples/access_components.c
@@ -0,0 +1,325 @@
+/* Access_component.c */
+
+#include "ical.h"
+
+#include <assert.h>
+#include <string.h> /* for strdup */
+#include <stdlib.h> /* for malloc */
+#include <stdio.h> /* for printf */
+#include <time.h> /* for time() */
+#include "icalmemory.h"
+
+/* Creating iCal Components
+
+ There are two ways to create new component in libical. You can
+ build the component from primitive parts, or you can create it
+ from a string.
+
+ There are two variations of the API for building the component from
+ primitive parts. In the first variation, you add each parameter and
+ value to a property, and then add each property to a
+ component. This results in a long series of function calls. This
+ style is show in create_new_component()
+
+ The second variation uses vargs lists to nest many primitive part
+ constructors, resulting in a compact, neatly formated way to create
+ components. This style is shown in create_new_component_with_va_args()
+
+
+
+*/
+
+icalcomponent* create_new_component()
+{
+
+ /* variable definitions */
+ icalcomponent* calendar;
+ icalcomponent* event;
+ struct icaltimetype atime = icaltime_from_timet( time(0),0,0);
+ struct icalperiodtype rtime;
+ icalproperty* property;
+
+ /* Define a time type that will use as data later. */
+ rtime.start = icaltime_from_timet( time(0),0,0);
+ rtime.end = icaltime_from_timet( time(0),0,0);
+ rtime.end.hour++;
+
+ /* Create calendar and add properties */
+
+ calendar = icalcomponent_new(ICAL_VCALENDAR_COMPONENT);
+
+ /* Nearly every libical function call has the same general
+ form. The first part of the name defines the 'class' for the
+ function, and the first argument will be a pointer to a struct
+ of that class. So, icalcomponent_ functions will all take
+ icalcomponent* as their first argument. */
+
+ /* The next call creates a new proeprty and immediately adds it to the
+ 'calendar' component. */
+
+ icalcomponent_add_property(
+ calendar,
+ icalproperty_new_version(strdup("2.0"))
+ );
+
+ /* Note the use of strdup() in the previous and next call. All
+ properties constructors for properties with value types of
+ TEXT will take control of the string you pass into them. Since
+ the string '2.0' is a static string, we need to duplicate it in
+ new memory before giving it to the property */
+
+ /* Here is the short version of the memory rules:
+
+ If the routine name has "new" in it:
+ Caller owns the returned memory.
+ If you pass in a string, the routine takes the memory.
+
+ If the routine name has "add" in it:
+ The routine takes control of the component, property,
+ parameter or value memory.
+
+ If the routine returns a string ( "get" and "as_ical_string" )
+ The library owns the returned memory.
+
+ There are more rules, so refer to the documentation for more
+ details.
+
+ */
+
+ icalcomponent_add_property(
+ calendar,
+ icalproperty_new_prodid(strdup("-//RDU Software//NONSGML HandCal//EN"))
+ );
+
+ /* Add an event */
+
+ event = icalcomponent_new(ICAL_VEVENT_COMPONENT);
+
+ icalcomponent_add_property(
+ event,
+ icalproperty_new_dtstamp(atime)
+ );
+
+ /* In the previous call, atime is a struct, and it is passed in by value.
+ This is how all compound types of values are handled. */
+
+ icalcomponent_add_property(
+ event,
+ icalproperty_new_uid(strdup("guid-1.host1.com"))
+ );
+
+ /* add a property that has parameters */
+ property = icalproperty_new_organizer(strdup("mailto:mrbig@host.com"));
+
+ icalproperty_add_parameter(
+ property,
+ icalparameter_new_role(ICAL_ROLE_CHAIR)
+ );
+
+ icalcomponent_add_property(event,property);
+
+ /* In this style of component creation, you need to use an extra
+ call to add parameters to properties, but the form of this
+ operation is the same as adding a property to a component */
+
+ /* add another property that has parameters */
+ property = icalproperty_new_attendee(strdup("mailto:employee-A@host.com"));
+
+ icalproperty_add_parameter(
+ property,
+ icalparameter_new_role(ICAL_ROLE_REQPARTICIPANT)
+ );
+
+ icalproperty_add_parameter(
+ property,
+ icalparameter_new_rsvp(1)
+ );
+
+ icalproperty_add_parameter(
+ property,
+ icalparameter_new_cutype(ICAL_CUTYPE_GROUP)
+ );
+
+ icalcomponent_add_property(event,property);
+
+
+ /* more properties */
+
+ icalcomponent_add_property(
+ event,
+ icalproperty_new_description(strdup("Project XYZ Review Meeting"))
+ );
+
+ icalcomponent_add_property(
+ event,
+ icalproperty_new_categories(strdup("MEETING"))
+ );
+
+ icalcomponent_add_property(
+ event,
+ icalproperty_new_class(strdup("PUBLIC"))
+ );
+
+ icalcomponent_add_property(
+ event,
+ icalproperty_new_created(atime)
+ );
+
+ icalcomponent_add_property(
+ event,
+ icalproperty_new_summary(strdup("XYZ Project Review"))
+ );
+
+ property = icalproperty_new_dtstart(atime);
+
+ icalproperty_add_parameter(
+ property,
+ icalparameter_new_tzid(strdup("US-Eastern"))
+ );
+
+ icalcomponent_add_property(event,property);
+
+
+ property = icalproperty_new_dtend(atime);
+
+ icalproperty_add_parameter(
+ property,
+ icalparameter_new_tzid(strdup("US-Eastern"))
+ );
+
+ icalcomponent_add_property(event,property);
+
+ icalcomponent_add_property(
+ event,
+ icalproperty_new_location(strdup("1CP Conference Room 4350"))
+ );
+
+ icalcomponent_add_component(calendar,event);
+
+ return calendar;
+}
+
+
+/* Now, create the same component as in the previous routine, but use
+the constructor style. */
+
+icalcomponent* create_new_component_with_va_args()
+{
+
+ /* This is a similar set up to the last routine */
+ icalcomponent* calendar;
+ struct icaltimetype atime = icaltime_from_timet( time(0),0,0);
+ struct icalperiodtype rtime;
+
+ rtime.start = icaltime_from_timet( time(0),0,0);
+ rtime.end = icaltime_from_timet( time(0),0,0);
+ rtime.end.hour++;
+
+ /* Some of these routines are the same as those in the previous
+ routine, but we've also added several 'vanew' routines. These
+ 'vanew' routines take a list of properties, parameters or
+ values and add each of them to the parent property or
+ component. */
+
+ calendar =
+ icalcomponent_vanew(
+ ICAL_VCALENDAR_COMPONENT,
+ icalproperty_new_version(strdup("2.0")),
+ icalproperty_new_prodid(strdup("-//RDU Software//NONSGML HandCal//EN")),
+ icalcomponent_vanew(
+ ICAL_VEVENT_COMPONENT,
+ icalproperty_new_dtstamp(atime),
+ icalproperty_new_uid(strdup("guid-1.host1.com")),
+ icalproperty_vanew_organizer(
+ strdup("mailto:mrbig@host.com"),
+ icalparameter_new_role(ICAL_ROLE_CHAIR),
+ 0
+ ),
+ icalproperty_vanew_attendee(
+ strdup("mailto:employee-A@host.com"),
+ icalparameter_new_role(ICAL_ROLE_REQPARTICIPANT),
+ icalparameter_new_rsvp(1),
+ icalparameter_new_cutype(ICAL_CUTYPE_GROUP),
+ 0
+ ),
+ icalproperty_new_description(strdup("Project XYZ Review Meeting")),
+
+ /* Again, note the use of strdup to give libical
+ ownership of a static string. */
+
+ icalproperty_new_categories(strdup("MEETING")),
+ icalproperty_new_class(strdup("PUBLIC")),
+ icalproperty_new_created(atime),
+ icalproperty_new_summary(strdup("XYZ Project Review")),
+ icalproperty_vanew_dtstart(
+ atime,
+ icalparameter_new_tzid(strdup("US-Eastern")),
+ 0
+ ),
+ icalproperty_vanew_dtend(
+ atime,
+ icalparameter_new_tzid(strdup("US-Eastern")),
+ 0
+ ),
+ icalproperty_new_location(strdup("1CP Conference Room 4350")),
+ 0
+ ),
+ 0
+ );
+
+
+ /* Note that properties with no parameters can use the regular
+ 'new' constructor, while those with parameters use the 'vanew'
+ constructor. And, be sure that the last argument in the 'vanew'
+ call is a zero. Without, your program will probably crash. */
+
+ return calendar;
+}
+
+
+void find_sub_components(icalcomponent* comp)
+{
+ icalcomponent *c;
+
+ /* The second parameter to icalcomponent_get_first_component
+ indicates the type of component to search for. This will
+ iterate through all sub-components */
+ for(c = icalcomponent_get_first_component(comp,ICAL_ANY_COMPONENT);
+ c != 0;
+ c = icalcomponent_get_next_component(comp,ICAL_ANY_COMPONENT)){
+
+ do_something(c);
+ }
+
+ /* This will iterate only though VEVENT sub-components */
+
+ for(c = icalcomponent_get_first_component(comp,ICAL_VEVENT_COMPONENT);
+ c != 0;
+ c = icalcomponent_get_next_component(comp,ICAL_VEVENT_COMPONENT)){
+
+ do_something(c);
+ }
+
+}
+
+/* Ical components only have one internal iterator, so removing the
+ object that the iterator points to can cause problems. Here is the
+ right way to remove components */
+
+void remove_vevent_sub_components(icalcomponent* comp){
+
+ icalcomponent *c, *next;
+
+ for( c = icalcomponent_get_first_component(comp,ICAL_VEVENT_COMPONENT);
+ c != 0;
+ c = next)
+ {
+ next = icalcomponent_get_next_component(comp,ICAL_VEVENT_COMPONENT);
+
+ icalcomponent_remove_component(comp,c);
+
+ do_something(c);
+ }
+
+}
+
diff --git a/libical/examples/access_properties_and_parameters.c b/libical/examples/access_properties_and_parameters.c
new file mode 100644
index 0000000000..e19656f57f
--- /dev/null
+++ b/libical/examples/access_properties_and_parameters.c
@@ -0,0 +1,144 @@
+/* access_properties_and_parameters.c */
+
+#include "ical.h"
+
+/* Get a particular parameter out of a component. This routine will
+ return a list of strings of all attendees who are required. Note
+ that this routine assumes that the component that we pass in is a
+ VEVENT. */
+
+void get_required_attendees(icalcomponent* event)
+{
+ icalproperty* p;
+ icalparameter* parameter;
+ int c=0;
+
+ assert(event != 0);
+ assert(icalcomponent_isa(event) == ICAL_VEVENT_COMPONENT);
+
+ /* This loop iterates over all of the ATTENDEE properties in the
+ event */
+
+ /* The iteration routines save their state in the event
+ struct, so the are not thread safe unless you lock the whole
+ component. */
+
+ for(
+ p = icalcomponent_get_first_property(event,ICAL_ATTENDEE_PROPERTY);
+ p != 0;
+ p = icalcomponent_get_next_property(event,ICAL_ATTENDEE_PROPERTY)
+ ) {
+
+ /* Get the first ROLE parameter in the property. There should
+ only be one, so we won't bother to iterate over them. But,
+ you can iterate over parameters just like with properties */
+
+ parameter = icalproperty_get_first_parameter(p,ICAL_ROLE_PARAMETER);
+
+ /* If the parameter indicates the participant is required, get
+ the attendees name and stick a copy of it into the output
+ array */
+
+ if ( icalparameter_get_role(parameter) == ICAL_ROLE_REQPARTICIPANT)
+ {
+ /* Remember, the caller does not own this string, so you
+ should strdup it if you want to change it. */
+ char *attendee = icalproperty_get_attendee(p);
+ }
+ }
+
+}
+
+/* Here is a similar example. If an attendee has a PARTSTAT of
+ NEEDSACTION or has no PARTSTAT parameter, change it to
+ TENTATIVE. */
+
+void update_attendees(icalcomponent* event)
+{
+ icalproperty* p;
+ icalparameter* parameter;
+
+ assert(event != 0);
+ assert(icalcomponent_isa(event) == ICAL_VEVENT_COMPONENT);
+
+ for(
+ p = icalcomponent_get_first_property(event,ICAL_ATTENDEE_PROPERTY);
+ p != 0;
+ p = icalcomponent_get_next_property(event,ICAL_ATTENDEE_PROPERTY)
+ ) {
+
+ parameter = icalproperty_get_first_parameter(p,ICAL_PARTSTAT_PARAMETER);
+
+ if (parameter == 0) {
+
+ /* There was no PARTSTAT parameter, so add one. */
+ icalproperty_add_parameter(
+ p,
+ icalparameter_new_partstat(ICAL_PARTSTAT_TENTATIVE)
+ );
+
+ } else if (icalparameter_get_partstat(parameter) == ICAL_PARTSTAT_NEEDSACTION) {
+ /* Remove the NEEDSACTION parameter and replace it with
+ TENTATIVE */
+
+ icalproperty_remove_parameter(p,ICAL_PARTSTAT_PARAMETER);
+
+ /* Don't forget to free it */
+ icalparameter_free(parameter);
+
+ /* Add a new one */
+ icalproperty_add_parameter(
+ p,
+ icalparameter_new_partstat(ICAL_PARTSTAT_TENTATIVE)
+ );
+ }
+
+ }
+}
+
+/* Here are some examples of manipulating properties */
+
+void test_properties()
+{
+ icalproperty *prop;
+ icalparameter *param;
+ icalvalue *value;
+
+ icalproperty *clone;
+
+ /* Create a new property */
+ prop = icalproperty_vanew_comment(
+ strdup("Another Comment"),
+ icalparameter_new_cn("A Common Name 1"),
+ icalparameter_new_cn("A Common Name 2"),
+ icalparameter_new_cn("A Common Name 3"),
+ icalparameter_new_cn("A Common Name 4"),
+ 0);
+
+ /* Iterate through all of the parameters in the property */
+ for(param = icalproperty_get_first_parameter(prop,ICAL_ANY_PROPERTY);
+ param != 0;
+ param = icalproperty_get_next_parameter(prop,ICAL_ANY_PROPERTY)) {
+
+ printf("Prop parameter: %s\n",icalparameter_get_cn(param));
+ }
+
+ /* Get a string representation of the property's value */
+ printf("Prop value: %s\n",icalproperty_get_comment(prop));
+
+ /* Spit out the property in its RFC 2445 representation */
+ printf("As iCAL string:\n %s\n",icalproperty_as_ical_string(prop));
+
+ /* Make a copy of the property. Caller owns the memory */
+ clone = icalproperty_new_clone(prop);
+
+ /* Get a reference to the value within the clone property */
+ value = icalproperty_get_value(clone);
+
+ printf("Value: %s",icalvalue_as_ical_string(value));
+
+ /* Free the original and the clone */
+ icalproperty_free(clone);
+ icalproperty_free(prop);
+
+}
diff --git a/libical/examples/errors.c b/libical/examples/errors.c
new file mode 100644
index 0000000000..071a2de816
--- /dev/null
+++ b/libical/examples/errors.c
@@ -0,0 +1,70 @@
+/* errors.c */
+
+#include "ical.h"
+#include <stdio.h>
+
+void program_errors()
+{
+ /*Most routines will set icalerrno on errors. This is an
+ enumeration defined in icalerror.h */
+
+ icalcomponent *c;
+
+ icalerror_clear_errno();
+
+ c = icalcomponent_new(ICAL_VEVENT_COMPONENT);
+
+ if (icalerrno != ICAL_NO_ERROR){
+
+ fprintf(stderr,"Horrible libical error: %s\n",
+ icalerror_strerror(icalerrno));
+
+ }
+
+}
+
+void component_errors(icalcomponent *comp)
+{
+ int errors;
+ icalproperty *p;
+
+ /* presume that we just got this component from the parser */
+
+ errors = icalcomponent_count_errors(comp);
+
+ printf("This component has %d parsing errors\n");
+
+ /* Print out all of the parsing errors. This is not strictly
+ correct, because it does not descend into any sub-components,
+ as icalcomponent_count_errors() does. */
+
+ for(p = icalcomponent_get_first_property(comp,ICAL_XLICERROR_PROPERTY);
+ p != 0;
+ p = icalcomponent_get_next_property(comp,ICAL_XLICERROR_PROPERTY))
+ {
+
+ printf("-- The error is %s:\n",icalproperty_get_xlicerror(p));
+ }
+
+
+
+ /* Check the component for iTIP compilance, and add more
+ X-LIC-ERROR properties if it is non-compilant. */
+ icalrestriction_check(comp);
+
+
+ /* Count the new errors. */
+ if(errors != icalcomponent_count_errors(comp)){
+ printf(" -- The component also has iTIP restriction errors \n");
+ }
+
+ /* Since there are iTIP restriction errors, it may be impossible
+ to process this component as an iTIP request. In this case, the
+ X-LIC-ERROR proeprties should be expressed as REQUEST-STATUS
+ properties in the reply. This following routine makes this
+ conversion */
+
+
+ icalcomponent_convert_errors(comp);
+
+}
diff --git a/libical/examples/main.c b/libical/examples/main.c
new file mode 100644
index 0000000000..88d6621365
--- /dev/null
+++ b/libical/examples/main.c
@@ -0,0 +1,9 @@
+/* This is just to make the code in the example directory link properly. */
+
+main()
+{
+}
+
+
+int do_something(){
+}
diff --git a/libical/examples/parse_text.c b/libical/examples/parse_text.c
new file mode 100644
index 0000000000..2761e6f951
--- /dev/null
+++ b/libical/examples/parse_text.c
@@ -0,0 +1,73 @@
+/* parse_text.c
+
+ */
+#include <stdio.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include "ical.h"
+
+#include <stdlib.h>
+
+/* The icalparser_get_line routine will create a single *content* line
+out of one or more input lines. The content line is all of the
+properties and values for a single property, and it can span several
+input lines. So, icalparser_get_line will need to be able to get more
+data on its own. Read_string is a routine that does this. You can
+write your own version of read stream to get data from other types of
+files, sockets, etc. */
+
+char* read_stream(char *s, size_t size, void *d)
+{
+ char *c = fgets(s,size, (FILE*)d);
+
+ return c;
+
+}
+
+int parse_text(int argc, char* argv[])
+{
+
+ int lineno = 0;
+ char* line;
+ FILE* stream;
+ icalcomponent *c;
+
+ /* Create a new parser object */
+ icalparser *parser = icalparser_new();
+
+ stream = fopen(argv[1],"r");
+
+ assert(stream != 0);
+
+ /* Tell the parser what input routie it should use. */
+ icalparser_set_gen_data(parser,stream);
+
+ do{
+
+ /* Get a single content line by making one or more calls to
+ read_stream()*/
+ line = icalparser_get_line(parser,read_stream);
+
+ /* Now, add that line into the parser object. If that line
+ completes a component, c will be non-zero */
+ c = icalparser_add_line(parser,line);
+
+
+ if (c != 0){
+ printf("%s",icalcomponent_as_ical_string(c));
+
+ /* Tell the parser that the caller will take ownership of
+ this component */
+ icalparser_claim(parser);
+
+ printf("\n---------------\n");
+
+ icalcomponent_free(c);
+ }
+
+ } while ( line != 0);
+
+
+ icalparser_free(parser);
+}
diff --git a/libical/src/libical/icalmime.c b/libical/src/libical/icalmime.c
new file mode 100644
index 0000000000..a3fb7deaef
--- /dev/null
+++ b/libical/src/libical/icalmime.c
@@ -0,0 +1,325 @@
+/* -*- Mode: C -*-*/
+/*======================================================================
+ FILE: icalmime.c
+ CREATOR: eric 26 July 2000
+
+
+ $Id$
+ $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 "sspm.h"
+#include "stdlib.h"
+
+#ifdef DMALLOC
+#include "dmalloc.h"
+#endif
+
+
+/* These *_part routines are called by the MIME parser via the
+ local_action_map */
+
+struct text_part
+{
+ char* buf;
+ char* buf_pos;
+ size_t buf_size;
+};
+
+void* icalmime_text_new_part()
+{
+
+#define BUF_SIZE 2048
+
+ struct text_part* impl;
+
+ if ( ( impl = (struct text_part*)
+ malloc(sizeof(struct text_part))) == 0) {
+ return 0;
+ }
+
+ impl->buf = icalmemory_new_buffer(BUF_SIZE);
+ impl->buf_pos = impl->buf;
+ impl->buf_size = BUF_SIZE;
+
+ return impl;
+}
+void icalmime_text_add_line(void *part,
+ struct sspm_header *header,
+ char* line, size_t size)
+{
+ struct text_part* impl = (struct text_part*) part;
+
+ icalmemory_append_string(&(impl->buf),&(impl->buf_pos),
+ &(impl->buf_size),line);
+
+}
+
+void* icalmime_textcalendar_end_part(void* part)
+{
+
+ struct text_part* impl = (struct text_part*) part;
+ icalcomponent *c = icalparser_parse_string(impl->buf);
+
+ icalmemory_free_buffer(impl->buf);
+ free(impl);
+
+ return c;
+
+}
+
+void* icalmime_text_end_part(void* part)
+{
+ struct text_part* impl = ( struct text_part*) part;
+
+ icalmemory_add_tmp_buffer(impl->buf);
+ free(impl);
+
+ return impl->buf;
+}
+
+void icalmime_text_free_part(void *part)
+{
+}
+
+
+/* Ignore Attachments for now */
+
+void* icalmime_attachment_new_part()
+{
+ return 0;
+}
+void icalmime_attachment_add_line(void *part, struct sspm_header *header,
+ char* line, size_t size)
+{
+}
+
+void* icalmime_attachment_end_part(void* part)
+{
+ return 0;
+}
+
+void icalmime_attachment_free_part(void *part)
+{
+}
+
+
+
+
+struct sspm_action_map icalmime_local_action_map[] =
+{
+ {SSPM_TEXT_MAJOR_TYPE,SSPM_CALENDAR_MINOR_TYPE,icalmime_text_new_part,icalmime_text_add_line,icalmime_textcalendar_end_part,icalmime_text_free_part},
+ {SSPM_TEXT_MAJOR_TYPE,SSPM_ANY_MINOR_TYPE,icalmime_text_new_part,icalmime_text_add_line,icalmime_text_end_part,icalmime_text_free_part},
+ {SSPM_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}
+};
+
+
+icalcomponent* icalmime_parse(char* (*get_string)(char *s, size_t size,
+ void *d),
+ void *data)
+{
+
+#define NUM_PARTS 100 /* HACK. Hard Limit */
+
+ struct sspm_part *parts;
+ int i, last_level=0;
+ icalcomponent *root=0, *parent=0, *comp=0, *last = 0;
+
+ if ( (parts = (struct sspm_part *)
+ malloc(NUM_PARTS*sizeof(struct sspm_part)))==0) {
+ icalerror_set_errno(ICAL_NEWFAILED_ERROR);
+ return 0;
+ }
+
+ memset(parts,0,sizeof(parts));
+
+ sspm_parse_mime(parts,
+ NUM_PARTS, /* Max parts */
+ icalmime_local_action_map, /* Actions */
+ get_string,
+ data, /* data for get_string*/
+ 0 /* First header */);
+
+
+
+ for(i = 0; i <NUM_PARTS && parts[i].header.major != SSPM_NO_MAJOR_TYPE ; i++){
+
+#define TMPSZ 1024
+ char mimetype[TMPSZ];
+ char* major = sspm_major_type_string(parts[i].header.major);
+ 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;
+ }
+
+ sprintf(mimetype,"%s/%s",major,minor);
+
+ printf("%d: %-10s %p\n",parts[i].level,mimetype,data);
+
+ comp = icalcomponent_new(ICAL_XLICMIMEPART_COMPONENT);
+
+ if(comp == 0){
+ /* HACK Handle Error */
+ assert(0);
+ }
+
+ if(parts[i].header.error!=SSPM_NO_ERROR){
+ char *str;
+ char* temp[256];
+
+ if(parts[i].header.error==SSPM_UNEXPECTED_BOUNDARY_ERROR){
+ str = "Got an unexpected boundary, possibly due to a MIME header for a MULTIPART part that is missing the Content-Type line";
+ }
+
+ if(parts[i].header.error==SSPM_WRONG_BOUNDARY_ERROR){
+ str = "Got the wrong boundary for the opening of a MULTIPART part.";
+ }
+
+ if(parts[i].header.error==SSPM_NO_BOUNDARY_ERROR){
+ str = "Got a multipart header that did not specify a boundary";
+ }
+
+ if(parts[i].header.error==SSPM_NO_HEADER_ERROR){
+ str = "Did not get a header for the part. Is there a blank
+line between the header and the previous boundary?";
+
+ }
+
+ if(parts[i].header.error_text != 0){
+ snprintf((char*)temp,256,
+ "%s: %s",str,parts[i].header.error_text);
+ } else {
+ strcpy((char*)temp,str);
+ }
+
+ icalcomponent_add_property
+ (comp,
+ icalproperty_vanew_xlicerror(
+ (char*)temp,
+ icalparameter_new_xlicerrortype(
+ ICAL_XLICERRORTYPE_MIMEPARSEERROR),
+ 0));
+ }
+
+#if 0
+ icalcomponent_add_property(comp,
+ icalproperty_new_xlicmimecontenttype((char*)icalmemory_strdup(mimetype)));
+
+ if (parts[i].header.encoding != 0){
+ icalcomponent_add_property(comp,
+ icalproperty_new_xlicmimeencoding(parts[i].header.encoding));
+ }
+#endif
+
+ if (parts[i].header.filename != 0){
+ icalcomponent_add_property(comp,
+ icalproperty_new_xlicmimefilename(parts[i].header.filename));
+ }
+
+ if (parts[i].header.content_id != 0){
+ icalcomponent_add_property(comp,
+ icalproperty_new_xlicmimecid(parts[i].header.content_id));
+ }
+
+ if (parts[i].header.charset != 0){
+ icalcomponent_add_property(comp,
+ icalproperty_new_xlicmimecharset(parts[i].header.charset));
+ }
+
+ /* Add iCal components as children of the component */
+ if(parts[i].header.major == SSPM_TEXT_MAJOR_TYPE &&
+ parts[i].header.minor == SSPM_CALENDAR_MINOR_TYPE &&
+ parts[i].data != 0){
+
+ icalcomponent_add_component(comp,
+ (icalcomponent*)parts[i].data);
+ parts[i].data = 0;
+
+ } else if(parts[i].header.major == SSPM_TEXT_MAJOR_TYPE &&
+ parts[i].header.minor != SSPM_CALENDAR_MINOR_TYPE &&
+ parts[i].data != 0){
+
+ /* Add other text components as "DESCRIPTION" properties */
+
+ icalcomponent_add_property(comp,
+ icalproperty_new_description(
+ (char*)icalmemory_strdup((char*)parts[i].data)));
+
+ parts[i].data = 0;
+ }
+
+
+ if(root!= 0 && parts[i].level == 0){
+ /* We've already assigned the root, but there is another
+ part at the root level. This is probably a parse
+ error*/
+ icalcomponent_free(comp);
+ continue;
+ }
+
+ if(parts[i].level == last_level && last_level != 0){
+ icalerror_assert(parent!=0,"No parent for adding component");
+
+ icalcomponent_add_component(parent,comp);
+
+ } else if (parts[i].level == last_level && last_level == 0 &&
+ root == 0) {
+
+ root = comp;
+ parent = comp;
+
+ } else if (parts[i].level > last_level){
+
+ parent = last;
+ icalcomponent_add_component(parent,comp);
+
+ last_level = parts[i].level;
+
+ } else if (parts[i].level < last_level){
+
+ parent = icalcomponent_get_parent(parent);
+ icalcomponent_add_component(parent,comp);
+
+ last_level = parts[i].level;
+ } else {
+ assert(0);
+ }
+
+ last = comp;
+ last_level = parts[i].level;
+ assert(parts[i].data == 0);
+ }
+
+ sspm_free_parts(parts,NUM_PARTS);
+ free(parts);
+
+ return root;
+}
+
+
+
diff --git a/libical/src/libical/icalmime.h b/libical/src/libical/icalmime.h
new file mode 100644
index 0000000000..d301debf7b
--- /dev/null
+++ b/libical/src/libical/icalmime.h
@@ -0,0 +1,41 @@
+// -*- Mode: C -*-
+/*======================================================================
+ FILE: icalmime.h
+ CREATOR: eric 26 July 2000
+
+
+ $Id$
+ $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
+
+
+======================================================================*/
+
+#ifndef ICALMIME_H
+#define ICALMIME_H
+
+#include "ical.h"
+
+icalcomponent* icalmime_parse( char* (*line_gen_func)(char *s, size_t size,
+ void *d),
+ void *data);
+
+#endif /* !ICALMIME_H */
+
+
+
diff --git a/libical/src/libical/icalrecur.c b/libical/src/libical/icalrecur.c
new file mode 100644
index 0000000000..a4b51e60d5
--- /dev/null
+++ b/libical/src/libical/icalrecur.c
@@ -0,0 +1,1174 @@
+/* -*- Mode: C -*-
+ ======================================================================
+ FILE: icalrecur.c
+ CREATOR: eric 16 May 2000
+
+ $Id$
+ $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/
+
+
+ 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, 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 check_restriction() to do its
+ work. Check_restriction() 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", check_restriction() would check that the time value
+ given to it has a month of either 6 or 10.
+ icalrecurrencetype_test()
+
+ Finally, icalrecur_iterator_next() does a few other checks on the
+ time value, and if it passes, it returns the time.
+
+ ======================================================================*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "icalrecur.h"
+#include "icalerror.h"
+#include "icalmemory.h"
+#include <stdlib.h> /* for malloc */
+#include <errno.h> /* for errno */
+#include <string.h> /* for icalmemory_strdup */
+#include <assert.h>
+#include <limits.h> /* for SHRT_MAX */
+
+#define TEMP_MAX 1024
+
+
+
+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;
+ struct icaltimetype last; /* last time return from _iterator_next*/
+ int occurrence_no; /* number of step made on this iterator */
+ struct icalrecurrencetype rule;
+
+ short days[366];
+ short days_index;
+
+ enum byrule byrule;
+ short by_indices[9];
+
+
+ short *by_ptrs[9]; /* Pointers into the by_* array elements of the rule */
+};
+
+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
+};
+
+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];
+};
+
+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. */
+int icalrecur_two_byrule(struct icalrecur_iterator_impl* impl,
+ enum byrule one,enum byrule two)
+{
+ short test_array[9];
+ enum byrule itr;
+ int passes = 0;
+
+ memset(test_array,0,9);
+
+ 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. */
+int icalrecur_one_byrule(struct icalrecur_iterator_impl* 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;
+}
+
+int count_byrules(struct icalrecur_iterator_impl* 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;
+}
+
+
+void setup_defaults(struct icalrecur_iterator_impl* impl,
+ enum byrule byrule, icalrecurrencetype_frequency req,
+ short 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 hav 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] = deftime;
+ }
+
+ /* Initialize the first occurence */
+ if( freq != req && expand_map[freq].map[byrule] != CONTRACT){
+ *timepart = impl->by_ptrs[byrule][0];
+ }
+
+
+}
+
+int expand_year_days(struct icalrecur_iterator_impl* impl,short year);
+
+
+icalrecur_iterator* icalrecur_iterator_new(struct icalrecurrencetype rule, struct icaltimetype dtstart)
+{
+ struct icalrecur_iterator_impl* impl;
+ icalrecurrencetype_frequency freq;
+
+ if ( ( impl = (struct icalrecur_iterator_impl *)
+ malloc(sizeof(struct icalrecur_iterator_impl))) == 0) {
+ icalerror_set_errno(ICAL_NEWFAILED_ERROR);
+ return 0;
+ }
+
+ memset(impl,0,sizeof(struct icalrecur_iterator_impl));
+
+ 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;
+
+
+ /* 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_USAGE_ERROR);
+
+ return 0;
+ }
+
+ /* BYWEEKNO and BYMONTH rule parts may not both appear.*/
+
+ if(icalrecur_two_byrule(impl,BY_WEEK_NO,BY_MONTH)){
+ icalerror_set_errno(ICAL_USAGE_ERROR);
+
+ icalerror_set_errno(ICAL_USAGE_ERROR);
+ return 0;
+ }
+
+ /* BYWEEKNO and BYMONTHDAY rule parts may not both appear.*/
+
+ if(icalrecur_two_byrule(impl,BY_WEEK_NO,BY_MONTH_DAY)){
+ icalerror_set_errno(ICAL_USAGE_ERROR);
+
+ icalerror_set_errno(ICAL_USAGE_ERROR);
+ return 0;
+ }
+
+
+ /*For MONTHLY recurrences (FREQ=MONTHLY) neither BYYEARDAY nor
+ BYWEEKNO may appear. */
+
+ if(freq == ICAL_MONTHLY_RECURRENCE &&
+ ( icalrecur_one_byrule(impl,BY_WEEK_NO) ||
+ icalrecur_one_byrule(impl,BY_YEAR_DAY)) ) {
+
+ icalerror_set_errno(ICAL_USAGE_ERROR);
+ return 0;
+ }
+
+
+ /*For WEEKLY recurrences (FREQ=WEEKLY) neither BYMONTHDAY nor
+ BYYEARDAY may appear. */
+
+ if(freq == ICAL_WEEKLY_RECURRENCE &&
+ ( icalrecur_one_byrule(impl,BY_MONTH_DAY) ||
+ icalrecur_one_byrule(impl,BY_YEAR_DAY)) ) {
+
+ icalerror_set_errno(ICAL_USAGE_ERROR);
+ return 0;
+ }
+
+
+ /* Rewrite some of the rules and set up defaults to make later
+ processing easier */
+
+
+ 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 &&
+ impl->by_ptrs[BY_DAY][0] == ICAL_RECURRENCE_ARRAY_MAX){
+ impl->by_ptrs[BY_DAY][0] = icaltime_day_of_week(impl->dtstart);
+ }
+
+
+ if(impl->rule.freq == ICAL_YEARLY_RECURRENCE){
+ expand_year_days(impl,impl->dtstart.year);
+ }
+
+ return impl;
+}
+
+
+void icalrecur_iterator_free(icalrecur_iterator* i)
+{
+
+ struct icalrecur_iterator_impl* impl =
+ (struct icalrecur_iterator_impl*)i;
+
+ icalerror_check_arg_rv((impl!=0),"impl");
+
+ free(impl);
+
+}
+
+
+
+
+void increment_year(struct icalrecur_iterator_impl* impl, int inc)
+{
+ impl->last.year+=inc;
+}
+
+
+
+
+void increment_month(struct icalrecur_iterator_impl* impl, int inc)
+{
+ impl->last.month+=inc;
+
+ if (impl->last.month > 12){
+ impl->last.month = 1;
+ increment_year(impl,1);
+ }
+}
+
+void increment_monthday(struct icalrecur_iterator_impl* impl, int inc)
+{
+
+ short days_in_month = icaltime_days_in_month(impl->last.month,impl->last.year);
+
+ impl->last.day+=inc;
+
+ if (impl->last.day > days_in_month){
+ int md = impl->last.day -days_in_month;
+ impl->last.day = md;
+ increment_month(impl,1);
+ }
+
+}
+
+
+void increment_hour(struct icalrecur_iterator_impl* impl, int inc)
+{
+ impl->last.hour+=inc;
+
+ if (impl->last.hour > 24){
+ impl->last.hour = 0;
+ increment_monthday(impl,1);
+ }
+
+}
+
+void increment_minute(struct icalrecur_iterator_impl* impl, int inc)
+{
+ impl->last.minute+=inc;
+
+ if (impl->last.minute > 59){
+ impl->last.minute = 0;
+ increment_hour(impl,1);
+ }
+
+}
+
+void increment_second(struct icalrecur_iterator_impl* impl, int inc)
+{
+ impl->last.second+=inc;
+
+ if (impl->last.second > 59){
+ impl->last.second = 0;
+ increment_minute(impl,1);
+ }
+
+}
+
+
+short next_second(struct icalrecur_iterator_impl* impl)
+{
+
+ short has_by_data = (impl->by_ptrs[BY_SECOND][0]!=ICAL_RECURRENCE_ARRAY_MAX);
+ short this_frequency = (impl->rule.freq == ICAL_SECONDLY_RECURRENCE);
+
+ short end_of_data = 0;
+
+ assert(has_by_data || this_frequency);
+
+ if( has_by_data ){
+ /* 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_data && 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_data && end_of_data && this_frequency ){
+ increment_minute(impl,1);
+ }
+
+ return end_of_data;
+
+}
+
+int next_minute(struct icalrecur_iterator_impl* impl)
+{
+
+ short has_by_data = (impl->by_ptrs[BY_MINUTE][0]!=ICAL_RECURRENCE_ARRAY_MAX);
+ short this_frequency = (impl->rule.freq == ICAL_MINUTELY_RECURRENCE);
+
+ short end_of_data = 0;
+
+ assert(has_by_data || this_frequency);
+
+
+ if (next_second(impl) == 0){
+ return 0;
+ }
+
+ if( has_by_data ){
+ /* 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_data && 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_data && end_of_data && this_frequency ){
+ increment_hour(impl,1);
+ }
+
+ return end_of_data;
+}
+
+int next_hour(struct icalrecur_iterator_impl* impl)
+{
+
+ short has_by_data = (impl->by_ptrs[BY_HOUR][0]!=ICAL_RECURRENCE_ARRAY_MAX);
+ short this_frequency = (impl->rule.freq == ICAL_HOURLY_RECURRENCE);
+
+ short end_of_data = 0;
+
+ assert(has_by_data || this_frequency);
+
+ if (next_minute(impl) == 0){
+ return 0;
+ }
+
+ if( has_by_data ){
+ /* 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_data && 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_data && end_of_data && this_frequency ){
+ increment_monthday(impl,1);
+ }
+
+ return end_of_data;
+
+}
+
+int next_day(struct icalrecur_iterator_impl* impl)
+{
+
+ short has_by_data = (impl->by_ptrs[BY_DAY][0]!=ICAL_RECURRENCE_ARRAY_MAX);
+ short this_frequency = (impl->rule.freq == ICAL_DAILY_RECURRENCE);
+
+ assert(has_by_data || 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;
+
+}
+
+/* This routine is only called by next_month and next_year, so it does
+ not have a clause for this_frequency */
+int next_monthday(struct icalrecur_iterator_impl* impl)
+{
+
+ short has_by_data = (impl->by_ptrs[BY_MONTH_DAY][0]!=ICAL_RECURRENCE_ARRAY_MAX);
+ short mday;
+ short end_of_data = 0;
+
+ assert(has_by_data );
+
+ if (next_hour(impl) == 0){
+ return 0;
+ }
+
+ impl->by_indices[BY_MONTH_DAY]++;
+
+ mday = impl->by_ptrs[BY_MONTH_DAY][impl->by_indices[BY_MONTH_DAY]];
+
+ if ( mday ==ICAL_RECURRENCE_ARRAY_MAX){
+ impl->by_indices[BY_MONTH_DAY] = 0;
+
+ end_of_data = 1;
+ }
+
+ if (mday > 0){
+ impl->last.day = mday;
+ } else {
+ short days_in_month = icaltime_days_in_month(impl->last.month,
+ impl->last.year);
+ impl->last.day = days_in_month-mday+1;
+ }
+
+ if(has_by_data && end_of_data ){
+ increment_month(impl,1);
+ }
+
+ return end_of_data;
+
+}
+
+int next_yearday(struct icalrecur_iterator_impl* impl)
+{
+
+ short has_by_data = (impl->by_ptrs[BY_YEAR_DAY][0]!=ICAL_RECURRENCE_ARRAY_MAX);
+
+ short end_of_data = 0;
+
+ assert(has_by_data );
+
+ 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_data && end_of_data){
+ increment_year(impl,1);
+ }
+
+ return end_of_data;
+
+}
+
+/* This routine is only called by next_week or next_month, so it does
+not have a clause for this_frequency. In both cases, it is certain
+that BY_DAY has data */
+
+int next_weekday(struct icalrecur_iterator_impl* impl)
+{
+
+ short end_of_data = 0;
+ short start_of_week, dow;
+ struct icaltimetype next;
+
+ if (next_hour(impl) == 0){
+ return 0;
+ }
+
+ assert( impl->by_ptrs[BY_DAY][0]!=ICAL_RECURRENCE_ARRAY_MAX);
+
+ impl->by_indices[BY_DAY]++;
+
+ if (impl->by_ptrs[BY_DAY][impl->by_indices[BY_DAY]]
+ ==ICAL_RECURRENCE_ARRAY_MAX){
+ impl->by_indices[BY_DAY] = 0;
+
+ end_of_data = 1;
+ }
+
+ dow = impl->by_ptrs[BY_DAY][impl->by_indices[BY_DAY]];
+
+ start_of_week = icaltime_start_doy_of_week(impl->last);
+ next = icaltime_from_day_of_year(start_of_week + dow - 1,impl->last.year);
+
+ impl->last.day = next.day;
+ impl->last.month = next.month;
+
+ return end_of_data;
+
+}
+
+int next_month(struct icalrecur_iterator_impl* impl)
+{
+
+ short has_by_data = (impl->by_ptrs[BY_MONTH][0]!=ICAL_RECURRENCE_ARRAY_MAX);
+ short this_frequency = (impl->rule.freq == ICAL_MONTHLY_RECURRENCE);
+
+ short end_of_data = 0;
+
+ assert(has_by_data || this_frequency);
+
+ /* Week day data overrides monthday data */
+ if(impl->by_ptrs[BY_DAY][0]!=ICAL_RECURRENCE_ARRAY_MAX){
+ if (next_weekday(impl) == 0){
+ return 0;
+ }
+ } else {
+ if (next_monthday(impl) == 0){
+ return 0;
+ }
+ }
+
+ if( has_by_data ){
+ /* 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;
+
+ end_of_data = 1;
+ }
+
+ impl->last.month =
+ impl->by_ptrs[BY_MONTH][impl->by_indices[BY_MONTH]];
+
+ } else if( !has_by_data && this_frequency ){
+ /* Compute the next value from the last time and the frequency interval*/
+ increment_month(impl,impl->rule.interval);
+
+ }
+
+ if(has_by_data && end_of_data && this_frequency ){
+ increment_year(impl,1);
+ }
+ return end_of_data;
+
+}
+
+
+int next_week(struct icalrecur_iterator_impl* impl)
+{
+ short has_by_data = (impl->by_ptrs[BY_WEEK_NO][0]!=ICAL_RECURRENCE_ARRAY_MAX);
+ short this_frequency = (impl->rule.freq == ICAL_WEEKLY_RECURRENCE);
+ short end_of_data = 0;
+
+ int sec_in_week = 60*60*24*7;
+
+ if (next_weekday(impl) == 0){
+ return 0;
+ }
+
+ if( impl->by_ptrs[BY_WEEK_NO][0]!=ICAL_RECURRENCE_ARRAY_MAX){
+ /* Use the Week Number byrule data */
+ int week_no;
+ time_t tt;
+ 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]];
+
+ tt = icaltime_as_timet(impl->last);
+
+ tt+=sec_in_week*week_no;
+
+ impl->last = icaltime_from_timet(tt,impl->last.is_date,impl->last.is_utc);
+
+ } else if( !has_by_data && this_frequency ){
+ increment_monthday(impl,7*impl->rule.interval);
+ }
+
+ if(has_by_data && end_of_data && this_frequency ){
+ increment_year(impl,1);
+ }
+
+ return end_of_data;
+
+}
+
+int has_by_data(struct icalrecur_iterator_impl* impl, enum byrule byrule){
+
+ return (impl->by_ptrs[byrule][0] != ICAL_RECURRENCE_ARRAY_MAX);
+}
+
+
+/* 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. */
+
+int expand_year_days(struct icalrecur_iterator_impl* impl,short year)
+{
+ int j,k;
+ int days_index=0;
+ struct icaltimetype t;
+
+
+ memset(&t,0,sizeof(t));
+ memset(impl->days,ICAL_RECURRENCE_ARRAY_MAX_BYTE,sizeof(impl->days));
+
+ if(has_by_data(impl,BY_MONTH) && !has_by_data(impl,BY_MONTH_DAY)){
+
+ for(j=0;impl->by_ptrs[BY_MONTH][j]!=ICAL_RECURRENCE_ARRAY_MAX;j++){
+ struct icaltimetype t;
+ short month = impl->by_ptrs[BY_MONTH][j];
+ short doy;
+
+ t = impl->dtstart;
+ t.year = year;
+ t.month = month;
+
+ doy = icaltime_day_of_year(t);
+
+ impl->days[days_index++] = doy;
+
+ }
+
+
+ }
+ else if ( has_by_data(impl,BY_MONTH) && has_by_data(impl,BY_DAY)){
+
+ for(j=0;impl->by_ptrs[BY_MONTH][j]!=ICAL_RECURRENCE_ARRAY_MAX;j++){
+ short month = impl->by_ptrs[BY_MONTH][j];
+ short days_in_month = icaltime_days_in_month(month,year);
+
+ struct icaltimetype t;
+ memset(&t,0,sizeof(struct icaltimetype));
+ t.day = 1;
+ t.year = year;
+ t.month = month;
+
+ for(t.day = 1; t.day <=days_in_month; t.day++){
+
+ short current_dow = icaltime_day_of_week(t);
+
+ for(k=0;impl->by_ptrs[BY_DAY][k]!=ICAL_RECURRENCE_ARRAY_MAX;k++){
+
+ enum icalrecurrencetype_weekday dow =
+ icalrecurrencetype_day_day_of_week(impl->by_ptrs[BY_DAY][k]);
+
+ if(current_dow == dow){
+ short doy = icaltime_day_of_year(t);
+ /* HACK, incomplete Nth day of week handling */
+ impl->days[days_index++] = doy;
+
+ }
+ }
+ }
+ }
+ } else if (has_by_data(impl,BY_MONTH) && has_by_data(impl,BY_MONTH_DAY)){
+
+ 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++)
+ {
+ short month = impl->by_ptrs[BY_MONTH][j];
+ short month_day = impl->by_ptrs[BY_MONTH_DAY][k];
+ short doy;
+
+ t.day = month_day;
+ t.month = month;
+ t.year = year;
+
+ doy = icaltime_day_of_year(t);
+
+ impl->days[days_index++] = doy;
+
+ }
+ }
+ } else if (has_by_data(impl,BY_WEEK_NO) && !has_by_data(impl,BY_DAY)){
+
+ struct icaltimetype t;
+ short dow;
+
+ t.day = impl->dtstart.day;
+ t.month = impl->dtstart.month;
+ t.year = year;
+
+ dow = icaltime_day_of_week(t);
+
+ } else if (has_by_data(impl,BY_WEEK_NO) && has_by_data(impl,BY_DAY)){
+
+ } else if (has_by_data(impl,BY_YEAR_DAY)){
+
+ } else if (has_by_data(impl,BY_MONTH_DAY) ){
+
+ } else if (has_by_data(impl,BY_DAY)){
+
+ } else {
+
+ }
+
+ return 0;
+}
+
+
+int next_year(struct icalrecur_iterator_impl* impl)
+{
+ struct icaltimetype next;
+ short end_of_data=0;
+
+ if (next_hour(impl) == 0){
+ return 0;
+ }
+
+ impl->days_index++;
+
+ if (impl->days[impl->days_index] == ICAL_RECURRENCE_ARRAY_MAX){
+ impl->days_index = 0;
+ end_of_data = 1;
+ }
+
+ next = icaltime_from_day_of_year(impl->days[impl->days_index],impl->last.year);
+
+ impl->last.day = next.day;
+ impl->last.month = next.month;
+
+
+ if(end_of_data){
+ increment_year(impl,impl->rule.interval);
+ expand_year_days(impl,impl->last.year);
+ }
+
+ return 1;
+}
+
+int check_restriction(struct icalrecur_iterator_impl* impl,
+ enum byrule byrule, short 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;
+ }
+}
+
+int check_contracting_rules(struct icalrecur_iterator_impl* impl)
+{
+ enum byrule;
+
+ int day_of_week=0;
+ int week_no=0;
+ int year_day=0;
+
+ if (
+ check_restriction(impl,BY_SECOND,impl->last.second) &&
+ check_restriction(impl,BY_MINUTE,impl->last.minute) &&
+ check_restriction(impl,BY_HOUR,impl->last.hour) &&
+ check_restriction(impl,BY_DAY,day_of_week) &&
+ check_restriction(impl,BY_WEEK_NO,week_no) &&
+ check_restriction(impl,BY_MONTH_DAY,impl->last.day) &&
+ check_restriction(impl,BY_MONTH,impl->last.month) &&
+ check_restriction(impl,BY_YEAR_DAY,year_day) )
+ {
+
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+struct icaltimetype icalrecur_iterator_next(icalrecur_iterator *itr)
+{
+ struct icalrecur_iterator_impl* impl =
+ (struct icalrecur_iterator_impl*)itr;
+
+ if( (impl->rule.count!=0 &&impl->occurrence_no >= impl->rule.count) ||
+ (!icaltime_is_null_time(impl->rule.until) &&
+ icaltime_compare(impl->last,impl->rule.until) > 0)) {
+ return icaltime_null_time();
+ }
+
+ if(impl->occurrence_no == 0){
+ impl->occurrence_no++;
+ return impl->last;
+ }
+
+
+ do {
+ 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: {
+ next_month(impl);
+ break;
+ }
+ case ICAL_YEARLY_RECURRENCE:{
+ next_year(impl);
+ break;
+ }
+ default:{
+ assert(0); /* HACK, need a better error */
+ }
+ }
+
+ if(impl->last.year >= 2038){
+ /* HACK */
+ return icaltime_null_time();
+ }
+
+
+ } while(!check_contracting_rules(impl)
+ || icaltime_compare(impl->last,impl->dtstart) < 0);
+
+
+ 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;
+}
+
+#include "ical.h"
+void icalrecurrencetype_test()
+{
+ icalvalue *v = icalvalue_new_from_string(
+ ICAL_RECUR_VALUE,
+ "FREQ=YEARLY;UNTIL=20060101T000000;INTERVAL=2;BYDAY=SU,WE;BYSECOND=15,30; BYMONTH=1,6,11");
+
+ struct icalrecurrencetype r = icalvalue_get_recur(v);
+ struct icaltimetype t = icaltime_from_timet( time(0), 0, 0);
+ struct icaltimetype next;
+ time_t tt;
+
+ struct icalrecur_iterator_impl* itr
+ = (struct icalrecur_iterator_impl*) icalrecur_iterator_new(r,t);
+
+ do {
+
+ next = icalrecur_iterator_next(itr);
+ tt = icaltime_as_timet(next);
+
+ printf("%s",ctime(&tt ));
+
+ } while( ! icaltime_is_null_time(next));
+
+}
+
diff --git a/libical/src/libical/icalrecur.h b/libical/src/libical/icalrecur.h
new file mode 100644
index 0000000000..cfafc01b02
--- /dev/null
+++ b/libical/src/libical/icalrecur.h
@@ -0,0 +1,44 @@
+/* -*- Mode: C -*- */
+/*======================================================================
+ FILE: icalrecur.h
+ CREATOR: eric 20 March 2000
+
+
+ (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 icaltypes.h
+
+======================================================================*/
+
+#ifndef ICALRECUR_H
+#define ICALRECUR_H
+
+#include <time.h>
+#include "icaltypes.h"
+#include "icalenums.h" /* for recurrence enums */
+
+typedef void icalrecur_iterator;
+void icalrecurrencetype_test();
+
+
+icalrecur_iterator* icalrecur_iterator_new(struct icalrecurrencetype rule, struct icaltimetype dtstart);
+
+struct icaltimetype icalrecur_iterator_next(icalrecur_iterator*);
+
+int icalrecur_iterator_count(icalrecur_iterator*);
+
+void icalrecur_iterator_free(icalrecur_iterator*);
+
+
+#endif
diff --git a/libical/src/libical/icaltime.c b/libical/src/libical/icaltime.c
new file mode 100644
index 0000000000..6f17278bde
--- /dev/null
+++ b/libical/src/libical/icaltime.c
@@ -0,0 +1,182 @@
+/* -*- Mode: C -*-
+ ======================================================================
+ FILE: icaltime.c
+ CREATOR: eric 02 June 2000
+
+ $Id$
+ $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 "icaltime.h"
+#include <assert.h>
+
+struct icaltimetype
+icaltime_from_timet(time_t tm, int is_date, int is_utc)
+{
+ struct icaltimetype tt;
+ struct tm t;
+
+ if(is_utc == 1){
+ t = *(gmtime(&tm));
+ } else {
+ t = *(localtime(&tm));
+ }
+ tt.second = t.tm_sec;
+ tt.minute = t.tm_min;
+ tt.hour = t.tm_hour;
+ tt.day = t.tm_mday;
+ tt.month = t.tm_mon + 1;
+ tt.year = t.tm_year+ 1900;
+
+ tt.is_utc = is_utc;
+ tt.is_date = is_date;
+
+ return tt;
+}
+
+time_t icaltime_as_timet(struct icaltimetype tt)
+{
+ struct tm stm;
+ time_t tut;
+
+ memset(&stm,0,sizeof( struct tm));
+
+ stm.tm_sec = tt.second;
+ stm.tm_min = tt.minute;
+ stm.tm_hour = tt.hour;
+ stm.tm_mday = tt.day;
+ stm.tm_mon = tt.month-1;
+ stm.tm_year = tt.year-1900;
+ stm.tm_isdst = -1; /* prevents mktime from changing hour based on
+ daylight savings */
+
+ tut = mktime(&stm);
+
+ return tut;
+}
+
+short days_in_month[] = {0,31,28,31,30,31,30,31,31,30,31,30,31};
+
+short icaltime_days_in_month(short month,short year)
+{
+ int is_leap =0;
+ int days = days_in_month[month];
+
+ assert(month > 0);
+ assert(month <= 12);
+
+ if( (year % 4 == 0 && year % 100 != 0) ||
+ year % 400 == 0){
+ is_leap =1;
+ }
+
+ if( month == 2){
+ days += is_leap;
+ }
+
+ return days;
+}
+
+/* 1-> Sunday, 7->Saturday */
+short icaltime_day_of_week(struct icaltimetype t){
+
+ time_t tt = icaltime_as_timet(t);
+ struct tm *tm;
+
+ tm = gmtime(&tt);
+
+ return tm->tm_wday+1;
+}
+
+short icaltime_start_doy_of_week(struct icaltimetype t){
+ time_t tt = icaltime_as_timet(t);
+ time_t start_tt;
+ struct tm *stm;
+
+ stm = gmtime(&tt);
+
+ start_tt = tt - stm->tm_wday*(60*60*24);
+
+ stm = gmtime(&start_tt);
+
+ return stm->tm_yday;
+}
+
+short icaltime_day_of_year(struct icaltimetype t){
+ time_t tt = icaltime_as_timet(t);
+ struct tm *stm;
+
+ stm = gmtime(&tt);
+
+ return stm->tm_yday;
+
+}
+
+struct icaltimetype icaltime_from_day_of_year(short doy, short year)
+{
+ struct tm stm;
+ time_t tt;
+
+ /* Get the time of january 1 of this year*/
+ memset(&stm,0,sizeof(struct tm));
+ stm.tm_year = year-1900;
+ stm.tm_mday = 1;
+
+ tt = mktime(&stm);
+
+ /* Now add in the days */
+
+ tt += doy *60*60*24;
+
+ return icaltime_from_timet(tt, 1, 1);
+}
+
+struct icaltimetype icaltime_null_time()
+{
+ struct icaltimetype t;
+ memset(&t,0,sizeof(struct icaltimetype));
+
+ return t;
+}
+int icaltime_is_null_time(struct icaltimetype t)
+{
+ if (t.second +t.minute+t.hour+t.day+t.month+t.year == 0){
+ return 1;
+ }
+
+ return 0;
+
+}
+
+int icaltime_compare(struct icaltimetype a,struct icaltimetype b)
+{
+ time_t t1 = icaltime_as_timet(a);
+ time_t t2 = icaltime_as_timet(b);
+
+ if (t1 > t2) {
+ return 1;
+ } else if (t1 < t2) {
+ return -1;
+ } else {
+ return 0;
+ }
+
+}
diff --git a/libical/src/libical/icaltime.h b/libical/src/libical/icaltime.h
new file mode 100644
index 0000000000..64eaee0431
--- /dev/null
+++ b/libical/src/libical/icaltime.h
@@ -0,0 +1,74 @@
+// -*- Mode: C -*-
+/*======================================================================
+ FILE: icaltime.h
+ CREATOR: eric 02 June 2000
+
+
+ $Id$
+ $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
+
+
+======================================================================*/
+
+#ifndef ICALTIME_H
+#define ICALTIME_H
+
+#include <time.h>
+
+struct icaltimetype
+{
+ int year;
+ int month;
+ 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. */
+};
+
+struct icaltimetype icaltime_null_time();
+
+int icaltime_is_null_time(struct icaltimetype t);
+
+struct icaltimetype icaltime_normalize(struct icaltimetype t);
+
+short icaltime_day_of_year(struct icaltimetype t);
+struct icaltimetype icaltime_from_day_of_year(short doy, short year);
+
+short icaltime_day_of_week(struct icaltimetype t);
+short icaltime_start_doy_of_week(struct icaltimetype t);
+
+struct icaltimetype icaltime_from_timet(time_t v, int is_date, int is_utc);
+time_t icaltime_as_timet(struct icaltimetype);
+
+short icaltime_week_number(short day_of_month, short month, short year);
+
+struct icaltimetype icaltime_from_week_number(short week_number, short year);
+
+int icaltime_compare(struct icaltimetype a,struct icaltimetype b);
+
+short icaltime_days_in_month(short month,short year);
+
+#endif /* !ICALTIME_H */
+
+
+
diff --git a/libical/src/libical/sspm.c b/libical/src/libical/sspm.c
new file mode 100644
index 0000000000..36f69b506a
--- /dev/null
+++ b/libical/src/libical/sspm.c
@@ -0,0 +1,1191 @@
+/* -*- Mode: C -*-
+ ======================================================================
+ FILE: sspm.c Parse Mime
+ CREATOR: eric 25 June 2000
+
+ $Id$
+ $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, 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 */
+
+
+#ifdef DMALLOC
+#include "dmalloc.h"
+#endif
+
+#define TMP_BUF_SIZE 1024
+
+
+enum mime_state {
+ UNKNOWN_STATE,
+ IN_HEADER,
+ END_OF_HEADER,
+ IN_BODY,
+ OPENING_PART,
+ END_OF_PART,
+ TERMINAL_END_OF_PART,
+ END_OF_INPUT
+};
+
+struct mime_impl{
+ struct sspm_part *parts;
+ size_t max_parts;
+ int part_no;
+ int level;
+ struct sspm_action_map *actions;
+ char* (*get_string)(char *s, size_t size, void* data);
+ void* get_string_data;
+ char temp[TMP_BUF_SIZE];
+ enum mime_state state;
+};
+
+void sspm_free_header(struct sspm_header *header);
+void* sspm_make_multipart_part(struct mime_impl *impl,struct sspm_header *header);
+void sspm_read_header(struct mime_impl *impl,struct sspm_header *header);
+
+char* sspm_strdup(char* str){
+
+ char* s;
+
+ s = strdup(str);
+
+ return s;
+}
+
+
+struct major_content_type_map
+{
+ enum sspm_major_type type;
+ 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,"" },
+};
+
+struct minor_content_type_map
+{
+ enum sspm_minor_type type;
+ 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,"" }
+};
+
+
+
+
+char* sspm_get_parameter(char* line, char* parameter)
+{
+ char *p,*s,*q;
+ static char name[1024];
+
+ /* Find where the parameter name is in the line */
+ p = strstr(line,parameter);
+
+ if( p == 0){
+ return 0;
+ }
+
+ /* skip over the parameter name, the '=' and any blank spaces */
+
+ p+=strlen(parameter);
+
+ while(*p==' ' || *p == '='){
+ p++;
+ }
+
+ /*now find the next semicolon*/
+
+ s = strchr(p,';');
+
+ /* Strip of leading quote */
+ q = strchr(p,'\"');
+
+ if(q !=0){
+ p = q+1;
+ }
+
+ if(s != 0){
+ strncpy(name,p,(size_t)s-(size_t)p);
+ } else {
+ strcpy(name,p);
+ }
+
+ /* Strip off trailing quote, if it exists */
+
+ q = strrchr(name,'\"');
+
+ if (q != 0){
+ *q='\0';
+ }
+
+ return name;
+}
+
+char* sspm_property_name(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 */
+
+ c = strchr(line,':');
+ 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;
+
+}
+
+char *mime_headers[] = {
+ "Content-Type",
+ "Content-Transfer-Encoding",
+ "Content-Disposition",
+ "Content-Id",
+ "Mime-Version",
+ 0
+};
+
+
+void* sspm_default_new_part()
+{
+ return 0;
+}
+void sspm_default_add_line(void *part, struct sspm_header *header,
+ char* line, size_t size)
+{
+}
+
+void* sspm_default_end_part(void* part)
+{
+ return 0;
+}
+
+void sspm_default_free_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
+};
+
+
+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;
+ }
+
+
+}
+
+
+struct sspm_action_map get_action(struct mime_impl *impl,
+ enum sspm_major_type major,
+ enum sspm_minor_type minor)
+{
+ int i;
+
+ /* Read caller suppled action map */
+
+ if (impl->actions != 0){
+ for(i=0; impl->actions[i].major != SSPM_UNKNOWN_MAJOR_TYPE; i++){
+ if((major == impl->actions[i].major &&
+ minor == impl->actions[i].minor) ||
+ (major == impl->actions[i].major &&
+ minor == SSPM_ANY_MINOR_TYPE)){
+ return impl->actions[i];
+ }
+ }
+ }
+
+ /* Else, read default action map */
+
+ for(i=0; sspm_action_map[i].major != SSPM_UNKNOWN_MAJOR_TYPE; i++){
+ if((major == sspm_action_map[i].major &&
+ minor == sspm_action_map[i].minor) ||
+ (major == sspm_action_map[i].major &&
+ minor == SSPM_ANY_MINOR_TYPE)){
+ break;
+ }
+ }
+
+ return sspm_action_map[i];
+}
+
+
+char* sspm_lowercase(char* str)
+{
+ char* p = 0;
+ char* new = sspm_strdup(str);
+
+ if(str ==0){
+ return 0;
+ }
+
+ for(p = new; *p!=0; p++){
+ *p = tolower(*p);
+ }
+
+ return new;
+}
+
+enum sspm_major_type sspm_find_major_content_type(char* type)
+{
+ int i;
+
+ char* ltype = sspm_lowercase(type);
+
+ for (i=0; major_content_type_map[i].type != SSPM_UNKNOWN_MINOR_TYPE; i++){
+ if(strncmp(ltype, major_content_type_map[i].str,
+ strlen(major_content_type_map[i].str))==0){
+ free(ltype);
+ return major_content_type_map[i].type;
+ }
+ }
+ free(ltype);
+ return major_content_type_map[i].type; /* Should return SSPM_UNKNOWN_MINOR_TYPE */
+}
+
+enum sspm_minor_type sspm_find_minor_content_type(char* type)
+{
+ int i;
+ char* ltype = sspm_lowercase(type);
+
+ char *p = strchr(ltype,'/');
+
+ if (p==0){
+ return SSPM_UNKNOWN_MINOR_TYPE;
+ }
+
+ p++; /* Skip the '/' */
+
+ for (i=0; minor_content_type_map[i].type != SSPM_UNKNOWN_MINOR_TYPE; i++){
+ if(strncmp(p, minor_content_type_map[i].str,
+ strlen(minor_content_type_map[i].str))==0){
+ free(ltype);
+ return minor_content_type_map[i].type;
+ }
+ }
+
+ free(ltype);
+ return minor_content_type_map[i].type; /* Should return SSPM_UNKNOWN_MINOR_TYPE */
+}
+
+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 */
+}
+
+char* sspm_minor_type_string(enum sspm_major_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 */
+}
+
+
+/* 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(strcmp(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(strcmp(prop,"Content-Transfer-Encoding")==0){
+ char* encoding = sspm_value(line);
+ char* lencoding = sspm_lowercase(encoding);
+
+ if(strcmp(lencoding,"base64")==0){
+ header->encoding = SSPM_BASE64_ENCODING;
+ } else if(strcmp(lencoding,"quoted-printable")==0){
+ header->encoding = SSPM_QUOTED_PRINTABLE_ENCODING;
+ } else if(strcmp(lencoding,"binary")==0){
+ header->encoding = SSPM_BINARY_ENCODING;
+ } else if(strcmp(lencoding,"7bit")==0){
+ header->encoding = SSPM_7BIT_ENCODING;
+ } else if(strcmp(lencoding,"8bit")==0){
+ header->encoding = SSPM_8BIT_ENCODING;
+ } else {
+ header->encoding = SSPM_UNKNOWN_ENCODING;
+ }
+
+ free(lencoding);
+
+ header->def = 0;
+
+ } else if(strcmp(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)
+{
+
+ impl->parts[impl->part_no].header = header;
+ impl->parts[impl->part_no].level = level;
+ impl->parts[impl->part_no].data = part;
+ 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)
+{
+
+ /* 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, *end_part;
+ int end = 0;
+
+ struct sspm_action_map action = get_action(
+ impl,
+ header->major,
+ header->minor);
+
+ 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){
+ /*printf("Error: %s\n",line);*/
+ if(strcmp(boundary,line)==0){
+ break;
+ }
+ }
+ free(boundary);
+
+ }
+ } else {
+ size_t size;
+ char* data;
+ char* rtrn=0;
+ size = strlen(line);
+
+ data = (char*)malloc(size+2);
+ 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;
+
+ 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){
+ /*printf("Error: %s\n",line);*/
+ 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);
+ 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);
+
+ /* 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 {
+ part = sspm_make_part(impl, &header,parent_header);
+
+ memset(&(impl->parts[impl->part_no]), 0, sizeof(struct sspm_part));
+
+ sspm_store_part(impl,header,impl->level,part);
+
+ }
+
+ return part;
+}
+
+void* sspm_make_multipart_part(struct mime_impl *impl,struct sspm_header *header)
+{
+ void *part=0;
+
+ /* Now descend a level into each of the children of this part */
+ impl->level++;
+
+ /* Now we are working on the CHILD */
+ memset(&(impl->parts[impl->part_no]), 0, sizeof(struct sspm_part));
+
+ do{
+ part = sspm_make_multipart_subpart(impl,header);
+
+ if (part==0){
+ /* Clean up the part in progress */
+ impl->parts[impl->part_no].header.major
+ = SSPM_NO_MAJOR_TYPE;
+ impl->parts[impl->part_no].header.minor
+ = SSPM_NO_MINOR_TYPE;
+
+ }
+
+
+ } while (get_line_type(impl->temp) != TERMINATING_BOUNDARY &&
+ impl->state != END_OF_INPUT);
+
+ impl->level--;
+
+ return 0;
+}
+
+
+void sspm_read_header(struct mime_impl *impl,struct sspm_header *header)
+{
+#define BUF_SIZE 1024
+#define MAX_HEADER_LINES 25
+
+ char *buf;
+ char header_lines[MAX_HEADER_LINES][BUF_SIZE]; /* HACK, hard limits */
+ int current_line = -1;
+ int end = 0;
+
+ memset(header_lines,0,sizeof(header_lines));
+ memset(header,0,sizeof(struct sspm_header));
+
+ /* Set up default header */
+ header->def = 1;
+ header->major = SSPM_TEXT_MAJOR_TYPE;
+ header->minor = SSPM_PLAIN_MINOR_TYPE;
+ header->error = SSPM_NO_ERROR;
+ header->error_text = 0;
+
+ /* Read all of the lines into memory */
+ while(end==0&& (buf=sspm_get_next_line(impl)) != 0){
+
+ enum line_type line_type = get_line_type(buf);
+
+ switch(line_type){
+ case BLANK: {
+ end = 1;
+ impl->state = END_OF_HEADER;
+ break;
+ }
+
+ case MAIL_HEADER:
+ case MIME_HEADER: {
+ impl->state = IN_HEADER;
+ current_line++;
+
+ assert(strlen(buf) < BUF_SIZE);
+
+ strcpy(header_lines[current_line],buf);
+
+ 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);
+
+ strcat(last_line,buf_start);
+
+ break;
+ }
+
+ default: {
+ sspm_set_error(header,SSPM_MALFORMED_HEADER_ERROR,buf);
+ return;
+ }
+ }
+ }
+
+
+ for(current_line = 0;
+ current_line < MAX_HEADER_LINES && header_lines[current_line][0] != 0;
+ current_line++){
+
+ sspm_build_header(header,header_lines[current_line]);
+ }
+
+
+}
+
+/* Root routine for parsing mime entries*/
+int sspm_parse_mime(struct sspm_part *parts,
+ size_t max_parts,
+ struct sspm_action_map *actions,
+ char* (*get_string)(char *s, size_t size, void* data),
+ void *get_string_data,
+ struct sspm_header *first_header
+ )
+{
+ struct mime_impl impl;
+ struct sspm_header header;
+ void *part;
+ int i;
+
+ /* 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);
+
+ part = sspm_make_multipart_part(&impl,child_header);
+
+ } else {
+ part = sspm_make_part(&impl, &header, 0);
+
+ memset(&(impl.parts[impl.part_no]), 0, sizeof(struct sspm_part));
+
+ sspm_store_part(&impl,header,impl.level,part);
+ }
+
+ 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.
+
+***********************************************************************/
+
+unsigned char *decode_quoted_printable(unsigned char *dest,
+ unsigned 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);
+}
+
+unsigned char *decode_base64(unsigned char *dest,
+ unsigned char *src,
+ size_t *size)
+{
+ int cc;
+ unsigned 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);
+}
+
+
diff --git a/libical/src/libical/sspm.h b/libical/src/libical/sspm.h
new file mode 100644
index 0000000000..657b77a8d7
--- /dev/null
+++ b/libical/src/libical/sspm.h
@@ -0,0 +1,138 @@
+/* -*- Mode: C -*-
+ ======================================================================
+ FILE: sspm.h Mime Parser
+ CREATOR: eric 25 June 2000
+
+ $Id$
+ $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, http://www.softwarestudio.org
+ ======================================================================*/
+
+#ifndef SSPM_H
+#define SSPM_H
+
+enum sspm_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,
+ SSPM_NO_MAJOR_TYPE
+};
+
+enum sspm_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,
+ SSPM_NO_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* 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;
+ void *data;
+};
+
+struct sspm_action_map {
+ enum sspm_major_type major;
+ enum sspm_minor_type minor;
+ void* (*new_part)();
+ void (*add_line)(void *part, struct sspm_header *header,
+ char* line, size_t size);
+ void* (*end_part)(void* part);
+ void (*free_part)(void *part);
+};
+
+char* sspm_major_type_string(enum sspm_major_type type);
+char* sspm_minor_type_string(enum sspm_major_type type);
+
+
+int sspm_parse_mime(struct sspm_part *parts,
+ size_t max_parts,
+ struct sspm_action_map *actions,
+ char* (*get_string)(char *s, size_t size, void* data),
+ void *get_string_data,
+ struct sspm_header *first_header
+ );
+
+void sspm_free_parts(struct sspm_part *parts, size_t max_parts);
+
+unsigned char *decode_quoted_printable(unsigned char *dest,
+ unsigned char *src,
+ size_t *size);
+unsigned char *decode_base64(unsigned char *dest,
+ unsigned char *src,
+ size_t *size);
+
+
+#endif SSPM_H
diff --git a/libical/src/libicalss/icaldirset.c b/libical/src/libicalss/icaldirset.c
new file mode 100644
index 0000000000..ff5357126c
--- /dev/null
+++ b/libical/src/libicalss/icaldirset.c
@@ -0,0 +1,718 @@
+/* -*- Mode: C -*-
+ ======================================================================
+ FILE: icaldirset.c
+ CREATOR: eric 28 November 1999
+
+ $Id$
+ $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
+
+
+ ======================================================================*/
+
+
+/*
+
+ icaldirset manages a database of ical components and offers
+ interfaces for reading, writting and searching for components.
+
+ icaldirset groups components in to clusters based on their DTSTART
+ time -- all components that start in the same month are grouped
+ together in a single file. All files in a sotre are kept in a single
+ directory. ( If a component does not have DTSTART, the store uses
+ DTSTAMP or CREATE )
+
+ The primary interfaces are icaldirset_first and icaldirset_next. These
+ routine iterate through all of the components in the store, subject
+ to the current gauge. A gauge is an icalcomponent that is tested
+ against other componets for a match. If a gauge has been set with
+ icaldirset_select, icaldirset_first and icaldirset_next will only
+ return componentes that match the gauge.
+
+ The Store generated UIDs for all objects that are stored if they do
+ not already have a UID. The UID is the name of the cluster (month &
+ year as MMYYYY) plus a unique serial number. The serial number is
+ stored as a property of the cluster.
+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+
+#include "ical.h"
+#include "icaldirset.h"
+#include "pvl.h"
+#include "icalerror.h"
+#include "icalparser.h"
+#include "icaldirset.h"
+#include "icalfileset.h"
+#include "icalfilesetimpl.h"
+#include "icalgauge.h"
+
+#include <limits.h>
+#include <dirent.h> /* for opendir() */
+#include <errno.h>
+#include <sys/types.h> /* for opendir() */
+#include <sys/stat.h> /* for stat */
+#include <unistd.h> /* for stat, getpid */
+#include <time.h> /* for clock() */
+#include <stdlib.h> /* for rand(), srand() */
+#include <sys/utsname.h> /* for uname */
+#include <string.h> /* for strdup */
+
+
+struct icaldirset_impl
+{
+ char* dir;
+ icalcomponent* gauge;
+ icaldirset* cluster;
+ int first_component;
+ pvl_list directory;
+ pvl_elem directory_iterator;
+};
+
+struct icaldirset_impl* icaldirset_new_impl()
+{
+ struct icaldirset_impl* comp;
+
+ if ( ( comp = (struct icaldirset_impl*)
+ malloc(sizeof(struct icaldirset_impl))) == 0) {
+ icalerror_set_errno(ICAL_NEWFAILED_ERROR);
+ return 0;
+ }
+
+ return comp;
+}
+
+char* icaldirset_path(icaldirset* cluster)
+{
+ struct icaldirset_impl *impl = icaldirset_new_impl();
+
+ return impl->dir;
+
+}
+
+void icaldirset_mark(icaldirset* store)
+{
+ struct icaldirset_impl *impl = (struct icaldirset_impl*)store;
+
+ icalfileset_mark(impl->cluster);
+}
+
+
+icalerrorenum icaldirset_commit(icaldirset* store)
+{
+ struct icaldirset_impl *impl = (struct icaldirset_impl*)store;
+
+ return icalfileset_commit(impl->cluster);
+
+}
+
+void icaldirset_lock(char* dir)
+{
+}
+
+
+void icaldirset_unlock(char* dir)
+{
+}
+
+/* Load the contents of the store directory into the store's internal directory list*/
+icalerrorenum icaldirset_read_directory(struct icaldirset_impl* impl)
+{
+ struct dirent *de;
+ DIR* dp;
+ char *str;
+
+ dp = opendir(impl->dir);
+
+ if ( dp == 0) {
+ icalerror_set_errno(ICAL_FILE_ERROR);
+ return ICAL_FILE_ERROR;
+ }
+
+ /* clear contents of directory list */
+ while((str = pvl_pop(impl->directory))){
+ free(str);
+ }
+
+ /* load all of the cluster names in the directory list */
+ for(de = readdir(dp);
+ de != 0;
+ de = readdir(dp)){
+
+ /* Remove known directory names '.' and '..'*/
+ if (strcmp(de->d_name,".") == 0 ||
+ strcmp(de->d_name,"..") == 0 ){
+ continue;
+ }
+
+ pvl_push(impl->directory, (void*)strdup(de->d_name));
+ }
+
+ closedir(dp);
+
+ return ICAL_NO_ERROR;
+}
+
+icaldirset* icaldirset_new(char* dir)
+{
+ struct icaldirset_impl *impl = icaldirset_new_impl();
+ struct stat sbuf;
+
+ if (impl == 0){
+ return 0;
+ }
+
+ icalerror_check_arg_rz( (dir!=0), "dir");
+
+ if (stat(dir,&sbuf) != 0){
+ icalerror_set_errno(ICAL_FILE_ERROR);
+ return 0;
+ }
+
+ /* dir is not the name of a direectory*/
+ if (!S_ISDIR(sbuf.st_mode)){
+ icalerror_set_errno(ICAL_USAGE_ERROR);
+ return 0;
+ }
+
+ icaldirset_lock(dir);
+
+ impl = icaldirset_new_impl();
+
+ if (impl ==0){
+ icalerror_set_errno(ICAL_ALLOCATION_ERROR);
+ return 0;
+ }
+
+ impl->directory = pvl_newlist();
+ impl->directory_iterator = 0;
+ impl->dir = (char*)strdup(dir);
+ impl->gauge = 0;
+ impl->first_component = 0;
+ impl->cluster = 0;
+
+ icaldirset_read_directory(impl);
+
+ return (icaldirset*) impl;
+}
+
+void icaldirset_free(icaldirset* s)
+{
+ struct icaldirset_impl *impl = (struct icaldirset_impl*)s;
+ char* str;
+
+ icaldirset_unlock(impl->dir);
+
+ if(impl->dir !=0){
+ free(impl->dir);
+ }
+
+ if(impl->gauge !=0){
+ icalcomponent_free(impl->gauge);
+ }
+
+ if(impl->cluster !=0){
+ icalfileset_free(impl->cluster);
+ }
+
+ while(impl->directory !=0 && (str=pvl_pop(impl->directory)) != 0){
+ free(str);
+ }
+
+ if(impl->directory != 0){
+ pvl_free(impl->directory);
+ }
+
+ impl->directory = 0;
+ impl->directory_iterator = 0;
+ impl->dir = 0;
+ impl->gauge = 0;
+ impl->first_component = 0;
+
+ free(impl);
+
+}
+
+/* icaldirset_next_uid_number updates a serial number in the Store
+ directory in a file called SEQUENCE */
+
+int icaldirset_next_uid_number(icaldirset* store)
+{
+ struct icaldirset_impl *impl = (struct icaldirset_impl*)store;
+ char sequence = 0;
+ char temp[128];
+ char filename[PATH_MAX];
+ char *r;
+ FILE *f;
+ struct stat sbuf;
+
+ icalerror_check_arg_rz( (store!=0), "store");
+
+ sprintf(filename,"%s/%s",impl->dir,"SEQUENCE");
+
+ /* Create the file if it does not exist.*/
+ if (stat(filename,&sbuf) == -1 || !S_ISREG(sbuf.st_mode)){
+
+ f = fopen(filename,"w");
+ if (f != 0){
+ fprintf(f,"0");
+ fclose(f);
+ } else {
+ icalerror_warn("Can't create SEQUENCE file in icaldirset_next_uid_number");
+ return 0;
+ }
+
+ }
+
+ if ( (f = fopen(filename,"r+")) != 0){
+
+ rewind(f);
+ r = fgets(temp,128,f);
+
+ if (r == 0){
+ sequence = 1;
+ } else {
+ sequence = atoi(temp)+1;
+ }
+
+ rewind(f);
+
+ fprintf(f,"%d",sequence);
+
+ fclose(f);
+
+ return sequence;
+
+ } else {
+ icalerror_warn("Can't create SEQUENCE file in icaldirset_next_uid_number");
+ return 0;
+ }
+
+}
+
+icalerrorenum icaldirset_next_cluster(icaldirset* store)
+{
+ struct icaldirset_impl *impl = (struct icaldirset_impl*)store;
+ char path[PATH_MAX];
+
+ if (impl->directory_iterator == 0){
+ icalerror_set_errno(ICAL_INTERNAL_ERROR);
+ return ICAL_INTERNAL_ERROR;
+ }
+ impl->directory_iterator = pvl_next(impl->directory_iterator);
+
+ if (impl->directory_iterator == 0){
+ /* There are no more clusters */
+ if(impl->cluster != 0){
+ icalfileset_free(impl->cluster);
+ impl->cluster = 0;
+ }
+ return ICAL_NO_ERROR;
+ }
+
+ sprintf(path,"%s/%s",impl->dir,(char*)pvl_data(impl->directory_iterator));
+
+ icalfileset_free(impl->cluster);
+
+ impl->cluster = icalfileset_new(path);
+
+ return icalerrno;
+}
+
+void icaldirset_add_uid(icaldirset* store, icaldirset* comp)
+{
+ char uidstring[PATH_MAX];
+ icalproperty *uid;
+ struct utsname unamebuf;
+
+ icalerror_check_arg_rv( (store!=0), "store");
+ icalerror_check_arg_rv( (comp!=0), "comp");
+
+ uid = icalcomponent_get_first_property(comp,ICAL_UID_PROPERTY);
+
+ if (uid == 0) {
+
+ uname(&unamebuf);
+
+ sprintf(uidstring,"%d-%s",(int)getpid(),unamebuf.nodename);
+
+ uid = icalproperty_new_uid(uidstring);
+ icalcomponent_add_property(comp,uid);
+ } else {
+
+ strcpy(uidstring,icalproperty_get_uid(uid));
+ }
+}
+
+
+/* This assumes that the top level component is a VCALENDAR, and there
+ is an inner component of type VEVENT, VTODO or VJOURNAL. The inner
+ component must have a DTSTART property */
+
+icalerrorenum icaldirset_add_component(icaldirset* store, icaldirset* comp)
+{
+ struct icaldirset_impl *impl;
+ char clustername[PATH_MAX];
+ icalproperty *dt;
+ icalvalue *v;
+ struct icaltimetype tm;
+ icalerrorenum error = ICAL_NO_ERROR;
+ icalcomponent *inner;
+
+ impl = (struct icaldirset_impl*)store;
+ icalerror_check_arg_rz( (store!=0), "store");
+ icalerror_check_arg_rz( (comp!=0), "comp");
+
+ errno = 0;
+
+ icaldirset_add_uid(store,comp);
+
+ /* Determine which cluster this object belongs in. This is a HACK */
+
+ for(inner = icalcomponent_get_first_component(comp,ICAL_ANY_COMPONENT);
+ inner != 0;
+ inner = icalcomponent_get_next_component(comp,ICAL_ANY_COMPONENT)){
+
+ dt = icalcomponent_get_first_property(inner,ICAL_DTSTART_PROPERTY);
+
+ if (dt != 0){
+ break;
+ }
+ }
+
+ if (dt == 0){
+ icalerror_warn("The component does not have a DTSTART property, so it cannot be added to the store");
+ icalerror_set_errno(ICAL_BADARG_ERROR);
+ return ICAL_BADARG_ERROR;
+ }
+
+ v = icalproperty_get_value(dt);
+
+ tm = icalvalue_get_datetime(v);
+
+ snprintf(clustername,PATH_MAX,"%s/%04d%02d",impl->dir,tm.year,tm.month);
+
+ /* Load the cluster and insert the object */
+
+ if(impl->cluster != 0 &&
+ strcmp(clustername,icalfileset_path(impl->cluster)) != 0 ){
+ icalfileset_free(impl->cluster);
+ impl->cluster = 0;
+ }
+
+ if (impl->cluster == 0){
+ impl->cluster = icalfileset_new(clustername);
+
+ if (impl->cluster == 0){
+ error = icalerrno;
+ }
+ }
+
+ if (error != ICAL_NO_ERROR){
+ icalerror_set_errno(error);
+ return error;
+ }
+
+ /* Add the component to the cluster */
+
+ icalfileset_add_component(impl->cluster,comp);
+
+ icalfileset_mark(impl->cluster);
+
+ return ICAL_NO_ERROR;
+}
+
+/* Remove a component in the current cluster. HACK. This routine is a
+ "friend" of icalfileset, and breaks its encapsulation. It was
+ either do it this way, or add several layers of interfaces that had
+ no other use. */
+icalerrorenum icaldirset_remove_component(icaldirset* store, icaldirset* comp)
+{
+ struct icaldirset_impl *impl = (struct icaldirset_impl*)store;
+
+ struct icalfileset_impl *filesetimpl =
+ (struct icalfileset_impl*)impl->cluster;
+
+ icalcomponent *filecomp = filesetimpl->cluster;
+
+ icalcompiter i;
+ int found = 0;
+
+ icalerror_check_arg_re((store!=0),"store",ICAL_BADARG_ERROR);
+ icalerror_check_arg_re((comp!=0),"comp",ICAL_BADARG_ERROR);
+ icalerror_check_arg_re((impl->cluster!=0),"Cluster pointer",ICAL_USAGE_ERROR);
+
+ for(i = icalcomponent_begin_component(filecomp,ICAL_ANY_COMPONENT);
+ icalcompiter_deref(&i)!= 0; icalcompiter_next(&i)){
+
+ icalcomponent *this = icalcompiter_deref(&i);
+
+ if (this == comp){
+ found = 1;
+ break;
+ }
+ }
+
+ if (found != 1){
+ icalerror_warn("icaldirset_remove_component: component is not part of current cluster");
+ icalerror_set_errno(ICAL_USAGE_ERROR);
+ return ICAL_USAGE_ERROR;
+ }
+
+ icalfileset_remove_component(impl->cluster,comp);
+
+ icalfileset_mark(impl->cluster);
+
+ /* If the removal emptied the fileset, get the next fileset */
+ if( icalfileset_count_components(impl->cluster,ICAL_ANY_COMPONENT)==0){
+
+ icalerrorenum error = icaldirset_next_cluster(store);
+
+ if(impl->cluster != 0 && error == ICAL_NO_ERROR){
+ icalfileset_get_first_component(impl->cluster,ICAL_ANY_COMPONENT);
+ } else {
+ /* HACK. Not strictly correct for impl->cluster==0 */
+ return error;
+ }
+ } else {
+ /* Do nothing */
+ }
+
+ return ICAL_NO_ERROR;
+}
+
+
+
+int icaldirset_count_components(icaldirset* store,
+ icalcomponent_kind kind);
+
+
+icalcomponent* icaldirset_fetch(icaldirset* store, char* uid)
+{
+ icalcomponent *gauge;
+ icalcomponent *old_gauge;
+ icalcomponent *c;
+ struct icaldirset_impl *impl = (struct icaldirset_impl*)store;
+
+ icalerror_check_arg_rz( (store!=0), "store");
+ icalerror_check_arg_rz( (uid!=0), "uid");
+
+ gauge =
+ icalcomponent_vanew(
+ ICAL_VCALENDAR_COMPONENT,
+ icalcomponent_vanew(
+ ICAL_VEVENT_COMPONENT,
+ icalproperty_vanew_uid(
+ uid,
+ icalparameter_new_xliccomparetype(
+ ICAL_XLICCOMPARETYPE_EQUAL),
+ 0),
+ 0),
+ 0);
+
+ old_gauge = impl->gauge;
+ impl->gauge = gauge;
+
+ c= icaldirset_get_first_component(store,ICAL_ANY_COMPONENT);
+
+ impl->gauge = old_gauge;
+
+ icalcomponent_free(gauge);
+
+ return c;
+}
+
+
+int icaldirset_has_uid(icaldirset* store, char* uid)
+{
+ icalcomponent *c;
+
+ icalerror_check_arg_rz( (store!=0), "store");
+ icalerror_check_arg_rz( (uid!=0), "uid");
+
+ /* HACK. This is a temporary implementation. _has_uid should use a
+ database, and _fetch should use _has_uid, not the other way
+ around */
+ c = icaldirset_fetch(store,uid);
+
+ return c!=0;
+
+}
+
+
+icalerrorenum icaldirset_select(icaldirset* store, icalcomponent* gauge)
+{
+ struct icaldirset_impl *impl = (struct icaldirset_impl*)store;
+
+ icalerror_check_arg_re( (store!=0), "store",ICAL_BADARG_ERROR);
+ icalerror_check_arg_re( (gauge!=0), "gauge",ICAL_BADARG_ERROR);
+
+ if (!icalcomponent_is_valid(gauge)){
+ return ICAL_BADARG_ERROR;
+ }
+
+ impl->gauge = gauge;
+
+ return ICAL_NO_ERROR;
+}
+
+void icaldirset_clear(icaldirset* store);
+icalcomponent* icaldirset_fetch(icaldirset* store, char* uid);
+int icaldirset_has_uid(icaldirset* store, char* uid);
+
+icalcomponent* icaldirset_get_current_component(icaldirset* store)
+{
+ struct icaldirset_impl *impl = (struct icaldirset_impl*)store;
+
+ if(impl->cluster == 0){
+ icaldirset_get_first_component(store,ICAL_ANY_COMPONENT);
+ }
+
+ return icalfileset_get_current_component(impl->cluster);
+
+}
+
+
+icalcomponent* icaldirset_get_first_component(icaldirset* store,
+ icalcomponent_kind kind)
+{
+ struct icaldirset_impl *impl = (struct icaldirset_impl*)store;
+ icalerrorenum error;
+ char path[PATH_MAX];
+
+ error = icaldirset_read_directory(impl);
+
+ if (error != ICAL_NO_ERROR){
+ icalerror_set_errno(error);
+ return 0;
+ }
+
+ impl->directory_iterator = pvl_head(impl->directory);
+
+ if (impl->directory_iterator == 0){
+ icalerror_set_errno(error);
+ return 0;
+ }
+
+ snprintf(path,PATH_MAX,"%s/%s",impl->dir,(char*)pvl_data(impl->directory_iterator));
+
+ /* If the next cluster we need is different than the current cluster,
+ delete the current one and get a new one */
+
+ if(impl->cluster != 0 && strcmp(path,icalfileset_path(impl->cluster)) != 0 ){
+ icalfileset_free(impl->cluster);
+ impl->cluster = 0;
+ }
+
+ if (impl->cluster == 0){
+ impl->cluster = icalfileset_new(path);
+
+ if (impl->cluster == 0){
+ error = icalerrno;
+ }
+ }
+
+ if (error != ICAL_NO_ERROR){
+ icalerror_set_errno(error);
+ return 0;
+ }
+
+ impl->first_component = 1;
+
+ return icaldirset_get_next_component(store, kind);
+}
+
+icalcomponent* icaldirset_get_next_component(icaldirset* store,
+ icalcomponent_kind kind)
+{
+ struct icaldirset_impl *impl;
+ icalcomponent *c;
+ icalerrorenum error;
+
+ icalerror_check_arg_rz( (store!=0), "store");
+
+ impl = (struct icaldirset_impl*)store;
+
+ if(impl->cluster == 0){
+
+ icalerror_warn("icaldirset_get_next_component called with a NULL cluster (Caller must call icaldirset_get_first_component first");
+ icalerror_set_errno(ICAL_USAGE_ERROR);
+ return 0;
+
+ }
+
+ /* Set the component iterator for the following for loop */
+ if (impl->first_component == 1){
+ icalfileset_get_first_component(impl->cluster,kind);
+ impl->first_component = 0;
+ } else {
+ icalfileset_get_next_component(impl->cluster,kind);
+ }
+
+
+ while(1){
+ /* Iterate through all of the objects in the cluster*/
+ for( c = icalfileset_get_current_component(impl->cluster);
+ c != 0;
+ c = icalfileset_get_next_component(
+ impl->cluster,
+ kind)){
+
+ /* If there is a gauge defined and the component does not
+ pass the gauge, skip the rest of the loop */
+ if (impl->gauge != 0 && icalgauge_test(c,impl->gauge) == 0){
+ continue;
+ }
+
+ /* Either there is no gauge, or the component passed the
+ gauge, so return it*/
+
+ return c;
+ }
+
+ /* Fell through the loop, so the component we want is not
+ in this cluster. Load a new cluster and try again.*/
+
+ error = icaldirset_next_cluster(store);
+
+ if(impl->cluster == 0 || error != ICAL_NO_ERROR){
+ /* No more clusters */
+ return 0;
+ } else {
+ c = icalfileset_get_first_component(
+ impl->cluster,
+ kind);
+
+ return c;
+ }
+
+ }
+
+ return 0; /* Should never get here */
+}
+
+
+
+
+
+
+
diff --git a/libical/src/libicalss/icaldirset.h b/libical/src/libicalss/icaldirset.h
new file mode 100644
index 0000000000..e9d6240aeb
--- /dev/null
+++ b/libical/src/libicalss/icaldirset.h
@@ -0,0 +1,84 @@
+/* -*- Mode: C -*- */
+/*======================================================================
+ FILE: icaldirset.h
+ CREATOR: eric 28 November 1999
+
+
+ $Id$
+ $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
+
+
+======================================================================*/
+
+#ifndef ICALDIRSET_H
+#define ICALDIRSET_H
+
+#include "ical.h"
+#include "icalerror.h"
+
+/* icaldirset Routines for storing, fetching, and searching for ical
+ * objects in a database */
+
+typedef void icaldirset;
+
+
+icaldirset* icaldirset_new(char* path);
+
+void icaldirset_free(icaldirset* store);
+
+char* icaldirset_path(icaldirset* store);
+
+/* Mark the cluster as changed, so it will be written to disk when it
+ is freed. Commit writes to disk immediately*/
+void icaldirset_mark(icaldirset* store);
+icalerrorenum icaldirset_commit(icaldirset* store);
+
+icalerrorenum icaldirset_add_component(icaldirset* store, icalcomponent* comp);
+icalerrorenum icaldirset_remove_component(icaldirset* store, icalcomponent* comp);
+
+int icaldirset_count_components(icaldirset* store,
+ icalcomponent_kind kind);
+
+/* Restrict the component returned by icaldirset_first, _next to those
+ that pass the gauge. _clear removes the gauge. */
+icalerrorenum icaldirset_select(icaldirset* store, icalcomponent* gauge);
+void icaldirset_clear(icaldirset* store);
+
+/* Get a component by uid */
+icalcomponent* icaldirset_fetch(icaldirset* store, char* uid);
+int icaldirset_has_uid(icaldirset* store, char* uid);
+
+/* Modify components according to the MODIFY method of CAP. Works on
+ the currently selected components. */
+icalerrorenum icaldirset_modify(icaldirset* store, icalcomponent *old,
+ icalcomponent *new);
+
+/* Iterate through the components. If a guage has been defined, these
+ will skip over components that do not pass the gauge */
+
+icalcomponent* icaldirset_get_current_component(icaldirset* store);
+icalcomponent* icaldirset_get_first_component(icaldirset* store,
+ icalcomponent_kind kind);
+icalcomponent* icaldirset_get_next_component(icaldirset* store,
+ icalcomponent_kind kind);
+
+#endif /* !ICALDIRSET_H */
+
+
+
diff --git a/libical/src/libicalss/icalfileset.c b/libical/src/libicalss/icalfileset.c
new file mode 100644
index 0000000000..46c5cd2586
--- /dev/null
+++ b/libical/src/libicalss/icalfileset.c
@@ -0,0 +1,427 @@
+/* -*- Mode: C -*-
+ ======================================================================
+ FILE: icalfileset.c
+ CREATOR: eric 23 December 1999
+
+ $Id$
+ $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 "icalfileset.h"
+#include <errno.h>
+#include <limits.h> /* For PATH_MAX */
+#include <sys/stat.h> /* for stat */
+#include <unistd.h> /* for stat, getpid */
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h> /* for fcntl */
+#include <unistd.h> /* for fcntl */
+
+#include "icalfilesetimpl.h"
+
+int icalfileset_lock(icalfileset *cluster);
+int icalfileset_unlock(icalfileset *cluster);
+
+
+icalerrorenum icalfileset_create_cluster(char *path);
+
+icalfileset* icalfileset_new_impl()
+{
+ struct icalfileset_impl* comp;
+
+ if ( ( comp = (struct icalfileset_impl*)
+ malloc(sizeof(struct icalfileset_impl))) == 0) {
+ icalerror_set_errno(ICAL_NEWFAILED_ERROR);
+ errno = ENOMEM;
+ return 0;
+ }
+
+ return comp;
+}
+
+char* read_from_file(char *s, size_t size, void *d)
+{
+ char *c = fgets(s,size, (FILE*)d);
+ return c;
+}
+
+icalfileset* icalfileset_new(char* path)
+{
+ struct icalfileset_impl *impl = icalfileset_new_impl();
+ struct stat sbuf;
+ int createclusterfile = 0;
+ icalerrorenum error = ICAL_NO_ERROR;
+ icalparser *parser;
+ struct icaltimetype tt;
+ off_t cluster_file_size;
+
+ memset(&tt,0,sizeof(struct icaltimetype));
+
+ icalerror_clear_errno();
+ icalerror_check_arg_rz( (path!=0), "path");
+
+ if (impl == 0){
+ return 0;
+ }
+
+ /*impl->path = strdup(path); icalfileset_load does this */
+ impl->changed = 0;
+
+ impl->cluster = 0;
+
+ impl->path = 0;
+ impl->stream = 0;
+
+ /* Check if the path already exists and if it is a regular file*/
+ if (stat(path,&sbuf) != 0){
+
+ /* A file by the given name does not exist, or there was
+ another error */
+ cluster_file_size = 0;
+ if (errno == ENOENT) {
+ /* It was because the file does not exist */
+ createclusterfile = 1;
+ } else {
+ /* It was because of another error */
+ icalerror_set_errno(ICAL_FILE_ERROR);
+ return 0;
+ }
+ } else {
+ /* A file by the given name exists, but is it a regular file */
+
+ if (!S_ISREG(sbuf.st_mode)){
+ /* Nope, not a directory */
+ icalerror_set_errno(ICAL_FILE_ERROR);
+ return 0;
+ } else {
+ /* Lets assume that it is a file of the right type */
+ cluster_file_size = sbuf.st_size;
+ createclusterfile = 0;
+ }
+ }
+
+ /* if cluster does not already exist, create it */
+
+ if (createclusterfile == 1) {
+ error = icalfileset_create_cluster(path);
+
+ if (error != ICAL_NO_ERROR){
+ icalerror_set_errno(error);
+ return 0;
+ }
+ }
+
+ impl->path = (char*)strdup(path);
+
+ errno = 0;
+ impl->stream = fopen(impl->path,"r");
+
+ if (impl->stream ==0 || errno != 0){
+ impl->cluster = 0;
+ icalerror_set_errno(ICAL_FILE_ERROR); /* Redundant, actually */
+ return 0;
+ }
+
+ icalfileset_lock(impl);
+
+ if(cluster_file_size > 0){
+ parser = icalparser_new();
+ icalparser_set_gen_data(parser,impl->stream);
+ impl->cluster = icalparser_parse(parser,read_from_file);
+ icalparser_free(parser);
+
+ if (icalcomponent_isa(impl->cluster) != ICAL_XROOT_COMPONENT){
+ /* The parser got a single component, so it did not put it in
+ an XROOT. */
+ icalcomponent *cl = impl->cluster;
+ impl->cluster = icalcomponent_new(ICAL_XROOT_COMPONENT);
+ icalcomponent_add_component(impl->cluster,cl);
+ }
+
+ } else {
+
+ impl->cluster = icalcomponent_new(ICAL_XROOT_COMPONENT);
+ }
+
+ if (impl->cluster == 0){
+ icalerror_set_errno(ICAL_PARSE_ERROR);
+ return 0;
+ }
+
+ if (error != ICAL_NO_ERROR){
+ return 0;
+ }
+
+ return impl;
+}
+
+void icalfileset_free(icalfileset* cluster)
+{
+ struct icalfileset_impl *impl = (struct icalfileset_impl*)cluster;
+
+ icalerror_check_arg_rv((cluster!=0),"cluster");
+
+ if (impl->cluster != 0){
+ icalfileset_commit(cluster);
+ icalcomponent_free(impl->cluster);
+ impl->cluster=0;
+ }
+
+ if(impl->path != 0){
+ free(impl->path);
+ impl->path = 0;
+ }
+
+ if(impl->stream != 0){
+ icalfileset_unlock(impl);
+ fclose(impl->stream);
+ impl->stream = 0;
+ }
+
+ free(impl);
+}
+
+char* icalfileset_path(icalfileset* cluster)
+{
+ struct icalfileset_impl *impl = (struct icalfileset_impl*)cluster;
+ icalerror_check_arg_rz((cluster!=0),"cluster");
+
+ return impl->path;
+}
+
+
+int icalfileset_lock(icalfileset *cluster)
+{
+ struct icalfileset_impl *impl = (struct icalfileset_impl*)cluster;
+ struct flock lock;
+ int fd;
+
+ icalerror_check_arg_rz((impl->stream!=0),"impl->stream");
+
+ fd = fileno(impl->stream);
+
+ lock.l_type = F_WRLCK; /* F_RDLCK, F_WRLCK, F_UNLCK */
+ lock.l_start = 0; /* byte offset relative to l_whence */
+ lock.l_whence = SEEK_SET; /* SEEK_SET, SEEK_CUR, SEEK_END */
+ lock.l_len = 0; /* #bytes (0 means to EOF) */
+
+ return (fcntl(fd, F_SETLKW, &lock));
+}
+
+int icalfileset_unlock(icalfileset *cluster)
+{
+ struct icalfileset_impl *impl = (struct icalfileset_impl*)cluster;
+ int fd;
+ struct flock lock;
+ icalerror_check_arg_rz((impl->stream!=0),"impl->stream");
+
+ fd = fileno(impl->stream);
+
+ lock.l_type = F_WRLCK; /* F_RDLCK, F_WRLCK, F_UNLCK */
+ lock.l_start = 0; /* byte offset relative to l_whence */
+ lock.l_whence = SEEK_SET; /* SEEK_SET, SEEK_CUR, SEEK_END */
+ lock.l_len = 0; /* #bytes (0 means to EOF) */
+
+ return (fcntl(fd, F_UNLCK, &lock));
+
+}
+
+icalerrorenum icalfileset_create_cluster(char *path)
+{
+
+ FILE* f;
+
+ icalerror_clear_errno();
+
+ f = fopen(path,"w");
+
+ if (f == 0){
+ icalerror_set_errno(ICAL_FILE_ERROR);
+ return ICAL_FILE_ERROR;
+ }
+
+
+ /* This used to write data to the file... */
+
+
+ fclose(f);
+
+ return ICAL_NO_ERROR;
+}
+
+icalerrorenum icalfileset_commit(icalfileset* cluster)
+{
+ FILE *f;
+ char tmp[PATH_MAX]; /* HACK Buffer overflow potential */
+ char *str;
+ icalcomponent *c;
+
+ struct icalfileset_impl *impl = (struct icalfileset_impl*)cluster;
+
+ icalerror_check_arg_re((impl!=0),"cluster",ICAL_BADARG_ERROR);
+
+ if (impl->changed == 0 ){
+ return ICAL_NO_ERROR;
+ }
+
+#ifdef ICAL_SAFESAVES
+ snprintf(tmp,PATH_MAX,"%s-tmp",impl->path);
+#else
+ strcpy(tmp,impl->path);
+#endif
+
+ if ( (f = fopen(tmp,"w")) < 0 ){
+ icalerror_set_errno(ICAL_FILE_ERROR);
+ return ICAL_FILE_ERROR;
+ }
+
+ for(c = icalcomponent_get_first_component(impl->cluster,ICAL_ANY_COMPONENT);
+ c != 0;
+ c = icalcomponent_get_next_component(impl->cluster,ICAL_ANY_COMPONENT)){
+
+ str = icalcomponent_as_ical_string(c);
+
+ if ( fwrite(str,sizeof(char),strlen(str),f) < strlen(str)){
+ fclose(f);
+ return ICAL_FILE_ERROR;
+ }
+ }
+
+ fclose(f);
+ impl->changed = 0;
+
+#ifdef ICAL_SAFESAVES
+ rename(tmp,impl->path); /* HACK, should check for error here */
+#endif
+
+ return ICAL_NO_ERROR;
+
+}
+
+void icalfileset_mark(icalfileset* cluster){
+
+ struct icalfileset_impl *impl = (struct icalfileset_impl*)cluster;
+
+ icalerror_check_arg_rv((impl!=0),"cluster");
+
+ impl->changed = 1;
+
+}
+
+icalcomponent* icalfileset_get_component(icalfileset* cluster){
+ struct icalfileset_impl *impl = (struct icalfileset_impl*)cluster;
+
+ icalerror_check_arg_re((impl!=0),"cluster",ICAL_BADARG_ERROR);
+
+ return impl->cluster;
+}
+
+
+/* manipulate the components in the cluster */
+
+icalerrorenum icalfileset_add_component(icalfileset *cluster,
+ icalcomponent* child)
+{
+ struct icalfileset_impl* impl = (struct icalfileset_impl*)cluster;
+
+ icalerror_check_arg_rv((cluster!=0),"cluster");
+ icalerror_check_arg_rv((child!=0),"child");
+
+ icalcomponent_add_component(impl->cluster,child);
+
+ icalfileset_mark(cluster);
+
+ return ICAL_NO_ERROR;
+
+}
+
+icalerrorenum icalfileset_remove_component(icalfileset *cluster,
+ icalcomponent* child)
+{
+ struct icalfileset_impl* impl = (struct icalfileset_impl*)cluster;
+
+ icalerror_check_arg_rv((cluster!=0),"cluster");
+ icalerror_check_arg_rv((child!=0),"child");
+
+ icalcomponent_remove_component(impl->cluster,child);
+
+ icalfileset_mark(cluster);
+
+ return ICAL_NO_ERROR;
+}
+
+int icalfileset_count_components(icalfileset *cluster,
+ icalcomponent_kind kind)
+{
+ struct icalfileset_impl* impl = (struct icalfileset_impl*)cluster;
+
+ if(cluster == 0){
+ icalerror_set_errno(ICAL_BADARG_ERROR);
+ return -1;
+ }
+
+ return icalcomponent_count_components(impl->cluster,kind);
+}
+
+icalerrorenum icalfileset_select(icalfileset* cluster, icalcomponent* gauge);
+void icalfileset_clear(icalfileset* cluster);
+
+icalcomponent* icalfileset_fetch(icalfileset* store, char* uid);
+int icalfileset_has_uid(icalfileset* store, char* uid);
+
+
+/* Iterate through components */
+icalcomponent* icalfileset_get_current_component (icalfileset* cluster)
+{
+ struct icalfileset_impl* impl = (struct icalfileset_impl*)cluster;
+
+ icalerror_check_arg_rz((cluster!=0),"cluster");
+
+ return icalcomponent_get_current_component(impl->cluster);
+}
+
+
+icalcomponent* icalfileset_get_first_component(icalfileset* cluster,
+ icalcomponent_kind kind)
+{
+ struct icalfileset_impl* impl = (struct icalfileset_impl*)cluster;
+
+ icalerror_check_arg_rz((cluster!=0),"cluster");
+
+ return icalcomponent_get_first_component(impl->cluster,kind);
+}
+
+icalcomponent* icalfileset_get_next_component(icalfileset* cluster,
+ icalcomponent_kind kind)
+{
+ struct icalfileset_impl* impl = (struct icalfileset_impl*)cluster;
+
+ icalerror_check_arg_rz((cluster!=0),"cluster");
+
+ return icalcomponent_get_next_component(impl->cluster,kind);
+}
+
diff --git a/libical/src/libicalss/icalfileset.h b/libical/src/libicalss/icalfileset.h
new file mode 100644
index 0000000000..8ceae632be
--- /dev/null
+++ b/libical/src/libicalss/icalfileset.h
@@ -0,0 +1,90 @@
+/* -*- Mode: C -*- */
+/*======================================================================
+ FILE: icalfileset.h
+ CREATOR: eric 23 December 1999
+
+
+ $Id$
+ $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
+
+
+======================================================================*/
+
+#ifndef ICALFILESET_H
+#define ICALFILESET_H
+
+#include "ical.h"
+
+typedef void icalfileset;
+
+
+/* icalfileset
+ icalfilesetfile
+ icalfilesetdir
+*/
+
+
+icalfileset* icalfileset_new(char* path);
+void icalfileset_free(icalfileset* cluster);
+
+char* icalfileset_path(icalfileset* cluster);
+
+/* Mark the cluster as changed, so it will be written to disk when it
+ is freed. Commit writes to disk immediately. */
+void icalfileset_mark(icalfileset* cluster);
+icalerrorenum icalfileset_commit(icalfileset* cluster);
+
+icalerrorenum icalfileset_add_component(icalfileset* cluster,
+ icalcomponent* child);
+
+icalerrorenum icalfileset_remove_component(icalfileset* cluster,
+ icalcomponent* child);
+
+int icalfileset_count_components(icalfileset* cluster,
+ icalcomponent_kind kind);
+
+/* Restrict the component returned by icalfileset_first, _next to those
+ that pass the gauge. _clear removes the gauge */
+icalerrorenum icalfileset_select(icalfileset* store, icalcomponent* gauge);
+void icalfileset_clear(icalfileset* store);
+
+/* Get and search for a component by uid */
+icalcomponent* icalfileset_fetch(icalfileset* cluster, char* uid);
+int icalfileset_has_uid(icalfileset* cluster, char* uid);
+
+
+/* Iterate through components. If a guage has been defined, these
+ will skip over components that do not pass the gauge */
+
+icalcomponent* icalfileset_get_current_component (icalfileset* cluster);
+icalcomponent* icalfileset_get_first_component(icalfileset* cluster,
+ icalcomponent_kind kind);
+icalcomponent* icalfileset_get_next_component(icalfileset* cluster,
+ icalcomponent_kind kind);
+
+/* Return a reference to the internal component. You probably should
+ not be using this. */
+
+icalcomponent* icalfileset_get_component(icalfileset* cluster);
+
+
+#endif /* !ICALFILESET_H */
+
+
+
diff --git a/libical/src/libicalss/icalfilesetimpl.h b/libical/src/libicalss/icalfilesetimpl.h
new file mode 100644
index 0000000000..de447c64eb
--- /dev/null
+++ b/libical/src/libicalss/icalfilesetimpl.h
@@ -0,0 +1,42 @@
+/* -*- Mode: C -*-
+ ======================================================================
+ FILE: icalfilesetimpl.h
+ CREATOR: eric 23 December 1999
+
+ $Id$
+ $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
+
+/* This definition is in its own file so it can be kept out of the
+ main header file, but used by "friend classes" like icaldirset*/
+
+struct icalfileset_impl {
+ char *path;
+ icalcomponent* cluster;
+ int changed;
+ FILE* stream;
+};
+
diff --git a/libical/src/libicalss/icalgauge.c b/libical/src/libicalss/icalgauge.c
new file mode 100644
index 0000000000..60ce1587cd
--- /dev/null
+++ b/libical/src/libicalss/icalgauge.c
@@ -0,0 +1,208 @@
+/* -*- Mode: C -*- */
+/*======================================================================
+ FILE: icalgauge.c
+ CREATOR: eric 23 December 1999
+
+
+ $Id$
+ $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 "ical.h"
+
+/* Convert a VQUERY component into a gauge */
+icalcomponent* icalgauge_make_gauge(icalcomponent* query);
+
+/* icaldirset_test compares a component against a gauge, and returns
+ true if the component passes the test
+
+ The gauge is a VCALENDAR component that specifies how to test the
+ target components. The guage holds a collection of VEVENT, VTODO or
+ VJOURNAL sub-components. Each of the sub-components has a
+ collection of properties that are compared to corresponding
+ properties in the target component, according to the
+ X-LIC-COMPARETYPE parameters to the gauge's properties.
+
+ When a gauge has several sub-components, the results of testing the
+ target against each of them is ORed together - the target
+ component will pass if it matches any of the sub-components in the
+ gauge. However, the results of matching the proeprties in a
+ sub-component are ANDed -- the target must match every property in
+ a gauge sub-component to match the sub-component.
+
+ Here is an example:
+
+ BEGIN:XROOT
+ BEGIN:VCOMPONENT
+ BEGIN:VEVENT
+ DTSTART;X-LIC-COMPARETYPE=LESS:19981025T020000
+ ORGANIZER;X-LIC-COMPARETYPE=EQUAL:mrbig@host.com
+ END:VEVENT
+ BEGIN:VEVENT
+ LOCATION;X-LIC-COMPARETYPE=EQUAL:McNary's Pub
+ END:VEVENT
+ END:VCALENDAR
+ END:XROOT
+
+ This gauge has two sub-components; one which will match a VEVENT
+ based on start time, and organizer, and another that matches based
+ on LOCATION. A target component will pass the test if it matched
+ either of the sub-components.
+
+ */
+
+
+int icalgauge_test_recurse(icalcomponent* comp, icalcomponent* gauge)
+{
+ int pass = 1,localpass = 0;
+ icalproperty *p;
+ icalcomponent *child,*subgauge;
+ icalcomponent_kind gaugekind, compkind;
+
+ icalerror_check_arg_rz( (comp!=0), "comp");
+ icalerror_check_arg_rz( (gauge!=0), "gauge");
+
+ gaugekind = icalcomponent_isa(gauge);
+ compkind = icalcomponent_isa(comp);
+
+ if( ! (gaugekind == compkind || gaugekind == ICAL_ANY_COMPONENT) ){
+ return 0;
+ }
+
+ /* Test properties. For each property in the gauge, search through
+ the component for a similar property. If one is found, compare
+ the two properties value with the comparison specified in the
+ gauge with the X-LIC-COMPARETYPE parameter */
+
+ for(p = icalcomponent_get_first_property(gauge,ICAL_ANY_PROPERTY);
+ p != 0;
+ p = icalcomponent_get_next_property(gauge,ICAL_ANY_PROPERTY)){
+
+ icalproperty* targetprop;
+ icalparameter* compareparam;
+ icalparameter_xliccomparetype compare;
+ int rel; /* The relationship between the gauge and target values.*/
+
+ /* Extract the comparison type from the gauge. If there is no
+ comparison type, assume that it is "EQUAL" */
+
+ compareparam = icalproperty_get_first_parameter(
+ p,
+ ICAL_XLICCOMPARETYPE_PARAMETER);
+
+ if (compareparam!=0){
+ compare = icalparameter_get_xliccomparetype(compareparam);
+ } else {
+ compare = ICAL_XLICCOMPARETYPE_EQUAL;
+ }
+
+ /* Find a property in the component that has the same type
+ as the gauge property. HACK -- multiples of a single
+ property type in the gauge will match only the first
+ instance in the component */
+
+ targetprop = icalcomponent_get_first_property(comp,
+ icalproperty_isa(p));
+
+ if(targetprop != 0){
+
+ /* Compare the values of the gauge property and the target
+ property */
+
+ rel = icalvalue_compare(icalproperty_get_value(p),
+ icalproperty_get_value(targetprop));
+
+ /* Now see if the comparison is equavalent to the comparison
+ specified in the gauge */
+
+ if (rel == compare){
+ localpass++;
+ } else if (compare == ICAL_XLICCOMPARETYPE_LESSEQUAL &&
+ ( rel == ICAL_XLICCOMPARETYPE_LESS ||
+ rel == ICAL_XLICCOMPARETYPE_EQUAL)) {
+ localpass++;
+ } else if (compare == ICAL_XLICCOMPARETYPE_GREATEREQUAL &&
+ ( rel == ICAL_XLICCOMPARETYPE_GREATER ||
+ rel == ICAL_XLICCOMPARETYPE_EQUAL)) {
+ localpass++;
+ } else if (compare == ICAL_XLICCOMPARETYPE_NOTEQUAL &&
+ ( rel == ICAL_XLICCOMPARETYPE_GREATER ||
+ rel == ICAL_XLICCOMPARETYPE_LESS)) {
+ localpass++;
+ } else {
+ localpass = 0;
+ }
+
+ pass = pass && (localpass>0);
+ }
+ }
+
+ /* Test subcomponents. Look for a child component that has a
+ counterpart in the gauge. If one is found, recursively call
+ icaldirset_test */
+
+ for(subgauge = icalcomponent_get_first_component(gauge,ICAL_ANY_COMPONENT);
+ subgauge != 0;
+ subgauge = icalcomponent_get_next_component(gauge,ICAL_ANY_COMPONENT)){
+
+ gaugekind = icalcomponent_isa(subgauge);
+
+ if (gaugekind == ICAL_ANY_COMPONENT){
+ child = icalcomponent_get_first_component(comp,ICAL_ANY_COMPONENT);
+ } else {
+ child = icalcomponent_get_first_component(comp,gaugekind);
+ }
+
+ if(child !=0){
+ localpass = icalgauge_test_recurse(child,subgauge);
+ pass = pass && localpass;
+ } else {
+ pass = 0;
+ }
+ }
+
+ return pass;
+}
+
+/* guagecontainer is an XROOT component that holds several gauges. The
+ results of comparing against these gauges are ORed together in this
+ routine */
+int icalgauge_test(icalcomponent* comp,
+ icalcomponent* gaugecontainer)
+{
+ int pass = 0;
+ icalcomponent *gauge;
+
+ icalerror_check_arg_rz( (comp!=0), "comp");
+ icalerror_check_arg_rz( (gauge!=0), "gauge");
+
+ for(gauge = icalcomponent_get_first_component(gaugecontainer,ICAL_ANY_COMPONENT);
+ gauge != 0;
+ gauge = icalcomponent_get_next_component(gaugecontainer,ICAL_ANY_COMPONENT)){
+
+ pass += icalgauge_test_recurse(comp, gauge);
+ }
+
+ return pass>0;
+
+}
+
+
diff --git a/libical/src/libicalss/icalgauge.h b/libical/src/libicalss/icalgauge.h
new file mode 100644
index 0000000000..401d9b7347
--- /dev/null
+++ b/libical/src/libicalss/icalgauge.h
@@ -0,0 +1,37 @@
+/* -*- Mode: C -*- */
+/*======================================================================
+ FILE: icalgauge.h
+ CREATOR: eric 23 December 1999
+
+
+ $Id$
+ $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
+
+
+======================================================================*/
+
+#ifndef ICALGAUGE_H
+#define ICALGAUGE_H
+
+icalcomponent* icalgauge_new_from_vquery(char* vquery);
+char* icalgauge_as_vquery(icalcomponent* gauge);
+int icalgauge_test(icalcomponent* comp, icalcomponent* gaugecontainer);
+
+
+#endif /* ICALGAUGE_H*/
diff --git a/libical/src/libicalss/icalset.c b/libical/src/libicalss/icalset.c
new file mode 100644
index 0000000000..01a36c0129
--- /dev/null
+++ b/libical/src/libicalss/icalset.c
@@ -0,0 +1,86 @@
+/* -*- Mode: C -*- */
+/*======================================================================
+ FILE: icalset.c
+ CREATOR: eric 17 Jul 2000
+
+
+ Icalset is the "base class" for representations of a collection of
+ iCal components. Derived classes (actually delegatees) include:
+
+ icalfileset Store componetns in a single file
+ icaldirset Store components in multiple files in a directory
+ icalheapset Store components on the heap
+ icalmysqlset Store components in a mysql database.
+
+ $Id$
+ $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 "ical.h"
+#include "icalset.h"
+#include "icalfileset.h"
+#include "icaldirset.h"
+/*#include "icalheapset.h"*/
+/*#include "icalmysqlset.h"*/
+
+icalset* icalset_new_file(char* path);
+
+icalset* icalset_new_dir(char* path);
+
+icalset* icalset_new_heap(void);
+
+icalset* icalset_new_mysql(char* path);
+
+void icalset_free(icalset* set);
+
+char* icalset_path(icalset* set);
+
+void icalset_mark(icalset* set);
+
+icalerrorenum icalset_commit(icalset* set);
+
+icalerrorenum icalset_add_component(icalset* set, icalcomponent* comp);
+
+icalerrorenum icalset_remove_component(icalset* set, icalcomponent* comp);
+
+int icalset_count_components(icalset* set,
+ icalcomponent_kind kind);
+
+icalerrorenum icalset_select(icalset* set, icalcomponent* gauge);
+
+void icalset_clear_select(icalset* set);
+
+icalcomponent* icalset_fetch(icalset* set, char* uid);
+
+int icalset_has_uid(icalset* set, char* uid);
+
+icalerrorenum icalset_modify(icalset* set, icalcomponent *old,
+ icalcomponent *new);
+
+icalcomponent* icalset_get_current_component(icalset* set);
+
+icalcomponent* icalset_get_first_component(icalset* set);
+
+icalcomponent* icalset_get_next_component(icalset* set);
+
+
+
+
diff --git a/libical/src/libicalss/icalset.h b/libical/src/libicalss/icalset.h
new file mode 100644
index 0000000000..15bb71f72e
--- /dev/null
+++ b/libical/src/libicalss/icalset.h
@@ -0,0 +1,100 @@
+/* -*- Mode: C -*- */
+/*======================================================================
+ FILE: icalset.h
+ CREATOR: eric 28 November 1999
+
+
+ Icalset is the "base class" for representations of a collection of
+ iCal components. Derived classes (actually delegatees) include:
+
+ icalfileset Store componetns in a single file
+ icaldirset Store components in multiple files in a directory
+ icalheapset Store components on the heap
+ icalmysqlset Store components in a mysql database.
+
+ $Id$
+ $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
+
+
+======================================================================*/
+
+#ifndef ICALSET_H
+#define ICALSET_H
+
+#include "ical.h"
+#include "icalerror.h"
+
+typedef void icalset;
+
+typedef enum icalset_kind {
+ ICAL_FILE_SET,
+ ICAL_DIR_SET,
+ ICAL_HEAP_SET,
+ ICAL_MYSQL_SET,
+ ICAL_CAP_SET
+} icalset_kind;
+
+
+/* Create a specific derived type of set */
+icalset* icalset_new_file(char* path);
+icalset* icalset_new_dir(char* path);
+icalset* icalset_new_heap(void);
+icalset* icalset_new_mysql(char* path);
+/*icalset* icalset_new_cap(icalcstp* cstp);*/
+
+void icalset_free(icalset* set);
+
+char* icalset_path(icalset* set);
+
+/* Mark the cluster as changed, so it will be written to disk when it
+ is freed. Commit writes to disk immediately*/
+void icalset_mark(icalset* set);
+icalerrorenum icalset_commit(icalset* set);
+
+icalerrorenum icalset_add_component(icalset* set, icalcomponent* comp);
+icalerrorenum icalset_remove_component(icalset* set, icalcomponent* comp);
+
+int icalset_count_components(icalset* set,
+ icalcomponent_kind kind);
+
+/* Restrict the component returned by icalset_first, _next to those
+ that pass the gauge. _clear removes the gauge. */
+icalerrorenum icalset_select(icalset* set, icalcomponent* gauge);
+void icalset_clear_select(icalset* set);
+
+/* Get a component by uid */
+icalcomponent* icalset_fetch(icalset* set, char* uid);
+int icalset_has_uid(icalset* set, char* uid);
+
+/* Modify components according to the MODIFY method of CAP. Works on
+ the currently selected components. */
+icalerrorenum icalset_modify(icalset* set, icalcomponent *old,
+ icalcomponent *new);
+
+/* Iterate through the components. If a guage has been defined, these
+ will skip over components that do not pass the gauge */
+
+icalcomponent* icalset_get_current_component(icalset* set);
+icalcomponent* icalset_get_first_component(icalset* set);
+icalcomponent* icalset_get_next_component(icalset* set);
+
+#endif /* !ICALSET_H */
+
+
+
diff --git a/libical/src/libicalvcal/Makefile.am b/libical/src/libicalvcal/Makefile.am
new file mode 100644
index 0000000000..5e773c5d69
--- /dev/null
+++ b/libical/src/libicalvcal/Makefile.am
@@ -0,0 +1,17 @@
+
+lib_LIBRARIES = libicalvcal.a
+
+libicalvcal_a_SOURCES = \
+ vcc.y \
+ vcc.h \
+ vobject.c \
+ vobject.h \
+ port.h \
+ vcaltmp.c \
+ vcaltmp.h \
+ icalvcal.c \
+ icalvcal.h
+
+EXTRA_DIST = README.TXT vcaltest.c vctest.c
+
+INCLUDES = -I../libical/ \ No newline at end of file
diff --git a/libical/src/libicalvcal/Makefile.in b/libical/src/libicalvcal/Makefile.in
new file mode 100644
index 0000000000..e0cb416de6
--- /dev/null
+++ b/libical/src/libicalvcal/Makefile.in
@@ -0,0 +1,309 @@
+# Makefile.in generated automatically by automake 1.4 from Makefile.am
+
+# Copyright (C) 1994, 1995-8, 1999 Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+
+SHELL = @SHELL@
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+
+bindir = @bindir@
+sbindir = @sbindir@
+libexecdir = @libexecdir@
+datadir = @datadir@
+sysconfdir = @sysconfdir@
+sharedstatedir = @sharedstatedir@
+localstatedir = @localstatedir@
+libdir = @libdir@
+infodir = @infodir@
+mandir = @mandir@
+includedir = @includedir@
+oldincludedir = /usr/include
+
+DESTDIR =
+
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+
+top_builddir = ../..
+
+ACLOCAL = @ACLOCAL@
+AUTOCONF = @AUTOCONF@
+AUTOMAKE = @AUTOMAKE@
+AUTOHEADER = @AUTOHEADER@
+
+INSTALL = @INSTALL@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@ $(AM_INSTALL_PROGRAM_FLAGS)
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+transform = @program_transform_name@
+
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+AR = @AR@
+CC = @CC@
+LEX = @LEX@
+LN_S = @LN_S@
+MAKEINFO = @MAKEINFO@
+PACKAGE = @PACKAGE@
+RANLIB = @RANLIB@
+VERSION = @VERSION@
+YACC = @YACC@
+
+lib_LIBRARIES = libicalvcal.a
+
+libicalvcal_a_SOURCES = vcc.y vcc.h vobject.c vobject.h port.h vcaltmp.c vcaltmp.h icalvcal.c icalvcal.h
+
+
+EXTRA_DIST = README.TXT vcaltest.c vctest.c
+
+INCLUDES = -I../libical/
+mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
+CONFIG_HEADER = ../../config.h
+CONFIG_CLEAN_FILES =
+LIBRARIES = $(lib_LIBRARIES)
+
+
+DEFS = @DEFS@ -I. -I$(srcdir) -I../..
+CPPFLAGS = @CPPFLAGS@
+LDFLAGS = @LDFLAGS@
+LIBS = @LIBS@
+libicalvcal_a_LIBADD =
+libicalvcal_a_OBJECTS = vcc.o vobject.o vcaltmp.o icalvcal.o
+CFLAGS = @CFLAGS@
+COMPILE = $(CC) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(LDFLAGS) -o $@
+DIST_COMMON = Makefile.am Makefile.in vcc.c
+
+
+DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) $(TEXINFOS) $(EXTRA_DIST)
+
+TAR = gtar
+GZIP_ENV = --best
+SOURCES = $(libicalvcal_a_SOURCES)
+OBJECTS = $(libicalvcal_a_OBJECTS)
+
+all: all-redirect
+.SUFFIXES:
+.SUFFIXES: .S .c .o .s .y
+$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4)
+ cd $(top_srcdir) && $(AUTOMAKE) --gnu --include-deps src/libicalvcal/Makefile
+
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ cd $(top_builddir) \
+ && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= $(SHELL) ./config.status
+
+
+mostlyclean-libLIBRARIES:
+
+clean-libLIBRARIES:
+ -test -z "$(lib_LIBRARIES)" || rm -f $(lib_LIBRARIES)
+
+distclean-libLIBRARIES:
+
+maintainer-clean-libLIBRARIES:
+
+install-libLIBRARIES: $(lib_LIBRARIES)
+ @$(NORMAL_INSTALL)
+ $(mkinstalldirs) $(DESTDIR)$(libdir)
+ @list='$(lib_LIBRARIES)'; for p in $$list; do \
+ if test -f $$p; then \
+ echo " $(INSTALL_DATA) $$p $(DESTDIR)$(libdir)/$$p"; \
+ $(INSTALL_DATA) $$p $(DESTDIR)$(libdir)/$$p; \
+ else :; fi; \
+ done
+ @$(POST_INSTALL)
+ @list='$(lib_LIBRARIES)'; for p in $$list; do \
+ if test -f $$p; then \
+ echo " $(RANLIB) $(DESTDIR)$(libdir)/$$p"; \
+ $(RANLIB) $(DESTDIR)$(libdir)/$$p; \
+ else :; fi; \
+ done
+
+uninstall-libLIBRARIES:
+ @$(NORMAL_UNINSTALL)
+ list='$(lib_LIBRARIES)'; for p in $$list; do \
+ rm -f $(DESTDIR)$(libdir)/$$p; \
+ done
+
+.c.o:
+ $(COMPILE) -c $<
+
+.s.o:
+ $(COMPILE) -c $<
+
+.S.o:
+ $(COMPILE) -c $<
+
+mostlyclean-compile:
+ -rm -f *.o core *.core
+
+clean-compile:
+
+distclean-compile:
+ -rm -f *.tab.c
+
+maintainer-clean-compile:
+
+libicalvcal.a: $(libicalvcal_a_OBJECTS) $(libicalvcal_a_DEPENDENCIES)
+ -rm -f libicalvcal.a
+ $(AR) cru libicalvcal.a $(libicalvcal_a_OBJECTS) $(libicalvcal_a_LIBADD)
+ $(RANLIB) libicalvcal.a
+.y.c:
+ $(YACC) $(AM_YFLAGS) $(YFLAGS) $< && mv y.tab.c $*.c
+ if test -f y.tab.h; then \
+ if cmp -s y.tab.h $*.h; then rm -f y.tab.h; else mv y.tab.h $*.h; fi; \
+ else :; fi
+vcc.h: vcc.c
+
+
+tags: TAGS
+
+ID: $(HEADERS) $(SOURCES) $(LISP)
+ list='$(SOURCES) $(HEADERS)'; \
+ unique=`for i in $$list; do echo $$i; done | \
+ awk ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ here=`pwd` && cd $(srcdir) \
+ && mkid -f$$here/ID $$unique $(LISP)
+
+TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS)'; \
+ unique=`for i in $$list; do echo $$i; done | \
+ awk ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ test -z "$(ETAGS_ARGS)$$unique$(LISP)$$tags" \
+ || (cd $(srcdir) && etags $(ETAGS_ARGS) $$tags $$unique $(LISP) -o $$here/TAGS)
+
+mostlyclean-tags:
+
+clean-tags:
+
+distclean-tags:
+ -rm -f TAGS ID
+
+maintainer-clean-tags:
+
+distdir = $(top_builddir)/$(PACKAGE)-$(VERSION)/$(subdir)
+
+subdir = src/libicalvcal
+
+distdir: $(DISTFILES)
+ @for file in $(DISTFILES); do \
+ d=$(srcdir); \
+ if test -d $$d/$$file; then \
+ cp -pr $$d/$$file $(distdir)/$$file; \
+ else \
+ test -f $(distdir)/$$file \
+ || ln $$d/$$file $(distdir)/$$file 2> /dev/null \
+ || cp -p $$d/$$file $(distdir)/$$file || :; \
+ fi; \
+ done
+icalvcal.o: icalvcal.c icalvcal.h ../libical/ical.h \
+ ../libical/icalversion.h ../libical/icalenums.h \
+ ../libical/icalvalue.h ../libical/icaltypes.h \
+ ../libical/icaltime.h ../libical/icalparameter.h \
+ ../libical/icalproperty.h ../libical/icalcomponent.h \
+ ../libical/pvl.h ../libical/icalparser.h \
+ ../libical/icalmemory.h ../libical/icalerror.h \
+ ../libical/icalrestriction.h ../libical/icalrecur.h vcc.h \
+ vobject.h port.h
+vcaltmp.o: vcaltmp.c vcaltmp.h vcc.h vobject.h port.h
+vcc.o: vcc.c vcc.h vobject.h port.h
+vobject.o: vobject.c vobject.h port.h
+
+info-am:
+info: info-am
+dvi-am:
+dvi: dvi-am
+check-am: all-am
+check: check-am
+installcheck-am:
+installcheck: installcheck-am
+install-exec-am: install-libLIBRARIES
+install-exec: install-exec-am
+
+install-data-am:
+install-data: install-data-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+install: install-am
+uninstall-am: uninstall-libLIBRARIES
+uninstall: uninstall-am
+all-am: Makefile $(LIBRARIES)
+all-redirect: all-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) AM_INSTALL_PROGRAM_FLAGS=-s install
+installdirs:
+ $(mkinstalldirs) $(DESTDIR)$(libdir)
+
+
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -rm -f Makefile $(CONFIG_CLEAN_FILES)
+ -rm -f config.cache config.log stamp-h stamp-h[0-9]*
+
+maintainer-clean-generic:
+ -test -z "vcchvccc" || rm -f vcch vccc
+mostlyclean-am: mostlyclean-libLIBRARIES mostlyclean-compile \
+ mostlyclean-tags mostlyclean-generic
+
+mostlyclean: mostlyclean-am
+
+clean-am: clean-libLIBRARIES clean-compile clean-tags clean-generic \
+ mostlyclean-am
+
+clean: clean-am
+
+distclean-am: distclean-libLIBRARIES distclean-compile distclean-tags \
+ distclean-generic clean-am
+
+distclean: distclean-am
+
+maintainer-clean-am: maintainer-clean-libLIBRARIES \
+ maintainer-clean-compile maintainer-clean-tags \
+ maintainer-clean-generic distclean-am
+ @echo "This command is intended for maintainers to use;"
+ @echo "it deletes files that may require special tools to rebuild."
+
+maintainer-clean: maintainer-clean-am
+
+.PHONY: mostlyclean-libLIBRARIES distclean-libLIBRARIES \
+clean-libLIBRARIES maintainer-clean-libLIBRARIES uninstall-libLIBRARIES \
+install-libLIBRARIES mostlyclean-compile distclean-compile \
+clean-compile maintainer-clean-compile tags mostlyclean-tags \
+distclean-tags clean-tags maintainer-clean-tags distdir info-am info \
+dvi-am dvi check check-am installcheck-am installcheck install-exec-am \
+install-exec install-data-am install-data install-am install \
+uninstall-am uninstall all-redirect all-am all installdirs \
+mostlyclean-generic distclean-generic clean-generic \
+maintainer-clean-generic clean mostlyclean distclean maintainer-clean
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/libical/src/libicalvcal/README.TXT b/libical/src/libicalvcal/README.TXT
new file mode 100644
index 0000000000..c8ce8b0979
--- /dev/null
+++ b/libical/src/libicalvcal/README.TXT
@@ -0,0 +1,951 @@
+NOTE: If you used the earlier APIs released by Versit
+then you will want to look at the document "migrate.doc"
+included with this package. It contains a discussion of
+the differences between the old API and this one.
+
+----------------------------------------------------------------
+
+The vCard/vCalendar C interface is implemented in the set
+of files as follows:
+
+vcc.y, yacc source, and vcc.c, the yacc output you will use
+implements the core parser
+
+vobject.c implements an API that insulates the caller from
+the parser and changes in the vCard/vCalendar BNF
+
+port.h defines compilation environment dependent stuff
+
+vcc.h and vobject.h are header files for their .c counterparts
+
+vcaltmp.h and vcaltmp.c implement vCalendar "macro" functions
+which you may find useful.
+
+test.c is a standalone test driver that exercises some of
+the features of the APIs provided. Invoke test.exe on a
+VCARD/VCALENDAR input text file and you will see the pretty
+print output of the internal representation (this pretty print
+output should give you a good idea of how the internal
+representation looks like -- there is one such output in the
+following too). Also, a file with the .out suffix is generated
+to show that the internal representation can be written back
+in the original text format.
+
+-----------------------------------------------------------------
+
+
+ VObject for VCard/VCalendar
+
+Table of Contents
+=================
+1. VObject
+2. Internal Representations of VCard/VCalendar
+3. Iterating Through VObject's Properties or Values
+4. Pretty Printing a VObject Tree
+5. Building A VObject Representation of A VCard/VCalendar
+6. Converting A VObject Representation Into Its Textual Representation
+7. Miscellaneous Notes On VObject APIs usages
+8. Brief descriptions of each APIs
+9. Additional Programming Notes.
+
+This document is mainly about the VObject and its APIs. The main
+use of a VObject is to represent a VCard or a VCalendar inside
+a program. However, its use is not limited to aforemention as it
+can represent an arbitrary information that makes up of a tree or
+forest of properties/values.
+
+1. VObject
+ =======
+A VObject can have a name (id) and a list of associated properties and
+a value. Each property is itself a VObject.
+
+2. Internal Representations of VCard/VCalendar
+ ===========================================
+A list of VCard or a VCalendar is represented by a list of VObjects.
+The name (id) of the VObjects in the list is either VCCardProp or
+VCCalProp. Each of these VObjects can have a list of properties.
+Since a property is represented as a VObject, each of these properties
+can have a name, a list of properties, and a value.
+
+For example, the input file "vobject.vcf":
+
+BEGIN:VCARD
+N:Alden;Roland
+FN:Roland H. Alden
+ORG:AT&T;Versit Project Office
+TITLE:Consultant
+EMAIL;WORK;PREF;INTERNET:sf!rincon!ralden@alden.attmail.com
+EMAIL;INTERNET:ralden@sfgate.com
+EMAIL;MCIMail:242-2200
+LABEL;DOM;POSTAL;PARCEL;HOME;WORK;QUOTED-PRINTABLE:Roland H. Alden=0A=
+Suite 2208=0A=
+One Pine Street=0A=
+San Francisco, CA 94111
+LABEL;POSTAL;PARCEL;HOME;WORK;QUOTED-PRINTABLE:Roland H. Alden=0A=
+Suite 2208=0A=
+One Pine Street=0A=
+San Francisco, CA 94111=0A=
+U.S.A.
+TEL;WORK;PREF;MSG:+1 415 296 9106
+TEL;WORK;FAX:+1 415 296 9016
+TEL;MSG;CELL:+1 415 608 5981
+ADR:;Suite 2208;One Pine Street;San Francisco;CA;94111;U.S.A.
+SOUND:ROW-LAND H ALL-DIN
+LOGO;GIF;BASE64:
+ R0lGODdhpgBOAMQAAP///+/v797e3s7Ozr29va2trZycnIyMjHt7e2NjY1JSUkJC
+ QjExMSEhIRAQEO///87v9973/73n95zW71K13jGl1nvG50Kt3iGc1gCMzq3e94zO
+ 7xCU1nO952O15wAAACwAAAAApgBOAAAF/yAgjmRpnmiqrmzrvnAsz3Rt33iu73zv
+ /8CgcEj8QTaeywWTyWCUno2kSK0KI5tLc8vtNi+WiHVMlj0mFK96nalsxOW4fPSw
+ cNj4tQc+7xcjGh4WExJTJYUTFkp3eU0eEH6RkpOUlTARhRoWm5ydFpCWoS0QEqAu
+ ARKaHRcVjV0borEoFl0cSre4Sq67FA+yvwAeTU8XHZ7HmxS6u2wVfMCVpAE3pJoW
+ ylrMptDcOqSF4OHg3eQ5pInInb7lcc86mNbLzBXsZbRfUOn6ucyNHvVWJHCpQFDf
+ MWwEEzLqx2YCQCqF3OnItClJNmYcJD7cSAKTuI/gtnEcOQKkyVIk6/+ds5CkFcMM
+ 61LiENikwi1jBnNyuvUSjwWZOS5uIZarqNFcNl32XMMB6I06GgoJ+bZp1ZKeDl8E
+ +MC1K1cBIhZ4HUu2LAsCZdOWRQDt20lxIlccSHsgrNq7Xc/ixcsWmNu34WKyYJCW
+ gQjCe9XqTZy2L4pv04gg2sSKSc8OLgTcBSuWsdkVaD2TdXyiQxebFyjo1Gnx6tJm
+ LuaqrdtZtNfFtruSNmF5IKujwIsmJbjwtRqNJhrcNVw79wcRAgogmE4ArIjQzj/s
+ JvHAGCFDQR4UqigPK4sBe62XwO51OwADiMcqUG+iOdcFAL+hW20BfAoEexlwAnu6
+ mZDAXQ1EVh//WfhxJB5gIbHgwFgOTOiVAgOuVQKAfKFg3weGwSBYFZMp4hpDGKyA
+ 3lgJKECWgiMQyBVpW+0V4oJjNfhCNkR1IgWEb21QlRK9GdfFCgeOZYBsXgm4noYj
+ GEBhAQHYh0J8XenoQnFGdrkUciJY6FUAK15ogozakcBhliKsyZWHDMZQ0wWC/Aim
+ DB6h01KRr/lXQgFxAqDcWDACgCZpUnrVQJtjwTnWjS6MWAYqqfDnSaEkJOlVXQBo
+ 2pWTMUJ53WgAuPncCR9q6VQMAYjZlXWJmknCoSUM2p4BC+SaKwG88hoZlvfFMM4f
+ hQh5TXkv+RklWYtC91mopJIAKFkJlDAW/wF25ShnLbeo5gmQ+1FGkJdrKCuCi2OR
+ BuwHBcwqKgABrMtVAgpem61XkLbAJ7n8uiIpvGVhO4KpH1QLbbpqLheZvQCkGoNL
+ thSzSTg2UGVBBzbtaxwKsYrmgLvRAlCmWgwMAADD66rKAgR3XlGspcdkZYK8ibU7
+ asgEl+XAyB8I7PCqMWiWncGGimpfAgO4ypXSPpOVLwsRCDJxRD2AoyeRRv5kApO5
+ fXwzwvfOKLKtaTWtbQxccmGLTZy8xYlVSvXbhbk0M2YzrYfJJ0K8m+V9NgxpyC04
+ UycI/aiuiH9Y8NftDUwWp1Wm5UABnAUKwwRsPFGBt4Oc9PZvGvNLwf8JOZt8Arpe
+ eY23yDovwIDiBX74NAsPVLDJj3Hh4JEExsKcjrlKf9DsCVx3ZfLqAKBuG1s/A90C
+ z2KjYHjjyPOdG1spz6BBUr+BcUxUb1nDCTa/VZD2Uv+YkLPAKJC9dNEh7628WgqI
+ ybzlaA+ufxMa6bxC6ciLUQLcx5UGIAAsAkDA6wQkOxrcY39yo4cQMNWCAPTKV1R4
+ wPkgaBxzOc8FtMiF1NoGoXBRJjgoPApmPsjCFlbMdzCM4TFy50IXxI2DPcHAv2rY
+ gghsEIeu8CAPW6ABIPYEFkOsAeaMyIz0JfGJUExBBGRIRX0IMYovWCIT1eBELNpA
+ i1vcgta8iANPCIQOghzQABl30J0tXqBla4wjFLFQxZzAUY42CIAd5OYBCuKxB2c4
+ I0b28EcrQKADgmSKB9RYyDhA4BqCxIBqrtjIMTwoFeCjYSU3KZMQAAA7
+
+BEGIN:VCALENDAR
+DCREATED:19960523T100522
+PRODID:-//Alden Roland/Hand Crafted In North Carolina//NONSGML Made By Hand//EN
+VERSION:0.3
+BEGIN:VEVENT
+START:19960523T120000
+END:19960523T130000
+SUBTYPE:PHONE CALL
+SUMMARY:VERSIT PDI PR Teleconference/Interview
+DESCRIPTION:VERSIT PDI PR Teleconference/Interview With Tom Streeter and Alden Roland
+END:VEVENT
+BEGIN:VEVENT
+START:19960523T113000
+END:19960523T115500
+SUBTYPE:LUNCH
+SUMMARY:Eat in the cafeteria today
+END:VEVENT
+END:VCALENDAR
+
+END:VCARD
+
+
+will conceptually be be represented as
+ vcard
+ VCNameProp
+ VCFamilyNameProp=Alden
+ VCGivenNameProp=Roland
+ VCFullNameProp=Roland H.Alden
+ ....
+
+note that
+ EMAIL;WORK;PREF;INTERNET:sf!rincon!ralden@alden.attmail.com
+will be represented as:
+ VCEmailAddress=sf!rincon!ralden@alden.attmail.com
+ VCWork
+ VCPreferred
+ VCInternet
+where the lower level properties are properties of the property
+VCEmailAddress.
+
+Groupings are flattened out in the VObject representation such
+that:
+ a.b:blah
+ a.c:blahblah
+are represented as:
+ b=blah
+ VCGrouping=a
+ c=blahblah
+ VCGrouping=a
+i.e. one can read the above as:
+ the property "b" has value "blah" and property "VCGrouping"
+ with the value "a".
+ the property "c" has value "blahblah" and property "VCGrouping"
+ with the value "a".
+likewise, multi-level groupings are flatten similarly. e.g.
+ a.b.c:blah
+ a.b.e:blahblah
+-->
+ c=blah
+ VCGrouping=b
+ VCGrouping=a
+ e=blahblah
+ VCGrouping=b
+ VCGrouping=a
+which read:
+ the property "c" has value "blah" and property "VCGrouping"
+ with the value "b" which has property "VCGrouping"
+ with value "a".
+ the property "e" has value "blahblah" and property "VCGrouping"
+ with the value "b" which has property "VCGrouping"
+ with value "a".
+
+3. Iterating Through VObject's Properties or Values
+ ================================================
+The following is a skeletal form of iterating through
+all properties of a vobject, o:
+
+ // assume the object of interest, o, is of type VObject
+ VObjectIterator i;
+ initPropIterator(&i,o);
+ while (moreIteration(&i)) {
+ VObject *each = nextVObject(&i);
+ // ... do something with "each" property
+ }
+
+Use the API vObjectName() to access a VObject's name.
+Use the API vObjectValueType() to determine if a VObject has
+ a value. For VCard/VCalendar application, you
+ should not need this function as practically
+ all values are either of type VCVT_USTRINGZ or
+ VCVT_RAW (i.e set by setVObjectUStringZValue and
+ setVObjectAnyValue APIs respectively), and the
+ value returned by calls to vObjectUStringZValue
+ and vObjectAnyValue are 0 if a VObject has no
+ value. (There is a minor exception where VObject with
+ VCDataSizeProp has value that is set by
+ setVObjectLongValue).
+Use the APIs vObject???Value() to access a VObject's value.
+ where ??? is the expected type.
+Use the APIs setvObject???Value() to set or modify a VObject's value.
+ where ??? is the expected type.
+Use the API isAPropertyOf() to query if a name match the name of
+ a property of a VObject. Since isAPropertyOf() return
+ the matching property, we can use that to retrieve
+ a property and subsequently the value of the property.
+
+4. Pretty Printing a VObject Tree
+ ==============================
+VObject tree can be pretty printed with the printVObject() function.
+The output of pretty printing a VObject representation of the input
+test file "vobject.vcf" is shown below. Note that the indentation
+indicates the tree hirerarchy where the immediate children nodes
+of a parent node is all at the same indentation level and the
+immediate children nodes are the immediate properties of the
+associated parent nodes. In the following, {N,FN,ORG,TITLE,...}
+are immediate properties of VCARD. {F and G} are properties of N
+with value {"Alden" and "Roland"} respectively; FN has no property
+but has the value "Roland H. Alden"; EMAIL has value and
+the properties WORK, PREF, and INTERNET.
+
+
+VCARD
+ N
+ F="Alden"
+ G="Roland"
+ FN="Roland H. Alden"
+ ORG
+ ORGNAME="AT&T"
+ OUN="Versit Project Office"
+ TITLE="Consultant"
+ EMAIL="sf!rincon!ralden@alden.attmail.com"
+ WORK
+ PREF
+ INTERNET
+ EMAIL="ralden@sfgate.com"
+ INTERNET
+ EMAIL="242-2200"
+ MCIMail
+ LABEL="Roland H. Alden
+ Suite 2208
+ One Pine Street
+ San Francisco, CA 94111"
+ DOM
+ POSTAL
+ PARCEL
+ HOME
+ WORK
+ QP
+ LABEL="Roland H. Alden
+ Suite 2208
+ One Pine Street
+ San Francisco, CA 94111
+ U.S.A."
+ POSTAL
+ PARCEL
+ HOME
+ WORK
+ QP
+ TEL="+1 415 296 9106"
+ WORK
+ PREF
+ MSG
+ TEL="+1 415 296 9016"
+ WORK
+ FAX
+ TEL="+1 415 608 5981"
+ MSG
+ CELL
+ ADR
+ EXT ADD="Suite 2208"
+ STREET="One Pine Street"
+ L="San Francisco"
+ R="CA"
+ PC="94111"
+ C="U.S.A."
+ SOUND="ROW-LAND H ALL-DIN"
+ LOGO=[raw data]
+ GIF
+ BASE64
+ DataSize=1482
+VCALENDAR
+ DCREATED="19960523T100522"
+ PRODID="-//Alden Roland/Hand Crafted In North Carolina//NONSGML Made By Hand//EN"
+ VERSION="0.3"
+ VEVENT
+ START="19960523T120000"
+ END="19960523T130000"
+ SUBTYPE="PHONE CALL"
+ SUMMARY="VERSIT PDI PR Teleconference/Interview"
+ DESCRIPTION="VERSIT PDI PR Teleconference/Interview With Tom Streeter and Alden Roland"
+ VEVENT
+ START="19960523T113000"
+ END="19960523T115500"
+ SUBTYPE="LUNCH"
+ SUMMARY="Eat in the cafeteria today"
+
+5. Building A VObject Representation of A VCard/VCalendar
+ ======================================================
+The parser in vcc.y converts an input file with one or more
+VCard/VCalendar that is in their textual representation
+into their corresponding VObject representation.
+
+VObject representation of a VCard/VCalendar can also be built
+directly with calls to the VObject building APIs. e.g.
+
+ VObject *prop;
+ VObject *vcard = newVObject(VCCardProp);
+ prop = addProp(vcard,VCNameProp);
+ addPropValue(prop,VCFamilyNameProp,"Alden");
+ addPropValue(prop,VCGivenNameProp,"Roland");
+ addPropValue(vcard,VCFullNameProp,"Roland H. Alden");
+ ....
+
+6. Converting A VObject Representation Into Its Textual Representation
+ ===================================================================
+The VObject representation can be converted back to its textual
+representation via the call to writeVObject() or writeMemVObject()
+API. e.g.
+ a. to write to a file:
+ // assume vcard is of type VObject
+ FILE *fp = fopen("alden.vcf","w");
+ writeVObject(fp,vcard);
+ a. to write to memory, and let the API allocate the required memory.
+ char* clipboard = writeVObject(0,0,vcard);
+ ... do something to clipboard
+ free(clipboard);
+ b. to write to a user allocated buffer:
+ char clipboard[16384];
+ int len = 16384;
+ char *buf = writeVObject(clipboard,&len,vcard);
+ ... buf will be equal to clipboard if the write
+ is successful otherwise 0.
+
+In the case of writing to memory, the memory buffer can be either
+allocated by the API or the user. If the user allocate the
+memory for the buffer, then the length of the buffer needs to be
+communicated to the API via a variable. The variable passed as
+the length argument will be overwritten with the actual size
+of the text output. A 0 return value from writeMemVObject()
+indicates an error which could be caused by overflowing the
+size of the buffer or lack of heap memory.
+
+7. Miscellaneous Notes On VObject APIs usages
+ ==========================================
+a. vcc.h -- contains basic interfaces to the parser:
+ VObject* Parse_MIME(const char *input, unsigned long len);
+ VObject* Parse_MIME_FromFile(FILE *file);
+ -- both of this return a null-terminated list of
+ VObject that is either a VCARD or VCALENDAR.
+ To iterate through this list, do
+ VObject *t, *v;
+ v = Parse_Mime_FromFile(fp);
+ while (v) {
+ // ... do something to v.
+ t = v;
+ v = nextVObjectInList(v);
+ cleanVObject(t);
+ }
+ note that call to cleanVObject will release
+ resource used to represent the VObject.
+
+b. vobject.h -- contains basic interfaces to the VObject APIs.
+ see the header for more details.
+ The structure of VObject is purposely (hiddened) not exposed
+ to the user. Every access has to be done via
+ the APIs. This way, if we need to change the
+ structure or implementation, the client need not
+ recompile as long as the interfaces remain the
+ same.
+
+c. values of a property is determined by the property definition
+ itself. The vobject APIs does not attempt to enforce
+ any of such definition. It is the consumer responsibility
+ to know what value is expected from a property. e.g
+ most properties have unicode string value, so to access
+ the value of these type of properties, you will use
+ the vObjectUStringZValue() to read the value and
+ setVObjectUStringZValue() to set or modify the value.
+ Refer to the VCard and VCalendar specifications for
+ the definition of each property.
+
+d. properties name (id) are case incensitive.
+
+8. Brief descriptions of each APIs
+ ===============================
+ * the predefined properties' names (id) are listed under vobject.h
+ each is of the form VC*Prop. e.g.
+ #define VC7bitProp "7BIT"
+ #define VCAAlarmProp "AALARM"
+ ....
+
+ * consumer of a VObject can only define pointers to VObject.
+
+ * a variable of type VObjectIterator, say "i", can be used to iterate
+ through a VObject's properties, say "o". The APIs related to
+ VObjectIterator are:
+ void initPropIterator(VObjectIterator *i, VObject *o);
+ -- e.g. usage
+ initPropIterator(&i,o);
+ int moreIteration(VObjectIterator *i);
+ -- e.g. usage
+ while (moreIteration(&i)) { ... }
+ VObject* nextVObject(VObjectIterator *i);
+ -- e.g. usage
+ while (moreIteration(&i)) {
+ VObject *each = nextVObject(&i);
+ }
+
+ * VObject can be chained together to form a list. e.g. of such
+ use is in the parser where the return value of the parser is
+ a link list of VObject. A link list of VObject can be
+ built by:
+ void addList(VObject **o, VObject *p);
+ and iterated by
+ VObject* nextVObjectInList(VObject *o);
+ -- next VObjectInList return 0 if the list
+ is exhausted.
+
+ * the following APIs are mainly used to construct a VObject tree:
+ VObject* newVObject(const char *id);
+ -- used extensively internally by VObject APIs but when
+ used externally, its use is mainly limited to the
+ construction of top level object (e.g. an object
+ with VCCardProp or VCCalendarProp id).
+
+ void deleteVObject(VObject *p);
+ -- to deallocate single VObject, for most user, use
+ cleanVObject(VObject *o) instead for freeing all
+ resources associated with the VObject.
+
+ char* dupStr(const char *s, unsigned int size);
+ -- duplicate a string s. If size is 0, the string is
+ assume to be a null-terminated.
+
+ void deleteStr(const char *p);
+ -- used to deallocate a string allocated by dupStr();
+
+ void setVObjectName(VObject *o, const char* id);
+ -- set the id of VObject o. This function is not
+ normally used by the user. The setting of id
+ is normally done as part of other APIs (e.g.
+ addProp()).
+
+ void setVObjectStringZValue(VObject *o, const char *s);
+ -- set a string value of a VObject.
+
+ void setVObjectUStringZValue(VObject *o, const wchar_t *s);
+ -- set a Unicode string value of a VObject.
+
+ void setVObjectIntegerValue(VObject *o, unsigned int i);
+ -- set an integer value of a VObject.
+
+ void setVObjectLongValue(VObject *o, unsigned long l);
+ -- set an long integer value of a VObject.
+
+ void setVObjectAnyValue(VObject *o, void *t);
+ -- set any value of a VObject. The value type is
+ unspecified.
+
+ VObject* setValueWithSize(VObject *prop, void *val, unsigned int size);
+ -- set a raw data (stream of bytes) value of a VObject
+ whose size is size. The internal VObject representation
+ is
+ this object = val
+ VCDataSizeProp=size
+ i.e. the value val will be attached to the VObject prop
+ and a property of VCDataSize whose value is size
+ is also added to the object.
+
+ void setVObjectVObjectValue(VObject *o, VObject *p);
+ -- set a VObject as the value of another VObject.
+
+ const char* vObjectName(VObject *o);
+ -- retrieve the VObject's Name (i.e. id).
+
+ const char* vObjectStringZValue(VObject *o);
+ -- retrieve the VObject's value interpreted as
+ null-terminated string.
+
+ const wchar_t* vObjectUStringZValue(VObject *o);
+ -- retrieve the VObject's value interpreted as
+ null-terminated unicode string.
+
+ unsigned int vObjectIntegerValue(VObject *o);
+ -- retrieve the VObject's value interpreted as
+ integer.
+
+ unsigned long vObjectLongValue(VObject *o);
+ -- retrieve the VObject's value interpreted as
+ long integer.
+
+ void* vObjectAnyValue(VObject *o);
+ -- retrieve the VObject's value interpreted as
+ any value.
+
+ VObject* vObjectVObjectValue(VObject *o);
+ -- retrieve the VObject's value interpreted as
+ a VObject.
+
+ VObject* addVObjectProp(VObject *o, VObject *p);
+ -- add a VObject p as a property of VObject o.
+ (not normally used externally for building a
+ VObject).
+
+ VObject* addProp(VObject *o, const char *id);
+ -- add a property whose name is id to VObject o.
+
+ VObject* addPropValue(VObject *o, const char *id, const char *v);
+ -- add a property whose name is id and whose value
+ is a null-terminated string to VObject o.
+
+ VObject* addPropSizedValue(VObject *o, const char *id,
+ const char *v, unsigned int size);
+ -- add a property whose name is id and whose value
+ is a stream of bytes of size size, to VObject o.
+
+ VObject* addGroup(VObject *o, const char *g);
+ -- add a group g to VObject o.
+ e.g. if g is a.b.c, you will have
+ o
+ c
+ VCGroupingProp=b
+ VCGroupingProp=a
+ and the object c is returned.
+
+ VObject* isAPropertyOf(VObject *o, const char *id);
+ -- query if a property by the name id is in o and
+ return the VObject that represent that property.
+
+ void printVObject(VObject *o);
+ -- pretty print VObject o to stdout (for debugging use).
+
+ void writeVObject(FILE *fp, VObject *o);
+ -- convert VObject o to its textual representation and
+ write it to file.
+
+ char* writeMemVObject(char *s, int *len, VObject *o);
+ -- convert VObject o to its textual representation and
+ write it to memory. If s is 0, then memory required
+ to hold the textual representation will be allocated
+ by this API. If a variable len is passed, len will
+ be overwriten with the byte size of the textual
+ representation. If s is non-zero, then s has to
+ be a user allocated buffer whose size has be passed
+ in len as a variable. Memory allocated by the API
+ has to be freed with call to free. The return value
+ of this API is either the user supplied buffer,
+ the memory allocated by the API, or 0 (in case of
+ failure).
+
+ void cleanStrTbl();
+ -- this function has to be called when all
+ VObject has been destroyed.
+
+ void cleanVObject(VObject *o);
+ -- release all resources used by VObject o.
+
+ wchar_t* fakeUnicode(const char *ps, int *bytes);
+ -- convert char* to wchar_t*.
+
+ extern int uStrLen(const wchar_t *u);
+ -- length of unicode u.
+
+ char *fakeCString(const wchar_t *u);
+ -- convert wchar_t to CString (blindly assumes that
+ this could be done).
+
+9. Additional Programming Notes
+ ============================
+In the following notes, please refers to the listing
+of Example.vcf and its VObject Representation
+(shown at the end of this section).
+
+* Handling the Return Value of the VCard/VCalendar Parser
+ The example input text file contains two root VObjects
+ (a VCalendar and a VCard). The output of the VCard/VCalendar
+ parser is a null-terminated list of VObjects. For this
+ particular input file, the list will have two VObjects.
+ The following shows a template for iterating through the
+ output of the Parser:
+
+ VObject *t, *v;
+ v = Parse_Mime_fromFileName("example.vcf");
+ while (v) {
+ // currently, v will either be a VCard or a VCalendar
+ // do whatever your application need to do to
+ // v here ...
+ t = v;
+ v = nextVObjectInList(v);
+ cleanVObject(t);
+ }
+
+* Iterating Through a VCard/VCalendar VObject
+ From the VObject APIs point of view, a VCard VObject
+ is the same as a VCalendar VObject. However, the application
+ needs to know what are in a VCard or a VCalendar.
+ For example, A VCalendar VObject can have VCDCreatedProp,
+ a VCGEOLocationProp, etc, and one or more VCEventProp and
+ or VCTodoProp. The VCEventProp and VCTodoProp can have
+ many properties of their own, which in turn could have
+ more properties (e.g. VCDAlarmProp can be a VCEventProp
+ VObject's property, and VCRunTimeProp can be a
+ VCDAlarmProp VObject's property. Because a VObject tree
+ can be arbitrarily complex, in general, to process all
+ properties and values of a VObject tree, a recursive walk
+ is desirable. An example recursive VObject tree walk
+ can be found in the vobject.c source lines for printVObject*
+ and writeVObject* APIs. Depending on what the application need
+ to do with a VCard or a VCalendar, a recursive walk
+ of the VObject tree may or may not be desirable. An example
+ template of a non-recursive walk is shown below:
+
+ void processVCardVCalendar(char *inputFile)
+ {
+ VObject *t, *v;
+ v = Parse_Mime_fromFileName(inputFile);
+ while (v) {
+ char *n = vObjectName(v);
+ if (strcmp(n,VCCardProp) == 0) {
+ do_VCard(v);
+ }
+ else if (strcmp(n,VCCalendarProp) == 0) {
+ do_VCalendar(v);
+ }
+ else {
+ // don't know how to handle anything else!
+ }
+ t = v;
+ v = nextVObjectInList(v);
+ cleanVObject(t);
+ }
+ }
+
+ void do_VCard(VObject *vcard)
+ {
+ VObjectIterator t;
+ initPropIterator(&t,vcard);
+ while (moreIteration(&t)) {
+ VObject *eachProp = nextVObject(&t);
+ // The primarly purpose of this example is to
+ // show how to iterate through a VCard VObject,
+ // it is not meant to be efficient at all.
+ char *n = vObjectName(eachProp);
+ if (strcmp(n,VCNameProp)==0) {
+ do_name(eachProp);
+ }
+ else if (strcmp(n,VCEmailProp)==0) {
+ do_email(eachProp);
+ }
+ else if (strcmp(n,VCLabelProp)==0) {
+ do_label(eachProp);
+ }
+ else if ....
+ }
+ }
+
+ void do_VCalendar(VObject *vcal)
+ {
+ VObjectIterator t;
+ initPropIterator(&t,vcard);
+ while (moreIteration(&t)) {
+ VObject *eachProp = nextVObject(&t);
+ // The primarly purpose of this example is to
+ // show how to iterate through a VCalendar VObject,
+ // it is not meant to be efficient at all.
+ char *n = vObjectName(eachProp);
+ if (strcmp(n,VCDCreatedProp)==0) {
+ do_DCreated(eachProp);
+ }
+ else if (strcmp(n,VCVersionProp)==0) {
+ do_Version(eachProp);
+ }
+ else if (strcmp(n,VCTodoProp)==0) {
+ do_Todo(eachProp);
+ }
+ else if (strcmp(n,VCEventProp)==0) {
+ do_Event(eachProp);
+ }
+ else if ....
+ }
+ }
+
+ void do_Todo(VObject *vtodo) { ... }
+
+ void do_Event(VObject *vevent) { ... }
+
+ ...
+
+* Property's Values and Properties
+ The VObject APIs do not attempt to check for the
+ correctness of the values of a property. Nor do they
+ will prevent the user from attaching a non-VCard/VCalendar
+ standard property to a VCard/VCalendar property. Take
+ the example of line [11] of the example, "O.K" is not
+ a valid value of VCStatusProp. It is up to the application
+ to accept or reject the value of a property.
+
+* Output of printVObject
+ PrintVObject pretty prints a VObject tree in human
+ readable form. See the listing at the end of the file
+ for an example output of printVObject on the example
+ input file "Example.vcf".
+
+ Note that binary data are not shown in the output of
+ printVObject. Instead, a note is made ([raw data]) to
+ indicate that there exists such a binary data.
+
+* Note on Binary Data
+ When the value of a property is a binary data, it is only
+ useful to know the size of the binary data.
+
+ In the case of the VCard/VCalendar parser, it chooses
+ to represent the size information as a separate property
+ called VCDataSizeProp whose value is the size of the binary
+ data. The APIs sequence to construct the VObject subtree
+ of line [44] of Example.vcf is
+
+ // VObject *vcard;
+ VObject *p1 = addProp(vcard,VCLogoProp);
+ (void) addProp(p1,VCGIFProp);
+ (void) addProp(p1,VCBASE64Prop);
+ VObject *p2 = addProp(p1,VCDataSizeProp);
+ (void) setVObjectLongValue(p2,1482);
+ setVObjectAnyValue(vcard,...pointer to binary data);
+
+ Note the presence of VCBase64Prop will cause the
+ writeVObject API to output the binary data as BASE64 text.
+ For VCard/VCalendar application, having the VCBase64Prop
+ property is pratically always neccessary for property with
+ binary data as its value.
+
+* Note on Quoted-Printable String
+ String value with embedded newline are written out as
+ quoted-prinatable string. It is therefore important
+ to mark a property with a string value that has
+ one or more embedded newlines, with the VCQutedPrintableProp
+ property. e.g.
+
+ // VObject *root;
+ char *msg="To be\nor\nnot to be";
+ VObject *p = addPropValue(root,VCDescriptionProp,msg);
+ // the following is how you mark a property with
+ // a property. In this case, the marker is
+ // VCQuotedPrintableProp
+ addProp(p,VCQuotedPrintableProp);
+
+* Note on Unicode
+ Although, the current parser takes ASCII text file only,
+ string values are all stored as Unicode in the VObject tree.
+ For now, when using the VObject APIs to construct a
+ VObject tree, one should always convert ASCII string value
+ to a Unicode string value:
+
+ // VObject *root;
+ VObject *p = addProp(root,VCSomeProp);
+ setVObjectUStringZValue(p,fakeUnicode(someASCIIStringZvalue));
+
+ An API is provided to simplify the above process:
+
+ addPropValue(root,VCSomeProp,someASCIIStringZValue);
+
+ Note that someASCIISTringZValue is automatically converted to
+ Unicode by addPropValue API, where as, the former code
+ sequence do an explicit call to fakeUnicode.
+
+ To read back the value, one should use the vObjectUStringZValue
+ API not vObjectStringZValue API. The value returned by the
+ vObjectUStringZValue API is a Unicode string. If the application
+ do not know how to handle Unicode string, it can use the
+ fakeCString API to convert it back to ASCII string (as long
+ as the conversion is meaningful).
+
+ Note that fakeCString return a heap allocated memory. It is
+ important to call deleteStr on fakeCString return value if
+ it is not longer required (or there will be memory leak).
+
+ NOTE: Unfortunately, at the point when this document is written,
+ there is still no consensus on how Unicode is to be handled
+ in the textual representation of VCard/VCalendar. So, there
+ is no version of writeVObject and the parser to output and
+ input Unicode textual representation of VCard/VCalendar.
+
+
+Example.vcf
+-----------
+line
+number Input Text (example.vcf)
+------ ----------
+1 BEGIN:VCALENDAR
+2 DCREATED:19961102T100522
+3 GEO:0,0
+4 VERSION:1.0
+5 BEGIN:VEVENT
+6 DTSTART:19961103T000000
+7 DTEND:20000101T000000
+8 DESCRIPTION;QUOTED-PRINTABLE:To be =0A=
+9 or =0A=
+10 not to be
+11 STATUS:O.K.
+12 X-ACTION:No action required
+13 DALARM:19961103T114500;5;3;Enjoy
+14 MALARM:19970101T120000;;;johny@nowhere.com;Call Mom.
+15 END:VEVENT
+16
+17 BEGIN:VTODO
+18 DUE:19960614T0173000
+19 DESCRIPTION:Relex.
+20 END:VTODO
+21
+22 END:VCALENDAR
+23
+24 BEGIN:VCARD
+25 N:Alden;Roland
+26 FN:Roland H. Alden
+27 ORG:AT&T;Versit Project Office
+28 TITLE:Consultant
+29 EMAIL;WORK;PREF;INTERNET:ralden@ralden.com
+30 LABEL;DOM;POSTAL;PARCEL;HOME;WORK;QUOTED-PRINTABLE:Roland H. Alden=0A=
+31 Suite 2208=0A=
+32 One Pine Street=0A=
+33 San Francisco, CA 94111
+34 LABEL;POSTAL;PARCEL;HOME;WORK;QUOTED-PRINTABLE:Roland H. Alden=0A=
+35 Suite 2208=0A=
+36 One Pine Street=0A=
+37 San Francisco, CA 94111=0A=
+38 U.S.A.
+39 TEL;WORK;PREF;MSG:+1 415 296 9106
+40 TEL;WORK;FAX:+1 415 296 9016
+41 TEL;MSG;CELL:+1 415 608 5981
+42 ADR:;Suite 2208;One Pine Street;San Francisco;CA;94111;U.S.A.
+43 SOUND:ROW-LAND H ALL-DIN
+44 LOGO;GIF;BASE64:
+45 R0lGODdhpgBOAMQAAP///+/v797e3s7Ozr29va2trZycnIyMjHt7e2NjY1JSUkJC
+ ... 30 lines of BASE64 data not shown here.
+76 END:VCARD
+
+
+VObject Representation of Example.vcf:
+-------------------------------------
+line
+in
+text
+file VObject Tree as Printed by printVObject API
+---- -------------------------------------------
+1 VCALENDAR
+2 DCREATED="19961102T100522"
+3 GEO="0,0"
+4 VERSION="1.0"
+5 VEVENT
+6 DTSTART="19961103T000000"
+7 DTEND="20000101T000000"
+8 DESCRIPTION="To be
+9 or
+10 not to be"
+8 QUOTED-PRINTABLE
+11 STATUS="O.K."
+12 X-ACTION="No action required"
+13 DALARM
+13 RUNTIME="19961103T114500"
+13 SNOOZETIME="5"
+13 REPEATCOUNT="3"
+13 DISPLAYSTRING="Enjoy"
+14 MALARM
+14 RUNTIME="19970101T120000"
+14 EMAIL="johny@nowhere.com"
+14 NOTE="Call Mom"
+17 VTODO
+18 DUE="19960614T0173000"
+19 DESCRIPTION="Relex."
+24 VCARD
+25 N
+25 F="Alden"
+25 G="Roland"
+26 FN="Roland H. Alden"
+27 ORG
+27 ORGNAME="AT&T"
+27 OUN="Versit Project Office"
+28 TITLE="Consultant"
+29 EMAIL="ralden@alden.com"
+29 WORK
+29 PREF
+29 INTERNET
+30 LABEL="Roland H. Alden
+31 Suite 2208
+32 One Pine Street
+33 San Francisco, CA 94111"
+30 DOM
+30 POSTAL
+30 PARCEL
+30 HOME
+30 WORK
+30 QUOTED-PRINTABLE
+34 LABEL="Roland H. Alden
+35 Suite 2208
+36 One Pine Street
+37 San Francisco, CA 94111
+38 U.S.A."
+34 POSTAL
+34 PARCEL
+34 HOME
+34 WORK
+34 QUOTED-PRINTABLE
+39 TEL="+1 415 296 9106"
+39 WORK
+39 PREF
+39 MSG
+40 TEL="+1 415 296 9016"
+40 WORK
+40 FAX
+41 TEL="+1 415 608 5981"
+41 MSG
+41 CELL
+42 ADR
+42 EXT ADD="Suite 2208"
+42 STREET="One Pine Street"
+42 L="San Francisco"
+42 R="CA"
+42 PC="94111"
+42 C="U.S.A."
+43 SOUND="ROW-LAND H ALL-DIN"
+44 LOGO=[raw data]
+44 GIF
+44 BASE64
+44 DATASIZE=1482
+
diff --git a/libical/src/libicalvcal/icalvcal.c b/libical/src/libicalvcal/icalvcal.c
new file mode 100644
index 0000000000..d21dd4acc6
--- /dev/null
+++ b/libical/src/libicalvcal/icalvcal.c
@@ -0,0 +1,498 @@
+/*======================================================================
+ FILE: icalvcal.c
+ CREATOR: eric 25 May 00
+
+ $Id$
+
+
+ (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 icalvcal.c
+
+ ======================================================================*/
+
+#include "icalvcal.h"
+#include <string.h>
+
+enum datatype {
+ COMPONENT,
+ PROPERTY,
+ PARAMETER,
+ UNSUPPORTED
+};
+
+
+struct conversion_table_struct {
+ char* vcalname;
+ enum datatype type;
+ void* (*conversion_func)(int icaltype, VObject *o);
+ int icaltype;
+};
+
+struct conversion_table_struct conversion_table[];
+void* dc_prop(int icaltype, VObject *object);
+
+static void traverse_objects(VObject *object,icalcomponent* last_comp,
+ icalproperty* last_prop)
+{
+ VObjectIterator iterator;
+ char* name = "[No Name]";
+ icalcomponent* subc = 0;
+ int i;
+
+ if ( vObjectName(object)== 0){
+ printf("ERROR, object has no name");
+ assert(0);
+ return;
+ }
+
+ name = (char*)vObjectName(object);
+
+ /* Lookup this object in the conversion table */
+ for (i = 0; conversion_table[i].vcalname != 0; i++){
+ if(strcmp(conversion_table[i].vcalname, name) == 0){
+ break;
+ }
+ }
+
+ if (conversion_table[i].vcalname == 0){
+
+ /* Handle X properties */
+ if(strncmp(name, "X-",2) == 0){
+ icalproperty* prop = (icalproperty*)dc_prop(ICAL_X_PROPERTY,object);
+ icalproperty_set_x_name(prop,name);
+ icalcomponent_add_property(last_comp,prop);
+ } else {
+ assert(0);
+ return;
+ }
+
+ } else {
+
+ /* The vCal property is in the table, and it is not an X
+ property, so try to convert it to an iCal component,
+ property or parameter. */
+
+ switch(conversion_table[i].type){
+
+
+ case COMPONENT: {
+ subc =
+ (icalcomponent*)(conversion_table[i].conversion_func
+ (conversion_table[i].icaltype,
+ object));
+
+ icalcomponent_add_component(last_comp,subc);
+
+ assert(subc!=0);
+
+ break;
+ }
+
+ case PROPERTY: {
+
+ if (vObjectValueType(object) &&
+ conversion_table[i].conversion_func != 0 ) {
+
+ icalproperty* prop =
+ (icalproperty*)(conversion_table[i].conversion_func
+ (conversion_table[i].icaltype,
+ object));
+
+ icalcomponent_add_property(last_comp,prop);
+ last_prop = prop;
+
+ }
+ break;
+ }
+
+ case PARAMETER: {
+ break;
+ }
+
+ case UNSUPPORTED: {
+
+ /* If the property is listed as UNSUPPORTED, insert a
+ X_LIC_ERROR property to note this fact. */
+
+ char temp[1024];
+ char* message = "Unsupported vCal property";
+ icalparameter *error_param;
+ icalproperty *error_prop;
+
+ snprintf(temp,1024,"%s: %s",message,name);
+
+ error_param = icalparameter_new_xlicerrortype(
+ ICAL_XLICERRORTYPE_UNKVCALPROP
+ );
+
+ error_prop = icalproperty_new_xlicerror(temp);
+ icalproperty_add_parameter(error_prop, error_param);
+
+ icalcomponent_add_property(last_comp,error_prop);
+
+ break;
+ }
+ }
+ }
+
+
+ /* Now, step down into the next vCalproperty */
+
+ initPropIterator(&iterator,object);
+ while (moreIteration(&iterator)) {
+ VObject *eachProp = nextVObject(&iterator);
+
+ /* If 'object' is a component, then the next traversal down
+ should use it as the 'last_comp' */
+
+ if(subc!=0){
+ traverse_objects(eachProp,subc,last_prop);
+
+ } else {
+ traverse_objects(eachProp,last_comp,last_prop);
+ }
+ }
+}
+
+icalcomponent* icalvcal_convert(VObject *object){
+
+ char* name = (char*)vObjectName(object);
+ icalcomponent* container = icalcomponent_new(ICAL_XROOT_COMPONENT);
+ icalcomponent* root;
+
+ icalerror_check_arg_rz( (object!=0),"Object");
+
+ /* The root object must be a VCALENDAR */
+ if(*name==0 || strcmp(name,VCCalProp) != 0){
+ return 0; /* HACK. Should return an error */
+ }
+
+
+ traverse_objects(object,container,0);
+
+ /* HACK. I am using the extra 'container' component because I am
+ lazy. I know there is a way to get rid of it, but I did not care
+ to find it. */
+
+ root = icalcomponent_get_first_component(container,ICAL_ANY_COMPONENT);
+
+ icalcomponent_remove_component(container, root);
+ icalcomponent_free(container);
+
+ return root;
+
+}
+
+/* comp() is useful for most components, but alarm, daylight and
+ * timezone are different. In vcal, they are properties, and in ical,
+ * they are components. Although because of the way that vcal treats
+ * everything as a property, alarm_comp() daylight_comp() and
+ * timezone_comp() may not really be necessary, I think it would be
+ * easier to use them. */
+
+void* comp(int icaltype, VObject *o)
+{
+ icalcomponent_kind kind = (icalcomponent_kind)icaltype;
+
+ icalcomponent* c = icalcomponent_new(kind);
+
+ return (void* )c;
+}
+
+void* alarm_comp(int icaltype, VObject *o)
+{
+ icalcomponent_kind kind = (icalcomponent_kind)icaltype;
+
+ icalcomponent* c = icalcomponent_new(kind);
+
+ return (void*)c;
+}
+
+void* daylight_comp(int icaltype, VObject *o)
+{
+ icalcomponent_kind kind = (icalcomponent_kind)icaltype;
+
+ icalcomponent* c = icalcomponent_new(kind);
+
+ return (void*)c;
+}
+
+void* timezone_comp(int icaltype, VObject *o)
+{
+ icalcomponent_kind kind = (icalcomponent_kind)icaltype;
+
+ icalcomponent* c = icalcomponent_new(kind);
+
+ return (void*)c;
+}
+
+
+/* These #defines indicate conversion routines that are not defined yet. */
+
+#define categories_prop 0
+#define transp_prop 0
+#define status_prop 0
+
+#define parameter 0
+#define rsvp_parameter 0
+
+
+
+/* directly convertable property. The string representation of vcal is
+ the same as ical */
+
+void* dc_prop(int icaltype, VObject *object)
+{
+ icalproperty_kind kind = (icalproperty_kind)icaltype;
+ icalproperty *prop;
+ icalvalue *value;
+ icalvalue_kind value_kind;
+ char *s,*t=0;
+
+ prop = icalproperty_new(kind);
+
+ value_kind =
+ icalenum_property_kind_to_value_kind(
+ icalproperty_isa(prop));
+
+
+ switch (vObjectValueType(object)) {
+ case VCVT_USTRINGZ: {
+ s = t = fakeCString(vObjectUStringZValue(object));
+ break;
+ }
+ case VCVT_STRINGZ: {
+ s = (char*)vObjectStringZValue(object);
+ break;
+ }
+ }
+
+ value = icalvalue_new_from_string(value_kind,s);
+
+ if(t!=0){
+ deleteStr(t);
+ }
+
+ icalproperty_set_value(prop,value);
+
+ return (void*)prop;
+}
+
+
+/* My extraction program screwed up, so this table does not have all
+of the vcal properties in it. I didn't feel like re-doing the entire
+table, so you'll have to find the missing properties the hard way --
+the code will assert */
+
+struct conversion_table_struct conversion_table[] =
+{
+{VCCalProp, COMPONENT, comp, ICAL_VCALENDAR_COMPONENT},
+{VCTodoProp, COMPONENT, comp, ICAL_VTODO_COMPONENT},
+{VCEventProp, COMPONENT, comp, ICAL_VEVENT_COMPONENT},
+{VCAAlarmProp, COMPONENT, alarm_comp, ICAL_XAUDIOALARM_COMPONENT},
+{VCDAlarmProp, COMPONENT, alarm_comp, ICAL_XDISPLAYALARM_COMPONENT},
+{VCMAlarmProp, COMPONENT, alarm_comp, ICAL_XEMAILALARM_COMPONENT},
+{VCPAlarmProp, COMPONENT, alarm_comp, ICAL_XPROCEDUREALARM_COMPONENT},
+{VCDayLightProp, COMPONENT, daylight_comp,0},
+{VCTimeZoneProp, COMPONENT, timezone_comp, ICAL_VTIMEZONE_COMPONENT},
+{VCProdIdProp, PROPERTY, dc_prop, ICAL_PRODID_PROPERTY},
+{VCClassProp, PROPERTY, dc_prop, ICAL_CLASS_PROPERTY},
+{VCDCreatedProp, PROPERTY, dc_prop, ICAL_CREATED_PROPERTY},
+{VCDescriptionProp, PROPERTY, dc_prop, ICAL_DESCRIPTION_PROPERTY},
+{VCAttendeeProp, PROPERTY, dc_prop, ICAL_ATTENDEE_PROPERTY},
+{VCCategoriesProp, PROPERTY, categories_prop,ICAL_CATEGORIES_PROPERTY},
+{VCDTendProp, PROPERTY, dc_prop, ICAL_DTEND_PROPERTY},
+{VCDTstartProp, PROPERTY, dc_prop, ICAL_DTSTART_PROPERTY},
+{VCDueProp, PROPERTY, dc_prop, ICAL_DUE_PROPERTY},
+{VCLocationProp, PROPERTY, dc_prop, ICAL_LOCATION_PROPERTY},
+{VCSummaryProp, PROPERTY, dc_prop, ICAL_SUMMARY_PROPERTY},
+{VCVersionProp, PROPERTY, dc_prop, ICAL_VERSION_PROPERTY},
+{VCTranspProp, PROPERTY, transp_prop, ICAL_TRANSP_PROPERTY},
+{VCUniqueStringProp, PROPERTY, dc_prop, ICAL_UID_PROPERTY},
+{VCURLProp, PROPERTY, dc_prop, ICAL_URL_PROPERTY},
+{VCLastModifiedProp, PROPERTY, dc_prop, ICAL_LASTMODIFIED_PROPERTY},
+{VCSequenceProp, PROPERTY, dc_prop, ICAL_SEQUENCE_PROPERTY},
+{VCPriorityProp, PROPERTY, dc_prop, ICAL_PRIORITY_PROPERTY},
+{VCStatusProp, PROPERTY, status_prop, ICAL_STATUS_PROPERTY},
+{VCRSVPProp, UNSUPPORTED, rsvp_parameter,ICAL_RSVP_PARAMETER },
+{VCEncodingProp, UNSUPPORTED, parameter, ICAL_ENCODING_PARAMETER},
+{VCRoleProp, UNSUPPORTED, parameter, ICAL_ROLE_PARAMETER},
+{VCStatusProp, UNSUPPORTED, parameter, ICAL_STATUS_PROPERTY},
+{VCQuotedPrintableProp,UNSUPPORTED,0, 0},
+{VC7bitProp, UNSUPPORTED,0, 0},
+{VC8bitProp, UNSUPPORTED,0, 0},
+{VCAdditionalNamesProp,UNSUPPORTED,0, 0},
+{VCAdrProp, UNSUPPORTED,0, 0},
+{VCAgentProp, UNSUPPORTED,0, 0},
+{VCAIFFProp, UNSUPPORTED,0, 0},
+{VCAOLProp, UNSUPPORTED,0, 0},
+{VCAppleLinkProp, UNSUPPORTED,0, 0},
+{VCAttachProp, UNSUPPORTED,0, 0},
+{VCATTMailProp, UNSUPPORTED,0, 0},
+{VCAudioContentProp, UNSUPPORTED,0, 0},
+{VCAVIProp, UNSUPPORTED,0, 0},
+{VCBase64Prop, UNSUPPORTED,0, 0},
+{VCBBSProp, UNSUPPORTED,0, 0},
+{VCBirthDateProp, UNSUPPORTED,0, 0},
+{VCBMPProp, UNSUPPORTED,0, 0},
+{VCBodyProp, UNSUPPORTED,0, 0},
+{VCCaptionProp, UNSUPPORTED,0, 0},
+{VCCarProp, UNSUPPORTED,0, 0},
+{VCCellularProp, UNSUPPORTED,0, 0},
+{VCCGMProp, UNSUPPORTED,0, 0},
+{VCCharSetProp, UNSUPPORTED,0, 0},
+{VCCIDProp, UNSUPPORTED,0, 0},
+{VCCISProp, UNSUPPORTED,0, 0},
+{VCCityProp, UNSUPPORTED,0, 0},
+{VCCommentProp, UNSUPPORTED,0, 0},
+{VCCompletedProp, UNSUPPORTED,0, 0},
+{VCCountryNameProp, UNSUPPORTED,0, 0},
+{VCDataSizeProp, UNSUPPORTED,0, 0},
+{VCDeliveryLabelProp, UNSUPPORTED,0, 0},
+{VCDIBProp, UNSUPPORTED,0, 0},
+{VCDisplayStringProp, UNSUPPORTED,0, 0},
+{VCDomesticProp, UNSUPPORTED,0, 0},
+{VCEmailAddressProp, UNSUPPORTED,0, 0},
+{VCEndProp, UNSUPPORTED,0, 0},
+{VCEWorldProp, UNSUPPORTED,0, 0},
+{VCExNumProp, UNSUPPORTED,0, 0},
+{VCExpDateProp, UNSUPPORTED,0, 0},
+{VCExpectProp, UNSUPPORTED,0, 0},
+{VCFamilyNameProp, UNSUPPORTED,0, 0},
+{VCFaxProp, UNSUPPORTED,0, 0},
+{VCFullNameProp, UNSUPPORTED,0, 0},
+{VCGeoProp, UNSUPPORTED,0, 0},
+{VCGeoLocationProp, UNSUPPORTED,0, 0},
+{VCGIFProp, UNSUPPORTED,0, 0},
+{VCGivenNameProp, UNSUPPORTED,0, 0},
+{VCGroupingProp, UNSUPPORTED,0, 0},
+{VCHomeProp, UNSUPPORTED,0, 0},
+{VCIBMMailProp, UNSUPPORTED,0, 0},
+{VCInlineProp, UNSUPPORTED,0, 0},
+{VCInternationalProp, UNSUPPORTED,0, 0},
+{VCInternetProp, UNSUPPORTED,0, 0},
+{VCISDNProp, UNSUPPORTED,0, 0},
+{VCJPEGProp, UNSUPPORTED,0, 0},
+{VCLanguageProp, UNSUPPORTED,0, 0},
+{VCLastRevisedProp, UNSUPPORTED,0, 0},
+{VCLogoProp, UNSUPPORTED,0, 0},
+{VCMailerProp, UNSUPPORTED,0, 0},
+{VCMCIMailProp, UNSUPPORTED,0, 0},
+{VCMessageProp, UNSUPPORTED,0, 0},
+{VCMETProp, UNSUPPORTED,0, 0},
+{VCModemProp, UNSUPPORTED,0, 0},
+{VCMPEG2Prop, UNSUPPORTED,0, 0},
+{VCMPEGProp, UNSUPPORTED,0, 0},
+{VCMSNProp, UNSUPPORTED,0, 0},
+{VCNamePrefixesProp, UNSUPPORTED,0, 0},
+{VCNameProp, UNSUPPORTED,0, 0},
+{VCNameSuffixesProp, UNSUPPORTED,0, 0},
+{VCNoteProp, UNSUPPORTED,0, 0},
+{VCOrgNameProp, UNSUPPORTED,0, 0},
+{VCOrgProp, UNSUPPORTED,0, 0},
+{VCOrgUnit2Prop, UNSUPPORTED,0, 0},
+{VCOrgUnit3Prop, UNSUPPORTED,0, 0},
+{VCOrgUnit4Prop, UNSUPPORTED,0, 0},
+{VCOrgUnitProp, UNSUPPORTED,0, 0},
+{VCPagerProp, UNSUPPORTED,0, 0},
+{VCParcelProp, UNSUPPORTED,0, 0},
+{VCPartProp, UNSUPPORTED,0, 0},
+{VCPCMProp, UNSUPPORTED,0, 0},
+{VCPDFProp, UNSUPPORTED,0, 0},
+{VCPGPProp, UNSUPPORTED,0, 0},
+{VCPhotoProp, UNSUPPORTED,0, 0},
+{VCPICTProp, UNSUPPORTED,0, 0},
+{VCPMBProp, UNSUPPORTED,0, 0},
+{VCPostalBoxProp, UNSUPPORTED,0, 0},
+{VCPostalCodeProp, UNSUPPORTED,0, 0},
+{VCPostalProp, UNSUPPORTED,0, 0},
+{VCPowerShareProp, UNSUPPORTED,0, 0},
+{VCPreferredProp, UNSUPPORTED,0, 0},
+{VCProcedureNameProp, UNSUPPORTED,0, 0},
+{VCProdigyProp, UNSUPPORTED,0, 0},
+{VCPronunciationProp, UNSUPPORTED,0, 0},
+{VCPSProp, UNSUPPORTED,0, 0},
+{VCPublicKeyProp, UNSUPPORTED,0, 0},
+{VCQPProp, UNSUPPORTED,0, 0},
+{VCQuickTimeProp, UNSUPPORTED,0, 0},
+{VCRDateProp, UNSUPPORTED,0, 0},
+{VCRegionProp, UNSUPPORTED,0, 0},
+{VCRepeatCountProp, UNSUPPORTED,0, 0},
+{VCResourcesProp, UNSUPPORTED,0, 0},
+{VCRNumProp, UNSUPPORTED,0, 0},
+{VCRRuleProp, UNSUPPORTED,0, 0},
+{VCRunTimeProp, UNSUPPORTED,0, 0},
+{VCSnoozeTimeProp, UNSUPPORTED,0, 0},
+{VCStartProp, UNSUPPORTED,0, 0},
+{VCStreetAddressProp, UNSUPPORTED,0, 0},
+{VCSubTypeProp, UNSUPPORTED,0, 0},
+{VCTelephoneProp, UNSUPPORTED,0, 0},
+{VCTIFFProp, UNSUPPORTED,0, 0},
+{VCTitleProp, UNSUPPORTED,0, 0},
+{VCTLXProp, UNSUPPORTED,0, 0},
+{VCURLValueProp, UNSUPPORTED,0, 0},
+{VCValueProp, UNSUPPORTED,0, 0},
+{VCVideoProp, UNSUPPORTED,0, 0},
+{VCVoiceProp, UNSUPPORTED,0, 0},
+{VCWAVEProp, UNSUPPORTED,0, 0},
+{VCWMFProp, UNSUPPORTED,0, 0},
+{VCWorkProp, UNSUPPORTED,0, 0},
+{VCX400Prop, UNSUPPORTED,0, 0},
+{VCX509Prop, UNSUPPORTED,0, 0},
+{VCXRuleProp, UNSUPPORTED,0, 0},
+{0,0,0,0}
+};
+
+
+#if 0
+ switch (vObjectValueType(object)) {
+ case VCVT_USTRINGZ: {
+ char c;
+ char *t,*s;
+ s = t = fakeCString(vObjectUStringZValue(object));
+ printf(" ustringzstring:%s\n",s);
+ deleteStr(s);
+ break;
+ }
+ case VCVT_STRINGZ: {
+ char c;
+ const char *s = vObjectStringZValue(object);
+ printf(" stringzstring:%s\n",s);
+ break;
+ }
+ case VCVT_UINT:
+ {
+ int i = vObjectIntegerValue(object);
+ printf(" int:%d\n",i);
+ break;
+ }
+ case VCVT_ULONG:
+ {
+ long l = vObjectLongValue(object);
+ printf(" int:%d\n",l);
+ break;
+ }
+ case VCVT_VOBJECT:
+ {
+ printf("ERROR, should not get here\n");
+ break;
+ }
+ case VCVT_RAW:
+ case 0:
+ default:
+ break;
+ }
+
+#endif
diff --git a/libical/src/libicalvcal/icalvcal.h b/libical/src/libicalvcal/icalvcal.h
new file mode 100644
index 0000000000..f2316c2d0f
--- /dev/null
+++ b/libical/src/libicalvcal/icalvcal.h
@@ -0,0 +1,39 @@
+/* -*- Mode: C -*-*/
+/*======================================================================
+ FILE: icalvcal.h
+ CREATOR: eric 25 May 00
+
+
+
+ (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 icalvcal.h
+
+
+======================================================================*/
+
+#ifndef ICALVCAL_H
+#define ICALVCAL_H
+
+#include "ical.h"
+#include "vcc.h"
+
+/* Convert a vObject into an icalcomponent */
+
+icalcomponent* icalvcal_convert(VObject *object);
+
+#endif /* !ICALVCAL_H */
+
+
+
diff --git a/libical/src/libicalvcal/port.h b/libical/src/libicalvcal/port.h
new file mode 100644
index 0000000000..1768beebd8
--- /dev/null
+++ b/libical/src/libicalvcal/port.h
@@ -0,0 +1,88 @@
+/***************************************************************************
+(C) Copyright 1996 Apple Computer, Inc., AT&T Corp., International
+Business Machines Corporation and Siemens Rolm Communications Inc.
+
+For purposes of this license notice, the term Licensors shall mean,
+collectively, Apple Computer, Inc., AT&T Corp., International
+Business Machines Corporation and Siemens Rolm Communications Inc.
+The term Licensor shall mean any of the Licensors.
+
+Subject to acceptance of the following conditions, permission is hereby
+granted by Licensors without the need for written agreement and without
+license or royalty fees, to use, copy, modify and distribute this
+software for any purpose.
+
+The above copyright notice and the following four paragraphs must be
+reproduced in all copies of this software and any software including
+this software.
+
+THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS AND NO LICENSOR SHALL HAVE
+ANY OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS OR
+MODIFICATIONS.
+
+IN NO EVENT SHALL ANY LICENSOR BE LIABLE TO ANY PARTY FOR DIRECT,
+INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT
+OF THE USE OF THIS SOFTWARE EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGE.
+
+EACH LICENSOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, EXPRESS OR IMPLIED,
+INCLUDING BUT NOT LIMITED TO ANY WARRANTY OF NONINFRINGEMENT OR THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.
+
+The software is provided with RESTRICTED RIGHTS. Use, duplication, or
+disclosure by the government are subject to restrictions set forth in
+DFARS 252.227-7013 or 48 CFR 52.227-19, as applicable.
+
+***************************************************************************/
+
+#ifndef __PORT_H__
+#define __PORT_H__ 1
+
+
+#if defined(__CPLUSPLUS__) || defined(__cplusplus)
+extern "C" {
+#endif
+
+/* some of these #defines are commented out because */
+/* Visual C++ sets them on the compiler command line instead */
+
+/* #define _DEBUG */
+/* #define WIN32 */
+/* #define WIN16 */
+/* #define _WINDOWS */
+/* #define __MWERKS__ */
+/* #define INCLUDEMFC */
+
+#define vCardClipboardFormat "+//ISBN 1-887687-00-9::versit::PDI//vCard"
+#define vCalendarClipboardFormat "+//ISBN 1-887687-00-9::versit::PDI//vCalendar"
+
+/* The above strings vCardClipboardFormat and vCalendarClipboardFormat
+are globally unique IDs which can be used to generate clipboard format
+ID's as per the requirements of a specific platform. For example, in
+Windows they are used as the parameter in a call to RegisterClipboardFormat.
+For example:
+
+ CLIPFORMAT foo = RegisterClipboardFormat(vCardClipboardFormat);
+
+*/
+
+#define vCardMimeType "text/x-vCard"
+#define vCalendarMimeType "text/x-vCalendar"
+
+#define DLLEXPORT(t) t
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#define stricmp strcasecmp
+
+#if defined(__CPLUSPLUS__) || defined(__cplusplus)
+}
+#endif
+
+#endif /* __PORT_H__ */
diff --git a/libical/src/libicalvcal/vcaltest.c b/libical/src/libicalvcal/vcaltest.c
new file mode 100644
index 0000000000..5528aab1d1
--- /dev/null
+++ b/libical/src/libicalvcal/vcaltest.c
@@ -0,0 +1,118 @@
+#include <stdio.h>
+#include <string.h>
+#include "vcaltmp.h"
+
+#if 0
+This testcase would generate a file call "frankcal.vcf" with
+the following content:
+
+BEGIN:VCALENDAR
+DCREATED:19960523T100522
+GEO:37.24,-17.87
+PRODID:-//Frank Dawson/Hand Crafted In North Carolina//NONSGML Made By Hand//EN
+VERSION:0.3
+BEGIN:VEVENT
+DTSTART:19960523T120000
+DTEND:19960523T130000
+DESCRIPTION;QUOTED-PRINTABLE:VERSIT PDI PR Teleconference/Interview =0A=
+With Tom Streeter and Frank Dawson - Discuss VERSIT PDI project and vCard and vCalendar=0A=
+activities with European Press representatives.
+SUMMARY:VERSIT PDI PR Teleconference/Interview
+SUBTYPE:PHONE CALL
+STATUS:CONFIRMED
+TRANSP:19960523T100522-4000F100582713-009251
+UID:http://www.ibm.com/raleigh/fdawson/~c:\or2\orgfiles\versit.or2
+DALARM:19960523T114500;5;3;Your Telecon Starts At Noon!!!;
+MALARM:19960522T120000;;;fdawson@raleigh.ibm.com;Remember 05/23 Noon Telecon!!!;
+PALARM:19960523T115500;;;c:\or2\organize.exe c:\or2\orgfiles\versit.or2;
+X-LDC-OR2-OLE:c:\temp\agenda.doc
+END:VEVENT
+
+BEGIN:VTODO
+DUE:19960614T0173000
+DESCRIPTION:Review VCalendar helper API.
+END:VTODO
+
+END:VCALENDAR
+
+#endif
+
+FILE *cfp;
+
+void testVcalAPIs() {
+ FILE *fp;
+ VObject *vcal, *vevent;
+#if _CONSOLE
+ cfp = stdout;
+#else
+ cfp = fopen("vcaltest.out","w");
+#endif
+ if (cfp == 0) return;
+ vcal = vcsCreateVCal(
+ "19960523T100522",
+ "37.24,-17.87",
+ "-//Frank Dawson/Hand Crafted In North Carolina//NONSGML Made By Hand//EN",
+ 0,
+ "0.3"
+ );
+
+ vevent = vcsAddEvent(
+ vcal,
+ "19960523T120000",
+ "19960523T130000",
+ "VERSIT PDI PR Teleconference/Interview \nWith Tom Streeter and Frank Dawson - Discuss VERSIT PDI project and vCard and vCalendar\nactivities with European Press representatives.",
+ "VERSIT PDI PR Teleconference/Interview",
+ "PHONE CALL",
+ 0,
+ "CONFIRMED",
+ "19960523T100522-4000F100582713-009251",
+ "http://www.ibm.com/raleigh/fdawson/~c:\\or2\\orgfiles\\versit.or2",
+ 0
+ );
+
+ vcsAddDAlarm(vevent, "19960523T114500", "5", "3",
+ "Your Telecon Starts At Noon!!!");
+ vcsAddMAlarm(vevent, "19960522T120000", 0, 0, "fdawson@raleigh.ibm.com",
+ "Remember 05/23 Noon Telecon!!!");
+ vcsAddPAlarm(vevent, "19960523T115500", 0 ,0,
+ "c:\\or2\\organize.exe c:\\or2\\orgfiles\\versit.or2");
+
+ addPropValue(vevent, "X-LDC-OR2-OLE", "c:\\temp\\agenda.doc");
+
+ vcsAddTodo(
+ vcal,
+ 0,
+ "19960614T0173000",
+ 0,
+ "Review VCalendar helper API.",
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ );
+
+ /* now do something to the resulting VObject */
+ /* pretty print on stdout for fun */
+ printVObject(cfp,vcal);
+ /* open the output text file */
+
+#define OUTFILE "frankcal.vcf"
+
+ fp = fopen(OUTFILE, "w");
+ if (fp) {
+ /* write it in text form */
+ writeVObject(fp,vcal);
+ fclose(fp);
+ }
+ else {
+ fprintf(cfp,"open output file '%s' failed\n", OUTFILE);
+ }
+ if (cfp != stdout) fclose(cfp);
+ }
+
+void main() {
+ testVcalAPIs();
+ }
+
diff --git a/libical/src/libicalvcal/vcaltmp.c b/libical/src/libicalvcal/vcaltmp.c
new file mode 100644
index 0000000000..ccb21a649a
--- /dev/null
+++ b/libical/src/libicalvcal/vcaltmp.c
@@ -0,0 +1,337 @@
+/*
+This module provides some helper APIs for creating
+a VCalendar object.
+
+Note on APIs:
+ 1. The APIs does not attempt to verify if the arguments
+ passed are correct.
+ 2. Where the argument to an API is not applicable, pass
+ the value 0.
+ 3. See the test program at the bottom of this file as an
+ example of usage.
+ 4. This code calls APIs in vobject.c.
+
+*/
+
+/***************************************************************************
+(C) Copyright 1996 Apple Computer, Inc., AT&T Corp., International
+Business Machines Corporation and Siemens Rolm Communications Inc.
+
+For purposes of this license notice, the term Licensors shall mean,
+collectively, Apple Computer, Inc., AT&T Corp., International
+Business Machines Corporation and Siemens Rolm Communications Inc.
+The term Licensor shall mean any of the Licensors.
+
+Subject to acceptance of the following conditions, permission is hereby
+granted by Licensors without the need for written agreement and without
+license or royalty fees, to use, copy, modify and distribute this
+software for any purpose.
+
+The above copyright notice and the following four paragraphs must be
+reproduced in all copies of this software and any software including
+this software.
+
+THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS AND NO LICENSOR SHALL HAVE
+ANY OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS OR
+MODIFICATIONS.
+
+IN NO EVENT SHALL ANY LICENSOR BE LIABLE TO ANY PARTY FOR DIRECT,
+INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT
+OF THE USE OF THIS SOFTWARE EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGE.
+
+EACH LICENSOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, EXPRESS OR IMPLIED,
+INCLUDING BUT NOT LIMITED TO ANY WARRANTY OF NONINFRINGEMENT OR THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.
+
+The software is provided with RESTRICTED RIGHTS. Use, duplication, or
+disclosure by the government are subject to restrictions set forth in
+DFARS 252.227-7013 or 48 CFR 52.227-19, as applicable.
+
+***************************************************************************/
+
+
+#include <stdio.h>
+#include <string.h>
+#include "vcaltmp.h"
+
+
+DLLEXPORT(VObject*) vcsCreateVCal(
+ char *date_created,
+ char *location,
+ char *product_id,
+ char *time_zone,
+ char *version
+ )
+ {
+ VObject *vcal = newVObject(VCCalProp);
+#define Z(p,v) if (v) addPropValue(vcal,p,v);
+ Z(VCDCreatedProp, date_created);
+ Z(VCLocationProp, location)
+ Z(VCProdIdProp, product_id)
+ Z(VCTimeZoneProp, time_zone)
+ Z(VCVersionProp, version)
+#undef Z
+ return vcal;
+ }
+
+
+DLLEXPORT(VObject*) vcsAddEvent(
+ VObject *vcal,
+ char *start_date_time,
+ char *end_date_time,
+ char *description,
+ char *summary,
+ char *categories,
+ char *classification,
+ char *status,
+ char *transparency,
+ char *uid,
+ char *url
+ )
+ {
+ VObject *vevent = addProp(vcal,VCEventProp);
+#define Z(p,v) if (v) addPropValue(vevent,p,v);
+ Z(VCDTstartProp,start_date_time);
+ Z(VCDTendProp,end_date_time);
+ if (description) {
+ VObject *p = addPropValue(vevent,VCDescriptionProp,description);
+ if (strchr(description,'\n'))
+ addProp(p,VCQuotedPrintableProp);
+ }
+ Z(VCSummaryProp,summary);
+ Z(VCCategoriesProp,categories);
+ Z(VCClassProp,classification);
+ Z(VCStatusProp,status);
+ Z(VCTranspProp,transparency);
+ Z(VCUniqueStringProp,uid);
+ Z(VCURLProp,url);
+#undef Z
+ return vevent;
+ }
+
+
+DLLEXPORT(VObject*) vcsAddTodo(
+ VObject *vcal,
+ char *start_date_time,
+ char *due_date_time,
+ char *date_time_complete,
+ char *description,
+ char *summary,
+ char *priority,
+ char *classification,
+ char *status,
+ char *uid,
+ char *url
+ )
+ {
+ VObject *vtodo = addProp(vcal,VCTodoProp);
+#define Z(p,v) if (v) addPropValue(vtodo,p,v);
+ Z(VCDTstartProp,start_date_time);
+ Z(VCDueProp,due_date_time);
+ Z(VCCompletedProp,date_time_complete);
+ if (description) {
+ VObject *p = addPropValue(vtodo,VCDescriptionProp,description);
+ if (strchr(description,'\n'))
+ addProp(p,VCQuotedPrintableProp);
+ }
+ Z(VCSummaryProp,summary);
+ Z(VCPriorityProp,priority);
+ Z(VCClassProp,classification);
+ Z(VCStatusProp,status);
+ Z(VCUniqueStringProp,uid);
+ Z(VCURLProp,url);
+#undef Z
+ return vtodo;
+ }
+
+
+DLLEXPORT(VObject*) vcsAddAAlarm(
+ VObject *vevent,
+ char *run_time,
+ char *snooze_time,
+ char *repeat_count,
+ char *audio_content
+ )
+ {
+ VObject *aalarm= addProp(vevent,VCAAlarmProp);
+#define Z(p,v) if (v) addPropValue(aalarm,p,v);
+ Z(VCRunTimeProp,run_time);
+ Z(VCSnoozeTimeProp,snooze_time);
+ Z(VCRepeatCountProp,repeat_count);
+ Z(VCAudioContentProp,audio_content);
+#undef Z
+ return aalarm;
+ }
+
+
+DLLEXPORT(VObject*) vcsAddMAlarm(
+ VObject *vevent,
+ char *run_time,
+ char *snooze_time,
+ char *repeat_count,
+ char *email_address,
+ char *note
+ )
+ {
+ VObject *malarm= addProp(vevent,VCMAlarmProp);
+#define Z(p,v) if (v) addPropValue(malarm,p,v);
+ Z(VCRunTimeProp,run_time);
+ Z(VCSnoozeTimeProp,snooze_time);
+ Z(VCRepeatCountProp,repeat_count);
+ Z(VCEmailAddressProp,email_address);
+ Z(VCNoteProp,note);
+#undef Z
+ return malarm;
+ }
+
+
+DLLEXPORT(VObject*) vcsAddDAlarm(
+ VObject *vevent,
+ char *run_time,
+ char *snooze_time,
+ char *repeat_count,
+ char *display_string
+ )
+ {
+ VObject *dalarm= addProp(vevent,VCDAlarmProp);
+#define Z(p,v) if (v) addPropValue(dalarm,p,v);
+ Z(VCRunTimeProp,run_time);
+ Z(VCSnoozeTimeProp,snooze_time);
+ Z(VCRepeatCountProp,repeat_count);
+ Z(VCDisplayStringProp,display_string);
+#undef Z
+ return dalarm;
+ }
+
+
+DLLEXPORT(VObject*) vcsAddPAlarm(
+ VObject *vevent,
+ char *run_time,
+ char *snooze_time,
+ char *repeat_count,
+ char *procedure_name
+ )
+ {
+ VObject *palarm= addProp(vevent,VCPAlarmProp);
+#define Z(p,v) if (v) addPropValue(palarm,p,v);
+ Z(VCRunTimeProp,run_time);
+ Z(VCSnoozeTimeProp,snooze_time);
+ Z(VCRepeatCountProp,repeat_count);
+ Z(VCProcedureNameProp,procedure_name);
+#undef Z
+ return palarm;
+ }
+
+
+#ifdef _TEST
+
+#if 0
+This testcase would generate a file call "frankcal.vcf" with
+the following content:
+
+BEGIN:VCALENDAR
+DCREATED:19960523T100522
+GEO:37.24,-17.87
+PRODID:-//Frank Dawson/Hand Crafted In North Carolina//NONSGML Made By Hand//EN
+VERSION:0.3
+BEGIN:VEVENT
+DTSTART:19960523T120000
+DTEND:19960523T130000
+DESCRIPTION;QUOTED-PRINTABLE:VERSIT PDI PR Teleconference/Interview =0A=
+With Tom Streeter and Frank Dawson - Discuss VERSIT PDI project and vCard and vCalendar=0A=
+activities with European Press representatives.
+SUMMARY:VERSIT PDI PR Teleconference/Interview
+CATEGORIES:PHONE CALL
+STATUS:CONFIRMED
+TRANSP:19960523T100522-4000F100582713-009251
+UID:http://www.ibm.com/raleigh/fdawson/~c:\or2\orgfiles\versit.or2
+DALARM:19960523T114500;5;3;Your Telecon Starts At Noon!!!;
+MALARM:19960522T120000;;;fdawson@raleigh.ibm.com;Remember 05/23 Noon Telecon!!!;
+PALARM:19960523T115500;;;c:\or2\organize.exe c:\or2\orgfiles\versit.or2;
+X-LDC-OR2-OLE:c:\temp\agenda.doc
+END:VEVENT
+
+BEGIN:VTODO
+DUE:19960614T0173000
+DESCRIPTION:Review VCalendar helper API.
+END:VTODO
+
+END:VCALENDAR
+
+#endif
+
+void testVcalAPIs() {
+ FILE *fp;
+ VObject *vcal = vcsCreateVCal(
+ "19960523T100522",
+ "37.24,-17.87",
+ "-//Frank Dawson/Hand Crafted In North Carolina//NONSGML Made By Hand//EN",
+ 0,
+ "0.3"
+ );
+
+ VObject *vevent = vcsAddEvent(
+ vcal,
+ "19960523T120000",
+ "19960523T130000",
+ "VERSIT PDI PR Teleconference/Interview \nWith Tom Streeter and Frank Dawson - Discuss VERSIT PDI project and vCard and vCalendar\nactivities with European Press representatives.",
+ "VERSIT PDI PR Teleconference/Interview",
+ "PHONE CALL",
+ 0,
+ "CONFIRMED",
+ "19960523T100522-4000F100582713-009251",
+ "http://www.ibm.com/raleigh/fdawson/~c:\\or2\\orgfiles\\versit.or2",
+ 0
+ );
+
+ vcsAddDAlarm(vevent, "19960523T114500", "5", "3",
+ "Your Telecon Starts At Noon!!!");
+ vcsAddMAlarm(vevent, "19960522T120000", 0, 0, "fdawson@raleigh.ibm.com",
+ "Remember 05/23 Noon Telecon!!!");
+ vcsAddPAlarm(vevent, "19960523T115500", 0 ,0,
+ "c:\\or2\\organize.exe c:\\or2\\orgfiles\\versit.or2");
+
+ addPropValue(vevent, "X-LDC-OR2-OLE", "c:\\temp\\agenda.doc");
+
+ vcsAddTodo(
+ vcal,
+ 0,
+ "19960614T0173000",
+ 0,
+ "Review VCalendar helper API.",
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ );
+
+ /* now do something to the resulting VObject */
+ /* pretty print on stdout for fun */
+ printVObject(vcal);
+ /* open the output text file */
+
+#define OUTFILE "frankcal.vcf"
+
+ fp = fopen(OUTFILE, "w");
+ if (fp) {
+ /* write it in text form */
+ writeVObject(fp,vcal);
+ fclose(fp);
+ }
+ else {
+ printf("open output file '%s' failed\n", OUTFILE);
+ }
+ }
+
+void main() {
+ testVcalAPIs();
+ }
+
+#endif
+
+
+/* end of source file vcaltmp.c */
diff --git a/libical/src/libicalvcal/vcaltmp.h b/libical/src/libicalvcal/vcaltmp.h
new file mode 100644
index 0000000000..4c4afde963
--- /dev/null
+++ b/libical/src/libicalvcal/vcaltmp.h
@@ -0,0 +1,128 @@
+/***************************************************************************
+(C) Copyright 1996 Apple Computer, Inc., AT&T Corp., International
+Business Machines Corporation and Siemens Rolm Communications Inc.
+
+For purposes of this license notice, the term Licensors shall mean,
+collectively, Apple Computer, Inc., AT&T Corp., International
+Business Machines Corporation and Siemens Rolm Communications Inc.
+The term Licensor shall mean any of the Licensors.
+
+Subject to acceptance of the following conditions, permission is hereby
+granted by Licensors without the need for written agreement and without
+license or royalty fees, to use, copy, modify and distribute this
+software for any purpose.
+
+The above copyright notice and the following four paragraphs must be
+reproduced in all copies of this software and any software including
+this software.
+
+THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS AND NO LICENSOR SHALL HAVE
+ANY OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS OR
+MODIFICATIONS.
+
+IN NO EVENT SHALL ANY LICENSOR BE LIABLE TO ANY PARTY FOR DIRECT,
+INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT
+OF THE USE OF THIS SOFTWARE EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGE.
+
+EACH LICENSOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, EXPRESS OR IMPLIED,
+INCLUDING BUT NOT LIMITED TO ANY WARRANTY OF NONINFRINGEMENT OR THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.
+
+The software is provided with RESTRICTED RIGHTS. Use, duplication, or
+disclosure by the government are subject to restrictions set forth in
+DFARS 252.227-7013 or 48 CFR 52.227-19, as applicable.
+
+***************************************************************************/
+
+#include "vcc.h"
+
+#ifndef __VCALTMP_H__
+#define __VCALTMP_H__
+
+#if defined(__CPLUSPLUS__) || defined(__cplusplus)
+extern "C" {
+#endif
+
+extern DLLEXPORT(VObject*) vcsCreateVCal(
+ char *date_created,
+ char *location,
+ char *product_id,
+ char *time_zone,
+ char *version
+ );
+
+extern DLLEXPORT(VObject*) vcsAddEvent(
+ VObject *vcal,
+ char *start_date_time,
+ char *end_date_time,
+ char *description,
+ char *summary,
+ char *categories,
+ char *classification,
+ char *status,
+ char *transparency,
+ char *uid,
+ char *url
+ );
+
+
+extern DLLEXPORT(VObject*) vcsAddTodo(
+ VObject *vcal,
+ char *start_date_time,
+ char *due_date_time,
+ char *date_time_complete,
+ char *description,
+ char *summary,
+ char *priority,
+ char *classification,
+ char *status,
+ char *uid,
+ char *url
+ );
+
+
+extern DLLEXPORT(VObject*) vcsAddAAlarm(
+ VObject *vevent,
+ char *run_time,
+ char *snooze_time,
+ char *repeat_count,
+ char *audio_content
+ );
+
+
+extern DLLEXPORT(VObject*) vcsAddMAlarm(
+ VObject *vevent,
+ char *run_time,
+ char *snooze_time,
+ char *repeat_count,
+ char *email_address,
+ char *note
+ );
+
+
+extern DLLEXPORT(VObject*) vcsAddDAlarm(
+ VObject *vevent,
+ char *run_time,
+ char *snooze_time,
+ char *repeat_count,
+ char *display_string
+ );
+
+
+extern DLLEXPORT(VObject*) vcsAddPAlarm(
+ VObject *vevent,
+ char *run_time,
+ char *snooze_time,
+ char *repeat_count,
+ char *procedure_name
+ );
+
+#if defined(__CPLUSPLUS__) || defined(__cplusplus)
+}
+#endif
+
+#endif /* __VCALTMP_H__ */
+
+
diff --git a/libical/src/libicalvcal/vcc.c b/libical/src/libicalvcal/vcc.c
new file mode 100644
index 0000000000..313e5d6d80
--- /dev/null
+++ b/libical/src/libicalvcal/vcc.c
@@ -0,0 +1,2142 @@
+
+/* A Bison parser, made from vcc.y
+ by GNU Bison version 1.27
+ */
+
+#define YYBISON 1 /* Identify Bison output. */
+
+#define EQ 257
+#define COLON 258
+#define DOT 259
+#define SEMICOLON 260
+#define SPACE 261
+#define HTAB 262
+#define LINESEP 263
+#define NEWLINE 264
+#define BEGIN_VCARD 265
+#define END_VCARD 266
+#define BEGIN_VCAL 267
+#define END_VCAL 268
+#define BEGIN_VEVENT 269
+#define END_VEVENT 270
+#define BEGIN_VTODO 271
+#define END_VTODO 272
+#define ID 273
+#define STRING 274
+
+#line 1 "vcc.y"
+
+
+/***************************************************************************
+(C) Copyright 1996 Apple Computer, Inc., AT&T Corp., International
+Business Machines Corporation and Siemens Rolm Communications Inc.
+
+For purposes of this license notice, the term Licensors shall mean,
+collectively, Apple Computer, Inc., AT&T Corp., International
+Business Machines Corporation and Siemens Rolm Communications Inc.
+The term Licensor shall mean any of the Licensors.
+
+Subject to acceptance of the following conditions, permission is hereby
+granted by Licensors without the need for written agreement and without
+license or royalty fees, to use, copy, modify and distribute this
+software for any purpose.
+
+The above copyright notice and the following four paragraphs must be
+reproduced in all copies of this software and any software including
+this software.
+
+THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS AND NO LICENSOR SHALL HAVE
+ANY OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS OR
+MODIFICATIONS.
+
+IN NO EVENT SHALL ANY LICENSOR BE LIABLE TO ANY PARTY FOR DIRECT,
+INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT
+OF THE USE OF THIS SOFTWARE EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGE.
+
+EACH LICENSOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, EXPRESS OR IMPLIED,
+INCLUDING BUT NOT LIMITED TO ANY WARRANTY OF NONINFRINGEMENT OR THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.
+
+The software is provided with RESTRICTED RIGHTS. Use, duplication, or
+disclosure by the government are subject to restrictions set forth in
+DFARS 252.227-7013 or 48 CFR 52.227-19, as applicable.
+
+***************************************************************************/
+
+/*
+ * src: vcc.c
+ * doc: Parser for vCard and vCalendar. Note that this code is
+ * generated by a yacc parser generator. Generally it should not
+ * be edited by hand. The real source is vcc.y. The #line directives
+ * can be commented out here to make it easier to trace through
+ * in a debugger. However, if a bug is found it should
+ * be fixed in vcc.y and this file regenerated.
+ */
+
+
+/* debugging utilities */
+#if __DEBUG
+#define DBG_(x) printf x
+#else
+#define DBG_(x)
+#endif
+
+/**** External Functions ****/
+
+/* assign local name to parser variables and functions so that
+ we can use more than one yacc based parser.
+*/
+
+#define yyparse mime_parse
+#define yylex mime_lex
+#define yyerror mime_error
+#define yychar mime_char
+/* #define p_yyval p_mime_val */
+#undef yyval
+#define yyval mime_yyval
+/* #define p_yylval p_mime_lval */
+#undef yylval
+#define yylval mime_yylval
+#define yydebug mime_debug
+#define yynerrs mime_nerrs
+#define yyerrflag mime_errflag
+#define yyss mime_ss
+#define yyssp mime_ssp
+#define yyvs mime_vs
+#define yyvsp mime_vsp
+#define yylhs mime_lhs
+#define yylen mime_len
+#define yydefred mime_defred
+#define yydgoto mime_dgoto
+#define yysindex mime_sindex
+#define yyrindex mime_rindex
+#define yygindex mime_gindex
+#define yytable mime_table
+#define yycheck mime_check
+#define yyname mime_name
+#define yyrule mime_rule
+#define YYPREFIX "mime_"
+
+
+#ifndef _NO_LINE_FOLDING
+#define _SUPPORT_LINE_FOLDING 1
+#endif
+
+/* undef below if compile with MFC */
+/* #define INCLUDEMFC 1 */
+
+#if defined(WIN32) || defined(_WIN32)
+#ifdef INCLUDEMFC
+#include <afx.h>
+#endif
+#endif
+
+#include <string.h>
+#ifndef __MWERKS__
+#include <malloc.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include "vcc.h"
+
+/**** Types, Constants ****/
+
+#define YYDEBUG 1 /* 1 to compile in some debugging code */
+#define MAXTOKEN 256 /* maximum token (line) length */
+#define YYSTACKSIZE 50 /* ~unref ? */
+#define MAXLEVEL 10 /* max # of nested objects parseable */
+ /* (includes outermost) */
+
+
+/**** Global Variables ****/
+int mime_lineNum, mime_numErrors; /* yyerror() can use these */
+static VObject* vObjList;
+static VObject *curProp;
+static VObject *curObj;
+static VObject* ObjStack[MAXLEVEL];
+static int ObjStackTop;
+
+
+/* A helpful utility for the rest of the app. */
+#if __CPLUSPLUS__
+extern "C" {
+#endif
+
+ extern void Parse_Debug(const char *s);
+ extern void yyerror(char *s);
+
+#if __CPLUSPLUS__
+ };
+#endif
+
+int yyparse();
+
+enum LexMode {
+ L_NORMAL,
+ L_VCARD,
+ L_VCAL,
+ L_VEVENT,
+ L_VTODO,
+ L_VALUES,
+ L_BASE64,
+ L_QUOTED_PRINTABLE
+ };
+
+/**** Private Forward Declarations ****/
+static int pushVObject(const char *prop);
+static VObject* popVObject();
+static char* lexDataFromBase64();
+static void lexPopMode(int top);
+static int lexWithinMode(enum LexMode mode);
+static void lexPushMode(enum LexMode mode);
+static void enterProps(const char *s);
+static void enterAttr(const char *s1, const char *s2);
+static void enterValues(const char *value);
+static void mime_error_(char *s);
+
+
+#line 179 "vcc.y"
+typedef union {
+ char *str;
+ VObject *vobj;
+ } YYSTYPE;
+#include <stdio.h>
+
+#ifndef __cplusplus
+#ifndef __STDC__
+#define const
+#endif
+#endif
+
+
+
+#define YYFINAL 62
+#define YYFLAG -32768
+#define YYNTBASE 21
+
+#define YYTRANSLATE(x) ((unsigned)(x) <= 274 ? yytranslate[x] : 51)
+
+static const char yytranslate[] = { 0,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 1, 3, 4, 5, 6,
+ 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+ 17, 18, 19, 20
+};
+
+#if YYDEBUG != 0
+static const short yyprhs[] = { 0,
+ 0, 2, 3, 7, 9, 11, 13, 14, 19, 20,
+ 24, 27, 29, 30, 36, 38, 39, 43, 45, 48,
+ 50, 53, 55, 59, 61, 62, 67, 69, 71, 72,
+ 73, 78, 79, 83, 86, 88, 90, 92, 94, 95,
+ 100, 101, 105, 106, 111, 112
+};
+
+static const short yyrhs[] = { 22,
+ 0, 0, 24, 23, 22, 0, 24, 0, 25, 0,
+ 40, 0, 0, 11, 26, 28, 12, 0, 0, 11,
+ 27, 12, 0, 29, 28, 0, 29, 0, 0, 31,
+ 4, 30, 37, 9, 0, 1, 0, 0, 36, 32,
+ 33, 0, 36, 0, 34, 33, 0, 34, 0, 6,
+ 35, 0, 36, 0, 36, 3, 36, 0, 19, 0,
+ 0, 39, 6, 38, 37, 0, 39, 0, 20, 0,
+ 0, 0, 13, 41, 43, 14, 0, 0, 13, 42,
+ 14, 0, 44, 43, 0, 44, 0, 45, 0, 48,
+ 0, 28, 0, 0, 15, 46, 28, 16, 0, 0,
+ 15, 47, 16, 0, 0, 17, 49, 28, 18, 0,
+ 0, 17, 50, 18, 0
+};
+
+#endif
+
+#if YYDEBUG != 0
+static const short yyrline[] = { 0,
+ 207, 210, 213, 213, 217, 218, 221, 227, 232, 238,
+ 244, 245, 248, 252, 258, 261, 266, 266, 272, 273,
+ 276, 279, 283, 290, 293, 294, 294, 298, 299, 302,
+ 306, 308, 311, 314, 315, 318, 320, 321, 324, 331,
+ 336, 342, 348, 355, 360, 366
+};
+#endif
+
+
+#if YYDEBUG != 0 || defined (YYERROR_VERBOSE)
+
+static const char * const yytname[] = { "$","error","$undefined.","EQ","COLON",
+"DOT","SEMICOLON","SPACE","HTAB","LINESEP","NEWLINE","BEGIN_VCARD","END_VCARD",
+"BEGIN_VCAL","END_VCAL","BEGIN_VEVENT","END_VEVENT","BEGIN_VTODO","END_VTODO",
+"ID","STRING","mime","vobjects","@1","vobject","vcard","@2","@3","items","item",
+"@4","prop","@5","attr_params","attr_param","attr","name","values","@6","value",
+"vcal","@7","@8","calitems","calitem","eventitem","@9","@10","todoitem","@11",
+"@12", NULL
+};
+#endif
+
+static const short yyr1[] = { 0,
+ 21, 23, 22, 22, 24, 24, 26, 25, 27, 25,
+ 28, 28, 30, 29, 29, 32, 31, 31, 33, 33,
+ 34, 35, 35, 36, 38, 37, 37, 39, 39, 41,
+ 40, 42, 40, 43, 43, 44, 44, 44, 46, 45,
+ 47, 45, 49, 48, 50, 48
+};
+
+static const short yyr2[] = { 0,
+ 1, 0, 3, 1, 1, 1, 0, 4, 0, 3,
+ 2, 1, 0, 5, 1, 0, 3, 1, 2, 1,
+ 2, 1, 3, 1, 0, 4, 1, 1, 0, 0,
+ 4, 0, 3, 2, 1, 1, 1, 1, 0, 4,
+ 0, 3, 0, 4, 0, 3
+};
+
+static const short yydefact[] = { 0,
+ 7, 30, 1, 2, 5, 6, 0, 0, 0, 0,
+ 0, 15, 24, 0, 0, 0, 16, 10, 39, 43,
+ 38, 0, 0, 36, 37, 33, 3, 8, 11, 13,
+ 0, 0, 0, 0, 0, 31, 34, 29, 0, 17,
+ 20, 0, 42, 0, 46, 28, 0, 27, 21, 22,
+ 19, 40, 44, 14, 25, 0, 29, 23, 26, 0,
+ 0, 0
+};
+
+static const short yydefgoto[] = { 60,
+ 3, 11, 4, 5, 7, 8, 21, 15, 38, 16,
+ 31, 40, 41, 49, 17, 47, 57, 48, 6, 9,
+ 10, 22, 23, 24, 32, 33, 25, 34, 35
+};
+
+static const short yypact[] = { -9,
+ -6, -5,-32768, 7,-32768,-32768, 2, -1, 19, 15,
+ -9,-32768,-32768, 1, 0, 26, 27,-32768, 16, 17,
+-32768, 23, 9,-32768,-32768,-32768,-32768,-32768,-32768,-32768,
+ 33, 2, 24, 2, 25,-32768,-32768, 13, 22,-32768,
+ 33, 28,-32768, 29,-32768,-32768, 36, 40,-32768, 39,
+-32768,-32768,-32768,-32768,-32768, 22, 13,-32768,-32768, 48,
+ 49,-32768
+};
+
+static const short yypgoto[] = {-32768,
+ 41,-32768,-32768,-32768,-32768,-32768, -7,-32768,-32768,-32768,
+-32768, 10,-32768,-32768, -34, -4,-32768,-32768,-32768,-32768,
+-32768, 31,-32768,-32768,-32768,-32768,-32768,-32768,-32768
+};
+
+
+#define YYLAST 54
+
+
+static const short yytable[] = { 14,
+ 12, 1, 12, 2, 50, -9, -4, 29, -32, 12,
+ 18, -12, 28, -12, -12, -12, -12, -12, 13, 12,
+ 13, 58, -35, 19, 42, 20, 44, 13, 26, 30,
+ -18, -41, 46, 19, -45, 20, 36, 13, 39, 43,
+ 13, 56, 45, 52, 54, 55, 53, 61, 62, 0,
+ 51, 27, 59, 37
+};
+
+static const short yycheck[] = { 7,
+ 1, 11, 1, 13, 39, 12, 0, 15, 14, 1,
+ 12, 12, 12, 14, 15, 16, 17, 18, 19, 1,
+ 19, 56, 14, 15, 32, 17, 34, 19, 14, 4,
+ 4, 16, 20, 15, 18, 17, 14, 19, 6, 16,
+ 19, 3, 18, 16, 9, 6, 18, 0, 0, -1,
+ 41, 11, 57, 23
+};
+/* -*-C-*- Note some compilers choke on comments on `#line' lines. */
+#line 3 "/usr/lib/bison.simple"
+/* This file comes from bison-1.27. */
+
+/* Skeleton output parser for bison,
+ Copyright (C) 1984, 1989, 1990 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+/* As a special exception, when this file is copied by Bison into a
+ Bison output file, you may use that output file without restriction.
+ This special exception was added by the Free Software Foundation
+ in version 1.24 of Bison. */
+
+/* This is the parser code that is written into each bison parser
+ when the %semantic_parser declaration is not specified in the grammar.
+ It was written by Richard Stallman by simplifying the hairy parser
+ used when %semantic_parser is specified. */
+
+#ifndef YYSTACK_USE_ALLOCA
+#ifdef alloca
+#define YYSTACK_USE_ALLOCA
+#else /* alloca not defined */
+#ifdef __GNUC__
+#define YYSTACK_USE_ALLOCA
+#define alloca __builtin_alloca
+#else /* not GNU C. */
+#if (!defined (__STDC__) && defined (sparc)) || defined (__sparc__) || defined (__sparc) || defined (__sgi) || (defined (__sun) && defined (__i386))
+#define YYSTACK_USE_ALLOCA
+#include <alloca.h>
+#else /* not sparc */
+/* We think this test detects Watcom and Microsoft C. */
+/* This used to test MSDOS, but that is a bad idea
+ since that symbol is in the user namespace. */
+#if (defined (_MSDOS) || defined (_MSDOS_)) && !defined (__TURBOC__)
+#if 0 /* No need for malloc.h, which pollutes the namespace;
+ instead, just don't use alloca. */
+#include <malloc.h>
+#endif
+#else /* not MSDOS, or __TURBOC__ */
+#if defined(_AIX)
+/* I don't know what this was needed for, but it pollutes the namespace.
+ So I turned it off. rms, 2 May 1997. */
+/* #include <malloc.h> */
+ #pragma alloca
+#define YYSTACK_USE_ALLOCA
+#else /* not MSDOS, or __TURBOC__, or _AIX */
+#if 0
+#ifdef __hpux /* haible@ilog.fr says this works for HPUX 9.05 and up,
+ and on HPUX 10. Eventually we can turn this on. */
+#define YYSTACK_USE_ALLOCA
+#define alloca __builtin_alloca
+#endif /* __hpux */
+#endif
+#endif /* not _AIX */
+#endif /* not MSDOS, or __TURBOC__ */
+#endif /* not sparc */
+#endif /* not GNU C */
+#endif /* alloca not defined */
+#endif /* YYSTACK_USE_ALLOCA not defined */
+
+#ifdef YYSTACK_USE_ALLOCA
+#define YYSTACK_ALLOC alloca
+#else
+#define YYSTACK_ALLOC malloc
+#endif
+
+/* Note: there must be only one dollar sign in this file.
+ It is replaced by the list of actions, each action
+ as one case of the switch. */
+
+#define yyerrok (yyerrstatus = 0)
+#define yyclearin (yychar = YYEMPTY)
+#define YYEMPTY -2
+#define YYEOF 0
+#define YYACCEPT goto yyacceptlab
+#define YYABORT goto yyabortlab
+#define YYERROR goto yyerrlab1
+/* Like YYERROR except do call yyerror.
+ This remains here temporarily to ease the
+ transition to the new meaning of YYERROR, for GCC.
+ Once GCC version 2 has supplanted version 1, this can go. */
+#define YYFAIL goto yyerrlab
+#define YYRECOVERING() (!!yyerrstatus)
+#define YYBACKUP(token, value) \
+do \
+ if (yychar == YYEMPTY && yylen == 1) \
+ { yychar = (token), yylval = (value); \
+ yychar1 = YYTRANSLATE (yychar); \
+ YYPOPSTACK; \
+ goto yybackup; \
+ } \
+ else \
+ { yyerror ("syntax error: cannot back up"); YYERROR; } \
+while (0)
+
+#define YYTERROR 1
+#define YYERRCODE 256
+
+#ifndef YYPURE
+#define YYLEX yylex()
+#endif
+
+#ifdef YYPURE
+#ifdef YYLSP_NEEDED
+#ifdef YYLEX_PARAM
+#define YYLEX yylex(&yylval, &yylloc, YYLEX_PARAM)
+#else
+#define YYLEX yylex(&yylval, &yylloc)
+#endif
+#else /* not YYLSP_NEEDED */
+#ifdef YYLEX_PARAM
+#define YYLEX yylex(&yylval, YYLEX_PARAM)
+#else
+#define YYLEX yylex(&yylval)
+#endif
+#endif /* not YYLSP_NEEDED */
+#endif
+
+/* If nonreentrant, generate the variables here */
+
+#ifndef YYPURE
+
+int yychar; /* the lookahead symbol */
+YYSTYPE yylval; /* the semantic value of the */
+ /* lookahead symbol */
+
+#ifdef YYLSP_NEEDED
+YYLTYPE yylloc; /* location data for the lookahead */
+ /* symbol */
+#endif
+
+int yynerrs; /* number of parse errors so far */
+#endif /* not YYPURE */
+
+#if YYDEBUG != 0
+int yydebug; /* nonzero means print parse trace */
+/* Since this is uninitialized, it does not stop multiple parsers
+ from coexisting. */
+#endif
+
+/* YYINITDEPTH indicates the initial size of the parser's stacks */
+
+#ifndef YYINITDEPTH
+#define YYINITDEPTH 200
+#endif
+
+/* YYMAXDEPTH is the maximum size the stacks can grow to
+ (effective only if the built-in stack extension method is used). */
+
+#if YYMAXDEPTH == 0
+#undef YYMAXDEPTH
+#endif
+
+#ifndef YYMAXDEPTH
+#define YYMAXDEPTH 10000
+#endif
+
+/* Define __yy_memcpy. Note that the size argument
+ should be passed with type unsigned int, because that is what the non-GCC
+ definitions require. With GCC, __builtin_memcpy takes an arg
+ of type size_t, but it can handle unsigned int. */
+
+#if __GNUC__ > 1 /* GNU C and GNU C++ define this. */
+#define __yy_memcpy(TO,FROM,COUNT) __builtin_memcpy(TO,FROM,COUNT)
+#else /* not GNU C or C++ */
+#ifndef __cplusplus
+
+/* This is the most reliable way to avoid incompatibilities
+ in available built-in functions on various systems. */
+static void
+__yy_memcpy (to, from, count)
+ char *to;
+ char *from;
+ unsigned int count;
+{
+ register char *f = from;
+ register char *t = to;
+ register int i = count;
+
+ while (i-- > 0)
+ *t++ = *f++;
+}
+
+#else /* __cplusplus */
+
+/* This is the most reliable way to avoid incompatibilities
+ in available built-in functions on various systems. */
+static void
+__yy_memcpy (char *to, char *from, unsigned int count)
+{
+ register char *t = to;
+ register char *f = from;
+ register int i = count;
+
+ while (i-- > 0)
+ *t++ = *f++;
+}
+
+#endif
+#endif
+
+#line 216 "/usr/lib/bison.simple"
+
+/* The user can define YYPARSE_PARAM as the name of an argument to be passed
+ into yyparse. The argument should have type void *.
+ It should actually point to an object.
+ Grammar actions can access the variable by casting it
+ to the proper pointer type. */
+
+#ifdef YYPARSE_PARAM
+#ifdef __cplusplus
+#define YYPARSE_PARAM_ARG void *YYPARSE_PARAM
+#define YYPARSE_PARAM_DECL
+#else /* not __cplusplus */
+#define YYPARSE_PARAM_ARG YYPARSE_PARAM
+#define YYPARSE_PARAM_DECL void *YYPARSE_PARAM;
+#endif /* not __cplusplus */
+#else /* not YYPARSE_PARAM */
+#define YYPARSE_PARAM_ARG
+#define YYPARSE_PARAM_DECL
+#endif /* not YYPARSE_PARAM */
+
+/* Prevent warning if -Wstrict-prototypes. */
+#ifdef __GNUC__
+#ifdef YYPARSE_PARAM
+int yyparse (void *);
+#else
+int yyparse (void);
+#endif
+#endif
+
+int
+yyparse(YYPARSE_PARAM_ARG)
+ YYPARSE_PARAM_DECL
+{
+ register int yystate;
+ register int yyn;
+ register short *yyssp;
+ register YYSTYPE *yyvsp;
+ int yyerrstatus; /* number of tokens to shift before error messages enabled */
+ int yychar1 = 0; /* lookahead token as an internal (translated) token number */
+
+ short yyssa[YYINITDEPTH]; /* the state stack */
+ YYSTYPE yyvsa[YYINITDEPTH]; /* the semantic value stack */
+
+ short *yyss = yyssa; /* refer to the stacks thru separate pointers */
+ YYSTYPE *yyvs = yyvsa; /* to allow yyoverflow to reallocate them elsewhere */
+
+#ifdef YYLSP_NEEDED
+ YYLTYPE yylsa[YYINITDEPTH]; /* the location stack */
+ YYLTYPE *yyls = yylsa;
+ YYLTYPE *yylsp;
+
+#define YYPOPSTACK (yyvsp--, yyssp--, yylsp--)
+#else
+#define YYPOPSTACK (yyvsp--, yyssp--)
+#endif
+
+ int yystacksize = YYINITDEPTH;
+ int yyfree_stacks = 0;
+
+#ifdef YYPURE
+ int yychar;
+ YYSTYPE yylval;
+ int yynerrs;
+#ifdef YYLSP_NEEDED
+ YYLTYPE yylloc;
+#endif
+#endif
+
+ YYSTYPE yyval; /* the variable used to return */
+ /* semantic values from the action */
+ /* routines */
+
+ int yylen;
+
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Starting parse\n");
+#endif
+
+ yystate = 0;
+ yyerrstatus = 0;
+ yynerrs = 0;
+ yychar = YYEMPTY; /* Cause a token to be read. */
+
+ /* Initialize stack pointers.
+ Waste one element of value and location stack
+ so that they stay on the same level as the state stack.
+ The wasted elements are never initialized. */
+
+ yyssp = yyss - 1;
+ yyvsp = yyvs;
+#ifdef YYLSP_NEEDED
+ yylsp = yyls;
+#endif
+
+/* Push a new state, which is found in yystate . */
+/* In all cases, when you get here, the value and location stacks
+ have just been pushed. so pushing a state here evens the stacks. */
+yynewstate:
+
+ *++yyssp = yystate;
+
+ if (yyssp >= yyss + yystacksize - 1)
+ {
+ /* Give user a chance to reallocate the stack */
+ /* Use copies of these so that the &'s don't force the real ones into memory. */
+ YYSTYPE *yyvs1 = yyvs;
+ short *yyss1 = yyss;
+#ifdef YYLSP_NEEDED
+ YYLTYPE *yyls1 = yyls;
+#endif
+
+ /* Get the current used size of the three stacks, in elements. */
+ int size = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+ /* Each stack pointer address is followed by the size of
+ the data in use in that stack, in bytes. */
+#ifdef YYLSP_NEEDED
+ /* This used to be a conditional around just the two extra args,
+ but that might be undefined if yyoverflow is a macro. */
+ yyoverflow("parser stack overflow",
+ &yyss1, size * sizeof (*yyssp),
+ &yyvs1, size * sizeof (*yyvsp),
+ &yyls1, size * sizeof (*yylsp),
+ &yystacksize);
+#else
+ yyoverflow("parser stack overflow",
+ &yyss1, size * sizeof (*yyssp),
+ &yyvs1, size * sizeof (*yyvsp),
+ &yystacksize);
+#endif
+
+ yyss = yyss1; yyvs = yyvs1;
+#ifdef YYLSP_NEEDED
+ yyls = yyls1;
+#endif
+#else /* no yyoverflow */
+ /* Extend the stack our own way. */
+ if (yystacksize >= YYMAXDEPTH)
+ {
+ yyerror("parser stack overflow");
+ if (yyfree_stacks)
+ {
+ free (yyss);
+ free (yyvs);
+#ifdef YYLSP_NEEDED
+ free (yyls);
+#endif
+ }
+ return 2;
+ }
+ yystacksize *= 2;
+ if (yystacksize > YYMAXDEPTH)
+ yystacksize = YYMAXDEPTH;
+#ifndef YYSTACK_USE_ALLOCA
+ yyfree_stacks = 1;
+#endif
+ yyss = (short *) YYSTACK_ALLOC (yystacksize * sizeof (*yyssp));
+ __yy_memcpy ((char *)yyss, (char *)yyss1,
+ size * (unsigned int) sizeof (*yyssp));
+ yyvs = (YYSTYPE *) YYSTACK_ALLOC (yystacksize * sizeof (*yyvsp));
+ __yy_memcpy ((char *)yyvs, (char *)yyvs1,
+ size * (unsigned int) sizeof (*yyvsp));
+#ifdef YYLSP_NEEDED
+ yyls = (YYLTYPE *) YYSTACK_ALLOC (yystacksize * sizeof (*yylsp));
+ __yy_memcpy ((char *)yyls, (char *)yyls1,
+ size * (unsigned int) sizeof (*yylsp));
+#endif
+#endif /* no yyoverflow */
+
+ yyssp = yyss + size - 1;
+ yyvsp = yyvs + size - 1;
+#ifdef YYLSP_NEEDED
+ yylsp = yyls + size - 1;
+#endif
+
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Stack size increased to %d\n", yystacksize);
+#endif
+
+ if (yyssp >= yyss + yystacksize - 1)
+ YYABORT;
+ }
+
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Entering state %d\n", yystate);
+#endif
+
+ goto yybackup;
+ yybackup:
+
+/* Do appropriate processing given the current state. */
+/* Read a lookahead token if we need one and don't already have one. */
+/* yyresume: */
+
+ /* First try to decide what to do without reference to lookahead token. */
+
+ yyn = yypact[yystate];
+ if (yyn == YYFLAG)
+ goto yydefault;
+
+ /* Not known => get a lookahead token if don't already have one. */
+
+ /* yychar is either YYEMPTY or YYEOF
+ or a valid token in external form. */
+
+ if (yychar == YYEMPTY)
+ {
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Reading a token: ");
+#endif
+ yychar = YYLEX;
+ }
+
+ /* Convert token to internal form (in yychar1) for indexing tables with */
+
+ if (yychar <= 0) /* This means end of input. */
+ {
+ yychar1 = 0;
+ yychar = YYEOF; /* Don't call YYLEX any more */
+
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Now at end of input.\n");
+#endif
+ }
+ else
+ {
+ yychar1 = YYTRANSLATE(yychar);
+
+#if YYDEBUG != 0
+ if (yydebug)
+ {
+ fprintf (stderr, "Next token is %d (%s", yychar, yytname[yychar1]);
+ /* Give the individual parser a way to print the precise meaning
+ of a token, for further debugging info. */
+#ifdef YYPRINT
+ YYPRINT (stderr, yychar, yylval);
+#endif
+ fprintf (stderr, ")\n");
+ }
+#endif
+ }
+
+ yyn += yychar1;
+ if (yyn < 0 || yyn > YYLAST || yycheck[yyn] != yychar1)
+ goto yydefault;
+
+ yyn = yytable[yyn];
+
+ /* yyn is what to do for this token type in this state.
+ Negative => reduce, -yyn is rule number.
+ Positive => shift, yyn is new state.
+ New state is final state => don't bother to shift,
+ just return success.
+ 0, or most negative number => error. */
+
+ if (yyn < 0)
+ {
+ if (yyn == YYFLAG)
+ goto yyerrlab;
+ yyn = -yyn;
+ goto yyreduce;
+ }
+ else if (yyn == 0)
+ goto yyerrlab;
+
+ if (yyn == YYFINAL)
+ YYACCEPT;
+
+ /* Shift the lookahead token. */
+
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Shifting token %d (%s), ", yychar, yytname[yychar1]);
+#endif
+
+ /* Discard the token being shifted unless it is eof. */
+ if (yychar != YYEOF)
+ yychar = YYEMPTY;
+
+ *++yyvsp = yylval;
+#ifdef YYLSP_NEEDED
+ *++yylsp = yylloc;
+#endif
+
+ /* count tokens shifted since error; after three, turn off error status. */
+ if (yyerrstatus) yyerrstatus--;
+
+ yystate = yyn;
+ goto yynewstate;
+
+/* Do the default action for the current state. */
+yydefault:
+
+ yyn = yydefact[yystate];
+ if (yyn == 0)
+ goto yyerrlab;
+
+/* Do a reduction. yyn is the number of a rule to reduce with. */
+yyreduce:
+ yylen = yyr2[yyn];
+ if (yylen > 0)
+ yyval = yyvsp[1-yylen]; /* implement default value of the action */
+
+#if YYDEBUG != 0
+ if (yydebug)
+ {
+ int i;
+
+ fprintf (stderr, "Reducing via rule %d (line %d), ",
+ yyn, yyrline[yyn]);
+
+ /* Print the symbols being reduced, and their result. */
+ for (i = yyprhs[yyn]; yyrhs[i] > 0; i++)
+ fprintf (stderr, "%s ", yytname[yyrhs[i]]);
+ fprintf (stderr, " -> %s\n", yytname[yyr1[yyn]]);
+ }
+#endif
+
+
+ switch (yyn) {
+
+case 2:
+#line 211 "vcc.y"
+{ addList(&vObjList, yyvsp[0].vobj); curObj = 0; ;
+ break;}
+case 4:
+#line 214 "vcc.y"
+{ addList(&vObjList, yyvsp[0].vobj); curObj = 0; ;
+ break;}
+case 7:
+#line 223 "vcc.y"
+{
+ lexPushMode(L_VCARD);
+ if (!pushVObject(VCCardProp)) YYERROR;
+ ;
+ break;}
+case 8:
+#line 228 "vcc.y"
+{
+ lexPopMode(0);
+ yyval.vobj = popVObject();
+ ;
+ break;}
+case 9:
+#line 233 "vcc.y"
+{
+ lexPushMode(L_VCARD);
+ if (!pushVObject(VCCardProp)) YYERROR;
+ ;
+ break;}
+case 10:
+#line 238 "vcc.y"
+{
+ lexPopMode(0);
+ yyval.vobj = popVObject();
+ ;
+ break;}
+case 13:
+#line 249 "vcc.y"
+{
+ lexPushMode(L_VALUES);
+ ;
+ break;}
+case 14:
+#line 253 "vcc.y"
+{
+ if (lexWithinMode(L_BASE64) || lexWithinMode(L_QUOTED_PRINTABLE))
+ lexPopMode(0);
+ lexPopMode(0);
+ ;
+ break;}
+case 16:
+#line 262 "vcc.y"
+{
+ enterProps(yyvsp[0].str);
+ ;
+ break;}
+case 18:
+#line 267 "vcc.y"
+{
+ enterProps(yyvsp[0].str);
+ ;
+ break;}
+case 22:
+#line 280 "vcc.y"
+{
+ enterAttr(yyvsp[0].str,0);
+ ;
+ break;}
+case 23:
+#line 284 "vcc.y"
+{
+ enterAttr(yyvsp[-2].str,yyvsp[0].str);
+
+ ;
+ break;}
+case 25:
+#line 293 "vcc.y"
+{ enterValues(yyvsp[-1].str); ;
+ break;}
+case 27:
+#line 295 "vcc.y"
+{ enterValues(yyvsp[0].str); ;
+ break;}
+case 29:
+#line 299 "vcc.y"
+{ yyval.str = 0; ;
+ break;}
+case 30:
+#line 304 "vcc.y"
+{ if (!pushVObject(VCCalProp)) YYERROR; ;
+ break;}
+case 31:
+#line 307 "vcc.y"
+{ yyval.vobj = popVObject(); ;
+ break;}
+case 32:
+#line 309 "vcc.y"
+{ if (!pushVObject(VCCalProp)) YYERROR; ;
+ break;}
+case 33:
+#line 311 "vcc.y"
+{ yyval.vobj = popVObject(); ;
+ break;}
+case 39:
+#line 326 "vcc.y"
+{
+ lexPushMode(L_VEVENT);
+ if (!pushVObject(VCEventProp)) YYERROR;
+ ;
+ break;}
+case 40:
+#line 332 "vcc.y"
+{
+ lexPopMode(0);
+ popVObject();
+ ;
+ break;}
+case 41:
+#line 337 "vcc.y"
+{
+ lexPushMode(L_VEVENT);
+ if (!pushVObject(VCEventProp)) YYERROR;
+ ;
+ break;}
+case 42:
+#line 342 "vcc.y"
+{
+ lexPopMode(0);
+ popVObject();
+ ;
+ break;}
+case 43:
+#line 350 "vcc.y"
+{
+ lexPushMode(L_VTODO);
+ if (!pushVObject(VCTodoProp)) YYERROR;
+ ;
+ break;}
+case 44:
+#line 356 "vcc.y"
+{
+ lexPopMode(0);
+ popVObject();
+ ;
+ break;}
+case 45:
+#line 361 "vcc.y"
+{
+ lexPushMode(L_VTODO);
+ if (!pushVObject(VCTodoProp)) YYERROR;
+ ;
+ break;}
+case 46:
+#line 366 "vcc.y"
+{
+ lexPopMode(0);
+ popVObject();
+ ;
+ break;}
+}
+ /* the action file gets copied in in place of this dollarsign */
+#line 542 "/usr/lib/bison.simple"
+
+ yyvsp -= yylen;
+ yyssp -= yylen;
+#ifdef YYLSP_NEEDED
+ yylsp -= yylen;
+#endif
+
+#if YYDEBUG != 0
+ if (yydebug)
+ {
+ short *ssp1 = yyss - 1;
+ fprintf (stderr, "state stack now");
+ while (ssp1 != yyssp)
+ fprintf (stderr, " %d", *++ssp1);
+ fprintf (stderr, "\n");
+ }
+#endif
+
+ *++yyvsp = yyval;
+
+#ifdef YYLSP_NEEDED
+ yylsp++;
+ if (yylen == 0)
+ {
+ yylsp->first_line = yylloc.first_line;
+ yylsp->first_column = yylloc.first_column;
+ yylsp->last_line = (yylsp-1)->last_line;
+ yylsp->last_column = (yylsp-1)->last_column;
+ yylsp->text = 0;
+ }
+ else
+ {
+ yylsp->last_line = (yylsp+yylen-1)->last_line;
+ yylsp->last_column = (yylsp+yylen-1)->last_column;
+ }
+#endif
+
+ /* Now "shift" the result of the reduction.
+ Determine what state that goes to,
+ based on the state we popped back to
+ and the rule number reduced by. */
+
+ yyn = yyr1[yyn];
+
+ yystate = yypgoto[yyn - YYNTBASE] + *yyssp;
+ if (yystate >= 0 && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+ yystate = yytable[yystate];
+ else
+ yystate = yydefgoto[yyn - YYNTBASE];
+
+ goto yynewstate;
+
+yyerrlab: /* here on detecting error */
+
+ if (! yyerrstatus)
+ /* If not already recovering from an error, report this error. */
+ {
+ ++yynerrs;
+
+#ifdef YYERROR_VERBOSE
+ yyn = yypact[yystate];
+
+ if (yyn > YYFLAG && yyn < YYLAST)
+ {
+ int size = 0;
+ char *msg;
+ int x, count;
+
+ count = 0;
+ /* Start X at -yyn if nec to avoid negative indexes in yycheck. */
+ for (x = (yyn < 0 ? -yyn : 0);
+ x < (sizeof(yytname) / sizeof(char *)); x++)
+ if (yycheck[x + yyn] == x)
+ size += strlen(yytname[x]) + 15, count++;
+ msg = (char *) malloc(size + 15);
+ if (msg != 0)
+ {
+ strcpy(msg, "parse error");
+
+ if (count < 5)
+ {
+ count = 0;
+ for (x = (yyn < 0 ? -yyn : 0);
+ x < (sizeof(yytname) / sizeof(char *)); x++)
+ if (yycheck[x + yyn] == x)
+ {
+ strcat(msg, count == 0 ? ", expecting `" : " or `");
+ strcat(msg, yytname[x]);
+ strcat(msg, "'");
+ count++;
+ }
+ }
+ yyerror(msg);
+ free(msg);
+ }
+ else
+ yyerror ("parse error; also virtual memory exceeded");
+ }
+ else
+#endif /* YYERROR_VERBOSE */
+ yyerror("parse error");
+ }
+
+ goto yyerrlab1;
+yyerrlab1: /* here on error raised explicitly by an action */
+
+ if (yyerrstatus == 3)
+ {
+ /* if just tried and failed to reuse lookahead token after an error, discard it. */
+
+ /* return failure if at end of input */
+ if (yychar == YYEOF)
+ YYABORT;
+
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Discarding token %d (%s).\n", yychar, yytname[yychar1]);
+#endif
+
+ yychar = YYEMPTY;
+ }
+
+ /* Else will try to reuse lookahead token
+ after shifting the error token. */
+
+ yyerrstatus = 3; /* Each real token shifted decrements this */
+
+ goto yyerrhandle;
+
+yyerrdefault: /* current state does not do anything special for the error token. */
+
+#if 0
+ /* This is wrong; only states that explicitly want error tokens
+ should shift them. */
+ yyn = yydefact[yystate]; /* If its default is to accept any token, ok. Otherwise pop it.*/
+ if (yyn) goto yydefault;
+#endif
+
+yyerrpop: /* pop the current state because it cannot handle the error token */
+
+ if (yyssp == yyss) YYABORT;
+ yyvsp--;
+ yystate = *--yyssp;
+#ifdef YYLSP_NEEDED
+ yylsp--;
+#endif
+
+#if YYDEBUG != 0
+ if (yydebug)
+ {
+ short *ssp1 = yyss - 1;
+ fprintf (stderr, "Error: state stack now");
+ while (ssp1 != yyssp)
+ fprintf (stderr, " %d", *++ssp1);
+ fprintf (stderr, "\n");
+ }
+#endif
+
+yyerrhandle:
+
+ yyn = yypact[yystate];
+ if (yyn == YYFLAG)
+ goto yyerrdefault;
+
+ yyn += YYTERROR;
+ if (yyn < 0 || yyn > YYLAST || yycheck[yyn] != YYTERROR)
+ goto yyerrdefault;
+
+ yyn = yytable[yyn];
+ if (yyn < 0)
+ {
+ if (yyn == YYFLAG)
+ goto yyerrpop;
+ yyn = -yyn;
+ goto yyreduce;
+ }
+ else if (yyn == 0)
+ goto yyerrpop;
+
+ if (yyn == YYFINAL)
+ YYACCEPT;
+
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Shifting error token, ");
+#endif
+
+ *++yyvsp = yylval;
+#ifdef YYLSP_NEEDED
+ *++yylsp = yylloc;
+#endif
+
+ yystate = yyn;
+ goto yynewstate;
+
+ yyacceptlab:
+ /* YYACCEPT comes here. */
+ if (yyfree_stacks)
+ {
+ free (yyss);
+ free (yyvs);
+#ifdef YYLSP_NEEDED
+ free (yyls);
+#endif
+ }
+ return 0;
+
+ yyabortlab:
+ /* YYABORT comes here. */
+ if (yyfree_stacks)
+ {
+ free (yyss);
+ free (yyvs);
+#ifdef YYLSP_NEEDED
+ free (yyls);
+#endif
+ }
+ return 1;
+}
+#line 372 "vcc.y"
+
+/*/////////////////////////////////////////////////////////////////////////*/
+static int pushVObject(const char *prop)
+ {
+ VObject *newObj;
+ if (ObjStackTop == MAXLEVEL)
+ return FALSE;
+
+ ObjStack[++ObjStackTop] = curObj;
+
+ if (curObj) {
+ newObj = addProp(curObj,prop);
+ curObj = newObj;
+ }
+ else
+ curObj = newVObject(prop);
+
+ return TRUE;
+ }
+
+
+/*/////////////////////////////////////////////////////////////////////////*/
+/* This pops the recently built vCard off the stack and returns it. */
+static VObject* popVObject()
+ {
+ VObject *oldObj;
+ if (ObjStackTop < 0) {
+ yyerror("pop on empty Object Stack\n");
+ return 0;
+ }
+ oldObj = curObj;
+ curObj = ObjStack[ObjStackTop--];
+
+ return oldObj;
+ }
+
+
+static void enterValues(const char *value)
+ {
+ if (fieldedProp && *fieldedProp) {
+ if (value) {
+ addPropValue(curProp,*fieldedProp,value);
+ }
+ /* else this field is empty, advance to next field */
+ fieldedProp++;
+ }
+ else {
+ if (value) {
+ setVObjectUStringZValue_(curProp,fakeUnicode(value,0));
+ }
+ }
+ deleteStr(value);
+ }
+
+static void enterProps(const char *s)
+ {
+ curProp = addGroup(curObj,s);
+ deleteStr(s);
+ }
+
+static void enterAttr(const char *s1, const char *s2)
+ {
+ const char *p1, *p2;
+ p1 = lookupProp_(s1);
+ if (s2) {
+ VObject *a;
+ p2 = lookupProp_(s2);
+ a = addProp(curProp,p1);
+ setVObjectStringZValue(a,p2);
+ }
+ else
+ addProp(curProp,p1);
+ if (stricmp(p1,VCBase64Prop) == 0 || (s2 && stricmp(p2,VCBase64Prop)==0))
+ lexPushMode(L_BASE64);
+ else if (stricmp(p1,VCQuotedPrintableProp) == 0
+ || (s2 && stricmp(p2,VCQuotedPrintableProp)==0))
+ lexPushMode(L_QUOTED_PRINTABLE);
+ deleteStr(s1); deleteStr(s2);
+ }
+
+
+#define MAX_LEX_LOOKAHEAD_0 32
+#define MAX_LEX_LOOKAHEAD 64
+#define MAX_LEX_MODE_STACK_SIZE 10
+#define LEXMODE() (lexBuf.lexModeStack[lexBuf.lexModeStackTop])
+
+struct LexBuf {
+ /* input */
+#ifdef INCLUDEMFC
+ CFile *inputFile;
+#else
+ FILE *inputFile;
+#endif
+ char *inputString;
+ unsigned long curPos;
+ unsigned long inputLen;
+ /* lookahead buffer */
+ /* -- lookahead buffer is short instead of char so that EOF
+ / can be represented correctly.
+ */
+ unsigned long len;
+ short buf[MAX_LEX_LOOKAHEAD];
+ unsigned long getPtr;
+ /* context stack */
+ unsigned long lexModeStackTop;
+ enum LexMode lexModeStack[MAX_LEX_MODE_STACK_SIZE];
+ /* token buffer */
+ unsigned long maxToken;
+ char *strs;
+ unsigned long strsLen;
+ } lexBuf;
+
+static void lexPushMode(enum LexMode mode)
+ {
+ if (lexBuf.lexModeStackTop == (MAX_LEX_MODE_STACK_SIZE-1))
+ yyerror("lexical context stack overflow");
+ else {
+ lexBuf.lexModeStack[++lexBuf.lexModeStackTop] = mode;
+ }
+ }
+
+static void lexPopMode(int top)
+ {
+ /* special case of pop for ease of error recovery -- this
+ version will never underflow */
+ if (top)
+ lexBuf.lexModeStackTop = 0;
+ else
+ if (lexBuf.lexModeStackTop > 0) lexBuf.lexModeStackTop--;
+ }
+
+static int lexWithinMode(enum LexMode mode) {
+ unsigned long i;
+ for (i=0;i<lexBuf.lexModeStackTop;i++)
+ if (mode == lexBuf.lexModeStack[i]) return 1;
+ return 0;
+ }
+
+static char lexGetc_()
+ {
+ /* get next char from input, no buffering. */
+ if (lexBuf.curPos == lexBuf.inputLen)
+ return EOF;
+ else if (lexBuf.inputString)
+ return *(lexBuf.inputString + lexBuf.curPos++);
+ else {
+#ifdef INCLUDEMFC
+ char result;
+ return lexBuf.inputFile->Read(&result, 1) == 1 ? result : EOF;
+#else
+ return fgetc(lexBuf.inputFile);
+#endif
+ }
+ }
+
+static int lexGeta()
+ {
+ ++lexBuf.len;
+ return (lexBuf.buf[lexBuf.getPtr] = lexGetc_());
+ }
+
+static int lexGeta_(int i)
+ {
+ ++lexBuf.len;
+ return (lexBuf.buf[(lexBuf.getPtr+i)%MAX_LEX_LOOKAHEAD] = lexGetc_());
+ }
+
+static void lexSkipLookahead() {
+ if (lexBuf.len > 0 && lexBuf.buf[lexBuf.getPtr]!=EOF) {
+ /* don't skip EOF. */
+ lexBuf.getPtr = (lexBuf.getPtr + 1) % MAX_LEX_LOOKAHEAD;
+ lexBuf.len--;
+ }
+ }
+
+static int lexLookahead() {
+ int c = (lexBuf.len)?
+ lexBuf.buf[lexBuf.getPtr]:
+ lexGeta();
+ /* do the \r\n -> \n or \r -> \n translation here */
+ if (c == '\r') {
+ int a = (lexBuf.len>1)?
+ lexBuf.buf[(lexBuf.getPtr+1)%MAX_LEX_LOOKAHEAD]:
+ lexGeta_(1);
+ if (a == '\n') {
+ lexSkipLookahead();
+ }
+ lexBuf.buf[lexBuf.getPtr] = c = '\n';
+ }
+ else if (c == '\n') {
+ int a = (lexBuf.len>1)?
+ lexBuf.buf[lexBuf.getPtr+1]:
+ lexGeta_(1);
+ if (a == '\r') {
+ lexSkipLookahead();
+ }
+ lexBuf.buf[lexBuf.getPtr] = '\n';
+ }
+ return c;
+ }
+
+static int lexGetc() {
+ int c = lexLookahead();
+ if (lexBuf.len > 0 && lexBuf.buf[lexBuf.getPtr]!=EOF) {
+ /* EOF will remain in lookahead buffer */
+ lexBuf.getPtr = (lexBuf.getPtr + 1) % MAX_LEX_LOOKAHEAD;
+ lexBuf.len--;
+ }
+ return c;
+ }
+
+static void lexSkipLookaheadWord() {
+ if (lexBuf.strsLen <= lexBuf.len) {
+ lexBuf.len -= lexBuf.strsLen;
+ lexBuf.getPtr = (lexBuf.getPtr + lexBuf.strsLen) % MAX_LEX_LOOKAHEAD;
+ }
+ }
+
+static void lexClearToken()
+ {
+ lexBuf.strsLen = 0;
+ }
+
+static void lexAppendc(int c)
+ {
+ lexBuf.strs[lexBuf.strsLen] = c;
+ /* append up to zero termination */
+ if (c == 0) return;
+ lexBuf.strsLen++;
+ if (lexBuf.strsLen > lexBuf.maxToken) {
+ /* double the token string size */
+ lexBuf.maxToken <<= 1;
+ lexBuf.strs = (char*) realloc(lexBuf.strs,(size_t)lexBuf.maxToken);
+ }
+ }
+
+static char* lexStr() {
+ return dupStr(lexBuf.strs,(size_t)lexBuf.strsLen+1);
+ }
+
+static void lexSkipWhite() {
+ int c = lexLookahead();
+ while (c == ' ' || c == '\t') {
+ lexSkipLookahead();
+ c = lexLookahead();
+ }
+ }
+
+static char* lexGetWord() {
+ int c;
+ lexSkipWhite();
+ lexClearToken();
+ c = lexLookahead();
+ while (c != EOF && !strchr("\t\n ;:=",c)) {
+ lexAppendc(c);
+ lexSkipLookahead();
+ c = lexLookahead();
+ }
+ lexAppendc(0);
+ return lexStr();
+ }
+
+static void lexPushLookahead(char *s, int len) {
+ int putptr;
+ if (len == 0) len = strlen(s);
+ putptr = (int)lexBuf.getPtr - len;
+ /* this function assumes that length of word to push back
+ / is not greater than MAX_LEX_LOOKAHEAD.
+ */
+ if (putptr < 0) putptr += MAX_LEX_LOOKAHEAD;
+ lexBuf.getPtr = putptr;
+ while (*s) {
+ lexBuf.buf[putptr] = *s++;
+ putptr = (putptr + 1) % MAX_LEX_LOOKAHEAD;
+ }
+ lexBuf.len += len;
+ }
+
+static void lexPushLookaheadc(int c) {
+ int putptr;
+ /* can't putback EOF, because it never leaves lookahead buffer */
+ if (c == EOF) return;
+ putptr = (int)lexBuf.getPtr - 1;
+ if (putptr < 0) putptr += MAX_LEX_LOOKAHEAD;
+ lexBuf.getPtr = putptr;
+ lexBuf.buf[putptr] = c;
+ lexBuf.len += 1;
+ }
+
+static char* lexLookaheadWord() {
+ /* this function can lookahead word with max size of MAX_LEX_LOOKAHEAD_0
+ / and thing bigger than that will stop the lookahead and return 0;
+ / leading white spaces are not recoverable.
+ */
+ int c;
+ int len = 0;
+ int curgetptr = 0;
+ lexSkipWhite();
+ lexClearToken();
+ curgetptr = (int)lexBuf.getPtr; /* remember! */
+ while (len < (MAX_LEX_LOOKAHEAD_0)) {
+ c = lexGetc();
+ len++;
+ if (c == EOF || strchr("\t\n ;:=", c)) {
+ lexAppendc(0);
+ /* restore lookahead buf. */
+ lexBuf.len += len;
+ lexBuf.getPtr = curgetptr;
+ return lexStr();
+ }
+ else
+ lexAppendc(c);
+ }
+ lexBuf.len += len; /* char that has been moved to lookahead buffer */
+ lexBuf.getPtr = curgetptr;
+ return 0;
+ }
+
+#ifdef _SUPPORT_LINE_FOLDING
+static void handleMoreRFC822LineBreak(int c) {
+ /* suport RFC 822 line break in cases like
+ * ADR: foo;
+ * morefoo;
+ * more foo;
+ */
+ if (c == ';') {
+ int a;
+ lexSkipLookahead();
+ /* skip white spaces */
+ a = lexLookahead();
+ while (a == ' ' || a == '\t') {
+ lexSkipLookahead();
+ a = lexLookahead();
+ }
+ if (a == '\n') {
+ lexSkipLookahead();
+ a = lexLookahead();
+ if (a == ' ' || a == '\t') {
+ /* continuation, throw away all the \n and spaces read so
+ * far
+ */
+ lexSkipWhite();
+ lexPushLookaheadc(';');
+ }
+ else {
+ lexPushLookaheadc('\n');
+ lexPushLookaheadc(';');
+ }
+ }
+ else {
+ lexPushLookaheadc(';');
+ }
+ }
+ }
+
+static char* lexGet1Value() {
+ int size = 0;
+ int c;
+ lexSkipWhite();
+ c = lexLookahead();
+ lexClearToken();
+ while (c != EOF && c != ';') {
+ if (c == '\n') {
+ int a;
+ lexSkipLookahead();
+ a = lexLookahead();
+ if (a == ' ' || a == '\t') {
+ lexAppendc(' ');
+ lexSkipLookahead();
+ }
+ else {
+ lexPushLookaheadc('\n');
+ break;
+ }
+ }
+ else {
+ lexAppendc(c);
+ lexSkipLookahead();
+ }
+ c = lexLookahead();
+ }
+ lexAppendc(0);
+ handleMoreRFC822LineBreak(c);
+ return c==EOF?0:lexStr();
+ }
+#endif
+
+static char* lexGetStrUntil(char *termset) {
+ int size = 0;
+ int c = lexLookahead();
+ lexClearToken();
+ while (c != EOF && !strchr(termset,c)) {
+ lexAppendc(c);
+ lexSkipLookahead();
+ c = lexLookahead();
+ }
+ lexAppendc(0);
+ return c==EOF?0:lexStr();
+ }
+
+static int match_begin_name(int end) {
+ char *n = lexLookaheadWord();
+ int token = ID;
+ if (n) {
+ if (!stricmp(n,"vcard")) token = end?END_VCARD:BEGIN_VCARD;
+ else if (!stricmp(n,"vcalendar")) token = end?END_VCAL:BEGIN_VCAL;
+ else if (!stricmp(n,"vevent")) token = end?END_VEVENT:BEGIN_VEVENT;
+ else if (!stricmp(n,"vtodo")) token = end?END_VTODO:BEGIN_VTODO;
+ deleteStr(n);
+ return token;
+ }
+ return 0;
+ }
+
+
+#ifdef INCLUDEMFC
+void initLex(const char *inputstring, unsigned long inputlen, CFile *inputfile)
+#else
+void initLex(const char *inputstring, unsigned long inputlen, FILE *inputfile)
+#endif
+ {
+ /* initialize lex mode stack */
+ lexBuf.lexModeStack[lexBuf.lexModeStackTop=0] = L_NORMAL;
+
+ /* iniatialize lex buffer. */
+ lexBuf.inputString = (char*) inputstring;
+ lexBuf.inputLen = inputlen;
+ lexBuf.curPos = 0;
+ lexBuf.inputFile = inputfile;
+
+ lexBuf.len = 0;
+ lexBuf.getPtr = 0;
+
+ lexBuf.maxToken = MAXTOKEN;
+ lexBuf.strs = (char*)malloc(MAXTOKEN);
+ lexBuf.strsLen = 0;
+
+ }
+
+static void finiLex() {
+ free(lexBuf.strs);
+ }
+
+
+/*/////////////////////////////////////////////////////////////////////////*/
+/* This parses and converts the base64 format for binary encoding into
+ * a decoded buffer (allocated with new). See RFC 1521.
+ */
+static char * lexGetDataFromBase64()
+ {
+ unsigned long bytesLen = 0, bytesMax = 0;
+ int quadIx = 0, pad = 0;
+ unsigned long trip = 0;
+ unsigned char b;
+ int c;
+ unsigned char *bytes = NULL;
+ unsigned char *oldBytes = NULL;
+
+ DBG_(("db: lexGetDataFromBase64\n"));
+ while (1) {
+ c = lexGetc();
+ if (c == '\n') {
+ ++mime_lineNum;
+ if (lexLookahead() == '\n') {
+ /* a '\n' character by itself means end of data */
+ break;
+ }
+ else continue; /* ignore '\n' */
+ }
+ else {
+ if ((c >= 'A') && (c <= 'Z'))
+ b = (unsigned char)(c - 'A');
+ else if ((c >= 'a') && (c <= 'z'))
+ b = (unsigned char)(c - 'a') + 26;
+ else if ((c >= '0') && (c <= '9'))
+ b = (unsigned char)(c - '0') + 52;
+ else if (c == '+')
+ b = 62;
+ else if (c == '/')
+ b = 63;
+ else if (c == '=') {
+ b = 0;
+ pad++;
+ } else if ((c == ' ') || (c == '\t')) {
+ continue;
+ } else { /* error condition */
+ if (bytes) free(bytes);
+ else if (oldBytes) free(oldBytes);
+ /* error recovery: skip until 2 adjacent newlines. */
+ DBG_(("db: invalid character 0x%x '%c'\n", c,c));
+ if (c != EOF) {
+ c = lexGetc();
+ while (c != EOF) {
+ if (c == '\n' && lexLookahead() == '\n') {
+ ++mime_lineNum;
+ break;
+ }
+ c = lexGetc();
+ }
+ }
+ return NULL;
+ }
+ trip = (trip << 6) | b;
+ if (++quadIx == 4) {
+ unsigned char outBytes[3];
+ int numOut;
+ int i;
+ for (i = 0; i < 3; i++) {
+ outBytes[2-i] = (unsigned char)(trip & 0xFF);
+ trip >>= 8;
+ }
+ numOut = 3 - pad;
+ if (bytesLen + numOut > bytesMax) {
+ if (!bytes) {
+ bytesMax = 1024;
+ bytes = (unsigned char*)malloc((size_t)bytesMax);
+ }
+ else {
+ bytesMax <<= 2;
+ oldBytes = bytes;
+ bytes = (unsigned char*)realloc(bytes,(size_t)bytesMax);
+ }
+ if (bytes == 0) {
+ mime_error("out of memory while processing BASE64 data\n");
+ }
+ }
+ if (bytes) {
+ memcpy(bytes + bytesLen, outBytes, numOut);
+ bytesLen += numOut;
+ }
+ trip = 0;
+ quadIx = 0;
+ }
+ }
+ } /* while */
+ DBG_(("db: bytesLen = %d\n", bytesLen));
+ /* kludge: all this won't be necessary if we have tree form
+ representation */
+ if (bytes) {
+ setValueWithSize(curProp,bytes,(unsigned int)bytesLen);
+ free(bytes);
+ }
+ else if (oldBytes) {
+ setValueWithSize(curProp,oldBytes,(unsigned int)bytesLen);
+ free(oldBytes);
+ }
+ return 0;
+ }
+
+static int match_begin_end_name(int end) {
+ int token;
+ lexSkipWhite();
+ if (lexLookahead() != ':') return ID;
+ lexSkipLookahead();
+ lexSkipWhite();
+ token = match_begin_name(end);
+ if (token == ID) {
+ lexPushLookaheadc(':');
+ DBG_(("db: ID '%s'\n", yylval.str));
+ return ID;
+ }
+ else if (token != 0) {
+ lexSkipLookaheadWord();
+ deleteStr(yylval.str);
+ DBG_(("db: begin/end %d\n", token));
+ return token;
+ }
+ return 0;
+ }
+
+static char* lexGetQuotedPrintable()
+ {
+ char cur;
+ unsigned long len = 0;
+
+ lexClearToken();
+ do {
+ cur = lexGetc();
+ switch (cur) {
+ case '=': {
+ int c = 0;
+ int next[2];
+ int i;
+ for (i = 0; i < 2; i++) {
+ next[i] = lexGetc();
+ if (next[i] >= '0' && next[i] <= '9')
+ c = c * 16 + next[i] - '0';
+ else if (next[i] >= 'A' && next[i] <= 'F')
+ c = c * 16 + next[i] - 'A' + 10;
+ else
+ break;
+ }
+ if (i == 0) {
+ /* single '=' follow by LINESEP is continuation sign? */
+ if (next[0] == '\n') {
+ ++mime_lineNum;
+ }
+ else {
+ lexPushLookaheadc('=');
+ goto EndString;
+ }
+ }
+ else if (i == 1) {
+ lexPushLookaheadc(next[1]);
+ lexPushLookaheadc(next[0]);
+ lexAppendc('=');
+ } else {
+ lexAppendc(c);
+ }
+ break;
+ } /* '=' */
+ case '\n': {
+ lexPushLookaheadc('\n');
+ goto EndString;
+ }
+ case (char)EOF:
+ break;
+ default:
+ lexAppendc(cur);
+ break;
+ } /* switch */
+ } while (cur != (char)EOF);
+
+EndString:
+ lexAppendc(0);
+ return lexStr();
+ } /* LexQuotedPrintable */
+
+static int yylex() {
+ int token = 0;
+
+ int lexmode = LEXMODE();
+ if (lexmode == L_VALUES) {
+ int c = lexGetc();
+ if (c == ';') {
+ DBG_(("db: SEMICOLON\n"));
+ lexPushLookaheadc(c);
+#ifdef _SUPPORT_LINE_FOLDING
+ handleMoreRFC822LineBreak(c);
+#endif
+ lexSkipLookahead();
+ return SEMICOLON;
+ }
+ else if (strchr("\n",c)) {
+ ++mime_lineNum;
+ /* consume all line separator(s) adjacent to each other */
+ c = lexLookahead();
+ while (strchr("\n",c)) {
+ lexSkipLookahead();
+ c = lexLookahead();
+ ++mime_lineNum;
+ }
+ DBG_(("db: LINESEP\n"));
+ return LINESEP;
+ }
+ else {
+ char *p = 0;
+ lexPushLookaheadc(c);
+ if (lexWithinMode(L_BASE64)) {
+ /* get each char and convert to bin on the fly... */
+ p = lexGetDataFromBase64();
+ yylval.str = p;
+ return STRING;
+ }
+ else if (lexWithinMode(L_QUOTED_PRINTABLE)) {
+ p = lexGetQuotedPrintable();
+ }
+ else {
+#ifdef _SUPPORT_LINE_FOLDING
+ p = lexGet1Value();
+#else
+ p = lexGetStrUntil(";\n");
+#endif
+ }
+ if (p) {
+ DBG_(("db: STRING: '%s'\n", p));
+ yylval.str = p;
+ return STRING;
+ }
+ else return 0;
+ }
+ }
+ else {
+ /* normal mode */
+ while (1) {
+ int c = lexGetc();
+ switch(c) {
+ case ':': {
+ /* consume all line separator(s) adjacent to each other */
+ /* ignoring linesep immediately after colon. */
+/* c = lexLookahead();
+ while (strchr("\n",c)) {
+ lexSkipLookahead();
+ c = lexLookahead();
+ ++mime_lineNum;
+ }*/
+ DBG_(("db: COLON\n"));
+ return COLON;
+ }
+ case ';':
+ DBG_(("db: SEMICOLON\n"));
+ return SEMICOLON;
+ case '=':
+ DBG_(("db: EQ\n"));
+ return EQ;
+ /* ignore whitespace in this mode */
+ case '\t':
+ case ' ': continue;
+ case '\n': {
+ ++mime_lineNum;
+ continue;
+ }
+ case EOF: return 0;
+ break;
+ default: {
+ lexPushLookaheadc(c);
+ if (isalpha(c)) {
+ char *t = lexGetWord();
+ yylval.str = t;
+ if (!stricmp(t, "begin")) {
+ return match_begin_end_name(0);
+ }
+ else if (!stricmp(t,"end")) {
+ return match_begin_end_name(1);
+ }
+ else {
+ DBG_(("db: ID '%s'\n", t));
+ return ID;
+ }
+ }
+ else {
+ /* unknow token */
+ return 0;
+ }
+ break;
+ }
+ }
+ }
+ }
+ return 0;
+ }
+
+
+/***************************************************************************/
+/*** Public Functions ****/
+/***************************************************************************/
+
+static VObject* Parse_MIMEHelper()
+ {
+ ObjStackTop = -1;
+ mime_numErrors = 0;
+ mime_lineNum = 1;
+ vObjList = 0;
+ curObj = 0;
+
+ if (yyparse() != 0)
+ return 0;
+
+ finiLex();
+ return vObjList;
+ }
+
+/*/////////////////////////////////////////////////////////////////////////*/
+DLLEXPORT(VObject*) Parse_MIME(const char *input, unsigned long len)
+ {
+ initLex(input, len, 0);
+ return Parse_MIMEHelper();
+ }
+
+
+#if INCLUDEMFC
+
+DLLEXPORT(VObject*) Parse_MIME_FromFile(CFile *file)
+ {
+ unsigned long startPos;
+ VObject *result;
+
+ initLex(0,-1,file);
+ startPos = file->GetPosition();
+ if (!(result = Parse_MIMEHelper()))
+ file->Seek(startPos, CFile::begin);
+ return result;
+ }
+
+#else
+
+VObject* Parse_MIME_FromFile(FILE *file)
+ {
+ VObject *result;
+ long startPos;
+
+ initLex(0,(unsigned long)-1,file);
+ startPos = ftell(file);
+ if (!(result = Parse_MIMEHelper())) {
+ fseek(file,startPos,SEEK_SET);
+ }
+ return result;
+ }
+
+DLLEXPORT(VObject*) Parse_MIME_FromFileName(char *fname)
+ {
+ FILE *fp = fopen(fname,"r");
+ if (fp) {
+ VObject* o = Parse_MIME_FromFile(fp);
+ fclose(fp);
+ return o;
+ }
+ else {
+ char msg[256];
+ snprintf(msg, sizeof(msg), "can't open file '%s' for reading\n", fname);
+ mime_error_(msg);
+ return 0;
+ }
+ }
+
+#endif
+
+/*/////////////////////////////////////////////////////////////////////////*/
+static void YYDebug(const char *s)
+{
+/* Parse_Debug(s); */
+}
+
+
+static MimeErrorHandler mimeErrorHandler;
+
+DLLEXPORT(void) registerMimeErrorHandler(MimeErrorHandler me)
+ {
+ mimeErrorHandler = me;
+ }
+
+static void mime_error(char *s)
+ {
+ char msg[256];
+ if (mimeErrorHandler) {
+ sprintf(msg,"%s at line %d", s, mime_lineNum);
+ mimeErrorHandler(msg);
+ }
+ }
+
+static void mime_error_(char *s)
+ {
+ if (mimeErrorHandler) {
+ mimeErrorHandler(s);
+ }
+ }
+
diff --git a/libical/src/libicalvcal/vcc.h b/libical/src/libicalvcal/vcc.h
new file mode 100644
index 0000000000..0e52034710
--- /dev/null
+++ b/libical/src/libicalvcal/vcc.h
@@ -0,0 +1,80 @@
+/***************************************************************************
+(C) Copyright 1996 Apple Computer, Inc., AT&T Corp., International
+Business Machines Corporation and Siemens Rolm Communications Inc.
+
+For purposes of this license notice, the term Licensors shall mean,
+collectively, Apple Computer, Inc., AT&T Corp., International
+Business Machines Corporation and Siemens Rolm Communications Inc.
+The term Licensor shall mean any of the Licensors.
+
+Subject to acceptance of the following conditions, permission is hereby
+granted by Licensors without the need for written agreement and without
+license or royalty fees, to use, copy, modify and distribute this
+software for any purpose.
+
+The above copyright notice and the following four paragraphs must be
+reproduced in all copies of this software and any software including
+this software.
+
+THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS AND NO LICENSOR SHALL HAVE
+ANY OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS OR
+MODIFICATIONS.
+
+IN NO EVENT SHALL ANY LICENSOR BE LIABLE TO ANY PARTY FOR DIRECT,
+INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT
+OF THE USE OF THIS SOFTWARE EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGE.
+
+EACH LICENSOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, EXPRESS OR IMPLIED,
+INCLUDING BUT NOT LIMITED TO ANY WARRANTY OF NONINFRINGEMENT OR THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.
+
+The software is provided with RESTRICTED RIGHTS. Use, duplication, or
+disclosure by the government are subject to restrictions set forth in
+DFARS 252.227-7013 or 48 CFR 52.227-19, as applicable.
+
+***************************************************************************/
+
+#ifndef __VCC_H__
+#define __VCC_H__ 1
+
+#include "vobject.h"
+
+
+#if defined(__CPLUSPLUS__) || defined(__cplusplus)
+extern "C" {
+#endif
+
+typedef void (*MimeErrorHandler)(char *);
+
+extern DLLEXPORT(void) registerMimeErrorHandler(MimeErrorHandler);
+
+extern DLLEXPORT(VObject*) Parse_MIME(const char *input, unsigned long len);
+extern DLLEXPORT(VObject*) Parse_MIME_FromFileName(char* fname);
+
+
+/* NOTE regarding Parse_MIME_FromFile
+The function above, Parse_MIME_FromFile, comes in two flavors,
+neither of which is exported from the DLL. Each version takes
+a CFile or FILE* as a parameter, neither of which can be
+passed across a DLL interface (at least that is my experience).
+If you are linking this code into your build directly then
+you may find them a more convenient API that the other flavors
+that take a file name. If you use them with the DLL LIB you
+will get a link error.
+*/
+
+
+#if INCLUDEMFC
+extern VObject* Parse_MIME_FromFile(CFile *file);
+#else
+extern VObject* Parse_MIME_FromFile(FILE *file);
+#endif
+
+#if defined(__CPLUSPLUS__) || defined(__cplusplus)
+}
+#endif
+
+#endif /* __VCC_H__ */
+
diff --git a/libical/src/libicalvcal/vcc.y b/libical/src/libicalvcal/vcc.y
new file mode 100644
index 0000000000..fa314efeb5
--- /dev/null
+++ b/libical/src/libicalvcal/vcc.y
@@ -0,0 +1,1218 @@
+%{
+
+/***************************************************************************
+(C) Copyright 1996 Apple Computer, Inc., AT&T Corp., International
+Business Machines Corporation and Siemens Rolm Communications Inc.
+
+For purposes of this license notice, the term Licensors shall mean,
+collectively, Apple Computer, Inc., AT&T Corp., International
+Business Machines Corporation and Siemens Rolm Communications Inc.
+The term Licensor shall mean any of the Licensors.
+
+Subject to acceptance of the following conditions, permission is hereby
+granted by Licensors without the need for written agreement and without
+license or royalty fees, to use, copy, modify and distribute this
+software for any purpose.
+
+The above copyright notice and the following four paragraphs must be
+reproduced in all copies of this software and any software including
+this software.
+
+THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS AND NO LICENSOR SHALL HAVE
+ANY OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS OR
+MODIFICATIONS.
+
+IN NO EVENT SHALL ANY LICENSOR BE LIABLE TO ANY PARTY FOR DIRECT,
+INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT
+OF THE USE OF THIS SOFTWARE EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGE.
+
+EACH LICENSOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, EXPRESS OR IMPLIED,
+INCLUDING BUT NOT LIMITED TO ANY WARRANTY OF NONINFRINGEMENT OR THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.
+
+The software is provided with RESTRICTED RIGHTS. Use, duplication, or
+disclosure by the government are subject to restrictions set forth in
+DFARS 252.227-7013 or 48 CFR 52.227-19, as applicable.
+
+***************************************************************************/
+
+/*
+ * src: vcc.c
+ * doc: Parser for vCard and vCalendar. Note that this code is
+ * generated by a yacc parser generator. Generally it should not
+ * be edited by hand. The real source is vcc.y. The #line directives
+ * can be commented out here to make it easier to trace through
+ * in a debugger. However, if a bug is found it should
+ * be fixed in vcc.y and this file regenerated.
+ */
+
+
+/* debugging utilities */
+#if __DEBUG
+#define DBG_(x) printf x
+#else
+#define DBG_(x)
+#endif
+
+/**** External Functions ****/
+
+/* assign local name to parser variables and functions so that
+ we can use more than one yacc based parser.
+*/
+
+#define yyparse mime_parse
+#define yylex mime_lex
+#define yyerror mime_error
+#define yychar mime_char
+/* #define p_yyval p_mime_val */
+#undef yyval
+#define yyval mime_yyval
+/* #define p_yylval p_mime_lval */
+#undef yylval
+#define yylval mime_yylval
+#define yydebug mime_debug
+#define yynerrs mime_nerrs
+#define yyerrflag mime_errflag
+#define yyss mime_ss
+#define yyssp mime_ssp
+#define yyvs mime_vs
+#define yyvsp mime_vsp
+#define yylhs mime_lhs
+#define yylen mime_len
+#define yydefred mime_defred
+#define yydgoto mime_dgoto
+#define yysindex mime_sindex
+#define yyrindex mime_rindex
+#define yygindex mime_gindex
+#define yytable mime_table
+#define yycheck mime_check
+#define yyname mime_name
+#define yyrule mime_rule
+#define YYPREFIX "mime_"
+
+
+#ifndef _NO_LINE_FOLDING
+#define _SUPPORT_LINE_FOLDING 1
+#endif
+
+/* undef below if compile with MFC */
+/* #define INCLUDEMFC 1 */
+
+#if defined(WIN32) || defined(_WIN32)
+#ifdef INCLUDEMFC
+#include <afx.h>
+#endif
+#endif
+
+#include <string.h>
+#ifndef __MWERKS__
+#include <malloc.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include "vcc.h"
+
+/**** Types, Constants ****/
+
+#define YYDEBUG 1 /* 1 to compile in some debugging code */
+#define MAXTOKEN 256 /* maximum token (line) length */
+#define YYSTACKSIZE 50 /* ~unref ? */
+#define MAXLEVEL 10 /* max # of nested objects parseable */
+ /* (includes outermost) */
+
+
+/**** Global Variables ****/
+int mime_lineNum, mime_numErrors; /* yyerror() can use these */
+static VObject* vObjList;
+static VObject *curProp;
+static VObject *curObj;
+static VObject* ObjStack[MAXLEVEL];
+static int ObjStackTop;
+
+
+/* A helpful utility for the rest of the app. */
+#if __CPLUSPLUS__
+extern "C" {
+#endif
+
+ extern void Parse_Debug(const char *s);
+ extern void yyerror(char *s);
+
+#if __CPLUSPLUS__
+ };
+#endif
+
+int yyparse();
+
+enum LexMode {
+ L_NORMAL,
+ L_VCARD,
+ L_VCAL,
+ L_VEVENT,
+ L_VTODO,
+ L_VALUES,
+ L_BASE64,
+ L_QUOTED_PRINTABLE
+ };
+
+/**** Private Forward Declarations ****/
+static int pushVObject(const char *prop);
+static VObject* popVObject();
+static char* lexDataFromBase64();
+static void lexPopMode(int top);
+static int lexWithinMode(enum LexMode mode);
+static void lexPushMode(enum LexMode mode);
+static void enterProps(const char *s);
+static void enterAttr(const char *s1, const char *s2);
+static void enterValues(const char *value);
+static void mime_error_(char *s);
+
+%}
+
+/***************************************************************************/
+/*** The grammar ****/
+/***************************************************************************/
+
+%union {
+ char *str;
+ VObject *vobj;
+ }
+
+%token
+ EQ COLON DOT SEMICOLON SPACE HTAB LINESEP NEWLINE
+ BEGIN_VCARD END_VCARD BEGIN_VCAL END_VCAL
+ BEGIN_VEVENT END_VEVENT BEGIN_VTODO END_VTODO
+ ID
+
+/*
+ * NEWLINE is the token that would occur outside a vCard,
+ * while LINESEP is the token that would occur inside a vCard.
+ */
+
+%token <str>
+ STRING ID
+
+%type <str> name value
+
+%type <vobj> vcard vcal vobject
+
+%start mime
+
+%%
+
+
+mime: vobjects
+ ;
+
+vobjects: vobject
+ { addList(&vObjList, $1); curObj = 0; }
+ vobjects
+ | vobject
+ { addList(&vObjList, $1); curObj = 0; }
+ ;
+
+vobject: vcard
+ | vcal
+ ;
+
+vcard:
+ BEGIN_VCARD
+ {
+ lexPushMode(L_VCARD);
+ if (!pushVObject(VCCardProp)) YYERROR;
+ }
+ items END_VCARD
+ {
+ lexPopMode(0);
+ $$ = popVObject();
+ }
+ | BEGIN_VCARD
+ {
+ lexPushMode(L_VCARD);
+ if (!pushVObject(VCCardProp)) YYERROR;
+ }
+ END_VCARD
+ {
+ lexPopMode(0);
+ $$ = popVObject();
+ }
+ ;
+
+items: item items
+ | item
+ ;
+
+item: prop COLON
+ {
+ lexPushMode(L_VALUES);
+ }
+ values LINESEP
+ {
+ if (lexWithinMode(L_BASE64) || lexWithinMode(L_QUOTED_PRINTABLE))
+ lexPopMode(0);
+ lexPopMode(0);
+ }
+ | error
+ ;
+
+prop: name
+ {
+ enterProps($1);
+ }
+ attr_params
+ | name
+ {
+ enterProps($1);
+ }
+ ;
+
+attr_params: attr_param attr_params
+ | attr_param
+ ;
+
+attr_param: SEMICOLON attr
+ ;
+
+attr: name
+ {
+ enterAttr($1,0);
+ }
+ | name EQ name
+ {
+ enterAttr($1,$3);
+
+ }
+ ;
+
+name: ID
+ ;
+
+values: value SEMICOLON { enterValues($1); } values
+ | value
+ { enterValues($1); }
+ ;
+
+value: STRING
+ | { $$ = 0; }
+ ;
+
+vcal:
+ BEGIN_VCAL
+ { if (!pushVObject(VCCalProp)) YYERROR; }
+ calitems
+ END_VCAL
+ { $$ = popVObject(); }
+ | BEGIN_VCAL
+ { if (!pushVObject(VCCalProp)) YYERROR; }
+ END_VCAL
+ { $$ = popVObject(); }
+ ;
+
+calitems: calitem calitems
+ | calitem
+ ;
+
+calitem:
+ eventitem
+ | todoitem
+ | items
+ ;
+
+eventitem:
+ BEGIN_VEVENT
+ {
+ lexPushMode(L_VEVENT);
+ if (!pushVObject(VCEventProp)) YYERROR;
+ }
+ items
+ END_VEVENT
+ {
+ lexPopMode(0);
+ popVObject();
+ }
+ | BEGIN_VEVENT
+ {
+ lexPushMode(L_VEVENT);
+ if (!pushVObject(VCEventProp)) YYERROR;
+ }
+ END_VEVENT
+ {
+ lexPopMode(0);
+ popVObject();
+ }
+ ;
+
+todoitem:
+ BEGIN_VTODO
+ {
+ lexPushMode(L_VTODO);
+ if (!pushVObject(VCTodoProp)) YYERROR;
+ }
+ items
+ END_VTODO
+ {
+ lexPopMode(0);
+ popVObject();
+ }
+ | BEGIN_VTODO
+ {
+ lexPushMode(L_VTODO);
+ if (!pushVObject(VCTodoProp)) YYERROR;
+ }
+ END_VTODO
+ {
+ lexPopMode(0);
+ popVObject();
+ }
+ ;
+
+%%
+/*/////////////////////////////////////////////////////////////////////////*/
+static int pushVObject(const char *prop)
+ {
+ VObject *newObj;
+ if (ObjStackTop == MAXLEVEL)
+ return FALSE;
+
+ ObjStack[++ObjStackTop] = curObj;
+
+ if (curObj) {
+ newObj = addProp(curObj,prop);
+ curObj = newObj;
+ }
+ else
+ curObj = newVObject(prop);
+
+ return TRUE;
+ }
+
+
+/*/////////////////////////////////////////////////////////////////////////*/
+/* This pops the recently built vCard off the stack and returns it. */
+static VObject* popVObject()
+ {
+ VObject *oldObj;
+ if (ObjStackTop < 0) {
+ yyerror("pop on empty Object Stack\n");
+ return 0;
+ }
+ oldObj = curObj;
+ curObj = ObjStack[ObjStackTop--];
+
+ return oldObj;
+ }
+
+
+static void enterValues(const char *value)
+ {
+ if (fieldedProp && *fieldedProp) {
+ if (value) {
+ addPropValue(curProp,*fieldedProp,value);
+ }
+ /* else this field is empty, advance to next field */
+ fieldedProp++;
+ }
+ else {
+ if (value) {
+ setVObjectUStringZValue_(curProp,fakeUnicode(value,0));
+ }
+ }
+ deleteStr(value);
+ }
+
+static void enterProps(const char *s)
+ {
+ curProp = addGroup(curObj,s);
+ deleteStr(s);
+ }
+
+static void enterAttr(const char *s1, const char *s2)
+ {
+ const char *p1, *p2;
+ p1 = lookupProp_(s1);
+ if (s2) {
+ VObject *a;
+ p2 = lookupProp_(s2);
+ a = addProp(curProp,p1);
+ setVObjectStringZValue(a,p2);
+ }
+ else
+ addProp(curProp,p1);
+ if (stricmp(p1,VCBase64Prop) == 0 || (s2 && stricmp(p2,VCBase64Prop)==0))
+ lexPushMode(L_BASE64);
+ else if (stricmp(p1,VCQuotedPrintableProp) == 0
+ || (s2 && stricmp(p2,VCQuotedPrintableProp)==0))
+ lexPushMode(L_QUOTED_PRINTABLE);
+ deleteStr(s1); deleteStr(s2);
+ }
+
+
+#define MAX_LEX_LOOKAHEAD_0 32
+#define MAX_LEX_LOOKAHEAD 64
+#define MAX_LEX_MODE_STACK_SIZE 10
+#define LEXMODE() (lexBuf.lexModeStack[lexBuf.lexModeStackTop])
+
+struct LexBuf {
+ /* input */
+#ifdef INCLUDEMFC
+ CFile *inputFile;
+#else
+ FILE *inputFile;
+#endif
+ char *inputString;
+ unsigned long curPos;
+ unsigned long inputLen;
+ /* lookahead buffer */
+ /* -- lookahead buffer is short instead of char so that EOF
+ / can be represented correctly.
+ */
+ unsigned long len;
+ short buf[MAX_LEX_LOOKAHEAD];
+ unsigned long getPtr;
+ /* context stack */
+ unsigned long lexModeStackTop;
+ enum LexMode lexModeStack[MAX_LEX_MODE_STACK_SIZE];
+ /* token buffer */
+ unsigned long maxToken;
+ char *strs;
+ unsigned long strsLen;
+ } lexBuf;
+
+static void lexPushMode(enum LexMode mode)
+ {
+ if (lexBuf.lexModeStackTop == (MAX_LEX_MODE_STACK_SIZE-1))
+ yyerror("lexical context stack overflow");
+ else {
+ lexBuf.lexModeStack[++lexBuf.lexModeStackTop] = mode;
+ }
+ }
+
+static void lexPopMode(int top)
+ {
+ /* special case of pop for ease of error recovery -- this
+ version will never underflow */
+ if (top)
+ lexBuf.lexModeStackTop = 0;
+ else
+ if (lexBuf.lexModeStackTop > 0) lexBuf.lexModeStackTop--;
+ }
+
+static int lexWithinMode(enum LexMode mode) {
+ unsigned long i;
+ for (i=0;i<lexBuf.lexModeStackTop;i++)
+ if (mode == lexBuf.lexModeStack[i]) return 1;
+ return 0;
+ }
+
+static char lexGetc_()
+ {
+ /* get next char from input, no buffering. */
+ if (lexBuf.curPos == lexBuf.inputLen)
+ return EOF;
+ else if (lexBuf.inputString)
+ return *(lexBuf.inputString + lexBuf.curPos++);
+ else {
+#ifdef INCLUDEMFC
+ char result;
+ return lexBuf.inputFile->Read(&result, 1) == 1 ? result : EOF;
+#else
+ return fgetc(lexBuf.inputFile);
+#endif
+ }
+ }
+
+static int lexGeta()
+ {
+ ++lexBuf.len;
+ return (lexBuf.buf[lexBuf.getPtr] = lexGetc_());
+ }
+
+static int lexGeta_(int i)
+ {
+ ++lexBuf.len;
+ return (lexBuf.buf[(lexBuf.getPtr+i)%MAX_LEX_LOOKAHEAD] = lexGetc_());
+ }
+
+static void lexSkipLookahead() {
+ if (lexBuf.len > 0 && lexBuf.buf[lexBuf.getPtr]!=EOF) {
+ /* don't skip EOF. */
+ lexBuf.getPtr = (lexBuf.getPtr + 1) % MAX_LEX_LOOKAHEAD;
+ lexBuf.len--;
+ }
+ }
+
+static int lexLookahead() {
+ int c = (lexBuf.len)?
+ lexBuf.buf[lexBuf.getPtr]:
+ lexGeta();
+ /* do the \r\n -> \n or \r -> \n translation here */
+ if (c == '\r') {
+ int a = (lexBuf.len>1)?
+ lexBuf.buf[(lexBuf.getPtr+1)%MAX_LEX_LOOKAHEAD]:
+ lexGeta_(1);
+ if (a == '\n') {
+ lexSkipLookahead();
+ }
+ lexBuf.buf[lexBuf.getPtr] = c = '\n';
+ }
+ else if (c == '\n') {
+ int a = (lexBuf.len>1)?
+ lexBuf.buf[lexBuf.getPtr+1]:
+ lexGeta_(1);
+ if (a == '\r') {
+ lexSkipLookahead();
+ }
+ lexBuf.buf[lexBuf.getPtr] = '\n';
+ }
+ return c;
+ }
+
+static int lexGetc() {
+ int c = lexLookahead();
+ if (lexBuf.len > 0 && lexBuf.buf[lexBuf.getPtr]!=EOF) {
+ /* EOF will remain in lookahead buffer */
+ lexBuf.getPtr = (lexBuf.getPtr + 1) % MAX_LEX_LOOKAHEAD;
+ lexBuf.len--;
+ }
+ return c;
+ }
+
+static void lexSkipLookaheadWord() {
+ if (lexBuf.strsLen <= lexBuf.len) {
+ lexBuf.len -= lexBuf.strsLen;
+ lexBuf.getPtr = (lexBuf.getPtr + lexBuf.strsLen) % MAX_LEX_LOOKAHEAD;
+ }
+ }
+
+static void lexClearToken()
+ {
+ lexBuf.strsLen = 0;
+ }
+
+static void lexAppendc(int c)
+ {
+ lexBuf.strs[lexBuf.strsLen] = c;
+ /* append up to zero termination */
+ if (c == 0) return;
+ lexBuf.strsLen++;
+ if (lexBuf.strsLen > lexBuf.maxToken) {
+ /* double the token string size */
+ lexBuf.maxToken <<= 1;
+ lexBuf.strs = (char*) realloc(lexBuf.strs,(size_t)lexBuf.maxToken);
+ }
+ }
+
+static char* lexStr() {
+ return dupStr(lexBuf.strs,(size_t)lexBuf.strsLen+1);
+ }
+
+static void lexSkipWhite() {
+ int c = lexLookahead();
+ while (c == ' ' || c == '\t') {
+ lexSkipLookahead();
+ c = lexLookahead();
+ }
+ }
+
+static char* lexGetWord() {
+ int c;
+ lexSkipWhite();
+ lexClearToken();
+ c = lexLookahead();
+ while (c != EOF && !strchr("\t\n ;:=",c)) {
+ lexAppendc(c);
+ lexSkipLookahead();
+ c = lexLookahead();
+ }
+ lexAppendc(0);
+ return lexStr();
+ }
+
+static void lexPushLookahead(char *s, int len) {
+ int putptr;
+ if (len == 0) len = strlen(s);
+ putptr = (int)lexBuf.getPtr - len;
+ /* this function assumes that length of word to push back
+ / is not greater than MAX_LEX_LOOKAHEAD.
+ */
+ if (putptr < 0) putptr += MAX_LEX_LOOKAHEAD;
+ lexBuf.getPtr = putptr;
+ while (*s) {
+ lexBuf.buf[putptr] = *s++;
+ putptr = (putptr + 1) % MAX_LEX_LOOKAHEAD;
+ }
+ lexBuf.len += len;
+ }
+
+static void lexPushLookaheadc(int c) {
+ int putptr;
+ /* can't putback EOF, because it never leaves lookahead buffer */
+ if (c == EOF) return;
+ putptr = (int)lexBuf.getPtr - 1;
+ if (putptr < 0) putptr += MAX_LEX_LOOKAHEAD;
+ lexBuf.getPtr = putptr;
+ lexBuf.buf[putptr] = c;
+ lexBuf.len += 1;
+ }
+
+static char* lexLookaheadWord() {
+ /* this function can lookahead word with max size of MAX_LEX_LOOKAHEAD_0
+ / and thing bigger than that will stop the lookahead and return 0;
+ / leading white spaces are not recoverable.
+ */
+ int c;
+ int len = 0;
+ int curgetptr = 0;
+ lexSkipWhite();
+ lexClearToken();
+ curgetptr = (int)lexBuf.getPtr; /* remember! */
+ while (len < (MAX_LEX_LOOKAHEAD_0)) {
+ c = lexGetc();
+ len++;
+ if (c == EOF || strchr("\t\n ;:=", c)) {
+ lexAppendc(0);
+ /* restore lookahead buf. */
+ lexBuf.len += len;
+ lexBuf.getPtr = curgetptr;
+ return lexStr();
+ }
+ else
+ lexAppendc(c);
+ }
+ lexBuf.len += len; /* char that has been moved to lookahead buffer */
+ lexBuf.getPtr = curgetptr;
+ return 0;
+ }
+
+#ifdef _SUPPORT_LINE_FOLDING
+static void handleMoreRFC822LineBreak(int c) {
+ /* suport RFC 822 line break in cases like
+ * ADR: foo;
+ * morefoo;
+ * more foo;
+ */
+ if (c == ';') {
+ int a;
+ lexSkipLookahead();
+ /* skip white spaces */
+ a = lexLookahead();
+ while (a == ' ' || a == '\t') {
+ lexSkipLookahead();
+ a = lexLookahead();
+ }
+ if (a == '\n') {
+ lexSkipLookahead();
+ a = lexLookahead();
+ if (a == ' ' || a == '\t') {
+ /* continuation, throw away all the \n and spaces read so
+ * far
+ */
+ lexSkipWhite();
+ lexPushLookaheadc(';');
+ }
+ else {
+ lexPushLookaheadc('\n');
+ lexPushLookaheadc(';');
+ }
+ }
+ else {
+ lexPushLookaheadc(';');
+ }
+ }
+ }
+
+static char* lexGet1Value() {
+ int size = 0;
+ int c;
+ lexSkipWhite();
+ c = lexLookahead();
+ lexClearToken();
+ while (c != EOF && c != ';') {
+ if (c == '\n') {
+ int a;
+ lexSkipLookahead();
+ a = lexLookahead();
+ if (a == ' ' || a == '\t') {
+ lexAppendc(' ');
+ lexSkipLookahead();
+ }
+ else {
+ lexPushLookaheadc('\n');
+ break;
+ }
+ }
+ else {
+ lexAppendc(c);
+ lexSkipLookahead();
+ }
+ c = lexLookahead();
+ }
+ lexAppendc(0);
+ handleMoreRFC822LineBreak(c);
+ return c==EOF?0:lexStr();
+ }
+#endif
+
+static char* lexGetStrUntil(char *termset) {
+ int size = 0;
+ int c = lexLookahead();
+ lexClearToken();
+ while (c != EOF && !strchr(termset,c)) {
+ lexAppendc(c);
+ lexSkipLookahead();
+ c = lexLookahead();
+ }
+ lexAppendc(0);
+ return c==EOF?0:lexStr();
+ }
+
+static int match_begin_name(int end) {
+ char *n = lexLookaheadWord();
+ int token = ID;
+ if (n) {
+ if (!stricmp(n,"vcard")) token = end?END_VCARD:BEGIN_VCARD;
+ else if (!stricmp(n,"vcalendar")) token = end?END_VCAL:BEGIN_VCAL;
+ else if (!stricmp(n,"vevent")) token = end?END_VEVENT:BEGIN_VEVENT;
+ else if (!stricmp(n,"vtodo")) token = end?END_VTODO:BEGIN_VTODO;
+ deleteStr(n);
+ return token;
+ }
+ return 0;
+ }
+
+
+#ifdef INCLUDEMFC
+void initLex(const char *inputstring, unsigned long inputlen, CFile *inputfile)
+#else
+void initLex(const char *inputstring, unsigned long inputlen, FILE *inputfile)
+#endif
+ {
+ /* initialize lex mode stack */
+ lexBuf.lexModeStack[lexBuf.lexModeStackTop=0] = L_NORMAL;
+
+ /* iniatialize lex buffer. */
+ lexBuf.inputString = (char*) inputstring;
+ lexBuf.inputLen = inputlen;
+ lexBuf.curPos = 0;
+ lexBuf.inputFile = inputfile;
+
+ lexBuf.len = 0;
+ lexBuf.getPtr = 0;
+
+ lexBuf.maxToken = MAXTOKEN;
+ lexBuf.strs = (char*)malloc(MAXTOKEN);
+ lexBuf.strsLen = 0;
+
+ }
+
+static void finiLex() {
+ free(lexBuf.strs);
+ }
+
+
+/*/////////////////////////////////////////////////////////////////////////*/
+/* This parses and converts the base64 format for binary encoding into
+ * a decoded buffer (allocated with new). See RFC 1521.
+ */
+static char * lexGetDataFromBase64()
+ {
+ unsigned long bytesLen = 0, bytesMax = 0;
+ int quadIx = 0, pad = 0;
+ unsigned long trip = 0;
+ unsigned char b;
+ int c;
+ unsigned char *bytes = NULL;
+ unsigned char *oldBytes = NULL;
+
+ DBG_(("db: lexGetDataFromBase64\n"));
+ while (1) {
+ c = lexGetc();
+ if (c == '\n') {
+ ++mime_lineNum;
+ if (lexLookahead() == '\n') {
+ /* a '\n' character by itself means end of data */
+ break;
+ }
+ else continue; /* ignore '\n' */
+ }
+ else {
+ if ((c >= 'A') && (c <= 'Z'))
+ b = (unsigned char)(c - 'A');
+ else if ((c >= 'a') && (c <= 'z'))
+ b = (unsigned char)(c - 'a') + 26;
+ else if ((c >= '0') && (c <= '9'))
+ b = (unsigned char)(c - '0') + 52;
+ else if (c == '+')
+ b = 62;
+ else if (c == '/')
+ b = 63;
+ else if (c == '=') {
+ b = 0;
+ pad++;
+ } else if ((c == ' ') || (c == '\t')) {
+ continue;
+ } else { /* error condition */
+ if (bytes) free(bytes);
+ else if (oldBytes) free(oldBytes);
+ /* error recovery: skip until 2 adjacent newlines. */
+ DBG_(("db: invalid character 0x%x '%c'\n", c,c));
+ if (c != EOF) {
+ c = lexGetc();
+ while (c != EOF) {
+ if (c == '\n' && lexLookahead() == '\n') {
+ ++mime_lineNum;
+ break;
+ }
+ c = lexGetc();
+ }
+ }
+ return NULL;
+ }
+ trip = (trip << 6) | b;
+ if (++quadIx == 4) {
+ unsigned char outBytes[3];
+ int numOut;
+ int i;
+ for (i = 0; i < 3; i++) {
+ outBytes[2-i] = (unsigned char)(trip & 0xFF);
+ trip >>= 8;
+ }
+ numOut = 3 - pad;
+ if (bytesLen + numOut > bytesMax) {
+ if (!bytes) {
+ bytesMax = 1024;
+ bytes = (unsigned char*)malloc((size_t)bytesMax);
+ }
+ else {
+ bytesMax <<= 2;
+ oldBytes = bytes;
+ bytes = (unsigned char*)realloc(bytes,(size_t)bytesMax);
+ }
+ if (bytes == 0) {
+ mime_error("out of memory while processing BASE64 data\n");
+ }
+ }
+ if (bytes) {
+ memcpy(bytes + bytesLen, outBytes, numOut);
+ bytesLen += numOut;
+ }
+ trip = 0;
+ quadIx = 0;
+ }
+ }
+ } /* while */
+ DBG_(("db: bytesLen = %d\n", bytesLen));
+ /* kludge: all this won't be necessary if we have tree form
+ representation */
+ if (bytes) {
+ setValueWithSize(curProp,bytes,(unsigned int)bytesLen);
+ free(bytes);
+ }
+ else if (oldBytes) {
+ setValueWithSize(curProp,oldBytes,(unsigned int)bytesLen);
+ free(oldBytes);
+ }
+ return 0;
+ }
+
+static int match_begin_end_name(int end) {
+ int token;
+ lexSkipWhite();
+ if (lexLookahead() != ':') return ID;
+ lexSkipLookahead();
+ lexSkipWhite();
+ token = match_begin_name(end);
+ if (token == ID) {
+ lexPushLookaheadc(':');
+ DBG_(("db: ID '%s'\n", yylval.str));
+ return ID;
+ }
+ else if (token != 0) {
+ lexSkipLookaheadWord();
+ deleteStr(yylval.str);
+ DBG_(("db: begin/end %d\n", token));
+ return token;
+ }
+ return 0;
+ }
+
+static char* lexGetQuotedPrintable()
+ {
+ char cur;
+ unsigned long len = 0;
+
+ lexClearToken();
+ do {
+ cur = lexGetc();
+ switch (cur) {
+ case '=': {
+ int c = 0;
+ int next[2];
+ int i;
+ for (i = 0; i < 2; i++) {
+ next[i] = lexGetc();
+ if (next[i] >= '0' && next[i] <= '9')
+ c = c * 16 + next[i] - '0';
+ else if (next[i] >= 'A' && next[i] <= 'F')
+ c = c * 16 + next[i] - 'A' + 10;
+ else
+ break;
+ }
+ if (i == 0) {
+ /* single '=' follow by LINESEP is continuation sign? */
+ if (next[0] == '\n') {
+ ++mime_lineNum;
+ }
+ else {
+ lexPushLookaheadc('=');
+ goto EndString;
+ }
+ }
+ else if (i == 1) {
+ lexPushLookaheadc(next[1]);
+ lexPushLookaheadc(next[0]);
+ lexAppendc('=');
+ } else {
+ lexAppendc(c);
+ }
+ break;
+ } /* '=' */
+ case '\n': {
+ lexPushLookaheadc('\n');
+ goto EndString;
+ }
+ case (char)EOF:
+ break;
+ default:
+ lexAppendc(cur);
+ break;
+ } /* switch */
+ } while (cur != (char)EOF);
+
+EndString:
+ lexAppendc(0);
+ return lexStr();
+ } /* LexQuotedPrintable */
+
+static int yylex() {
+ int token = 0;
+
+ int lexmode = LEXMODE();
+ if (lexmode == L_VALUES) {
+ int c = lexGetc();
+ if (c == ';') {
+ DBG_(("db: SEMICOLON\n"));
+ lexPushLookaheadc(c);
+#ifdef _SUPPORT_LINE_FOLDING
+ handleMoreRFC822LineBreak(c);
+#endif
+ lexSkipLookahead();
+ return SEMICOLON;
+ }
+ else if (strchr("\n",c)) {
+ ++mime_lineNum;
+ /* consume all line separator(s) adjacent to each other */
+ c = lexLookahead();
+ while (strchr("\n",c)) {
+ lexSkipLookahead();
+ c = lexLookahead();
+ ++mime_lineNum;
+ }
+ DBG_(("db: LINESEP\n"));
+ return LINESEP;
+ }
+ else {
+ char *p = 0;
+ lexPushLookaheadc(c);
+ if (lexWithinMode(L_BASE64)) {
+ /* get each char and convert to bin on the fly... */
+ p = lexGetDataFromBase64();
+ yylval.str = p;
+ return STRING;
+ }
+ else if (lexWithinMode(L_QUOTED_PRINTABLE)) {
+ p = lexGetQuotedPrintable();
+ }
+ else {
+#ifdef _SUPPORT_LINE_FOLDING
+ p = lexGet1Value();
+#else
+ p = lexGetStrUntil(";\n");
+#endif
+ }
+ if (p) {
+ DBG_(("db: STRING: '%s'\n", p));
+ yylval.str = p;
+ return STRING;
+ }
+ else return 0;
+ }
+ }
+ else {
+ /* normal mode */
+ while (1) {
+ int c = lexGetc();
+ switch(c) {
+ case ':': {
+ /* consume all line separator(s) adjacent to each other */
+ /* ignoring linesep immediately after colon. */
+/* c = lexLookahead();
+ while (strchr("\n",c)) {
+ lexSkipLookahead();
+ c = lexLookahead();
+ ++mime_lineNum;
+ }*/
+ DBG_(("db: COLON\n"));
+ return COLON;
+ }
+ case ';':
+ DBG_(("db: SEMICOLON\n"));
+ return SEMICOLON;
+ case '=':
+ DBG_(("db: EQ\n"));
+ return EQ;
+ /* ignore whitespace in this mode */
+ case '\t':
+ case ' ': continue;
+ case '\n': {
+ ++mime_lineNum;
+ continue;
+ }
+ case EOF: return 0;
+ break;
+ default: {
+ lexPushLookaheadc(c);
+ if (isalpha(c)) {
+ char *t = lexGetWord();
+ yylval.str = t;
+ if (!stricmp(t, "begin")) {
+ return match_begin_end_name(0);
+ }
+ else if (!stricmp(t,"end")) {
+ return match_begin_end_name(1);
+ }
+ else {
+ DBG_(("db: ID '%s'\n", t));
+ return ID;
+ }
+ }
+ else {
+ /* unknow token */
+ return 0;
+ }
+ break;
+ }
+ }
+ }
+ }
+ return 0;
+ }
+
+
+/***************************************************************************/
+/*** Public Functions ****/
+/***************************************************************************/
+
+static VObject* Parse_MIMEHelper()
+ {
+ ObjStackTop = -1;
+ mime_numErrors = 0;
+ mime_lineNum = 1;
+ vObjList = 0;
+ curObj = 0;
+
+ if (yyparse() != 0)
+ return 0;
+
+ finiLex();
+ return vObjList;
+ }
+
+/*/////////////////////////////////////////////////////////////////////////*/
+DLLEXPORT(VObject*) Parse_MIME(const char *input, unsigned long len)
+ {
+ initLex(input, len, 0);
+ return Parse_MIMEHelper();
+ }
+
+
+#if INCLUDEMFC
+
+DLLEXPORT(VObject*) Parse_MIME_FromFile(CFile *file)
+ {
+ unsigned long startPos;
+ VObject *result;
+
+ initLex(0,-1,file);
+ startPos = file->GetPosition();
+ if (!(result = Parse_MIMEHelper()))
+ file->Seek(startPos, CFile::begin);
+ return result;
+ }
+
+#else
+
+VObject* Parse_MIME_FromFile(FILE *file)
+ {
+ VObject *result;
+ long startPos;
+
+ initLex(0,(unsigned long)-1,file);
+ startPos = ftell(file);
+ if (!(result = Parse_MIMEHelper())) {
+ fseek(file,startPos,SEEK_SET);
+ }
+ return result;
+ }
+
+DLLEXPORT(VObject*) Parse_MIME_FromFileName(char *fname)
+ {
+ FILE *fp = fopen(fname,"r");
+ if (fp) {
+ VObject* o = Parse_MIME_FromFile(fp);
+ fclose(fp);
+ return o;
+ }
+ else {
+ char msg[256];
+ snprintf(msg, sizeof(msg), "can't open file '%s' for reading\n", fname);
+ mime_error_(msg);
+ return 0;
+ }
+ }
+
+#endif
+
+/*/////////////////////////////////////////////////////////////////////////*/
+static void YYDebug(const char *s)
+{
+/* Parse_Debug(s); */
+}
+
+
+static MimeErrorHandler mimeErrorHandler;
+
+DLLEXPORT(void) registerMimeErrorHandler(MimeErrorHandler me)
+ {
+ mimeErrorHandler = me;
+ }
+
+static void mime_error(char *s)
+ {
+ char msg[256];
+ if (mimeErrorHandler) {
+ sprintf(msg,"%s at line %d", s, mime_lineNum);
+ mimeErrorHandler(msg);
+ }
+ }
+
+static void mime_error_(char *s)
+ {
+ if (mimeErrorHandler) {
+ mimeErrorHandler(s);
+ }
+ }
+
diff --git a/libical/src/libicalvcal/vctest.c b/libical/src/libicalvcal/vctest.c
new file mode 100644
index 0000000000..7975d1e200
--- /dev/null
+++ b/libical/src/libicalvcal/vctest.c
@@ -0,0 +1,95 @@
+
+#include <stdio.h>
+#include <string.h>
+#include "vcc.h"
+
+FILE *cfp;
+
+void myMimeErrorHandler(char *s)
+{
+ printf("%s\n", s);
+}
+
+void main(int argc, char **argv)
+{
+ int testmem = 0;
+
+ char * foo[2] = {"foo","alden.vcf"};
+
+argc = 2;
+argv = foo;
+
+#ifdef _CONSOLE
+ cfp = stdout;
+ registerMimeErrorHandler(myMimeErrorHandler);
+#else
+ cfp = fopen("vctest.out", "w");
+ if (!cfp) return;
+#endif
+ ++argv;
+ while (--argc) {
+ FILE *fp;
+ if (strcmp(*argv,"-testmem") == 0) {
+ testmem = 1;
+ argv++;
+ continue;
+ }
+ fprintf(cfp,"processing %s\n",*argv);
+ fp = fopen(*argv,"r");
+ if (!fp) {
+ fprintf(cfp,"error opening file\n");
+ }
+ else {
+ VObject *v, *t;
+ FILE *ofp;
+ char buf[256];
+ char *p;
+ strcpy(buf,*argv);
+ p = strchr(buf,'.');
+ if (p) *p = 0;
+ strcat(buf,".out");
+ fprintf(cfp,"reading text input from '%s'...\n", *argv);
+ /*v = Parse_MIME_FromFile(fp); */
+ v = Parse_MIME_FromFileName(*argv);
+ writeVObjectToFile(buf,v);
+ cleanVObject(v);
+
+ /*
+ fprintf(cfp,"pretty print internal format of '%s'...\n", *argv);
+ ofp = fopen(buf,"w");
+ while (v) {
+ printVObject(cfp,v);
+ if (testmem) {
+ char *s, *p;
+ fprintf(cfp,"test writing to mem...\n");
+ p = s = writeMemVObject(0,0,v);
+ if (s) {
+ while (*s) {
+ fputc(*s,ofp);
+ s++;
+ }
+ free(p);
+ }
+ }
+ else {
+ writeVObject(ofp,v);
+ }
+ t = v;
+ v = nextVObjectInList(v);
+ cleanVObject(t);
+ }
+
+ fclose(ofp);
+ fclose(fp);
+ */
+ }
+
+ cleanStrTbl();
+ argv++;
+
+ }
+
+ if (cfp != stdout) fclose(cfp);
+
+}
+
diff --git a/libical/src/libicalvcal/vobject.c b/libical/src/libicalvcal/vobject.c
new file mode 100644
index 0000000000..d685b04278
--- /dev/null
+++ b/libical/src/libicalvcal/vobject.c
@@ -0,0 +1,1452 @@
+/***************************************************************************
+(C) Copyright 1996 Apple Computer, Inc., AT&T Corp., International
+Business Machines Corporation and Siemens Rolm Communications Inc.
+
+For purposes of this license notice, the term Licensors shall mean,
+collectively, Apple Computer, Inc., AT&T Corp., International
+Business Machines Corporation and Siemens Rolm Communications Inc.
+The term Licensor shall mean any of the Licensors.
+
+Subject to acceptance of the following conditions, permission is hereby
+granted by Licensors without the need for written agreement and without
+license or royalty fees, to use, copy, modify and distribute this
+software for any purpose.
+
+The above copyright notice and the following four paragraphs must be
+reproduced in all copies of this software and any software including
+this software.
+
+THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS AND NO LICENSOR SHALL HAVE
+ANY OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS OR
+MODIFICATIONS.
+
+IN NO EVENT SHALL ANY LICENSOR BE LIABLE TO ANY PARTY FOR DIRECT,
+INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT
+OF THE USE OF THIS SOFTWARE EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGE.
+
+EACH LICENSOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, EXPRESS OR IMPLIED,
+INCLUDING BUT NOT LIMITED TO ANY WARRANTY OF NONINFRINGEMENT OR THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.
+
+The software is provided with RESTRICTED RIGHTS. Use, duplication, or
+disclosure by the government are subject to restrictions set forth in
+DFARS 252.227-7013 or 48 CFR 52.227-19, as applicable.
+
+***************************************************************************/
+
+/*
+ * src: vobject.c
+ * doc: vobject and APIs to construct vobject, APIs pretty print
+ * vobject, and convert a vobject into its textual representation.
+ */
+
+#ifndef MWERKS
+#include <malloc.h>
+#endif
+
+#include "vobject.h"
+#include <string.h>
+#include <stdio.h>
+#include <fcntl.h>
+
+
+#define NAME_OF(o) o->id
+#define VALUE_TYPE(o) o->valType
+#define STRINGZ_VALUE_OF(o) o->val.strs
+#define USTRINGZ_VALUE_OF(o) o->val.ustrs
+#define INTEGER_VALUE_OF(o) o->val.i
+#define LONG_VALUE_OF(o) o->val.l
+#define ANY_VALUE_OF(o) o->val.any
+#define VOBJECT_VALUE_OF(o) o->val.vobj
+
+typedef union ValueItem {
+ const char *strs;
+ const wchar_t *ustrs;
+ unsigned int i;
+ unsigned long l;
+ void *any;
+ VObject *vobj;
+ } ValueItem;
+
+struct VObject {
+ VObject *next;
+ const char *id;
+ VObject *prop;
+ unsigned short valType;
+ ValueItem val;
+ };
+
+typedef struct StrItem StrItem;
+
+struct StrItem {
+ StrItem *next;
+ const char *s;
+ unsigned int refCnt;
+ };
+
+const char** fieldedProp;
+
+
+
+/*----------------------------------------------------------------------
+ The following functions involve with memory allocation:
+ newVObject
+ deleteVObject
+ dupStr
+ deleteStr
+ newStrItem
+ deleteStrItem
+ ----------------------------------------------------------------------*/
+
+DLLEXPORT(VObject*) newVObject_(const char *id)
+{
+ VObject *p = (VObject*)malloc(sizeof(VObject));
+ p->next = 0;
+ p->id = id;
+ p->prop = 0;
+ VALUE_TYPE(p) = 0;
+ ANY_VALUE_OF(p) = 0;
+ return p;
+}
+
+DLLEXPORT(VObject*) newVObject(const char *id)
+{
+ return newVObject_(lookupStr(id));
+}
+
+DLLEXPORT(void) deleteVObject(VObject *p)
+{
+ unUseStr(p->id);
+ free(p);
+}
+
+DLLEXPORT(char*) dupStr(const char *s, unsigned int size)
+{
+ char *t;
+ if (size == 0) {
+ size = strlen(s);
+ }
+ t = (char*)malloc(size+1);
+ if (t) {
+ memcpy(t,s,size);
+ t[size] = 0;
+ return t;
+ }
+ else {
+ return (char*)0;
+ }
+}
+
+DLLEXPORT(void) deleteStr(const char *p)
+{
+ if (p) free((void*)p);
+}
+
+
+static StrItem* newStrItem(const char *s, StrItem *next)
+{
+ StrItem *p = (StrItem*)malloc(sizeof(StrItem));
+ p->next = next;
+ p->s = s;
+ p->refCnt = 1;
+ return p;
+}
+
+static void deleteStrItem(StrItem *p)
+{
+ free((void*)p);
+}
+
+
+/*----------------------------------------------------------------------
+ The following function provide accesses to VObject's value.
+ ----------------------------------------------------------------------*/
+
+DLLEXPORT(const char*) vObjectName(VObject *o)
+{
+ return NAME_OF(o);
+}
+
+DLLEXPORT(void) setVObjectName(VObject *o, const char* id)
+{
+ NAME_OF(o) = id;
+}
+
+DLLEXPORT(const char*) vObjectStringZValue(VObject *o)
+{
+ return STRINGZ_VALUE_OF(o);
+}
+
+DLLEXPORT(void) setVObjectStringZValue(VObject *o, const char *s)
+{
+ STRINGZ_VALUE_OF(o) = dupStr(s,0);
+ VALUE_TYPE(o) = VCVT_STRINGZ;
+}
+
+DLLEXPORT(void) setVObjectStringZValue_(VObject *o, const char *s)
+{
+ STRINGZ_VALUE_OF(o) = s;
+ VALUE_TYPE(o) = VCVT_STRINGZ;
+}
+
+DLLEXPORT(const wchar_t*) vObjectUStringZValue(VObject *o)
+{
+ return USTRINGZ_VALUE_OF(o);
+}
+
+DLLEXPORT(void) setVObjectUStringZValue(VObject *o, const wchar_t *s)
+{
+ USTRINGZ_VALUE_OF(o) = (wchar_t*) dupStr((char*)s,(uStrLen(s)+1)*2);
+ VALUE_TYPE(o) = VCVT_USTRINGZ;
+}
+
+DLLEXPORT(void) setVObjectUStringZValue_(VObject *o, const wchar_t *s)
+{
+ USTRINGZ_VALUE_OF(o) = s;
+ VALUE_TYPE(o) = VCVT_USTRINGZ;
+}
+
+DLLEXPORT(unsigned int) vObjectIntegerValue(VObject *o)
+{
+ return INTEGER_VALUE_OF(o);
+}
+
+DLLEXPORT(void) setVObjectIntegerValue(VObject *o, unsigned int i)
+{
+ INTEGER_VALUE_OF(o) = i;
+ VALUE_TYPE(o) = VCVT_UINT;
+}
+
+DLLEXPORT(unsigned long) vObjectLongValue(VObject *o)
+{
+ return LONG_VALUE_OF(o);
+}
+
+DLLEXPORT(void) setVObjectLongValue(VObject *o, unsigned long l)
+{
+ LONG_VALUE_OF(o) = l;
+ VALUE_TYPE(o) = VCVT_ULONG;
+}
+
+DLLEXPORT(void*) vObjectAnyValue(VObject *o)
+{
+ return ANY_VALUE_OF(o);
+}
+
+DLLEXPORT(void) setVObjectAnyValue(VObject *o, void *t)
+{
+ ANY_VALUE_OF(o) = t;
+ VALUE_TYPE(o) = VCVT_RAW;
+}
+
+DLLEXPORT(VObject*) vObjectVObjectValue(VObject *o)
+{
+ return VOBJECT_VALUE_OF(o);
+}
+
+DLLEXPORT(void) setVObjectVObjectValue(VObject *o, VObject *p)
+{
+ VOBJECT_VALUE_OF(o) = p;
+ VALUE_TYPE(o) = VCVT_VOBJECT;
+}
+
+DLLEXPORT(int) vObjectValueType(VObject *o)
+{
+ return VALUE_TYPE(o);
+}
+
+
+/*----------------------------------------------------------------------
+ The following functions can be used to build VObject.
+ ----------------------------------------------------------------------*/
+
+DLLEXPORT(VObject*) addVObjectProp(VObject *o, VObject *p)
+{
+ /* circular link list pointed to tail */
+ /*
+ o {next,id,prop,val}
+ V
+ pn {next,id,prop,val}
+ V
+ ...
+ p1 {next,id,prop,val}
+ V
+ pn
+ -->
+ o {next,id,prop,val}
+ V
+ pn {next,id,prop,val}
+ V
+ p {next,id,prop,val}
+ ...
+ p1 {next,id,prop,val}
+ V
+ pn
+ */
+
+ VObject *tail = o->prop;
+ if (tail) {
+ p->next = tail->next;
+ o->prop = tail->next = p;
+ }
+ else {
+ o->prop = p->next = p;
+ }
+ return p;
+}
+
+DLLEXPORT(VObject*) addProp(VObject *o, const char *id)
+{
+ return addVObjectProp(o,newVObject(id));
+}
+
+DLLEXPORT(VObject*) addProp_(VObject *o, const char *id)
+{
+ return addVObjectProp(o,newVObject_(id));
+}
+
+DLLEXPORT(void) addList(VObject **o, VObject *p)
+{
+ p->next = 0;
+ if (*o == 0) {
+ *o = p;
+ }
+ else {
+ VObject *t = *o;
+ while (t->next) {
+ t = t->next;
+ }
+ t->next = p;
+ }
+}
+
+DLLEXPORT(VObject*) nextVObjectInList(VObject *o)
+{
+ return o->next;
+}
+
+DLLEXPORT(VObject*) setValueWithSize_(VObject *prop, void *val, unsigned int size)
+{
+ VObject *sizeProp;
+ setVObjectAnyValue(prop, val);
+ sizeProp = addProp(prop,VCDataSizeProp);
+ setVObjectLongValue(sizeProp, size);
+ return prop;
+}
+
+DLLEXPORT(VObject*) setValueWithSize(VObject *prop, void *val, unsigned int size)
+{
+ void *p = dupStr((const char *)val,size);
+ return setValueWithSize_(prop,p,p?size:0);
+}
+
+DLLEXPORT(void) initPropIterator(VObjectIterator *i, VObject *o)
+{
+ i->start = o->prop;
+ i->next = 0;
+}
+
+DLLEXPORT(void) initVObjectIterator(VObjectIterator *i, VObject *o)
+{
+ i->start = o->next;
+ i->next = 0;
+}
+
+DLLEXPORT(int) moreIteration(VObjectIterator *i)
+{
+ return (i->start && (i->next==0 || i->next!=i->start));
+}
+
+DLLEXPORT(VObject*) nextVObject(VObjectIterator *i)
+{
+ if (i->start && i->next != i->start) {
+ if (i->next == 0) {
+ i->next = i->start->next;
+ return i->next;
+ }
+ else {
+ i->next = i->next->next;
+ return i->next;
+ }
+ }
+ else return (VObject*)0;
+}
+
+DLLEXPORT(VObject*) isAPropertyOf(VObject *o, const char *id)
+{
+ VObjectIterator i;
+ initPropIterator(&i,o);
+ while (moreIteration(&i)) {
+ VObject *each = nextVObject(&i);
+ if (!stricmp(id,each->id))
+ return each;
+ }
+ return (VObject*)0;
+}
+
+DLLEXPORT(VObject*) addGroup(VObject *o, const char *g)
+{
+ /*
+ a.b.c
+ -->
+ prop(c)
+ prop(VCGrouping=b)
+ prop(VCGrouping=a)
+ */
+ char *dot = strrchr(g,'.');
+ if (dot) {
+ VObject *p, *t;
+ char *gs, *n = dot+1;
+ gs = dupStr(g,0); /* so we can write to it. */
+ /* used to be
+ * t = p = addProp_(o,lookupProp_(n));
+ */
+ t = p = addProp_(o,lookupProp(n));
+ dot = strrchr(gs,'.');
+ *dot = 0;
+ do {
+ dot = strrchr(gs,'.');
+ if (dot) {
+ n = dot+1;
+ *dot=0;
+ }
+ else
+ n = gs;
+ /* property(VCGroupingProp=n);
+ * and the value may have VCGrouping property
+ */
+ t = addProp(t,VCGroupingProp);
+ setVObjectStringZValue(t,lookupProp_(n));
+ } while (n != gs);
+ deleteStr(gs);
+ return p;
+ }
+ else
+ return addProp_(o,lookupProp(g));
+}
+
+DLLEXPORT(VObject*) addPropValue(VObject *o, const char *p, const char *v)
+{
+ VObject *prop;
+ prop = addProp(o,p);
+ setVObjectUStringZValue_(prop, fakeUnicode(v,0));
+ return prop;
+}
+
+DLLEXPORT(VObject*) addPropSizedValue_(VObject *o, const char *p, const char *v,
+ unsigned int size)
+{
+ VObject *prop;
+ prop = addProp(o,p);
+ setValueWithSize_(prop, (void*)v, size);
+ return prop;
+}
+
+DLLEXPORT(VObject*) addPropSizedValue(VObject *o, const char *p, const char *v,
+ unsigned int size)
+{
+ return addPropSizedValue_(o,p,dupStr(v,size),size);
+}
+
+
+
+/*----------------------------------------------------------------------
+ The following pretty print a VObject
+ ----------------------------------------------------------------------*/
+
+static void printVObject_(FILE *fp, VObject *o, int level);
+
+static void indent(FILE *fp, int level)
+{
+ int i;
+ for (i=0;i<level*4;i++) {
+ fputc(' ', fp);
+ }
+}
+
+static void printValue(FILE *fp, VObject *o, int level)
+{
+ switch (VALUE_TYPE(o)) {
+ case VCVT_USTRINGZ: {
+ char c;
+ char *t,*s;
+ s = t = fakeCString(USTRINGZ_VALUE_OF(o));
+ fputc('"',fp);
+ while (c=*t,c) {
+ fputc(c,fp);
+ if (c == '\n') indent(fp,level+2);
+ t++;
+ }
+ fputc('"',fp);
+ deleteStr(s);
+ break;
+ }
+ case VCVT_STRINGZ: {
+ char c;
+ const char *s = STRINGZ_VALUE_OF(o);
+ fputc('"',fp);
+ while (c=*s,c) {
+ fputc(c,fp);
+ if (c == '\n') indent(fp,level+2);
+ s++;
+ }
+ fputc('"',fp);
+ break;
+ }
+ case VCVT_UINT:
+ fprintf(fp,"%d", INTEGER_VALUE_OF(o)); break;
+ case VCVT_ULONG:
+ fprintf(fp,"%ld", LONG_VALUE_OF(o)); break;
+ case VCVT_RAW:
+ fprintf(fp,"[raw data]"); break;
+ case VCVT_VOBJECT:
+ fprintf(fp,"[vobject]\n");
+ printVObject_(fp,VOBJECT_VALUE_OF(o),level+1);
+ break;
+ case 0:
+ fprintf(fp,"[none]"); break;
+ default:
+ fprintf(fp,"[unknown]"); break;
+ }
+}
+
+static void printNameValue(FILE *fp,VObject *o, int level)
+{
+ indent(fp,level);
+ if (NAME_OF(o)) {
+ fprintf(fp,"%s", NAME_OF(o));
+ }
+ if (VALUE_TYPE(o)) {
+ fputc('=',fp);
+ printValue(fp,o, level);
+ }
+ fprintf(fp,"\n");
+}
+
+static void printVObject_(FILE *fp, VObject *o, int level)
+ {
+ VObjectIterator t;
+ if (o == 0) {
+ fprintf(fp,"[NULL]\n");
+ return;
+ }
+ printNameValue(fp,o,level);
+ initPropIterator(&t,o);
+ while (moreIteration(&t)) {
+ VObject *eachProp = nextVObject(&t);
+ printVObject_(fp,eachProp,level+1);
+ }
+ }
+
+void printVObject(FILE *fp,VObject *o)
+{
+ printVObject_(fp,o,0);
+}
+
+DLLEXPORT(void) printVObjectToFile(char *fname,VObject *o)
+{
+ FILE *fp = fopen(fname,"w");
+ if (fp) {
+ printVObject(fp,o);
+ fclose(fp);
+ }
+}
+
+DLLEXPORT(void) printVObjectsToFile(char *fname,VObject *list)
+{
+ FILE *fp = fopen(fname,"w");
+ if (fp) {
+ while (list) {
+ printVObject(fp,list);
+ list = nextVObjectInList(list);
+ }
+ fclose(fp);
+ }
+}
+
+DLLEXPORT(void) cleanVObject(VObject *o)
+{
+ if (o == 0) return;
+ if (o->prop) {
+ /* destroy time: cannot use the iterator here.
+ Have to break the cycle in the circular link
+ list and turns it into regular NULL-terminated
+ list -- since at some point of destruction,
+ the reference entry for the iterator to work
+ will not longer be valid.
+ */
+ VObject *p;
+ p = o->prop->next;
+ o->prop->next = 0;
+ do {
+ VObject *t = p->next;
+ cleanVObject(p);
+ p = t;
+ } while (p);
+ }
+ switch (VALUE_TYPE(o)) {
+ case VCVT_USTRINGZ:
+ case VCVT_STRINGZ:
+ case VCVT_RAW:
+ /* assume they are all allocated by malloc. */
+ free((char*)STRINGZ_VALUE_OF(o));
+ break;
+ case VCVT_VOBJECT:
+ cleanVObject(VOBJECT_VALUE_OF(o));
+ break;
+ }
+ deleteVObject(o);
+}
+
+DLLEXPORT(void) cleanVObjects(VObject *list)
+{
+ while (list) {
+ VObject *t = list;
+ list = nextVObjectInList(list);
+ cleanVObject(t);
+ }
+}
+
+/*----------------------------------------------------------------------
+ The following is a String Table Facilities.
+ ----------------------------------------------------------------------*/
+
+#define STRTBLSIZE 255
+
+static StrItem *strTbl[STRTBLSIZE];
+
+static unsigned int hashStr(const char *s)
+{
+ unsigned int h = 0;
+ int i;
+ for (i=0;s[i];i++) {
+ h += s[i]*i;
+ }
+ return h % STRTBLSIZE;
+}
+
+DLLEXPORT(const char*) lookupStr(const char *s)
+{
+ StrItem *t;
+ unsigned int h = hashStr(s);
+ if ((t = strTbl[h]) != 0) {
+ do {
+ if (stricmp(t->s,s) == 0) {
+ t->refCnt++;
+ return t->s;
+ }
+ t = t->next;
+ } while (t);
+ }
+ s = dupStr(s,0);
+ strTbl[h] = newStrItem(s,strTbl[h]);
+ return s;
+}
+
+DLLEXPORT(void) unUseStr(const char *s)
+{
+ StrItem *t, *p;
+ unsigned int h = hashStr(s);
+ if ((t = strTbl[h]) != 0) {
+ p = t;
+ do {
+ if (stricmp(t->s,s) == 0) {
+ t->refCnt--;
+ if (t->refCnt == 0) {
+ if (p == strTbl[h]) {
+ strTbl[h] = t->next;
+ }
+ else {
+ p->next = t->next;
+ }
+ deleteStr(t->s);
+ deleteStrItem(t);
+ return;
+ }
+ }
+ p = t;
+ t = t->next;
+ } while (t);
+ }
+}
+
+DLLEXPORT(void) cleanStrTbl()
+{
+ int i;
+ for (i=0; i<STRTBLSIZE;i++) {
+ StrItem *t = strTbl[i];
+ while (t) {
+ StrItem *p;
+ deleteStr(t->s);
+ p = t;
+ t = t->next;
+ deleteStrItem(p);
+ } while (t);
+ strTbl[i] = 0;
+ }
+}
+
+
+struct PreDefProp {
+ const char *name;
+ const char *alias;
+ const char** fields;
+ unsigned int flags;
+ };
+
+/* flags in PreDefProp */
+#define PD_BEGIN 0x1
+#define PD_INTERNAL 0x2
+
+static const char *adrFields[] = {
+ VCPostalBoxProp,
+ VCExtAddressProp,
+ VCStreetAddressProp,
+ VCCityProp,
+ VCRegionProp,
+ VCPostalCodeProp,
+ VCCountryNameProp,
+ 0
+};
+
+static const char *nameFields[] = {
+ VCFamilyNameProp,
+ VCGivenNameProp,
+ VCAdditionalNamesProp,
+ VCNamePrefixesProp,
+ VCNameSuffixesProp,
+ NULL
+ };
+
+static const char *orgFields[] = {
+ VCOrgNameProp,
+ VCOrgUnitProp,
+ VCOrgUnit2Prop,
+ VCOrgUnit3Prop,
+ VCOrgUnit4Prop,
+ NULL
+ };
+
+static const char *AAlarmFields[] = {
+ VCRunTimeProp,
+ VCSnoozeTimeProp,
+ VCRepeatCountProp,
+ VCAudioContentProp,
+ 0
+ };
+
+/* ExDate -- has unamed fields */
+/* RDate -- has unamed fields */
+
+static const char *DAlarmFields[] = {
+ VCRunTimeProp,
+ VCSnoozeTimeProp,
+ VCRepeatCountProp,
+ VCDisplayStringProp,
+ 0
+ };
+
+static const char *MAlarmFields[] = {
+ VCRunTimeProp,
+ VCSnoozeTimeProp,
+ VCRepeatCountProp,
+ VCEmailAddressProp,
+ VCNoteProp,
+ 0
+ };
+
+static const char *PAlarmFields[] = {
+ VCRunTimeProp,
+ VCSnoozeTimeProp,
+ VCRepeatCountProp,
+ VCProcedureNameProp,
+ 0
+ };
+
+static struct PreDefProp propNames[] = {
+ { VC7bitProp, 0, 0, 0 },
+ { VC8bitProp, 0, 0, 0 },
+ { VCAAlarmProp, 0, AAlarmFields, 0 },
+ { VCAdditionalNamesProp, 0, 0, 0 },
+ { VCAdrProp, 0, adrFields, 0 },
+ { VCAgentProp, 0, 0, 0 },
+ { VCAIFFProp, 0, 0, 0 },
+ { VCAOLProp, 0, 0, 0 },
+ { VCAppleLinkProp, 0, 0, 0 },
+ { VCAttachProp, 0, 0, 0 },
+ { VCAttendeeProp, 0, 0, 0 },
+ { VCATTMailProp, 0, 0, 0 },
+ { VCAudioContentProp, 0, 0, 0 },
+ { VCAVIProp, 0, 0, 0 },
+ { VCBase64Prop, 0, 0, 0 },
+ { VCBBSProp, 0, 0, 0 },
+ { VCBirthDateProp, 0, 0, 0 },
+ { VCBMPProp, 0, 0, 0 },
+ { VCBodyProp, 0, 0, 0 },
+ { VCBusinessRoleProp, 0, 0, 0 },
+ { VCCalProp, 0, 0, PD_BEGIN },
+ { VCCaptionProp, 0, 0, 0 },
+ { VCCardProp, 0, 0, PD_BEGIN },
+ { VCCarProp, 0, 0, 0 },
+ { VCCategoriesProp, 0, 0, 0 },
+ { VCCellularProp, 0, 0, 0 },
+ { VCCGMProp, 0, 0, 0 },
+ { VCCharSetProp, 0, 0, 0 },
+ { VCCIDProp, VCContentIDProp, 0, 0 },
+ { VCCISProp, 0, 0, 0 },
+ { VCCityProp, 0, 0, 0 },
+ { VCClassProp, 0, 0, 0 },
+ { VCCommentProp, 0, 0, 0 },
+ { VCCompletedProp, 0, 0, 0 },
+ { VCContentIDProp, 0, 0, 0 },
+ { VCCountryNameProp, 0, 0, 0 },
+ { VCDAlarmProp, 0, DAlarmFields, 0 },
+ { VCDataSizeProp, 0, 0, PD_INTERNAL },
+ { VCDayLightProp, 0, 0, 0 },
+ { VCDCreatedProp, 0, 0, 0 },
+ { VCDeliveryLabelProp, 0, 0, 0 },
+ { VCDescriptionProp, 0, 0, 0 },
+ { VCDIBProp, 0, 0, 0 },
+ { VCDisplayStringProp, 0, 0, 0 },
+ { VCDomesticProp, 0, 0, 0 },
+ { VCDTendProp, 0, 0, 0 },
+ { VCDTstartProp, 0, 0, 0 },
+ { VCDueProp, 0, 0, 0 },
+ { VCEmailAddressProp, 0, 0, 0 },
+ { VCEncodingProp, 0, 0, 0 },
+ { VCEndProp, 0, 0, 0 },
+ { VCEventProp, 0, 0, PD_BEGIN },
+ { VCEWorldProp, 0, 0, 0 },
+ { VCExNumProp, 0, 0, 0 },
+ { VCExpDateProp, 0, 0, 0 },
+ { VCExpectProp, 0, 0, 0 },
+ { VCExtAddressProp, 0, 0, 0 },
+ { VCFamilyNameProp, 0, 0, 0 },
+ { VCFaxProp, 0, 0, 0 },
+ { VCFullNameProp, 0, 0, 0 },
+ { VCGeoLocationProp, 0, 0, 0 },
+ { VCGeoProp, 0, 0, 0 },
+ { VCGIFProp, 0, 0, 0 },
+ { VCGivenNameProp, 0, 0, 0 },
+ { VCGroupingProp, 0, 0, 0 },
+ { VCHomeProp, 0, 0, 0 },
+ { VCIBMMailProp, 0, 0, 0 },
+ { VCInlineProp, 0, 0, 0 },
+ { VCInternationalProp, 0, 0, 0 },
+ { VCInternetProp, 0, 0, 0 },
+ { VCISDNProp, 0, 0, 0 },
+ { VCJPEGProp, 0, 0, 0 },
+ { VCLanguageProp, 0, 0, 0 },
+ { VCLastModifiedProp, 0, 0, 0 },
+ { VCLastRevisedProp, 0, 0, 0 },
+ { VCLocationProp, 0, 0, 0 },
+ { VCLogoProp, 0, 0, 0 },
+ { VCMailerProp, 0, 0, 0 },
+ { VCMAlarmProp, 0, MAlarmFields, 0 },
+ { VCMCIMailProp, 0, 0, 0 },
+ { VCMessageProp, 0, 0, 0 },
+ { VCMETProp, 0, 0, 0 },
+ { VCModemProp, 0, 0, 0 },
+ { VCMPEG2Prop, 0, 0, 0 },
+ { VCMPEGProp, 0, 0, 0 },
+ { VCMSNProp, 0, 0, 0 },
+ { VCNamePrefixesProp, 0, 0, 0 },
+ { VCNameProp, 0, nameFields, 0 },
+ { VCNameSuffixesProp, 0, 0, 0 },
+ { VCNoteProp, 0, 0, 0 },
+ { VCOrgNameProp, 0, 0, 0 },
+ { VCOrgProp, 0, orgFields, 0 },
+ { VCOrgUnit2Prop, 0, 0, 0 },
+ { VCOrgUnit3Prop, 0, 0, 0 },
+ { VCOrgUnit4Prop, 0, 0, 0 },
+ { VCOrgUnitProp, 0, 0, 0 },
+ { VCPagerProp, 0, 0, 0 },
+ { VCPAlarmProp, 0, PAlarmFields, 0 },
+ { VCParcelProp, 0, 0, 0 },
+ { VCPartProp, 0, 0, 0 },
+ { VCPCMProp, 0, 0, 0 },
+ { VCPDFProp, 0, 0, 0 },
+ { VCPGPProp, 0, 0, 0 },
+ { VCPhotoProp, 0, 0, 0 },
+ { VCPICTProp, 0, 0, 0 },
+ { VCPMBProp, 0, 0, 0 },
+ { VCPostalBoxProp, 0, 0, 0 },
+ { VCPostalCodeProp, 0, 0, 0 },
+ { VCPostalProp, 0, 0, 0 },
+ { VCPowerShareProp, 0, 0, 0 },
+ { VCPreferredProp, 0, 0, 0 },
+ { VCPriorityProp, 0, 0, 0 },
+ { VCProcedureNameProp, 0, 0, 0 },
+ { VCProdIdProp, 0, 0, 0 },
+ { VCProdigyProp, 0, 0, 0 },
+ { VCPronunciationProp, 0, 0, 0 },
+ { VCPSProp, 0, 0, 0 },
+ { VCPublicKeyProp, 0, 0, 0 },
+ { VCQPProp, VCQuotedPrintableProp, 0, 0 },
+ { VCQuickTimeProp, 0, 0, 0 },
+ { VCQuotedPrintableProp, 0, 0, 0 },
+ { VCRDateProp, 0, 0, 0 },
+ { VCRegionProp, 0, 0, 0 },
+ { VCRelatedToProp, 0, 0, 0 },
+ { VCRepeatCountProp, 0, 0, 0 },
+ { VCResourcesProp, 0, 0, 0 },
+ { VCRNumProp, 0, 0, 0 },
+ { VCRoleProp, 0, 0, 0 },
+ { VCRRuleProp, 0, 0, 0 },
+ { VCRSVPProp, 0, 0, 0 },
+ { VCRunTimeProp, 0, 0, 0 },
+ { VCSequenceProp, 0, 0, 0 },
+ { VCSnoozeTimeProp, 0, 0, 0 },
+ { VCStartProp, 0, 0, 0 },
+ { VCStatusProp, 0, 0, 0 },
+ { VCStreetAddressProp, 0, 0, 0 },
+ { VCSubTypeProp, 0, 0, 0 },
+ { VCSummaryProp, 0, 0, 0 },
+ { VCTelephoneProp, 0, 0, 0 },
+ { VCTIFFProp, 0, 0, 0 },
+ { VCTimeZoneProp, 0, 0, 0 },
+ { VCTitleProp, 0, 0, 0 },
+ { VCTLXProp, 0, 0, 0 },
+ { VCTodoProp, 0, 0, PD_BEGIN },
+ { VCTranspProp, 0, 0, 0 },
+ { VCUniqueStringProp, 0, 0, 0 },
+ { VCURLProp, 0, 0, 0 },
+ { VCURLValueProp, 0, 0, 0 },
+ { VCValueProp, 0, 0, 0 },
+ { VCVersionProp, 0, 0, 0 },
+ { VCVideoProp, 0, 0, 0 },
+ { VCVoiceProp, 0, 0, 0 },
+ { VCWAVEProp, 0, 0, 0 },
+ { VCWMFProp, 0, 0, 0 },
+ { VCWorkProp, 0, 0, 0 },
+ { VCX400Prop, 0, 0, 0 },
+ { VCX509Prop, 0, 0, 0 },
+ { VCXRuleProp, 0, 0, 0 },
+ { 0,0,0,0 }
+ };
+
+
+static struct PreDefProp* lookupPropInfo(const char* str)
+{
+ /* brute force for now, could use a hash table here. */
+ int i;
+
+ for (i = 0; propNames[i].name; i++)
+ if (stricmp(str, propNames[i].name) == 0) {
+ return &propNames[i];
+ }
+
+ return 0;
+}
+
+
+DLLEXPORT(const char*) lookupProp_(const char* str)
+{
+ int i;
+
+ for (i = 0; propNames[i].name; i++)
+ if (stricmp(str, propNames[i].name) == 0) {
+ const char* s;
+ s = propNames[i].alias?propNames[i].alias:propNames[i].name;
+ return lookupStr(s);
+ }
+ return lookupStr(str);
+}
+
+
+DLLEXPORT(const char*) lookupProp(const char* str)
+{
+ int i;
+
+ for (i = 0; propNames[i].name; i++)
+ if (stricmp(str, propNames[i].name) == 0) {
+ const char *s;
+ fieldedProp = propNames[i].fields;
+ s = propNames[i].alias?propNames[i].alias:propNames[i].name;
+ return lookupStr(s);
+ }
+ fieldedProp = 0;
+ return lookupStr(str);
+}
+
+
+/*----------------------------------------------------------------------
+ APIs to Output text form.
+ ----------------------------------------------------------------------*/
+#define OFILE_REALLOC_SIZE 256
+typedef struct OFile {
+ FILE *fp;
+ char *s;
+ int len;
+ int limit;
+ int alloc:1;
+ int fail:1;
+ } OFile;
+
+#if 0
+static void appendsOFile(OFile *fp, const char *s)
+{
+ int slen;
+ if (fp->fail) return;
+ slen = strlen(s);
+ if (fp->fp) {
+ fwrite(s,1,slen,fp->fp);
+ }
+ else {
+stuff:
+ if (fp->len + slen < fp->limit) {
+ memcpy(fp->s+fp->len,s,slen);
+ fp->len += slen;
+ return;
+ }
+ else if (fp->alloc) {
+ fp->limit = fp->limit + OFILE_REALLOC_SIZE;
+ if (OFILE_REALLOC_SIZE <= slen) fp->limit += slen;
+ fp->s = (char *) realloc(fp->s,fp->limit);
+ if (fp->s) goto stuff;
+ }
+ if (fp->alloc)
+ free(fp->s);
+ fp->s = 0;
+ fp->fail = 1;
+ }
+}
+
+static void appendcOFile(OFile *fp, char c)
+{
+ if (fp->fail) return;
+ if (fp->fp) {
+ fputc(c,fp->fp);
+ }
+ else {
+stuff:
+ if (fp->len+1 < fp->limit) {
+ fp->s[fp->len] = c;
+ fp->len++;
+ return;
+ }
+ else if (fp->alloc) {
+ fp->limit = fp->limit + OFILE_REALLOC_SIZE;
+ fp->s = (char *) realloc(fp->s,fp->limit);
+ if (fp->s) goto stuff;
+ }
+ if (fp->alloc)
+ free(fp->s);
+ fp->s = 0;
+ fp->fail = 1;
+ }
+}
+#else
+static void appendcOFile_(OFile *fp, char c)
+{
+ if (fp->fail) return;
+ if (fp->fp) {
+ fputc(c,fp->fp);
+ }
+ else {
+stuff:
+ if (fp->len+1 < fp->limit) {
+ fp->s[fp->len] = c;
+ fp->len++;
+ return;
+ }
+ else if (fp->alloc) {
+ fp->limit = fp->limit + OFILE_REALLOC_SIZE;
+ fp->s = realloc(fp->s,fp->limit);
+ if (fp->s) goto stuff;
+ }
+ if (fp->alloc)
+ free(fp->s);
+ fp->s = 0;
+ fp->fail = 1;
+ }
+}
+
+static void appendcOFile(OFile *fp, char c)
+{
+ if (c == '\n') {
+ /* write out as <CR><LF> */
+ appendcOFile_(fp,0xd);
+ appendcOFile_(fp,0xa);
+ }
+ else
+ appendcOFile_(fp,c);
+}
+
+static void appendsOFile(OFile *fp, const char *s)
+{
+ int i, slen;
+ slen = strlen(s);
+ for (i=0; i<slen; i++) {
+ appendcOFile(fp,s[i]);
+ }
+}
+
+#endif
+
+static void initOFile(OFile *fp, FILE *ofp)
+{
+ fp->fp = ofp;
+ fp->s = 0;
+ fp->len = 0;
+ fp->limit = 0;
+ fp->alloc = 0;
+ fp->fail = 0;
+}
+
+static void initMemOFile(OFile *fp, char *s, int len)
+{
+ fp->fp = 0;
+ fp->s = s;
+ fp->len = 0;
+ fp->limit = s?len:0;
+ fp->alloc = s?0:1;
+ fp->fail = 0;
+}
+
+
+static int writeBase64(OFile *fp, unsigned char *s, long len)
+{
+ long cur = 0;
+ int i, numQuads = 0;
+ unsigned long trip;
+ unsigned char b;
+ char quad[5];
+#define MAXQUADS 16
+
+ quad[4] = 0;
+
+ while (cur < len) {
+ /* collect the triplet of bytes into 'trip' */
+ trip = 0;
+ for (i = 0; i < 3; i++) {
+ b = (cur < len) ? *(s + cur) : 0;
+ cur++;
+ trip = trip << 8 | b;
+ }
+ /* fill in 'quad' with the appropriate four characters */
+ for (i = 3; i >= 0; i--) {
+ b = (unsigned char)(trip & 0x3F);
+ trip = trip >> 6;
+ if ((3 - i) < (cur - len))
+ quad[i] = '='; /* pad char */
+ else if (b < 26) quad[i] = (char)b + 'A';
+ else if (b < 52) quad[i] = (char)(b - 26) + 'a';
+ else if (b < 62) quad[i] = (char)(b - 52) + '0';
+ else if (b == 62) quad[i] = '+';
+ else quad[i] = '/';
+ }
+ /* now output 'quad' with appropriate whitespace and line ending */
+ appendsOFile(fp, (numQuads == 0 ? " " : ""));
+ appendsOFile(fp, quad);
+ appendsOFile(fp, ((cur >= len)?"\n" :(numQuads==MAXQUADS-1?"\n" : "")));
+ numQuads = (numQuads + 1) % MAXQUADS;
+ }
+ appendcOFile(fp,'\n');
+
+ return 1;
+}
+
+static void writeString(OFile *fp, const char *s)
+{
+ appendsOFile(fp,s);
+}
+
+static void writeQPString(OFile *fp, const char *s)
+{
+ char buf[4];
+ int count=0;
+ const char *p = s;
+
+ while (*p) {
+ /* break up lines biggger than 75 chars */
+ if(count >=74){
+ count=0;
+ appendsOFile(fp,"=\n");
+ }
+
+ /* escape any non ASCII characters and '=' as per rfc1521 */
+ if (*p<= 0x1f || *p >=0x7f || *p == '=' ) {
+ sprintf(buf,"=%02X",(unsigned char)*p);
+ appendsOFile(fp,buf);
+ count+=3;
+ } else {
+ appendcOFile(fp,*p);
+ count++;
+ }
+ p++;
+ }
+}
+
+
+
+static void writeVObject_(OFile *fp, VObject *o);
+
+static void writeValue(OFile *fp, VObject *o, unsigned long size,int quote)
+{
+ if (o == 0) return;
+ switch (VALUE_TYPE(o)) {
+ case VCVT_USTRINGZ: {
+ char *s = fakeCString(USTRINGZ_VALUE_OF(o));
+ if(quote) writeQPString(fp, s);
+ else writeString(fp,s);
+ deleteStr(s);
+ break;
+ }
+ case VCVT_STRINGZ: {
+ if(quote) writeQPString(fp, STRINGZ_VALUE_OF(o));
+ else writeString(fp,STRINGZ_VALUE_OF(o));
+ break;
+ }
+ case VCVT_UINT: {
+ char buf[16];
+ sprintf(buf,"%u", INTEGER_VALUE_OF(o));
+ appendsOFile(fp,buf);
+ break;
+ }
+ case VCVT_ULONG: {
+ char buf[16];
+ sprintf(buf,"%lu", LONG_VALUE_OF(o));
+ appendsOFile(fp,buf);
+ break;
+ }
+ case VCVT_RAW: {
+ appendcOFile(fp,'\n');
+ writeBase64(fp,(unsigned char*)(ANY_VALUE_OF(o)),size);
+ break;
+ }
+ case VCVT_VOBJECT:
+ appendcOFile(fp,'\n');
+ writeVObject_(fp,VOBJECT_VALUE_OF(o));
+ break;
+ }
+}
+
+static void writeAttrValue(OFile *fp, VObject *o)
+{
+ if (NAME_OF(o)) {
+ struct PreDefProp *pi;
+ pi = lookupPropInfo(NAME_OF(o));
+ if (pi && ((pi->flags & PD_INTERNAL) != 0)) return;
+ appendcOFile(fp,';');
+ appendsOFile(fp,NAME_OF(o));
+ }
+ else
+ appendcOFile(fp,';');
+ if (VALUE_TYPE(o)) {
+ appendcOFile(fp,'=');
+ writeValue(fp,o,0,0);
+ }
+}
+
+static void writeGroup(OFile *fp, VObject *o)
+{
+ char buf1[256];
+ char buf2[256];
+ strcpy(buf1,NAME_OF(o));
+ while ((o=isAPropertyOf(o,VCGroupingProp)) != 0) {
+ strcpy(buf2,STRINGZ_VALUE_OF(o));
+ strcat(buf2,".");
+ strcat(buf2,buf1);
+ strcpy(buf1,buf2);
+ }
+ appendsOFile(fp,buf1);
+}
+
+static int inList(const char **list, const char *s)
+{
+ if (list == 0) return 0;
+ while (*list) {
+ if (stricmp(*list,s) == 0) return 1;
+ list++;
+ }
+ return 0;
+}
+
+static void writeProp(OFile *fp, VObject *o)
+{
+ int isQuoted=0;
+ if (NAME_OF(o)) {
+ struct PreDefProp *pi;
+ VObjectIterator t;
+ const char **fields_ = 0;
+ pi = lookupPropInfo(NAME_OF(o));
+ if (pi && ((pi->flags & PD_BEGIN) != 0)) {
+ writeVObject_(fp,o);
+ return;
+ }
+ if (isAPropertyOf(o,VCGroupingProp))
+ writeGroup(fp,o);
+ else
+ appendsOFile(fp,NAME_OF(o));
+ if (pi) fields_ = pi->fields;
+ initPropIterator(&t,o);
+ while (moreIteration(&t)) {
+ const char *s;
+ VObject *eachProp = nextVObject(&t);
+ s = NAME_OF(eachProp);
+ if (stricmp(VCGroupingProp,s) && !inList(fields_,s))
+ writeAttrValue(fp,eachProp);
+ if (stricmp(VCQPProp,s)==0 || stricmp(VCQuotedPrintableProp,s)==0)
+ isQuoted=1;
+ }
+ if (fields_) {
+ int i = 0, n = 0;
+ const char** fields = fields_;
+ /* output prop as fields */
+ appendcOFile(fp,':');
+ while (*fields) {
+ VObject *t = isAPropertyOf(o,*fields);
+ i++;
+ if (t) n = i;
+ fields++;
+ }
+ fields = fields_;
+ for (i=0;i<n;i++) {
+ writeValue(fp,isAPropertyOf(o,*fields),0,isQuoted);
+ fields++;
+ if (i<(n-1)) appendcOFile(fp,';');
+ }
+ }
+ }
+
+ if (VALUE_TYPE(o)) {
+ unsigned long size = 0;
+ VObject *p = isAPropertyOf(o,VCDataSizeProp);
+ if (p) size = LONG_VALUE_OF(p);
+ appendcOFile(fp,':');
+ writeValue(fp,o,size,isQuoted);
+ }
+
+ appendcOFile(fp,'\n');
+}
+
+static void writeVObject_(OFile *fp, VObject *o)
+{
+ if (NAME_OF(o)) {
+ struct PreDefProp *pi;
+ pi = lookupPropInfo(NAME_OF(o));
+
+ if (pi && ((pi->flags & PD_BEGIN) != 0)) {
+ VObjectIterator t;
+ const char *begin = NAME_OF(o);
+ appendsOFile(fp,"BEGIN:");
+ appendsOFile(fp,begin);
+ appendcOFile(fp,'\n');
+ initPropIterator(&t,o);
+ while (moreIteration(&t)) {
+ VObject *eachProp = nextVObject(&t);
+ writeProp(fp, eachProp);
+ }
+ appendsOFile(fp,"END:");
+ appendsOFile(fp,begin);
+ appendsOFile(fp,"\n\n");
+ }
+ }
+}
+
+void writeVObject(FILE *fp, VObject *o)
+{
+ OFile ofp;
+ initOFile(&ofp,fp);
+ writeVObject_(&ofp,o);
+}
+
+DLLEXPORT(void) writeVObjectToFile(char *fname, VObject *o)
+{
+ FILE *fp = fopen(fname,"w");
+ if (fp) {
+ writeVObject(fp,o);
+ fclose(fp);
+ }
+}
+
+DLLEXPORT(void) writeVObjectsToFile(char *fname, VObject *list)
+{
+ FILE *fp = fopen(fname,"w");
+ if (fp) {
+ while (list) {
+ writeVObject(fp,list);
+ list = nextVObjectInList(list);
+ }
+ fclose(fp);
+ }
+}
+
+DLLEXPORT(char*) writeMemVObject(char *s, int *len, VObject *o)
+{
+ OFile ofp;
+ initMemOFile(&ofp,s,len?*len:0);
+ writeVObject_(&ofp,o);
+ if (len) *len = ofp.len;
+ appendcOFile(&ofp,0);
+ return ofp.s;
+}
+
+DLLEXPORT(char*) writeMemVObjects(char *s, int *len, VObject *list)
+{
+ OFile ofp;
+ initMemOFile(&ofp,s,len?*len:0);
+ while (list) {
+ writeVObject_(&ofp,list);
+ list = nextVObjectInList(list);
+ }
+ if (len) *len = ofp.len;
+ appendcOFile(&ofp,0);
+ return ofp.s;
+}
+
+/*----------------------------------------------------------------------
+ APIs to do fake Unicode stuff.
+ ----------------------------------------------------------------------*/
+DLLEXPORT(wchar_t*) fakeUnicode(const char *ps, int *bytes)
+{
+ wchar_t *r, *pw;
+ int len = strlen(ps)+1;
+
+ pw = r = (wchar_t*)malloc(sizeof(wchar_t)*len);
+ if (bytes)
+ *bytes = len * sizeof(wchar_t);
+
+ while (*ps) {
+ if (*ps == '\n')
+ *pw = (wchar_t)0x2028;
+ else if (*ps == '\r')
+ *pw = (wchar_t)0x2029;
+ else
+ *pw = (wchar_t)(unsigned char)*ps;
+ ps++; pw++;
+ }
+ *pw = (wchar_t)0;
+
+ return r;
+}
+
+DLLEXPORT(int) uStrLen(const wchar_t *u)
+{
+ int i = 0;
+ while (*u != (wchar_t)0) { u++; i++; }
+ return i;
+}
+
+DLLEXPORT(char*) fakeCString(const wchar_t *u)
+{
+ char *s, *t;
+ int len = uStrLen(u) + 1;
+ t = s = (char*)malloc(len);
+ while (*u) {
+ if (*u == (wchar_t)0x2028)
+ *t = '\n';
+ else if (*u == (wchar_t)0x2029)
+ *t = '\r';
+ else
+ *t = (char)*u;
+ u++; t++;
+ }
+ *t = 0;
+ return s;
+}
+
+/* end of source file vobject.c */
diff --git a/libical/src/libicalvcal/vobject.h b/libical/src/libicalvcal/vobject.h
new file mode 100644
index 0000000000..bc31dc8297
--- /dev/null
+++ b/libical/src/libicalvcal/vobject.h
@@ -0,0 +1,366 @@
+/***************************************************************************
+(C) Copyright 1996 Apple Computer, Inc., AT&T Corp., International
+Business Machines Corporation and Siemens Rolm Communications Inc.
+
+For purposes of this license notice, the term Licensors shall mean,
+collectively, Apple Computer, Inc., AT&T Corp., International
+Business Machines Corporation and Siemens Rolm Communications Inc.
+The term Licensor shall mean any of the Licensors.
+
+Subject to acceptance of the following conditions, permission is hereby
+granted by Licensors without the need for written agreement and without
+license or royalty fees, to use, copy, modify and distribute this
+software for any purpose.
+
+The above copyright notice and the following four paragraphs must be
+reproduced in all copies of this software and any software including
+this software.
+
+THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS AND NO LICENSOR SHALL HAVE
+ANY OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS OR
+MODIFICATIONS.
+
+IN NO EVENT SHALL ANY LICENSOR BE LIABLE TO ANY PARTY FOR DIRECT,
+INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT
+OF THE USE OF THIS SOFTWARE EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGE.
+
+EACH LICENSOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, EXPRESS OR IMPLIED,
+INCLUDING BUT NOT LIMITED TO ANY WARRANTY OF NONINFRINGEMENT OR THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.
+
+The software is provided with RESTRICTED RIGHTS. Use, duplication, or
+disclosure by the government are subject to restrictions set forth in
+DFARS 252.227-7013 or 48 CFR 52.227-19, as applicable.
+
+***************************************************************************/
+
+/*
+
+The vCard/vCalendar C interface is implemented in the set
+of files as follows:
+
+vcc.y, yacc source, and vcc.c, the yacc output you will use
+implements the core parser
+
+vobject.c implements an API that insulates the caller from
+the parser and changes in the vCard/vCalendar BNF
+
+port.h defines compilation environment dependent stuff
+
+vcc.h and vobject.h are header files for their .c counterparts
+
+vcaltmp.h and vcaltmp.c implement vCalendar "macro" functions
+which you may find useful.
+
+test.c is a standalone test driver that exercises some of
+the features of the APIs provided. Invoke test.exe on a
+VCARD/VCALENDAR input text file and you will see the pretty
+print output of the internal representation (this pretty print
+output should give you a good idea of how the internal
+representation looks like -- there is one such output in the
+following too). Also, a file with the .out suffix is generated
+to show that the internal representation can be written back
+in the original text format.
+
+For more information on this API see the readme.txt file
+which accompanied this distribution.
+
+ Also visit:
+
+ http://www.versit.com
+ http://www.ralden.com
+
+*/
+
+
+#ifndef __VOBJECT_H__
+#define __VOBJECT_H__ 1
+
+
+#include "port.h"
+#include <stdlib.h>
+#include <stdio.h>
+
+#if defined(__CPLUSPLUS__) || defined(__cplusplus)
+extern "C" {
+#endif
+
+
+#define VC7bitProp "7BIT"
+#define VC8bitProp "8BIT"
+#define VCAAlarmProp "AALARM"
+#define VCAdditionalNamesProp "ADDN"
+#define VCAdrProp "ADR"
+#define VCAgentProp "AGENT"
+#define VCAIFFProp "AIFF"
+#define VCAOLProp "AOL"
+#define VCAppleLinkProp "APPLELINK"
+#define VCAttachProp "ATTACH"
+#define VCAttendeeProp "ATTENDEE"
+#define VCATTMailProp "ATTMAIL"
+#define VCAudioContentProp "AUDIOCONTENT"
+#define VCAVIProp "AVI"
+#define VCBase64Prop "BASE64"
+#define VCBBSProp "BBS"
+#define VCBirthDateProp "BDAY"
+#define VCBMPProp "BMP"
+#define VCBodyProp "BODY"
+#define VCBusinessRoleProp "ROLE"
+#define VCCalProp "VCALENDAR"
+#define VCCaptionProp "CAP"
+#define VCCardProp "VCARD"
+#define VCCarProp "CAR"
+#define VCCategoriesProp "CATEGORIES"
+#define VCCellularProp "CELL"
+#define VCCGMProp "CGM"
+#define VCCharSetProp "CS"
+#define VCCIDProp "CID"
+#define VCCISProp "CIS"
+#define VCCityProp "L"
+#define VCClassProp "CLASS"
+#define VCCommentProp "NOTE"
+#define VCCompletedProp "COMPLETED"
+#define VCContentIDProp "CONTENT-ID"
+#define VCCountryNameProp "C"
+#define VCDAlarmProp "DALARM"
+#define VCDataSizeProp "DATASIZE"
+#define VCDayLightProp "DAYLIGHT"
+#define VCDCreatedProp "DCREATED"
+#define VCDeliveryLabelProp "LABEL"
+#define VCDescriptionProp "DESCRIPTION"
+#define VCDIBProp "DIB"
+#define VCDisplayStringProp "DISPLAYSTRING"
+#define VCDomesticProp "DOM"
+#define VCDTendProp "DTEND"
+#define VCDTstartProp "DTSTART"
+#define VCDueProp "DUE"
+#define VCEmailAddressProp "EMAIL"
+#define VCEncodingProp "ENCODING"
+#define VCEndProp "END"
+#define VCEventProp "VEVENT"
+#define VCEWorldProp "EWORLD"
+#define VCExNumProp "EXNUM"
+#define VCExpDateProp "EXDATE"
+#define VCExpectProp "EXPECT"
+#define VCExtAddressProp "EXT ADD"
+#define VCFamilyNameProp "F"
+#define VCFaxProp "FAX"
+#define VCFullNameProp "FN"
+#define VCGeoProp "GEO"
+#define VCGeoLocationProp "GEO"
+#define VCGIFProp "GIF"
+#define VCGivenNameProp "G"
+#define VCGroupingProp "Grouping"
+#define VCHomeProp "HOME"
+#define VCIBMMailProp "IBMMail"
+#define VCInlineProp "INLINE"
+#define VCInternationalProp "INTL"
+#define VCInternetProp "INTERNET"
+#define VCISDNProp "ISDN"
+#define VCJPEGProp "JPEG"
+#define VCLanguageProp "LANG"
+#define VCLastModifiedProp "LAST-MODIFIED"
+#define VCLastRevisedProp "REV"
+#define VCLocationProp "LOCATION"
+#define VCLogoProp "LOGO"
+#define VCMailerProp "MAILER"
+#define VCMAlarmProp "MALARM"
+#define VCMCIMailProp "MCIMAIL"
+#define VCMessageProp "MSG"
+#define VCMETProp "MET"
+#define VCModemProp "MODEM"
+#define VCMPEG2Prop "MPEG2"
+#define VCMPEGProp "MPEG"
+#define VCMSNProp "MSN"
+#define VCNamePrefixesProp "NPRE"
+#define VCNameProp "N"
+#define VCNameSuffixesProp "NSUF"
+#define VCNoteProp "NOTE"
+#define VCOrgNameProp "ORGNAME"
+#define VCOrgProp "ORG"
+#define VCOrgUnit2Prop "OUN2"
+#define VCOrgUnit3Prop "OUN3"
+#define VCOrgUnit4Prop "OUN4"
+#define VCOrgUnitProp "OUN"
+#define VCPagerProp "PAGER"
+#define VCPAlarmProp "PALARM"
+#define VCParcelProp "PARCEL"
+#define VCPartProp "PART"
+#define VCPCMProp "PCM"
+#define VCPDFProp "PDF"
+#define VCPGPProp "PGP"
+#define VCPhotoProp "PHOTO"
+#define VCPICTProp "PICT"
+#define VCPMBProp "PMB"
+#define VCPostalBoxProp "BOX"
+#define VCPostalCodeProp "PC"
+#define VCPostalProp "POSTAL"
+#define VCPowerShareProp "POWERSHARE"
+#define VCPreferredProp "PREF"
+#define VCPriorityProp "PRIORITY"
+#define VCProcedureNameProp "PROCEDURENAME"
+#define VCProdIdProp "PRODID"
+#define VCProdigyProp "PRODIGY"
+#define VCPronunciationProp "SOUND"
+#define VCPSProp "PS"
+#define VCPublicKeyProp "KEY"
+#define VCQPProp "QP"
+#define VCQuickTimeProp "QTIME"
+#define VCQuotedPrintableProp "QUOTED-PRINTABLE"
+#define VCRDateProp "RDATE"
+#define VCRegionProp "R"
+#define VCRelatedToProp "RELATED-TO"
+#define VCRepeatCountProp "REPEATCOUNT"
+#define VCResourcesProp "RESOURCES"
+#define VCRNumProp "RNUM"
+#define VCRoleProp "ROLE"
+#define VCRRuleProp "RRULE"
+#define VCRSVPProp "RSVP"
+#define VCRunTimeProp "RUNTIME"
+#define VCSequenceProp "SEQUENCE"
+#define VCSnoozeTimeProp "SNOOZETIME"
+#define VCStartProp "START"
+#define VCStatusProp "STATUS"
+#define VCStreetAddressProp "STREET"
+#define VCSubTypeProp "SUBTYPE"
+#define VCSummaryProp "SUMMARY"
+#define VCTelephoneProp "TEL"
+#define VCTIFFProp "TIFF"
+#define VCTimeZoneProp "TZ"
+#define VCTitleProp "TITLE"
+#define VCTLXProp "TLX"
+#define VCTodoProp "VTODO"
+#define VCTranspProp "TRANSP"
+#define VCUniqueStringProp "UID"
+#define VCURLProp "URL"
+#define VCURLValueProp "URLVAL"
+#define VCValueProp "VALUE"
+#define VCVersionProp "VERSION"
+#define VCVideoProp "VIDEO"
+#define VCVoiceProp "VOICE"
+#define VCWAVEProp "WAVE"
+#define VCWMFProp "WMF"
+#define VCWorkProp "WORK"
+#define VCX400Prop "X400"
+#define VCX509Prop "X509"
+#define VCXRuleProp "XRULE"
+
+/* Extensions */
+
+#define XPilotIdProp "X-PILOTID"
+#define XPilotStatusProp "X-PILOTSTAT"
+
+typedef struct VObject VObject;
+
+typedef struct VObjectIterator {
+ VObject* start;
+ VObject* next;
+ } VObjectIterator;
+
+extern DLLEXPORT(VObject*) newVObject(const char *id);
+extern DLLEXPORT(void) deleteVObject(VObject *p);
+extern DLLEXPORT(char*) dupStr(const char *s, unsigned int size);
+extern DLLEXPORT(void) deleteStr(const char *p);
+extern DLLEXPORT(void) unUseStr(const char *s);
+
+extern DLLEXPORT(void) setVObjectName(VObject *o, const char* id);
+extern DLLEXPORT(void) setVObjectStringZValue(VObject *o, const char *s);
+extern DLLEXPORT(void) setVObjectStringZValue_(VObject *o, const char *s);
+extern DLLEXPORT(void) setVObjectUStringZValue(VObject *o, const wchar_t *s);
+extern DLLEXPORT(void) setVObjectUStringZValue_(VObject *o, const wchar_t *s);
+extern DLLEXPORT(void) setVObjectIntegerValue(VObject *o, unsigned int i);
+extern DLLEXPORT(void) setVObjectLongValue(VObject *o, unsigned long l);
+extern DLLEXPORT(void) setVObjectAnyValue(VObject *o, void *t);
+extern DLLEXPORT(VObject*) setValueWithSize(VObject *prop, void *val, unsigned int size);
+extern DLLEXPORT(VObject*) setValueWithSize_(VObject *prop, void *val, unsigned int size);
+
+extern DLLEXPORT(const char*) vObjectName(VObject *o);
+extern DLLEXPORT(const char*) vObjectStringZValue(VObject *o);
+extern DLLEXPORT(const wchar_t*) vObjectUStringZValue(VObject *o);
+extern DLLEXPORT(unsigned int) vObjectIntegerValue(VObject *o);
+extern DLLEXPORT(unsigned long) vObjectLongValue(VObject *o);
+extern DLLEXPORT(void*) vObjectAnyValue(VObject *o);
+extern DLLEXPORT(VObject*) vObjectVObjectValue(VObject *o);
+extern DLLEXPORT(void) setVObjectVObjectValue(VObject *o, VObject *p);
+
+extern DLLEXPORT(VObject*) addVObjectProp(VObject *o, VObject *p);
+extern DLLEXPORT(VObject*) addProp(VObject *o, const char *id);
+extern DLLEXPORT(VObject*) addProp_(VObject *o, const char *id);
+extern DLLEXPORT(VObject*) addPropValue(VObject *o, const char *p, const char *v);
+extern DLLEXPORT(VObject*) addPropSizedValue_(VObject *o, const char *p, const char *v, unsigned int size);
+extern DLLEXPORT(VObject*) addPropSizedValue(VObject *o, const char *p, const char *v, unsigned int size);
+extern DLLEXPORT(VObject*) addGroup(VObject *o, const char *g);
+extern DLLEXPORT(void) addList(VObject **o, VObject *p);
+
+extern DLLEXPORT(VObject*) isAPropertyOf(VObject *o, const char *id);
+
+extern DLLEXPORT(VObject*) nextVObjectInList(VObject *o);
+extern DLLEXPORT(void) initPropIterator(VObjectIterator *i, VObject *o);
+extern DLLEXPORT(int) moreIteration(VObjectIterator *i);
+extern DLLEXPORT(VObject*) nextVObject(VObjectIterator *i);
+
+extern DLLEXPORT(char*) writeMemVObject(char *s, int *len, VObject *o);
+extern DLLEXPORT(char*) writeMemVObjects(char *s, int *len, VObject *list);
+
+extern DLLEXPORT(const char*) lookupStr(const char *s);
+extern DLLEXPORT(void) cleanStrTbl();
+
+extern DLLEXPORT(void) cleanVObject(VObject *o);
+extern DLLEXPORT(void) cleanVObjects(VObject *list);
+
+extern DLLEXPORT(const char*) lookupProp(const char* str);
+extern DLLEXPORT(const char*) lookupProp_(const char* str);
+
+extern DLLEXPORT(wchar_t*) fakeUnicode(const char *ps, int *bytes);
+extern DLLEXPORT(int) uStrLen(const wchar_t *u);
+extern DLLEXPORT(char*) fakeCString(const wchar_t *u);
+
+extern DLLEXPORT(void) printVObjectToFile(char *fname,VObject *o);
+extern DLLEXPORT(void) printVObjectsToFile(char *fname,VObject *list);
+extern DLLEXPORT(void) writeVObjectToFile(char *fname, VObject *o);
+extern DLLEXPORT(void) writeVObjectsToFile(char *fname, VObject *list);
+
+extern DLLEXPORT(int) vObjectValueType(VObject *o);
+
+/* return type of vObjectValueType: */
+#define VCVT_NOVALUE 0
+ /* if the VObject has no value associated with it. */
+#define VCVT_STRINGZ 1
+ /* if the VObject has value set by setVObjectStringZValue. */
+#define VCVT_USTRINGZ 2
+ /* if the VObject has value set by setVObjectUStringZValue. */
+#define VCVT_UINT 3
+ /* if the VObject has value set by setVObjectIntegerValue. */
+#define VCVT_ULONG 4
+ /* if the VObject has value set by setVObjectLongValue. */
+#define VCVT_RAW 5
+ /* if the VObject has value set by setVObjectAnyValue. */
+#define VCVT_VOBJECT 6
+ /* if the VObject has value set by setVObjectVObjectValue. */
+
+extern const char** fieldedProp;
+
+/* NOTE regarding printVObject and writeVObject
+
+The functions below are not exported from the DLL because they
+take a FILE* as a parameter, which cannot be passed across a DLL
+interface (at least that is my experience). Instead you can use
+their companion functions which take file names or pointers
+to memory. However, if you are linking this code into
+your build directly then you may find them a more convenient API
+and you can go ahead and use them. If you try to use them with
+the DLL LIB you will get a link error.
+*/
+extern void printVObject(FILE *fp,VObject *o);
+extern void writeVObject(FILE *fp, VObject *o);
+
+
+#if defined(__CPLUSPLUS__) || defined(__cplusplus)
+}
+#endif
+
+#endif /* __VOBJECT_H__ */
+
+
diff --git a/libical/src/test/recur.c b/libical/src/test/recur.c
new file mode 100644
index 0000000000..4d3188f9c2
--- /dev/null
+++ b/libical/src/test/recur.c
@@ -0,0 +1,96 @@
+/* -*- Mode: C -*-
+ ======================================================================
+ FILE: recur.c
+ CREATOR: ebusboom 8jun00
+
+ DESCRIPTION:
+
+ Test program for expanding recurrences. Run as:
+
+ ./recur ../../test-data/recur.txt
+
+
+ (C) COPYRIGHT 1999 Eric Busboom
+ 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.
+
+ The original author is Eric Busboom
+ The original code is usecases.c
+
+
+ ======================================================================*/
+
+#include "ical.h"
+#include <assert.h>
+#include <string.h> /* for strdup */
+#include <stdlib.h> /* for malloc */
+#include <stdio.h> /* for printf */
+#include <time.h> /* for time() */
+#include "icalmemory.h"
+#include "icaldirset.h"
+#include "icalfileset.h"
+
+int main(int argc, char *argv[])
+{
+ icalfileset *cin;
+ struct icaltimetype start, next;
+ icalcomponent *itr;
+ icalproperty *desc, *dtstart, *rrule;
+ struct icalrecurrencetype recur;
+ icalrecur_iterator* ritr;
+ time_t tt;
+
+ cin = icalfileset_new(argv[1]);
+ assert(cin != 0);
+
+ for (itr = icalfileset_get_first_component(cin,
+ ICAL_ANY_COMPONENT);
+ itr != 0;
+ itr = icalfileset_get_next_component(cin,
+ ICAL_ANY_COMPONENT)){
+
+ desc = icalcomponent_get_first_property(itr,ICAL_DESCRIPTION_PROPERTY);
+ assert(desc !=0);
+
+ dtstart = icalcomponent_get_first_property(itr,ICAL_DTSTART_PROPERTY);
+ assert(dtstart !=0);
+
+ rrule = icalcomponent_get_first_property(itr,ICAL_RRULE_PROPERTY);
+ assert(rrule !=0);
+
+
+ recur = icalproperty_get_rrule(rrule);
+ start = icalproperty_get_dtstart(dtstart);
+
+ ritr = icalrecur_iterator_new(recur,start);
+
+ tt = icaltime_as_timet(start);
+
+ printf("\n\n#### %s\n",icalproperty_get_description(desc));
+ printf("#### %s\n",icalvalue_as_ical_string(icalproperty_get_value(rrule)));
+ printf("#### %s\n",ctime(&tt ));
+
+ for(ritr = icalrecur_iterator_new(recur,start),
+ next = icalrecur_iterator_next(ritr);
+ !icaltime_is_null_time(next);
+ next = icalrecur_iterator_next(ritr)){
+
+ tt = icaltime_as_timet(next);
+
+ printf(" %s",ctime(&tt ));
+
+ }
+
+ }
+
+ return 0;
+}
diff --git a/libical/src/test/testmime.c b/libical/src/test/testmime.c
new file mode 100644
index 0000000000..a912983f84
--- /dev/null
+++ b/libical/src/test/testmime.c
@@ -0,0 +1,339 @@
+/* -*- Mode: C -*-
+ ======================================================================
+ FILE:
+ CREATOR: eric 25 June 2000
+
+ $Id$
+ $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, http://www.softwarestudio.org
+ ======================================================================*/
+
+#include "ical.h"
+#include "sspm.h"
+#include "icalmime.h"
+#include <stdlib.h> /* For rand */
+#include <string.h> /* for strrchr, strdup*/
+#include <unistd.h> /* for getopt */
+
+/*int sspm_parse_mime(struct sspm_part *parts,
+ size_t max_parts,
+ struct sspm_action_map *actions,
+ char* (*get_string)(char *s, size_t size, void* data),
+ void *get_string_data,
+ struct sspm_header *first_header
+ );
+*/
+
+
+
+char* major_type_string[] = {
+ "TEXT",
+ "IMAGE",
+ "AUDIO",
+ "VIDEO",
+ "APPLICATION",
+ "MULTIPART",
+ "MESSAGE",
+ "UNKNOWN",
+ "NO"
+};
+
+char* minor_type_string[] = {
+ "ANY",
+ "PLAIN",
+ "RFC822",
+ "DIGEST",
+ "CALENDAR",
+ "MIXED",
+ "RELATED",
+ "ALTERNATIVE",
+ "PARALLEL",
+ "UNKNOWN",
+ "NO"
+};
+
+
+char* read_stream(char *s, size_t size, void *d)
+{
+ char *c = fgets(s,size, (FILE*)d);
+
+ return c;
+
+}
+
+int main(int argc, char* argv[]) {
+
+ FILE *f;
+ int c;
+ extern char *optarg;
+ extern int optind, optopt;
+ int errflg=0;
+ char* program_name;
+
+ struct options{
+ int normal;
+ int stress;
+ int base64;
+ int qp;
+ int sleep;
+ int count;
+ char* input_file;
+ } opt;
+
+ memset(&opt,0,sizeof(opt));
+
+ program_name = (char*)strrchr((char*)argv[0],'/');
+ program_name++;
+
+ while ((c = getopt(argc, argv, "nsbqi:S:c:")) != -1) {
+ switch (c) {
+ case 'i': { /* Input comes from named file */
+ opt.input_file = strdup(optarg);
+ break;
+ }
+ case 'n':{ /* Normal */
+
+ if(opt.stress+opt.base64+opt.qp != 0){
+ fprintf(stderr,
+ "%s: Use only one of n,s,b and q\n",
+ program_name);
+ }
+ opt.normal = 1;
+ break;
+ }
+ case 's':{ /* Stress-test*/
+ if(opt.base64+opt.normal+opt.qp != 0){
+ fprintf(stderr,
+ "%s: Use only one of n,s,b and q\n",
+ program_name);
+ }
+ opt.stress = 1;
+ break;
+ }
+ case 'b':{ /* test base64 encoding*/
+ if(opt.stress+opt.normal+opt.qp != 0){
+ fprintf(stderr,
+ "%s: Use only one of n,s,b and q\n",
+ program_name);
+ }
+ opt.base64 = 1;
+ break;
+ }
+ case 'q':{ /* test quoted-printable encoding*/
+ if(opt.stress+opt.base64+opt.normal != 0){
+ fprintf(stderr,
+ "%s: Use only one of n,s,b and q\n",
+ program_name);
+ }
+ opt.qp = 1;
+ break;
+ }
+ case 'S':{ /* sleep at end of run */
+ opt.sleep = atoi(optarg);
+ break;
+ }
+
+ case 'c':{ /* number of iterations of stress test */
+ opt.count = atoi(optarg);
+ break;
+ }
+
+ case ':': {/* Option given without an operand */
+ fprintf(stderr,
+ "%s: Option -%c requires an operand\n",
+ program_name,optopt);
+ errflg++;
+ break;
+ }
+ case '?': {
+ errflg++;
+ }
+ }
+ }
+
+ if (errflg >0){
+ fprintf(stderr,"Usage: %s [-n|-s|-b|-q] [-i input_file]\n",
+ program_name);
+ exit(1);
+ }
+
+ if(opt.stress+opt.base64+opt.normal+opt.qp == 0){
+ fprintf(stderr,
+ "%s: Must have one of n,s,b or q\n",
+ program_name);
+ }
+
+ if(opt.input_file){
+ f = fopen(opt.input_file,"r");
+ if (f == 0){
+ fprintf(stderr,"Could not open input file \"%s\"\n",
+ opt.input_file);
+ exit(1);
+ }
+ } else {
+ f = stdin;
+ }
+
+
+
+ if(opt.normal == 1){
+ icalcomponent *c;
+
+ c = icalmime_parse(read_stream,f);
+
+ printf("%s\n",icalcomponent_as_ical_string(c));
+
+ icalcomponent_free(c);
+
+ } else if (opt.stress==1 ){
+ /* Read file in by lines, then randomize the lines into a
+ string buffer */
+
+ char *array[1024];
+ char temp[1024];
+ char *buf;
+ int i,last;
+ int size;
+ int non_rand;
+ int rand_lines;
+ int r;
+ int j;
+ icalcomponent *c;
+ struct slg_data {
+ char* pos;
+ char* str;
+ } d;
+
+ for(i=0; !feof(f); i++){
+ fgets(temp,1024,f);
+ array[i] = strdup(temp);
+ size += strlen(temp);
+ }
+ last = i;
+
+ buf = malloc(size*2);
+ assert(buf != 0);
+
+
+ for(j=0; j<opt.count; j++){
+
+ srand(j);
+ memset(buf,0,size*2);
+ /* First insert some non-randomized lines */
+ non_rand = ((float)rand()/(float)RAND_MAX) * last;
+ for(i=0;i<non_rand;i++){
+ strcat(buf,array[i]);
+ }
+
+ /* Then, insert some lines at random */
+
+ rand_lines = last - non_rand;
+
+ for(i=0;i<rand_lines;i++){
+ srand(i);
+ r = ((float)rand()/(float)RAND_MAX) * rand_lines;
+ strcat(buf,array[r+non_rand]);
+
+ }
+
+ d.pos = 0;
+ d.str = buf;
+
+ c = icalmime_parse(string_line_generator,&d);
+
+ printf("%s\n",icalcomponent_as_ical_string(c));
+
+ icalcomponent_free(c);
+
+ }
+
+ free(buf);
+
+ for(i=0; i<last; i++){
+ free(array[i]);
+ }
+
+ } else if(opt.qp == 1){
+ char str[4096];
+ char conv[4096];
+
+ memset(str,0,4096);
+
+ while(!feof(f) && fgets(str,4096,f)!=0){
+ size_t size;
+
+ size = strlen(str);
+ memset(conv,0,4096);
+ decode_quoted_printable(conv,str,&size);
+
+ conv[size] = '\0';
+ printf("%s",conv);
+ memset(str,0,4096);
+
+ }
+ } else if (opt.base64 == 1) {
+ char str[4096];
+ char conv[4096];
+
+ memset(str,0,4096);
+
+ while(!feof(f) && fgets(str,4096,f)!=0){
+ size_t size;
+
+ size = strlen(str);
+ memset(conv,0,4096);
+ decode_base64(conv,str,&size);
+
+ conv[size] = '\0';
+ printf("%s",conv);
+ memset(str,0,4096);
+
+ }
+ }
+
+ if (opt.sleep != 0){
+ sleep(opt.sleep);
+ }
+
+ if( opt.input_file != 0){
+ free(opt.input_file);
+ }
+
+ icalmemory_free_ring();
+
+ return 0;
+
+}
+
+
+
+
+
+
+
+
diff --git a/libical/src/test/testvcal.c b/libical/src/test/testvcal.c
new file mode 100644
index 0000000000..4777162623
--- /dev/null
+++ b/libical/src/test/testvcal.c
@@ -0,0 +1,56 @@
+/* -*- Mode: C -*-
+ ======================================================================
+ FILE: vcal.c
+ CREATOR: eric 26 May 2000
+
+ $Id$
+ $Locker$
+
+ (C) COPYRIGHT 2000 Eric Busboom
+ 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.
+
+ The Original Code is eric. The Initial Developer of the Original
+ Code is Eric Busboom
+
+
+ ======================================================================*/
+
+#include "icalvcal.h"
+#include <stdio.h>
+
+/* Given a vCal data file as its first argument, this program will
+ print out an equivalent iCal component.
+
+ For instance:
+
+ ./testvcal ../../test-data/user-cal.vcf
+
+*/
+
+int main(int argc, char* argv[])
+{
+ VObject *vcal = 0;
+ icalcomponent *comp;
+
+ vcal = Parse_MIME_FromFileName(argv[1]);
+
+ assert(vcal != 0);
+
+ comp = icalvcal_convert(vcal);
+
+ printf("%s\n",icalcomponent_as_ical_string(comp));
+
+ return 0;
+}
+
+
diff --git a/libical/test-data/complex-mime.txt b/libical/test-data/complex-mime.txt
new file mode 100644
index 0000000000..32cb106dfd
--- /dev/null
+++ b/libical/test-data/complex-mime.txt
@@ -0,0 +1,81 @@
+From: foo1@example.com
+MIME-Version: 1.0
+To: foo2@example.com,foo3@example.com
+Subject: REQUEST - Phone Conference
+Content-Type:multipart/related;boundary="--FEE3790DC7E35189CA67CE2C"
+
+----FEE3790DC7E35189CA67CE2C
+Content-Type: multipart/alternative;
+ boundary="--00FEE3790DC7E35189CA67CE2C00"
+
+----00FEE3790DC7E35189CA67CE2C00
+Content-Type: text/plain; charset=us-ascii
+Content-Transfer-Encoding: 7bit
+
+When: 7/1/1997 10:00PM PDT- 7/1/97 10:30 PM PDT
+Where:
+Organizer: foo1@example.com
+Summary: Let's discuss the attached document
+
+
+----00FEE3790DC7E35189CA67CE2C00
+Content-Type:text/calendar; method=REQUEST; charset=US-ASCII;
+ Component=vevent
+Content-Transfer-Encoding: quoted-printable
+Content-Disposition: attachment; filename="event.vcs"
+
+BEGIN:VCALENDAR
+PRODID:-//ACME/DesktopCalendar//EN
+VERSION:2.0
+METHOD:REQUEST
+BEGIN:VEVENT
+ORGANIZER:foo1@example.com
+ATTENDEE;RSVP=3DTRUE;ROLE=3DREQ-PARTICIPANT;CUTYPE=3DGROUP:MAILTO:employe=
+e-A@host.com
+ATTENDEE;RSVP=3DTRUE;ROLE=3DREQ-PARTICIPANT;CUTYPE=3DGROUP:mailto:Employe=
+e-B@HOST.com
+ATTENDEE;RSVP=3DTRUE;ROLE=3DREQ-PARTICIPANT;CUTYPE=3DGROUP:MailTo:Eric@Ag=
+ony.Busboom.org
+DTSTAMP:19970611T190000Z
+DTSTART:19970621T170000Z
+DTEND:199706211T173000Z
+SUMMARY:Let's discuss the attached document
+UID:calsvr.example.com-873970198738777-8aa
+SEQUENCE:0
+STATUS:CONFIRMED
+END:VEVENT
+END:VCALENDAR
+
+----00FEE3790DC7E35189CA67CE2C00
+Content-Type:text/calendar; method=REQUEST; charset=US-ASCII;
+ Component=vevent
+Content-Transfer-Encoding: base64
+Content-Disposition: attachment; filename="event.vcs"
+
+QkVHSU46VkNBTEVOREFSClBST0RJRDotLy9BQ01FL0Rlc2t0b3BDYWxlbmRhci8vRU4KVkVS
+U0lPTjoyLjAKQkVHSU46VkVWRU5UCk9SR0FOSVpFUjpmb28xQGV4YW1wbGUuY29tCkFUVEVO
+REVFO1JTVlA9VFJVRTtST0xFPVJFUS1QQVJUSUNJUEFOVDtDVVRZUEU9R1JPVVA6TUFJTFRP
+OmVtcGxveWVlLUFAaG9zdC5jb20KQVRURU5ERUU7UlNWUD1UUlVFO1JPTEU9UkVRLVBBUlRJ
+Q0lQQU5UO0NVVFlQRT1HUk9VUDptYWlsdG86RW1wbG95ZWUtQkBIT1NULmNvbQpBVFRFTkRF
+RTtSU1ZQPVRSVUU7Uk9MRT1SRVEtUEFSVElDSVBBTlQ7Q1VUWVBFPUdST1VQOk1haWxUbzpF
+cmljQEFnb255LlF1YWxjb21tLmNvbQpEVFNUQU1QOjE5OTcwNjExVDE5MDAwMFoKRFRTVEFS
+VDoxOTk3MDYyMVQxNzAwMDBaCkRURU5EOjE5OTcwNjIxMVQxNzMwMDBaClNVTU1BUlk6TGV0
+J3MgZGlzY3VzcyB0aGUgYXR0YWNoZWQgZG9jdW1lbnQKVUlEOmNhbHN2ci5leGFtcGxlLmNv
+bS04NzM5NzAxOTg3Mzg3NzctOGFhClNFUVVFTkNFOjAKU1RBVFVTOkNPTkZJUk1FRApFTkQ6
+VkVWRU5UCkVORDpWQ0FMRU5EQVIK
+
+
+----00FEE3790DC7E35189CA67CE2C00--
+
+----FEE3790DC7E35189CA67CE2C
+Content-Type: application/msword; name="FieldReport.doc"
+Content-Transfer-Encoding: base64
+Content-Disposition: inline; filename="FieldReport.doc"
+Content-ID: <calsvr.example.com-12345aaa>
+
+
+R0lGODdhTAQZAJEAAFVVVd3d3e4AAP///ywAAAAATAQZAAAC/5yPOSLhD6OctNqLs94XqAG
+4kiW5omm6sq27gvH8kzX9o1y+s73/g8MCofEovGITCoxKMbyCR16cNSq9YrNarfcrvdriIH
+5LL5jE6rxc3G+v2cguf0uv2Oz+v38L7/DxgoOKjURnjIIbe3yNjo+AgZWYVIWWl5iZnJY6J.
+
+----FEE3790DC7E35189CA67CE2C--
diff --git a/libical/test-data/recur.txt b/libical/test-data/recur.txt
new file mode 100644
index 0000000000..8c977b3c05
--- /dev/null
+++ b/libical/test-data/recur.txt
@@ -0,0 +1,632 @@
+BEGIN:VEVENT
+DESCRIPTION
+ :Daily for 10 occurrences:
+DTSTART
+ ;TZID=US-Eastern
+ :19970902T090000
+RRULE
+ :FREQ=DAILY;COUNT=10;INTERVAL=1
+END:VEVENT
+BEGIN:VEVENT
+DESCRIPTION
+ :Daily until December 24\, 1997:
+DTSTART
+ ;TZID=US-Eastern
+ :19970902T090000
+RRULE
+ :FREQ=DAILY;UNTIL=19971224T000000Z;INTERVAL=1
+END:VEVENT
+BEGIN:VEVENT
+DESCRIPTION
+ :Every other day - forever:
+DTSTART
+ ;TZID=US-Eastern
+ :19970902T090000
+RRULE
+ :FREQ=DAILY;COUNT=10;INTERVAL=2
+END:VEVENT
+BEGIN:VEVENT
+DESCRIPTION
+ :Every 10 days\, 5 occurrences:
+DTSTART
+ ;TZID=US-Eastern
+ :19970902T090000
+RRULE
+ :FREQ=DAILY;COUNT=5;INTERVAL=10
+END:VEVENT
+BEGIN:VEVENT
+DESCRIPTION
+ :Daily for 10 occurrences:
+DTSTART
+ ;TZID=US-Eastern
+ :19970902T090000
+RRULE
+ :FREQ=DAILY;COUNT=10;INTERVAL=1
+END:VEVENT
+BEGIN:VEVENT
+DESCRIPTION
+ :Daily until December 24\, 1997:
+DTSTART
+ ;TZID=US-Eastern
+ :19970902T090000
+RRULE
+ :FREQ=DAILY;UNTIL=19971224T000000Z;INTERVAL=1
+END:VEVENT
+BEGIN:VEVENT
+DESCRIPTION
+ :Every other day - forever:
+DTSTART
+ ;TZID=US-Eastern
+ :19970902T090000
+RRULE
+ :FREQ=DAILY;COUNT=10;INTERVAL=2
+END:VEVENT
+BEGIN:VEVENT
+DESCRIPTION
+ :Every 10 days\, 5 occurrences:
+DTSTART
+ ;TZID=US-Eastern
+ :19970902T090000
+RRULE
+ :FREQ=DAILY;COUNT=5;INTERVAL=10
+END:VEVENT
+BEGIN:VEVENT
+DESCRIPTION
+ :Daily for 10 occurrences:
+DTSTART
+ ;TZID=US-Eastern
+ :19970902T090000
+RRULE
+ :FREQ=DAILY;COUNT=10;INTERVAL=1
+END:VEVENT
+BEGIN:VEVENT
+DESCRIPTION
+ :Daily until December 24\, 1997:
+DTSTART
+ ;TZID=US-Eastern
+ :19970902T090000
+RRULE
+ :FREQ=DAILY;UNTIL=19971224T000000Z;INTERVAL=1
+END:VEVENT
+BEGIN:VEVENT
+DESCRIPTION
+ :Every other day - forever:
+DTSTART
+ ;TZID=US-Eastern
+ :19970902T090000
+RRULE
+ :FREQ=DAILY;COUNT=10;INTERVAL=2
+END:VEVENT
+BEGIN:VEVENT
+DESCRIPTION
+ :Every 10 days\, 5 occurrences:
+DTSTART
+ ;TZID=US-Eastern
+ :19970902T090000
+RRULE
+ :FREQ=DAILY;COUNT=5;INTERVAL=10
+END:VEVENT
+BEGIN:VEVENT
+DESCRIPTION
+ :Daily for 10 occurrences:
+DTSTART
+ ;TZID=US-Eastern
+ :19970902T090000
+RRULE
+ :FREQ=DAILY;COUNT=10;INTERVAL=1
+END:VEVENT
+BEGIN:VEVENT
+DESCRIPTION
+ :Daily until December 24\, 1997:
+DTSTART
+ ;TZID=US-Eastern
+ :19970902T090000
+RRULE
+ :FREQ=DAILY;UNTIL=19971224T000000Z;INTERVAL=1
+END:VEVENT
+BEGIN:VEVENT
+DESCRIPTION
+ :Every other day - forever:
+DTSTART
+ ;TZID=US-Eastern
+ :19970902T090000
+RRULE
+ :FREQ=DAILY;COUNT=10;INTERVAL=2
+END:VEVENT
+BEGIN:VEVENT
+DESCRIPTION
+ :Every 10 days\, 5 occurrences:
+DTSTART
+ ;TZID=US-Eastern
+ :19970902T090000
+RRULE
+ :FREQ=DAILY;COUNT=5;INTERVAL=10
+END:VEVENT
+BEGIN:VEVENT
+DESCRIPTION
+ :Daily for 10 occurrences:
+DTSTART
+ ;TZID=US-Eastern
+ :19970902T090000
+RRULE
+ :FREQ=DAILY;COUNT=10;INTERVAL=1
+END:VEVENT
+BEGIN:VEVENT
+DESCRIPTION
+ :Daily until December 24\, 1997:
+DTSTART
+ ;TZID=US-Eastern
+ :19970902T090000
+RRULE
+ :FREQ=DAILY;UNTIL=19971224T000000Z;INTERVAL=1
+END:VEVENT
+BEGIN:VEVENT
+DESCRIPTION
+ :Every other day - forever:
+DTSTART
+ ;TZID=US-Eastern
+ :19970902T090000
+RRULE
+ :FREQ=DAILY;COUNT=10;INTERVAL=2
+END:VEVENT
+BEGIN:VEVENT
+DESCRIPTION
+ :Every 10 days\, 5 occurrences:
+DTSTART
+ ;TZID=US-Eastern
+ :19970902T090000
+RRULE
+ :FREQ=DAILY;COUNT=5;INTERVAL=10
+END:VEVENT
+BEGIN:VEVENT
+DESCRIPTION
+ :Daily for 10 occurrences:
+DTSTART
+ ;TZID=US-Eastern
+ :19970902T090000
+RRULE
+ :FREQ=DAILY;COUNT=10;INTERVAL=1
+END:VEVENT
+BEGIN:VEVENT
+DESCRIPTION
+ :Daily until December 24\, 1997:
+DTSTART
+ ;TZID=US-Eastern
+ :19970902T090000
+RRULE
+ :FREQ=DAILY;UNTIL=19971224T000000Z;INTERVAL=1
+END:VEVENT
+BEGIN:VEVENT
+DESCRIPTION
+ :Every other day - forever:
+DTSTART
+ ;TZID=US-Eastern
+ :19970902T090000
+RRULE
+ :FREQ=DAILY;COUNT=10;INTERVAL=2
+END:VEVENT
+BEGIN:VEVENT
+DESCRIPTION
+ :Every 10 days\, 5 occurrences:
+DTSTART
+ ;TZID=US-Eastern
+ :19970902T090000
+RRULE
+ :FREQ=DAILY;COUNT=5;INTERVAL=10
+END:VEVENT
+BEGIN:VEVENT
+DESCRIPTION
+ :Daily for 10 occurrences:
+DTSTART
+ ;TZID=US-Eastern
+ :19970902T090000
+RRULE
+ :FREQ=DAILY;COUNT=10;INTERVAL=1
+END:VEVENT
+BEGIN:VEVENT
+DESCRIPTION
+ :Daily until December 24\, 1997:
+DTSTART
+ ;TZID=US-Eastern
+ :19970902T090000
+RRULE
+ :FREQ=DAILY;UNTIL=19971224T000000Z;INTERVAL=1
+END:VEVENT
+BEGIN:VEVENT
+DESCRIPTION
+ :Every other day - forever:
+DTSTART
+ ;TZID=US-Eastern
+ :19970902T090000
+RRULE
+ :FREQ=DAILY;COUNT=10;INTERVAL=2
+END:VEVENT
+BEGIN:VEVENT
+DESCRIPTION
+ :Every 10 days\, 5 occurrences:
+DTSTART
+ ;TZID=US-Eastern
+ :19970902T090000
+RRULE
+ :FREQ=DAILY;COUNT=5;INTERVAL=10
+END:VEVENT
+BEGIN:VEVENT
+DESCRIPTION
+ :Daily for 10 occurrences:
+DTSTART
+ ;TZID=US-Eastern
+ :19970902T090000
+RRULE
+ :FREQ=DAILY;COUNT=10;INTERVAL=1
+END:VEVENT
+BEGIN:VEVENT
+DESCRIPTION
+ :Daily until December 24\, 1997:
+DTSTART
+ ;TZID=US-Eastern
+ :19970902T090000
+RRULE
+ :FREQ=DAILY;UNTIL=19971224T000000Z;INTERVAL=1
+END:VEVENT
+BEGIN:VEVENT
+DESCRIPTION
+ :Every other day - forever:
+DTSTART
+ ;TZID=US-Eastern
+ :19970902T090000
+RRULE
+ :FREQ=DAILY;COUNT=10;INTERVAL=2
+END:VEVENT
+BEGIN:VEVENT
+DESCRIPTION
+ :Every 10 days\, 5 occurrences:
+DTSTART
+ ;TZID=US-Eastern
+ :19970902T090000
+RRULE
+ :FREQ=DAILY;COUNT=5;INTERVAL=10
+END:VEVENT
+BEGIN:VEVENT
+DESCRIPTION
+ :Everyday in January\, for 3 years
+DTSTART
+ ;TZID=US-Eastern
+ :19980101T090000
+RRULE
+ :FREQ=YEARLY;UNTIL=20000131T090000Z;INTERVAL=1;BYDAY=SU,MO,TU,WE,TH,FR,SA;BYMONTH=1
+END:VEVENT
+BEGIN:VEVENT
+DESCRIPTION
+ :Everyday in January\, for 3 years:
+DTSTART
+ ;TZID=US-Eastern
+ :19980101T090000
+RRULE
+ :FREQ=DAILY;UNTIL=20000131T090000Z;INTERVAL=1;BYMONTH=1
+END:VEVENT
+BEGIN:VEVENT
+DESCRIPTION
+ :Weekly for 10 occurrences
+DTSTART
+ ;TZID=US-Eastern
+ :19970902T090000
+RRULE
+ :FREQ=WEEKLY;COUNT=10;INTERVAL=1
+END:VEVENT
+BEGIN:VEVENT
+DESCRIPTION
+ :Weekly until December 24\, 1997
+DTSTART
+ ;TZID=US-Eastern
+ :19970902T090000
+RRULE
+ :FREQ=WEEKLY;UNTIL=19971224T000000Z;INTERVAL=1
+END:VEVENT
+BEGIN:VEVENT
+DESCRIPTION
+ :Every other week - forever:
+DTSTART
+ ;TZID=US-Eastern
+ :19970902T090000
+RRULE
+ :FREQ=WEEKLY;INTERVAL=2
+END:VEVENT
+BEGIN:VEVENT
+DESCRIPTION
+ :Weekly on Tuesday and Thursday for 5 weeks:
+DTSTART
+ ;TZID=US-Eastern
+ :19970902T090000
+RRULE
+ :FREQ=WEEKLY;UNTIL=19971007T000000Z;INTERVAL=1;BYDAY=TU,TH
+END:VEVENT
+BEGIN:VEVENT
+DESCRIPTION
+ :Weekly on Tuesday and Thursday for 5 weeks:
+DTSTART
+ ;TZID=US-Eastern
+ :19970902T090000
+RRULE
+ :FREQ=WEEKLY;COUNT=10;INTERVAL=1;BYDAY=TU,TH
+END:VEVENT
+BEGIN:VEVENT
+DESCRIPTION
+ :Every other week on Monday\, Wednesday and Friday until December
+ 24\,1997\, but starting on Tuesday\, September 2\, 1997:
+DTSTART
+ ;TZID=US-Eastern
+ :19970902T090000
+X-LIC-ERROR
+ ;X-LIC-ERRORTYPE=VALUE_PARSE_ERROR
+ :Can't parse as RECUR value in RRULE property. Removing entire property:
+ 'FREQ=WEEKLY\;INTERVAL=2\;UNTIL=19971224T000000Z\;WKST=SU\;'
+END:VEVENT
+BEGIN:VEVENT
+DESCRIPTION
+ :Every other week on Tuesday and Thursday\, for 8 occurrences:
+DTSTART
+ ;TZID=US-Eastern
+ :19970902T090000
+RRULE
+ :FREQ=WEEKLY;COUNT=8;INTERVAL=2;BYDAY=TU,TH
+END:VEVENT
+BEGIN:VEVENT
+DESCRIPTION
+ :Monthly on the 1st Friday for ten occurrences:
+DTSTART
+ ;TZID=US-Eastern
+ :19970905T090000
+RRULE
+ :FREQ=MONTHLY;COUNT=10;INTERVAL=1;BYDAY=FR
+END:VEVENT
+BEGIN:VEVENT
+DESCRIPTION
+ :Monthly on the 1st Friday until December 24\, 1997:
+DTSTART
+ ;TZID=US-Eastern
+ :19970905T090000
+RRULE
+ :FREQ=MONTHLY;UNTIL=19971224T000000Z;INTERVAL=1;BYDAY=FR
+END:VEVENT
+BEGIN:VEVENT
+DESCRIPTION
+ :Every other month on the 1st and last Sunday of the month for 10 occurrences
+ :
+DTSTART
+ ;TZID=US-Eastern
+ :19970907T090000
+X-LIC-ERROR
+ ;X-LIC-ERRORTYPE=VALUE_PARSE_ERROR
+ :Can't parse as RECUR value in RRULE property. Removing entire property:
+ 'FREQ=MONTHLY\;INTERVAL=2\;COUNT=10\;BYDAY=1SU\,-1SU'
+END:VEVENT
+BEGIN:VEVENT
+DESCRIPTION
+ :Monthly on the second to last Monday of the month for 6 months:
+DTSTART
+ ;TZID=US-Eastern
+ :19970922T090000
+RRULE
+ :FREQ=MONTHLY;COUNT=6;INTERVAL=1;BYDAY=MO
+END:VEVENT
+BEGIN:VEVENT
+DESCRIPTION
+ :Monthly on the third to the last day of the month\, forever:
+DTSTART
+ ;TZID=US-Eastern
+ :19970928T090000
+RRULE
+ :FREQ=MONTHLY;INTERVAL=1;BYMONTHDAY=-3
+END:VEVENT
+BEGIN:VEVENT
+DESCRIPTION
+ :Monthly on the 2nd and 15th of the month for 10 occurrences:
+DTSTART
+ ;TZID=US-Eastern
+ :19970902T090000
+RRULE
+ :FREQ=MONTHLY;COUNT=10;INTERVAL=1;BYMONTHDAY=2,15
+END:VEVENT
+BEGIN:VEVENT
+DESCRIPTION
+ :Monthly on the first and last day of the month for 10 occurrences:\nDTSTART
+ \;TZID=US-Eastern:19970930T090000
+RRULE
+ :FREQ=MONTHLY;COUNT=10;INTERVAL=1;BYMONTHDAY=1,-1
+END:VEVENT
+BEGIN:VEVENT
+DESCRIPTION
+ :Every 18 months on the 10th thru 15th of the month for 10 occurrences:
+DTSTART
+ ;TZID=US-Eastern
+ :19970910T090000
+RRULE
+ :FREQ=MONTHLY;COUNT=10;INTERVAL=18;BYMONTHDAY=10,11,12,13,14,15
+END:VEVENT
+BEGIN:VEVENT
+DESCRIPTION
+ :Every Tuesday\, every other month:
+DTSTART
+ ;TZID=US-Eastern
+ :19970902T090000
+RRULE
+ :FREQ=MONTHLY;INTERVAL=2;BYDAY=TH
+END:VEVENT
+BEGIN:VEVENT
+DESCRIPTION
+ :Yearly in June and July for 10 occurrences:
+DTSTART
+ ;TZID=US-Eastern
+ :19970610T090000
+RRULE
+ :FREQ=YEARLY;COUNT=10;INTERVAL=1;BYMONTH=6,7
+END:VEVENT
+BEGIN:VEVENT
+DESCRIPTION
+ :Every other year on January\, February\, and March for 10 occurrences:
+DTSTART
+ ;TZID=US-Eastern
+ :19970310T090000
+RRULE
+ :FREQ=YEARLY;COUNT=10;INTERVAL=2;BYMONTH=1,2,3
+END:VEVENT
+BEGIN:VEVENT
+DESCRIPTION
+ :Every 3rd year on the 1st\, 100th and 200th day for 10 occurrences:
+DTSTART
+ ;TZID=US-Eastern
+ :19970101T090000
+RRULE
+ :FREQ=YEARLY;COUNT=10;INTERVAL=3;BYYEARDAY=1,100,200
+END:VEVENT
+BEGIN:VEVENT
+DESCRIPTION
+ :Every 20th Monday of the year\, forever:
+DTSTART
+ ;TZID=US-Eastern
+ :19970519T090000
+RRULE
+ :FREQ=YEARLY;INTERVAL=1;BYDAY=MO
+END:VEVENT
+BEGIN:VEVENT
+DESCRIPTION
+ :Monday of week number 20 (where the default start of the week is Monday)\,
+ forever:
+DTSTART
+ ;TZID=US-Eastern
+ :19970512T090000
+RRULE
+ :FREQ=YEARLY;INTERVAL=1;BYDAY=MO;BYWEEKNO=20
+END:VEVENT
+BEGIN:VEVENT
+DESCRIPTION
+ :Every Thursday in March\, forever:
+DTSTART
+ ;TZID=US-Eastern
+ :19970313T090000
+RRULE
+ :FREQ=YEARLY;INTERVAL=1;BYDAY=TH;BYMONTH=3
+END:VEVENT
+BEGIN:VEVENT
+DESCRIPTION
+ :Every Thursday\, but only during June\, July\, and August\, forever:
+DTSTART
+ ;TZID=US-Eastern
+ :19970605T090000
+RRULE
+ :FREQ=YEARLY;INTERVAL=1;BYDAY=TH;BYMONTH=6,7,8
+END:VEVENT
+BEGIN:VEVENT
+DESCRIPTION
+ :Every Friday the 13th\, forever:
+DTSTART
+ ;TZID=US-Eastern
+ :19970902T090000
+RRULE
+ :FREQ=MONTHLY;INTERVAL=1;BYDAY=FR;BYMONTHDAY=13
+END:VEVENT
+BEGIN:VEVENT
+DESCRIPTION
+ :The first Saturday that follows the first Sunday of the month\,
+DTSTART
+ ;TZID=US-Eastern
+ :19970913T090000
+RRULE
+ :FREQ=MONTHLY;INTERVAL=1;BYDAY=SA;BYMONTHDAY=7,8,9,10,11,12,13
+END:VEVENT
+BEGIN:VEVENT
+DESCRIPTION
+ :Every four years\, the first Tuesday after a Monday in November\,
+ forever (U.S. Presidential Election day):
+DTSTART
+ ;TZID=US-Eastern
+ :19961105T090000
+RRULE
+ :FREQ=YEARLY;INTERVAL=4;BYDAY=TU;BYMONTHDAY=2,3,4,5,6,7,8;BYMONTH=11
+END:VEVENT
+BEGIN:VEVENT
+DESCRIPTION
+ :The 3rd instance into the month of one of Tuesday\, Wednesday or
+ Thursday\, for the next 3 months:
+DTSTART
+ ;TZID=US-Eastern
+ :19970904T090000
+RRULE
+ :FREQ=MONTHLY;COUNT=3;INTERVAL=1;BYDAY=TU,WE,TH;BYSETPOS=3
+END:VEVENT
+BEGIN:VEVENT
+DESCRIPTION
+ :The 2nd to last weekday of the month:
+DTSTART
+ ;TZID=US-Eastern
+ :19970929T090000
+RRULE
+ :FREQ=MONTHLY;INTERVAL=1;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=-2
+END:VEVENT
+BEGIN:VEVENT
+DESCRIPTION
+ :Every 3 hours from 9:00 AM to 5:00 PM on a specific day:
+DTSTART
+ ;TZID=US-Eastern
+ :19970902T090000
+RRULE
+ :FREQ=HOURLY;UNTIL=19970902T170000Z;INTERVAL=3
+END:VEVENT
+BEGIN:VEVENT
+DESCRIPTION
+ :Every 15 minutes for 6 occurrences:
+DTSTART
+ ;TZID=US-Eastern
+ :19970902T090000
+RRULE
+ :FREQ=MINUTELY;COUNT=6;INTERVAL=15
+END:VEVENT
+BEGIN:VEVENT
+DESCRIPTION
+ :Every hour and a half for 4 occurrences:
+DTSTART
+ ;TZID=US-Eastern
+ :19970902T090000
+RRULE
+ :FREQ=MINUTELY;COUNT=4;INTERVAL=90
+END:VEVENT
+BEGIN:VEVENT
+DESCRIPTION
+ :Every 20 minutes from 9:00 AM to 4:40 PM every day:
+DTSTART
+ ;TZID=US-Eastern
+ :19970902T090000
+RRULE
+ :FREQ=DAILY;INTERVAL=1;BYMINUTE=0,20,40;BYHOUR=9,10,11,12,13,14,15,16
+END:VEVENT
+BEGIN:VEVENT
+DESCRIPTION
+ :Every 20 minutes from 9:00 AM to 4:40 PM every day:
+DTSTART
+ ;TZID=US-Eastern
+ :19970902T090000
+RRULE
+ :FREQ=MINUTELY;INTERVAL=20;BYHOUR=9,10,11,12,13,14,15,16
+END:VEVENT
+BEGIN:VEVENT
+DESCRIPTION
+ :An example where the days generated makes a difference because of
+ WKST:
+DTSTART
+ ;TZID=US-Eastern
+ :19970805T090000
+RRULE
+ :FREQ=WEEKLY;COUNT=4;INTERVAL=2;BYDAY=TU,SU
+END:VEVENT
+BEGIN:VEVENT
+DESCRIPTION
+ :An example where the days generated makes a difference because of
+ WKST. Changing only WKST from MO to SU
+DESCRIPTION
+ : yields different results...
+DTSTART
+ ;TZID=US-Eastern
+ :19970805T090000
+RRULE
+ :FREQ=WEEKLY;COUNT=4;INTERVAL=2;BYDAY=TU,SU
+END:VEVENT
diff --git a/libical/test-data/simple-mime.txt b/libical/test-data/simple-mime.txt
new file mode 100644
index 0000000000..aa7113a82f
--- /dev/null
+++ b/libical/test-data/simple-mime.txt
@@ -0,0 +1,26 @@
+From: foo1@example.com
+MIME-Version: 1.0
+To: foo2@example.com,foo3@example.com
+Subject: REQUEST - Phone Conference
+Content-Type:text/calendar; method=REQUEST; charset=US-ASCII;
+ Component=vevent
+Content-Transfer-Encoding: 7bit
+Content-Disposition: attachment; filename="event.vcs"
+
+BEGIN:VCALENDAR
+PRODID:-//ACME/DesktopCalendar//EN
+VERSION:2.0
+BEGIN:VEVENT
+ORGANIZER:foo1@example.com
+ATTENDEE;RSVP=TRUE;ROLE=REQ-PARTICIPANT;CUTYPE=GROUP:MAILTO:employee-A@host.com
+ATTENDEE;RSVP=TRUE;ROLE=REQ-PARTICIPANT;CUTYPE=GROUP:mailto:Employee-B@HOST.com
+ATTENDEE;RSVP=TRUE;ROLE=REQ-PARTICIPANT;CUTYPE=GROUP:MailTo:Eric@Agony.Qualcomm.com
+DTSTAMP:19970611T190000Z
+DTSTART:19970621T170000Z
+DTEND:199706211T173000Z
+SUMMARY:Let's discuss the attached document
+UID:calsvr.example.com-873970198738777-8aa
+SEQUENCE:0
+STATUS:CONFIRMED
+END:VEVENT
+END:VCALENDAR
diff --git a/libical/test-data/user-cal.vcf b/libical/test-data/user-cal.vcf
new file mode 100644
index 0000000000..3cf005fa21
--- /dev/null
+++ b/libical/test-data/user-cal.vcf
@@ -0,0 +1,76 @@
+BEGIN:VCALENDAR
+X-COMMENT: This file was contributed by Ola Lundqvist <olalu526@student.liu.se>
+PRODID:-//GNOME//NONSGML GnomeCalendar//EN
+TZ:CEST
+VERSION:1.1.4
+BEGIN:VEVENT
+UID:20000511T125528-23888-500-1-494
+SEQUENCE:-1
+DTSTART:20000530T150000
+DTEND:20000530T170000
+DCREATED:20000524T125250
+LAST-MODIFIED:20000524T125250
+SUMMARY:Omphalic for ataraxia
+STATUS:NEEDS ACTION
+CLASS:PUBLIC
+PRIORITY:0
+TRANSP:0
+ORGNAME:ola
+X-PILOTID:4441126
+X-PILOTSTAT:0
+END:VEVENT
+
+BEGIN:VEVENT
+UID:20000511T125528-23888-500-1-492
+SEQUENCE:-1
+DTSTART:20000529T130000
+DTEND:20000529T130000
+DCREATED:20000524T125250
+LAST-MODIFIED:20000524T125250
+SUMMARY:Eurphrates et filistu
+STATUS:NEEDS ACTION
+CLASS:PUBLIC
+PRIORITY:0
+TRANSP:0
+ORGNAME:ola
+X-PILOTID:4441124
+X-PILOTSTAT:0
+END:VEVENT
+
+BEGIN:VEVENT
+UID:20000524T125250-1004-500-1-13
+SEQUENCE:-1
+DTSTART:20000528T000000
+DTEND:20000528T235900
+DCREATED:20000524T125250
+LAST-MODIFIED:20000524T125250
+SUMMARY:Calaphgyian, napiform malefactors
+STATUS:NEEDS ACTION
+CLASS:PUBLIC
+PRIORITY:0
+TRANSP:0
+ORGNAME:ola
+X-PILOTID:4441156
+X-PILOTSTAT:0
+END:VEVENT
+
+BEGIN:VEVENT
+UID:20000524T125250-1004-500-1-12
+SEQUENCE:-1
+DTSTART:20000527T150000
+DTEND:20000527T160000
+DCREATED:20000524T125250
+LAST-MODIFIED:20000524T125250
+SUMMARY: aphasiac puntildictus
+STATUS:NEEDS ACTION
+CLASS:PUBLIC
+PRIORITY:0
+TRANSP:0
+ORGNAME:ola
+X-PILOTID:4441155
+X-PILOTSTAT:0
+END:VEVENT
+
+
+END:VCALENDAR
+