]> git.datanom.net - webcal.git/commitdiff
Initial upload master
authorMichael Rasmussen <mir@datanom.net>
Thu, 25 Jul 2013 00:26:25 +0000 (02:26 +0200)
committerMichael Rasmussen <mir@datanom.net>
Thu, 25 Jul 2013 00:26:25 +0000 (02:26 +0200)
107 files changed:
Doxyfile [new file with mode: 0644]
README [new file with mode: 0644]
caldav/awl/AWLUtilities.php [new file with mode: 0644]
caldav/awl/Translation.php [new file with mode: 0644]
caldav/awl/XMLElement.php [new file with mode: 0644]
caldav/awl/iCalendar.php [new file with mode: 0644]
caldav/caldav-client.php [new file with mode: 0644]
caldav/caldavresource.class.php [new file with mode: 0644]
caldav/calendar.class.php [new file with mode: 0644]
caldav/ical.class.php [new file with mode: 0644]
caldav/icomponent.class.php [new file with mode: 0644]
caldav/rfc2445.html [new file with mode: 0644]
caldav/rfc4791.html [new file with mode: 0644]
caldav/rruleparser.class.php [new file with mode: 0644]
caldav/vevent.class.php [new file with mode: 0644]
css/top_level.css [new file with mode: 0644]
error.html [new file with mode: 0644]
events/delete_event.php [new file with mode: 0644]
events/edit_event.php [new file with mode: 0644]
events/eventgui.class.php [new file with mode: 0644]
events/new_event.php [new file with mode: 0644]
events/update.php [new file with mode: 0644]
include/footer.inc.php [new file with mode: 0644]
include/header.inc.php [new file with mode: 0644]
include/menu.inc.php [new file with mode: 0644]
include/user_settings.inc.php [new file with mode: 0644]
index.php [new file with mode: 0644]
install/config.inc.php.patch [new file with mode: 0644]
install/config.inc.php1.patch [new file with mode: 0644]
install/install.php [new file with mode: 0644]
install/pgsql_db_upgrade_0_7_3.sql [new file with mode: 0644]
install/pgsql_db_upgrade_0_7_4.sql [new file with mode: 0644]
install/pgsql_db_upgrade_0_7_5.sql [new file with mode: 0644]
install/pgsql_db_upgrade_0_8_0.sql [new file with mode: 0644]
install/pgsql_db_upgrade_0_8_1.sql [new file with mode: 0644]
install/setup [new file with mode: 0644]
install/sqlite_db_upgrade_0_7_3.sql [new file with mode: 0644]
install/sqlite_db_upgrade_0_7_4.sql [new file with mode: 0644]
install/sqlite_db_upgrade_0_7_5.sql [new file with mode: 0644]
install/sqlite_db_upgrade_0_8_0.sql [new file with mode: 0644]
install/sqlite_db_upgrade_0_8_1.sql [new file with mode: 0644]
install/timezone.txt [new file with mode: 0644]
install/tz.pl [new file with mode: 0644]
install/upgrade.sh [new file with mode: 0644]
js/ajax_wrapper.js [new file with mode: 0644]
js/calendar.css [new file with mode: 0644]
js/calendar_db.js [new file with mode: 0644]
js/helper.js [new file with mode: 0644]
js/img/cal.gif [new file with mode: 0644]
js/img/next_mon.gif [new file with mode: 0644]
js/img/next_year.gif [new file with mode: 0644]
js/img/no_cal.gif [new file with mode: 0644]
js/img/pixel.gif [new file with mode: 0644]
js/img/prev_mon.gif [new file with mode: 0644]
js/img/prev_year.gif [new file with mode: 0644]
js/img/shade_bl.png [new file with mode: 0644]
js/img/shade_bm.png [new file with mode: 0644]
js/img/shade_br.png [new file with mode: 0644]
js/img/shade_mr.png [new file with mode: 0644]
js/img/shade_tr.png [new file with mode: 0644]
js/license.txt [new file with mode: 0644]
license.txt [new file with mode: 0644]
login.php [new file with mode: 0644]
logout.php [new file with mode: 0644]
makedoc [new file with mode: 0644]
navigate/goto_today.php [new file with mode: 0644]
navigate/show_day.php [new file with mode: 0644]
navigate/show_month.php [new file with mode: 0644]
navigate/show_week.php [new file with mode: 0644]
pixmaps/add-event.png [new file with mode: 0644]
pixmaps/calendar.png [new file with mode: 0644]
pixmaps/configuration.png [new file with mode: 0644]
pixmaps/day.png [new file with mode: 0644]
pixmaps/delete-event.png [new file with mode: 0644]
pixmaps/edit-event.png [new file with mode: 0644]
pixmaps/exit.png [new file with mode: 0644]
pixmaps/favicon.ico [new file with mode: 0644]
pixmaps/goto-today.png [new file with mode: 0644]
pixmaps/left.png [new file with mode: 0644]
pixmaps/month.png [new file with mode: 0644]
pixmaps/print.png [new file with mode: 0644]
pixmaps/reload.png [new file with mode: 0644]
pixmaps/right.png [new file with mode: 0644]
pixmaps/stock_init.png [new file with mode: 0644]
pixmaps/week.png [new file with mode: 0644]
templates/day_view.class.php [new file with mode: 0644]
templates/month_view.class.php [new file with mode: 0644]
templates/view.class.php [new file with mode: 0644]
templates/week_view.class.php [new file with mode: 0644]
user_exist_error.php [new file with mode: 0644]
user_validate.php [new file with mode: 0644]
utils/authenticate.php [new file with mode: 0644]
utils/calendar.php [new file with mode: 0644]
utils/configure.php [new file with mode: 0644]
utils/dateformat.class.php [new file with mode: 0644]
utils/db_create.postgresql.php [new file with mode: 0644]
utils/db_create.sql [new file with mode: 0644]
utils/db_create.sqlite.php [new file with mode: 0644]
utils/helper.php [new file with mode: 0644]
utils/ldap.php [new file with mode: 0644]
utils/newuser.php [new file with mode: 0644]
utils/persistens.php [new file with mode: 0644]
utils/pgsql.php [new file with mode: 0644]
utils/sqlite.php [new file with mode: 0644]
utils/timezone.php [new file with mode: 0644]
utils/upgrade_db.php [new file with mode: 0644]
utils/users.php [new file with mode: 0644]

diff --git a/Doxyfile b/Doxyfile
new file mode 100644 (file)
index 0000000..4f1ddae
--- /dev/null
+++ b/Doxyfile
@@ -0,0 +1,1544 @@
+# $Id$
+# Doxyfile 1.5.9
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project
+#
+# All text after a hash (#) is considered a comment and will be ignored
+# The format is:
+#       TAG = value [value, ...]
+# For lists items can also be appended using:
+#       TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ")
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all
+# text before the first occurrence of this tag. Doxygen uses libiconv (or the
+# iconv built into libc) for the transcoding. See
+# http://www.gnu.org/software/libiconv for the list of possible encodings.
+
+DOXYFILE_ENCODING      = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
+# by quotes) that should identify the project.
+
+PROJECT_NAME           = webcal
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
+# if some version control system is used.
+
+PROJECT_NUMBER         = 0.7.4
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
+# base path where the generated documentation will be put.
+# If a relative path is entered, it will be relative to the location
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY       = doc
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
+# 4096 sub-directories (in 2 levels) under the output directory of each output
+# format and will distribute the generated files over these directories.
+# Enabling this option can be useful when feeding doxygen a huge amount of
+# source files, where putting all generated files in the same directory would
+# otherwise cause performance problems for the file system.
+
+CREATE_SUBDIRS         = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
+# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German,
+# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English
+# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian,
+# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak,
+# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese.
+
+OUTPUT_LANGUAGE        = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC      = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF           = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator
+# that is used to form the text in various listings. Each string
+# in this list, if found as the leading text of the brief description, will be
+# stripped from the text and the result after processing the whole list, is
+# used as the annotated text. Otherwise, the brief description is used as-is.
+# If left blank, the following values are used ("$name" is automatically
+# replaced with the name of the entity): "The $name class" "The $name widget"
+# "The $name file" "is" "provides" "specifies" "contains"
+# "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF       = "The $name class" \
+                         "The $name widget" \
+                         "The $name file" \
+                         is \
+                         provides \
+                         specifies \
+                         contains \
+                         represents \
+                         a \
+                         an \
+                         the
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
+# description.
+
+ALWAYS_DETAILED_SEC    = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+
+INLINE_INHERITED_MEMB  = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES        = YES
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user-defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the
+# path to strip.
+
+STRIP_FROM_PATH        = /Users/dimitri/doxygen/mail/1.5.7/doxywizard/
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
+# the path mentioned in the documentation of a class, which tells
+# the reader which header file to include in order to use a class.
+# If left blank only the name of the header file containing the class
+# definition is used. Otherwise one should specify the include paths that
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH    =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful is your file systems
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES            = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like regular Qt-style comments
+# (thus requiring an explicit @brief command for a brief description.)
+
+JAVADOC_AUTOBRIEF      = NO
+
+# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
+# interpret the first line (until the first dot) of a Qt-style
+# comment as the brief description. If set to NO, the comments
+# will behave just like regular Qt-style comments (thus requiring
+# an explicit \brief command for a brief description.)
+
+QT_AUTOBRIEF           = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
+# treat a multi-line C++ special comment block (i.e. a block of //! or ///
+# comments) as a brief description. This used to be the default behaviour.
+# The new default is to treat a multi-line C++ comment block as a detailed
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
+# re-implements.
+
+INHERIT_DOCS           = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
+# a new page for each member. If set to NO, the documentation of a member will
+# be part of the file/class/namespace that contains it.
+
+SEPARATE_MEMBER_PAGES  = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE               = 8
+
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user-defined paragraph with heading "Side Effects:".
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES                =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
+# sources only. Doxygen will then generate output that is more tailored for C.
+# For instance, some of the names that are used will be different. The list
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C  = YES
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
+# sources only. Doxygen will then generate output that is more tailored for
+# Java. For instance, namespaces will be presented as packages, qualified
+# scopes will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA   = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources only. Doxygen will then generate output that is more tailored for
+# Fortran.
+
+OPTIMIZE_FOR_FORTRAN   = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for
+# VHDL.
+
+OPTIMIZE_OUTPUT_VHDL   = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it parses.
+# With this tag you can assign which parser to use for a given extension.
+# Doxygen has a built-in mapping, but you can override or extend it using this tag.
+# The format is ext=language, where ext is a file extension, and language is one of
+# the parsers supported by doxygen: IDL, Java, Javascript, C#, C, C++, D, PHP,
+# Objective-C, Python, Fortran, VHDL, C, C++. For instance to make doxygen treat
+# .inc files as Fortran files (default is PHP), and .f files as C (default is Fortran),
+# use: inc=Fortran f=C. Note that for custom extensions you also need to set
+# FILE_PATTERNS otherwise the files are not read by doxygen.
+
+EXTENSION_MAPPING      =
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should
+# set this tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
+# func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+
+BUILTIN_STL_SUPPORT    = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+
+CPP_CLI_SUPPORT        = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
+# Doxygen will parse them like normal C++ but will assume all classes use public
+# instead of private inheritance when no explicit protection keyword is present.
+
+SIP_SUPPORT            = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate getter
+# and setter methods for a property. Setting this option to YES (the default)
+# will make doxygen to replace the get and set methods by a property in the
+# documentation. This will only work if the methods are indeed getting or
+# setting a simple type. If this is not the case, or you want to show the
+# methods anyway, you should set this option to NO.
+
+IDL_PROPERTY_SUPPORT   = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC   = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
+# the same type (for instance a group of public functions) to be put as a
+# subgroup of that type (e.g. under the Public Functions section). Set it to
+# NO to prevent subgrouping. Alternatively, this can be done per class using
+# the \nosubgrouping command.
+
+SUBGROUPING            = YES
+
+# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
+# is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically
+# be useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+
+TYPEDEF_HIDES_STRUCT   = NO
+
+# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to
+# determine which symbols to keep in memory and which to flush to disk.
+# When the cache is full, less often used symbols will be written to disk.
+# For small to medium size projects (<1000 input files) the default value is
+# probably good enough. For larger projects a too small cache size can cause
+# doxygen to be busy swapping symbols to and from disk most of the time
+# causing a significant performance penality.
+# If the system has enough physical memory increasing the cache will improve the
+# performance by keeping more symbols in memory. Note that the value works on
+# a logarithmic scale so increasing the size by one will rougly double the
+# memory usage. The cache size is given by this formula:
+# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0,
+# corresponding to a cache size of 2^16 = 65536 symbols
+
+SYMBOL_CACHE_SIZE      = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL            = YES
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
+# will be included in the documentation.
+
+EXTRACT_PRIVATE        = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
+# will be included in the documentation.
+
+EXTRACT_STATIC         = NO
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
+# defined locally in source files will be included in the documentation.
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES  = YES
+
+# This flag is only useful for Objective-C code. When set to YES local
+# methods, which are defined in the implementation section but not in
+# the interface are included in the documentation.
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS  = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base
+# name of the file that contains the anonymous namespace. By default
+# anonymous namespace are hidden.
+
+EXTRACT_ANON_NSPACES   = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS     = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these classes will be included in the various
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES     = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
+# friend (class|struct|union) declarations.
+# If set to NO (the default) these declarations will be included in the
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS  = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
+# documentation blocks found inside the body of a function.
+# If set to NO (the default) these blocks will be appended to the
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS      = NO
+
+# The INTERNAL_DOCS tag determines if documentation
+# that is typed after a \internal command is included. If the tag is set
+# to NO (the default) then the documentation will be excluded.
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS          = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
+# file names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_NAMES       = NO
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
+# will show members with their full class and namespace scopes in the
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES       = YES
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
+# will put a list of the files that are included by a file in the documentation
+# of that file.
+
+SHOW_INCLUDE_FILES     = YES
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
+# is inserted in the documentation for inline members.
+
+INLINE_INFO            = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
+# will sort the (detailed) documentation of file and class members
+# alphabetically by member name. If set to NO the members will appear in
+# declaration order.
+
+SORT_MEMBER_DOCS       = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
+# brief documentation of file, namespace and class members alphabetically
+# by member name. If set to NO (the default) the members will appear in
+# declaration order.
+
+SORT_BRIEF_DOCS        = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
+# hierarchy of group names into alphabetical order. If set to NO (the default)
+# the group names will appear in their defined order.
+
+SORT_GROUP_NAMES       = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
+# sorted by fully-qualified names, including namespaces. If set to
+# NO (the default), the class list will be sorted only by class name,
+# not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME     = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or
+# disable (NO) the todo list. This list is created by putting \todo
+# commands in the documentation.
+
+GENERATE_TODOLIST      = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or
+# disable (NO) the test list. This list is created by putting \test
+# commands in the documentation.
+
+GENERATE_TESTLIST      = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or
+# disable (NO) the bug list. This list is created by putting \bug
+# commands in the documentation.
+
+GENERATE_BUGLIST       = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
+# disable (NO) the deprecated list. This list is created by putting
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS       =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
+# the initial value of a variable or define consists of for it to appear in
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely.
+# The appearance of the initializer of individual variables and defines in the
+# documentation can be controlled using \showinitializer or \hideinitializer
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES  = 30
+
+# If the sources in your project are distributed over multiple directories
+# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy
+# in the documentation. The default is NO.
+
+SHOW_DIRECTORIES       = NO
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page.
+# This will remove the Files entry from the Quick Index and from the
+# Folder Tree View (if specified). The default is YES.
+
+SHOW_FILES             = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the
+# Namespaces page.  This will remove the Namespaces entry from the Quick Index
+# and from the Folder Tree View (if specified). The default is YES.
+
+SHOW_NAMESPACES        = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command <command> <input-file>, where <command> is the value of
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
+# provided by doxygen. Whatever the program writes to standard output
+# is used as the file version. See the manual for examples.
+
+FILE_VERSION_FILTER    =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed by
+# doxygen. The layout file controls the global structure of the generated output files
+# in an output format independent way. The create the layout file that represents
+# doxygen's defaults, run doxygen with the -l option. You can optionally specify a
+# file name after the option, if omitted DoxygenLayout.xml will be used as the name
+# of the layout file.
+
+LAYOUT_FILE            =
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET                  = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated by doxygen. Possible values are YES and NO. If left blank
+# NO is used.
+
+WARNINGS               = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED   = YES
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some
+# parameters in a documented function, or documenting parameters that
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR      = YES
+
+# This WARN_NO_PARAMDOC option can be abled to get warnings for
+# functions that are documented, but have no documentation for their parameters
+# or return value. If set to NO (the default) doxygen will only warn about
+# wrong or incomplete parameter documentation, but not about the absence of
+# documentation.
+
+WARN_NO_PARAMDOC       = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that
+# doxygen can produce. The string should contain the $file, $line, and $text
+# tags, which will be replaced by the file and line number from which the
+# warning originated and the warning text. Optionally the format may contain
+# $version, which will be replaced by the version of the file (if it could
+# be obtained via FILE_VERSION_FILTER)
+
+WARN_FORMAT            = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning
+# and error messages should be written. If left blank the output is written
+# to stderr.
+
+WARN_LOGFILE           =
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain
+# documented source files. You may enter file names like "myfile.cpp" or
+# directories like "/usr/src/myproject". Separate the files or directories
+# with spaces.
+
+INPUT                  = ./doc_source
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
+# also the default input encoding. Doxygen uses libiconv (or the iconv built
+# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for
+# the list of possible encodings.
+
+INPUT_ENCODING         = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank the following patterns are tested:
+# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx
+# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90
+
+FILE_PATTERNS          = *.c \
+                         *.cc \
+                         *.cxx \
+                         *.cpp \
+                         *.c++ \
+                         *.d \
+                         *.java \
+                         *.ii \
+                         *.ixx \
+                         *.ipp \
+                         *.i++ \
+                         *.inl \
+                         *.h \
+                         *.hh \
+                         *.hxx \
+                         *.hpp \
+                         *.h++ \
+                         *.idl \
+                         *.odl \
+                         *.cs \
+                         *.php \
+                         *.php3 \
+                         *.inc \
+                         *.m \
+                         *.mm \
+                         *.dox \
+                         *.py \
+                         *.f90 \
+                         *.f \
+                         *.vhd \
+                         *.vhdl
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
+# If left blank NO is used.
+
+RECURSIVE              = YES
+
+# The EXCLUDE tag can be used to specify files and/or directories that should
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE                =
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or
+# directories that are symbolic links (a Unix filesystem feature) are excluded
+# from the input.
+
+EXCLUDE_SYMLINKS       = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories. Note that the wildcards are matched
+# against the file with absolute path, so to exclude all test directories
+# for example use the pattern */test/*
+
+EXCLUDE_PATTERNS       =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+
+EXCLUDE_SYMBOLS        =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see
+# the \include command).
+
+EXAMPLE_PATH           =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank all files are included.
+
+EXAMPLE_PATTERNS       = *
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude
+# commands irrespective of the value of the RECURSIVE tag.
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE      = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or
+# directories that contain image that are included in the documentation (see
+# the \image command).
+
+IMAGE_PATH             =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
+# input file. Doxygen will then use the output that the filter program writes
+# to standard output.  If FILTER_PATTERNS is specified, this tag will be
+# ignored.
+
+INPUT_FILTER           =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis.  Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match.  The filters are a list of the form:
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
+# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER
+# is applied to all files.
+
+FILTER_PATTERNS        =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will be used to filter the input files when producing source
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES    = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will
+# be generated. Documented entities will be cross-referenced with these sources.
+# Note: To get rid of all source code in the generated output, make sure also
+# VERBATIM_HEADERS is set to NO.
+
+SOURCE_BROWSER         = YES
+
+# Setting the INLINE_SOURCES tag to YES will include the body
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES         = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
+# doxygen to hide any special comment blocks from generated source code
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS    = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES
+# then for each documented function all documented
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = NO
+
+# If the REFERENCES_RELATION tag is set to YES
+# then for each documented function all documented entities
+# called/used by that function will be listed.
+
+REFERENCES_RELATION    = NO
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
+# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
+# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
+# link to the source code.  Otherwise they will link to the documentation.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code
+# will point to the HTML generated by the htags(1) tool instead of doxygen
+# built-in source browser. The htags tool is part of GNU's global source
+# tagging system (see http://www.gnu.org/software/global/global.html). You
+# will need version 4.8.6 or higher.
+
+USE_HTAGS              = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
+# will generate a verbatim copy of the header file for each class for
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS       = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
+# of all compounds will be generated. Enable this if the project
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX     = NO
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX    = 5
+
+# In case all classes in a project start with a common prefix, all
+# classes will be put under the same header in the alphabetical index.
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX          =
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
+# generate HTML output.
+
+GENERATE_HTML          = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT            = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION    = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard header.
+
+HTML_HEADER            =
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard footer.
+
+HTML_FOOTER            =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
+# style sheet that is used by each HTML page. It can be used to
+# fine-tune the look of the HTML output. If the tag is left blank doxygen
+# will generate a default style sheet. Note that doxygen will try to copy
+# the style sheet file to the HTML output directory, so don't put your own
+# stylesheet in the HTML output directory as well, or it will be erased!
+
+HTML_STYLESHEET        =
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
+# files or namespaces will be aligned in HTML using tables. If set to
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS     = YES
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded. For this to work a browser that supports
+# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox
+# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
+
+HTML_DYNAMIC_SECTIONS  = NO
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files
+# will be generated that can be used as input for Apple's Xcode 3
+# integrated development environment, introduced with OSX 10.5 (Leopard).
+# To create a documentation set, doxygen will generate a Makefile in the
+# HTML output directory. Running make will produce the docset in that
+# directory and running "make install" will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
+# it at startup.
+# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html for more information.
+
+GENERATE_DOCSET        = NO
+
+# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the
+# feed. A documentation feed provides an umbrella under which multiple
+# documentation sets from a single provider (such as a company or product suite)
+# can be grouped.
+
+DOCSET_FEEDNAME        = "Doxygen generated docs"
+
+# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that
+# should uniquely identify the documentation set bundle. This should be a
+# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen
+# will append .docset to the name.
+
+DOCSET_BUNDLE_ID       = org.doxygen.Project
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files
+# will be generated that can be used as input for tools like the
+# Microsoft HTML help workshop to generate a compiled HTML help file (.chm)
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP      = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
+# be used to specify the file name of the resulting .chm file. You
+# can add a path in front of the file if the result should not be
+# written to the html output directory.
+
+CHM_FILE               =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
+# be used to specify the location (absolute path including file name) of
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION           =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
+# controls if a separate .chi index file is generated (YES) or that
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI           = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
+# is used to encode HtmlHelp index (hhk), content (hhc) and project file
+# content.
+
+CHM_INDEX_ENCODING     =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
+# controls whether a binary table of contents is generated (YES) or a
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC             = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND             = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and QHP_VIRTUAL_FOLDER
+# are set, an additional index file will be generated that can be used as input for
+# Qt's qhelpgenerator to generate a Qt Compressed Help (.qch) of the generated
+# HTML documentation.
+
+GENERATE_QHP           = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can
+# be used to specify the file name of the resulting .qch file.
+# The path specified is relative to the HTML output folder.
+
+QCH_FILE               =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#namespace
+
+QHP_NAMESPACE          =
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#virtual-folders
+
+QHP_VIRTUAL_FOLDER     = doc
+
+# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to add.
+# For more information please see
+# http://doc.trolltech.com/qthelpproject.html#custom-filters
+
+QHP_CUST_FILTER_NAME   =
+
+# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the custom filter to add.For more information please see
+# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">Qt Help Project / Custom Filters</a>.
+
+QHP_CUST_FILTER_ATTRS  =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this project's
+# filter section matches.
+# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">Qt Help Project / Filter Attributes</a>.
+
+QHP_SECT_FILTER_ATTRS  =
+
+# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can
+# be used to specify the location of Qt's qhelpgenerator.
+# If non-empty doxygen will try to run qhelpgenerator on the generated
+# .qhp file.
+
+QHG_LOCATION           =
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
+# top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it.
+
+DISABLE_INDEX          = NO
+
+# This tag can be used to set the number of enum values (range [1..20])
+# that doxygen will group on one line in the generated HTML documentation.
+
+ENUM_VALUES_PER_LINE   = 4
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information.
+# If the tag value is set to FRAME, a side panel will be generated
+# containing a tree-like index structure (just like the one that
+# is generated for HTML Help). For this to work a browser that supports
+# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+,
+# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are
+# probably better off using the HTML help feature. Other possible values
+# for this tag are: HIERARCHIES, which will generate the Groups, Directories,
+# and Class Hierarchy pages using a tree view instead of an ordered list;
+# ALL, which combines the behavior of FRAME and HIERARCHIES; and NONE, which
+# disables this behavior completely. For backwards compatibility with previous
+# releases of Doxygen, the values YES and NO are equivalent to FRAME and NONE
+# respectively.
+
+GENERATE_TREEVIEW      = ALL
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
+# used to set the initial width (in pixels) of the frame in which the tree
+# is shown.
+
+TREEVIEW_WIDTH         = 250
+
+# Use this tag to change the font size of Latex formulas included
+# as images in the HTML documentation. The default is 10. Note that
+# when you change the font size after a successful doxygen run you need
+# to manually remove any form_*.png images from the HTML output directory
+# to force them to be regenerated.
+
+FORMULA_FONTSIZE       = 10
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
+# generate Latex output.
+
+GENERATE_LATEX         = YES
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT           = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked. If left blank `latex' will be used as the default command name.
+
+LATEX_CMD_NAME         = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
+# generate index for LaTeX. If left blank `makeindex' will be used as the
+# default command name.
+
+MAKEINDEX_CMD_NAME     = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
+# LaTeX documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_LATEX          = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used
+# by the printer. Possible values are: a4, a4wide, letter, legal and
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE             = a4
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES         =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
+# the generated latex document. The header should contain everything until
+# the first chapter. If it is left blank doxygen will generate a
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER           =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will
+# contain links (just like the HTML output) instead of page references
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS         = YES
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
+# plain latex in the generated Makefile. Set this option to YES to get a
+# higher quality PDF documentation.
+
+USE_PDFLATEX           = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
+# command to the generated LaTeX files. This will instruct LaTeX to keep
+# running if errors occur, instead of asking the user for help.
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE        = NO
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not
+# include the index chapters (such as File Index, Compound Index, etc.)
+# in the output.
+
+LATEX_HIDE_INDICES     = NO
+
+# If LATEX_SOURCE_CODE is set to YES then doxygen will include
+# source code with syntax highlighting in the LaTeX output.
+# Note that which sources are shown also depends on other settings
+# such as SOURCE_BROWSER.
+
+LATEX_SOURCE_CODE      = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
+# The RTF output is optimized for Word 97 and may not look very pretty with
+# other RTF readers or editors.
+
+GENERATE_RTF           = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT             = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
+# RTF documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_RTF            = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
+# will contain hyperlink fields. The RTF file will
+# contain links (just like the HTML output) instead of page references.
+# This makes the output suitable for online browsing using WORD or other
+# programs which support those fields.
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS         = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's
+# config file, i.e. a series of assignments. You only have to provide
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE    =
+
+# Set optional variables used in the generation of an rtf document.
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE    =
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
+# generate man pages
+
+GENERATE_MAN           = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT             = man
+
+# The MAN_EXTENSION tag determines the extension that is added to
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION          = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
+# then it will generate one additional man file for each entity
+# documented in the real man page(s). These additional files
+# only source the real man page, but without them the man command
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS              = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will
+# generate an XML file that captures the structure of
+# the code including all documentation.
+
+GENERATE_XML           = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT             = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_SCHEMA             =
+
+# The XML_DTD tag can be used to specify an XML DTD,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_DTD                =
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
+# dump the program listings (including syntax highlighting
+# and cross-referencing information) to the XML output. Note that
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING     = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
+# generate an AutoGen Definitions (see autogen.sf.net) file
+# that captures the structure of the code including all
+# documentation. Note that this feature is still experimental
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF   = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will
+# generate a Perl module file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
+# moment.
+
+GENERATE_PERLMOD       = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX          = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
+# nicely formatted so it can be parsed by a human reader.  This is useful
+# if you want to understand what is going on.  On the other hand, if this
+# tag is set to NO the size of the Perl module output will be much smaller
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY         = YES
+
+# The names of the make variables in the generated doxyrules.make file
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
+# This is useful so different doxyrules.make files included by the same
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
+# files.
+
+ENABLE_PREPROCESSING   = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
+# names in the source code. If set to NO (the default) only conditional
+# compilation will be performed. Macro expansion can be done in a controlled
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION        = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
+# then the macro expansion is limited to the macros specified with the
+# PREDEFINED and EXPAND_AS_DEFINED tags.
+
+EXPAND_ONLY_PREDEF     = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
+# in the INCLUDE_PATH (see below) will be search if a #include is found.
+
+SEARCH_INCLUDES        = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by
+# the preprocessor.
+
+INCLUDE_PATH           =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will
+# be used.
+
+INCLUDE_FILE_PATTERNS  =
+
+# The PREDEFINED tag can be used to specify one or more macro names that
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). If the definition and the = are
+# omitted =1 is assumed. To prevent a macro definition from being
+# undefined via #undef or recursively expanded use the := operator
+# instead of the = operator.
+
+PREDEFINED             =
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
+# this tag can be used to specify a list of macro names that should be expanded.
+# The macro definition that is found in the sources will be used.
+# Use the PREDEFINED tag if you want to use a different macro definition.
+
+EXPAND_AS_DEFINED      =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
+# doxygen's preprocessor will remove all function-like macros that are alone
+# on a line, have an all uppercase name, and do not end with a semicolon. Such
+# function macros are typically used for boiler-plate code, and will confuse
+# the parser if not removed.
+
+SKIP_FUNCTION_MACROS   = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles.
+# Optionally an initial location of the external documentation
+# can be added for each tagfile. The format of a tag file without
+# this location is as follows:
+#   TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+#   TAGFILES = file1=loc1 "file2 = loc2" ...
+# where "loc1" and "loc2" can be relative or absolute paths or
+# URLs. If a location is present for each tag, the installdox tool
+# does not have to be run to correct the links.
+# Note that each tag file must have a unique name
+# (where the name does NOT include the path)
+# If a tag file is not located in the directory in which doxygen
+# is run, you must also specify the path to the tagfile here.
+
+TAGFILES               =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE       =
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed
+# in the class index. If set to NO only the inherited external classes
+# will be listed.
+
+ALLEXTERNALS           = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will
+# be listed.
+
+EXTERNAL_GROUPS        = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH              = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
+# or super classes. Setting the tag to NO turns the diagrams off. Note that
+# this option is superseded by the HAVE_DOT option below. This is only a
+# fallback. It is recommended to install and use dot, since it yields more
+# powerful graphs.
+
+CLASS_DIAGRAMS         = NO
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see
+# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH            =
+
+# If set to YES, the inheritance and collaboration graphs will hide
+# inheritance and usage relations if the target is undocumented
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS   = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz, a graph visualization
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT               = YES
+
+# By default doxygen will write a font called FreeSans.ttf to the output
+# directory and reference it in all dot files that doxygen generates. This
+# font does not include all possible unicode characters however, so when you need
+# these (or just want a differently looking font) you can specify the font name
+# using DOT_FONTNAME. You need need to make sure dot is able to find the font,
+# which can be done by putting it in a standard location or by setting the
+# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory
+# containing the font.
+
+DOT_FONTNAME           = FreeSans
+
+# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs.
+# The default size is 10pt.
+
+DOT_FONTSIZE           = 10
+
+# By default doxygen will tell dot to use the output directory to look for the
+# FreeSans.ttf font (which doxygen will put there itself). If you specify a
+# different font using DOT_FONTNAME you can set the path where dot
+# can find it using this tag.
+
+DOT_FONTPATH           =
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect inheritance relations. Setting this tag to YES will force the
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH            = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect implementation dependencies (inheritance, containment, and
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH    = YES
+
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for groups, showing the direct groups dependencies
+
+GROUP_GRAPHS           = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+
+UML_LOOK               = YES
+
+# If set to YES, the inheritance and collaboration graphs will show the
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS     = NO
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
+# tags are set to YES then doxygen will generate a graph for each documented
+# file showing the direct and indirect include dependencies of the file with
+# other documented files.
+
+INCLUDE_GRAPH          = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
+# documented header file showing the documented files that directly or
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH      = YES
+
+# If the CALL_GRAPH and HAVE_DOT options are set to YES then
+# doxygen will generate a call dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable call graphs
+# for selected functions only using the \callgraph command.
+
+CALL_GRAPH             = YES
+
+# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then
+# doxygen will generate a caller dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable caller
+# graphs for selected functions only using the \callergraph command.
+
+CALLER_GRAPH           = YES
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
+# will graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY    = YES
+
+# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES
+# then doxygen will show the dependencies a directory has on other directories
+# in a graphical way. The dependency relations are determined by the #include
+# relations between the files in the directories.
+
+DIRECTORY_GRAPH        = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. Possible values are png, jpg, or gif
+# If left blank png will be used.
+
+DOT_IMAGE_FORMAT       = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+
+DOT_PATH               =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the
+# \dotfile command).
+
+DOTFILE_DIRS           =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
+# nodes that will be shown in the graph. If the number of nodes in a graph
+# becomes larger than this value, doxygen will truncate the graph, which is
+# visualized by representing a node as a red box. Note that doxygen if the
+# number of direct children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note
+# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+
+DOT_GRAPH_MAX_NODES    = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
+# graphs generated by dot. A depth value of 3 means that only nodes reachable
+# from the root by following a path via at most 3 edges will be shown. Nodes
+# that lay further from the root node will be omitted. Note that setting this
+# option to 1 or 2 may greatly reduce the computation time needed for large
+# code bases. Also note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+
+MAX_DOT_GRAPH_DEPTH    = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not
+# seem to support this out of the box. Warning: Depending on the platform used,
+# enabling this option may lead to badly anti-aliased labels on the edges of
+# a graph (i.e. they become hard to read).
+
+DOT_TRANSPARENT        = YES
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10)
+# support this, this feature is disabled by default.
+
+DOT_MULTI_TARGETS      = YES
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
+# generate a legend page explaining the meaning of the various boxes and
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND        = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
+# remove the intermediate dot files that are used to generate
+# the various graphs.
+
+DOT_CLEANUP            = YES
+
+#---------------------------------------------------------------------------
+# Options related to the search engine
+#---------------------------------------------------------------------------
+
+# The SEARCHENGINE tag specifies whether or not a search engine should be
+# used. If set to NO the values of all tags below this one will be ignored.
+
+SEARCHENGINE           = NO
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..07dd198
--- /dev/null
+++ b/README
@@ -0,0 +1,15 @@
+/* $Id$ */
+
+There are two ways to install webcal. Outside web scope and inside web
+scope.
+
+Inside web scope:
+Untar the package in a web enabled directory and point your browser to
+this directory. To install webcal simply follow the automatically
+started wizard.
+
+Outside web scope:
+Untar the package to any directory outside web scope and go to the
+directory install and run the script setup as root or by using sudo.
+Answer the question and follow the directions given. The script requires
+Perl.
diff --git a/caldav/awl/AWLUtilities.php b/caldav/awl/AWLUtilities.php
new file mode 100644 (file)
index 0000000..5b11df1
--- /dev/null
@@ -0,0 +1,403 @@
+<?php
+/**
+* Utility functions of a general nature which are used by
+* most AWL library classes.
+*
+* @package   awl
+* @subpackage   Utilities
+* @author    Andrew McMillan <andrew@catalyst.net.nz>
+* @copyright Catalyst IT Ltd
+* @license   http://gnu.org/copyleft/gpl.html GNU GPL v2
+*/
+
+if ( !function_exists('dbg_error_log') ) {
+  /**
+  * Writes a debug message into the error log using printf syntax.  If the first
+  * parameter is "ERROR" then the message will _always_ be logged.
+  * Otherwise, the first parameter is a "component" name, and will only be logged
+  * if $c->dbg["component"] is set to some non-null value.
+  *
+  * If you want to see every log message then $c->dbg["ALL"] can be set, to
+  * override the debugging status of the individual components.
+  *
+  * @var string $component The component to identify itself, or "ERROR", or "LOG:component"
+  * @var string $format A format string for the log message
+  * @var [string $parameter ...] Parameters for the format string.
+  */
+  function dbg_error_log() {
+    global $c;
+    $argc = func_num_args();
+    $args = func_get_args();
+    $type = "DBG";
+    $component = array_shift($args);
+    if ( substr( $component, 0, 3) == "LOG" ) {
+      // Special escape case for stuff that always gets logged.
+      $type = 'LOG';
+      $component = substr($component,4);
+    }
+    else if ( $component == "ERROR" ) {
+      $type = "***";
+    }
+    else if ( isset($c->dbg["ALL"]) ) {
+      $type = "ALL";
+    }
+    else if ( !isset($c->dbg[strtolower($component)]) ) return;
+
+    if ( 2 <= $argc ) {
+      $format = array_shift($args);
+    }
+    else {
+      $format = "%s";
+    }
+    @error_log( $c->sysabbr.": $type: $component:". vsprintf( $format, $args ) );
+  }
+}
+
+
+
+if ( !function_exists('apache_request_headers') ) {
+  /**
+  * Forward compatibility so we can use the non-deprecated name in PHP4
+  * @package awl
+  */
+  function apache_request_headers() {
+    return getallheaders();
+  }
+}
+
+
+
+if ( !function_exists('dbg_log_array') ) {
+  /**
+  * Function to dump an array to the error log, possibly recursively
+  *
+  * @var string $component Which component should this log message identify itself from
+  * @var string $name What name should this array dump identify itself as
+  * @var array $arr The array to be dumped.
+  * @var boolean $recursive Should the dump recurse into arrays/objects in the array
+  */
+  function dbg_log_array( $component, $name, $arr, $recursive = false ) {
+    if ( !isset($arr) || (gettype($arr) != 'array' && gettype($arr) != 'object') ) {
+      dbg_error_log( $component, "%s: array is not set, or is not an array!", $name);
+      return;
+    }
+    foreach ($arr as $key => $value) {
+      dbg_error_log( $component, "%s: >>%s<< = >>%s<<", $name, $key,
+                      (gettype($value) == 'array' || gettype($value) == 'object' ? gettype($value) : $value) );
+      if ( $recursive && (gettype($value) == 'array' || (gettype($value) == 'object' && "$key" != 'self' && "$key" != 'parent') ) ) {
+        dbg_log_array( $component, "$name"."[$key]", $value, $recursive );
+      }
+    }
+  }
+}
+
+
+
+if ( !function_exists("session_salted_md5") ) {
+  /**
+  * Make a salted MD5 string, given a string and (possibly) a salt.
+  *
+  * If no salt is supplied we will generate a random one.
+  *
+  * @param string $instr The string to be salted and MD5'd
+  * @param string $salt Some salt to sprinkle into the string to be MD5'd so we don't get the same PW always hashing to the same value.
+  * @return string The salt, a * and the MD5 of the salted string, as in SALT*SALTEDHASH
+  */
+  function session_salted_md5( $instr, $salt = "" ) {
+    if ( $salt == "" ) $salt = substr( md5(rand(100000,999999)), 2, 8);
+    dbg_error_log( "Login", "Making salted MD5: salt=$salt, instr=$instr, md5($salt$instr)=".md5($salt . $instr) );
+    return ( sprintf("*%s*%s", $salt, md5($salt . $instr) ) );
+  }
+}
+
+
+
+if ( !function_exists("session_salted_sha1") && version_compare(phpversion(), "4.9.9") > 0 ) {
+  /**
+  * Make a salted SHA1 string, given a string and (possibly) a salt.  PHP5 only (although it
+  * could be made to work on PHP4 (@see http://www.openldap.org/faq/data/cache/347.html). The
+  * algorithm used here is compatible with OpenLDAP so passwords generated through this function
+  * should be able to be migrated to OpenLDAP by using the part following the second '*', i.e.
+  * the '{SSHA}....' part.
+  *
+  * If no salt is supplied we will generate a random one.
+  *
+  * @param string $instr The string to be salted and SHA1'd
+  * @param string $salt Some salt to sprinkle into the string to be SHA1'd so we don't get the same PW always hashing to the same value.
+  * @return string A *, the salt, a * and the SHA1 of the salted string, as in *SALT*SALTEDHASH
+  */
+  function session_salted_sha1( $instr, $salt = "" ) {
+    if ( $salt == "" ) $salt = substr( str_replace('*','',base64_encode(sha1(rand(100000,9999999),true))), 2, 9);
+    dbg_error_log( "Login", "Making salted SHA1: salt=$salt, instr=$instr, encoded($instr$salt)=".base64_encode(sha1($instr . $salt, true).$salt) );
+    return ( sprintf("*%s*{SSHA}%s", $salt, base64_encode(sha1($instr.$salt, true) . $salt ) ) );
+  }
+}
+
+
+if ( !function_exists("session_validate_password") ) {
+  /**
+  * Checks what a user entered against the actual password on their account.
+  * @param string $they_sent What the user entered.
+  * @param string $we_have What we have in the database as their password.  Which may (or may not) be a salted MD5.
+  * @return boolean Whether or not the users attempt matches what is already on file.
+  */
+  function session_validate_password( $they_sent, $we_have ) {
+    if ( preg_match('/^\*\*.+$/', $we_have ) ) {
+      //  The "forced" style of "**plaintext" to allow easier admin setting
+      return ( "**$they_sent" == $we_have );
+    }
+
+    if ( preg_match('/^\*(.+)\*{[A-Z]+}.+$/', $we_have, $regs ) ) {
+      if ( function_exists("session_salted_sha1") ) {
+        // A nicely salted sha1sum like "*<salt>*{SSHA}<salted_sha1>"
+        $salt = $regs[1];
+        $sha1_sent = session_salted_sha1( $they_sent, $salt ) ;
+        return ( $sha1_sent == $we_have );
+      }
+      else {
+        dbg_error_log( "ERROR", "Password is salted SHA-1 but you are using PHP4!" );
+        echo <<<EOERRMSG
+<html>
+<head>
+<title>Salted SHA1 Password format not supported with PHP4</title>
+</head>
+<body>
+<h1>Salted SHA1 Password format not supported with PHP4</h1>
+<p>At some point you have used PHP5 to set the password for this user and now you are
+   using PHP4.  You will need to assign a new password to this user using PHP4, or ensure
+   you use PHP5 everywhere (recommended).</p>
+<p>AWL has now switched to using salted SHA-1 passwords by preference in a format
+   compatible with OpenLDAP.</p>
+</body>
+</html>
+EOERRMSG;
+        exit;
+      }
+    }
+
+    if ( preg_match('/^\*(.+)\*.+$/', $we_have, $regs ) ) {
+      // A nicely salted md5sum like "*<salt>*<salted_md5>"
+      $salt = $regs[1];
+      $md5_sent = session_salted_md5( $they_sent, $salt ) ;
+      return ( $md5_sent == $we_have );
+    }
+
+    // Anything else is bad
+    return false;
+
+  }
+}
+
+
+
+if ( !function_exists("replace_uri_params") ) {
+  /**
+  * Given a URL (presumably the current one) and a parameter, replace the value of parameter,
+  * extending the URL as necessary if the parameter is not already there.
+  * @param string $uri The URI we will be replacing parameters in.
+  * @param array $replacements An array of replacement pairs array( "replace_this" => "with this" )
+  * @return string The URI with the replacements done.
+  */
+  function replace_uri_params( $uri, $replacements ) {
+    $replaced = $uri;
+    foreach( $replacements AS $param => $new_value ) {
+      $rxp = preg_replace( '/([\[\]])/', '\\\\$1', $param );  // Some parameters may be arrays.
+      $regex = "/([&?])($rxp)=([^&]+)/";
+      dbg_error_log("core", "Looking for [%s] to replace with [%s] regex is %s and searching [%s]", $param, $new_value, $regex, $replaced );
+      if ( preg_match( $regex, $replaced ) )
+        $replaced = preg_replace( $regex, "\$1$param=$new_value", $replaced);
+      else
+        $replaced .= "&$param=$new_value";
+    }
+    if ( ! preg_match( '/\?/', $replaced  ) ) {
+      $replaced = preg_replace("/&(.+)$/", "?\$1", $replaced);
+    }
+    $replaced = str_replace("&amp;", "--AmPeRsAnD--", $replaced);
+    $replaced = str_replace("&", "&amp;", $replaced);
+    $replaced = str_replace("--AmPeRsAnD--", "&amp;", $replaced);
+    dbg_error_log("core", "URI <<$uri>> morphed to <<$replaced>>");
+    return $replaced;
+  }
+}
+
+
+if ( !function_exists("uuid") ) {
+/**
+ * Generates a Universally Unique IDentifier, version 4.
+ *
+ * RFC 4122 (http://www.ietf.org/rfc/rfc4122.txt) defines a special type of Globally
+ * Unique IDentifiers (GUID), as well as several methods for producing them. One
+ * such method, described in section 4.4, is based on truly random or pseudo-random
+ * number generators, and is therefore implementable in a language like PHP.
+ *
+ * We choose to produce pseudo-random numbers with the Mersenne Twister, and to always
+ * limit single generated numbers to 16 bits (ie. the decimal value 65535). That is
+ * because, even on 32-bit systems, PHP's RAND_MAX will often be the maximum *signed*
+ * value, with only the equivalent of 31 significant bits. Producing two 16-bit random
+ * numbers to make up a 32-bit one is less efficient, but guarantees that all 32 bits
+ * are random.
+ *
+ * The algorithm for version 4 UUIDs (ie. those based on random number generators)
+ * states that all 128 bits separated into the various fields (32 bits, 16 bits, 16 bits,
+ * 8 bits and 8 bits, 48 bits) should be random, except : (a) the version number should
+ * be the last 4 bits in the 3rd field, and (b) bits 6 and 7 of the 4th field should
+ * be 01. We try to conform to that definition as efficiently as possible, generating
+ * smaller values where possible, and minimizing the number of base conversions.
+ *
+ * @copyright  Copyright (c) CFD Labs, 2006. This function may be used freely for
+ *              any purpose ; it is distributed without any form of warranty whatsoever.
+ * @author      David Holmes <dholmes@cfdsoftware.net>
+ *
+ * @return  string  A UUID, made up of 32 hex digits and 4 hyphens.
+ */
+
+  function uuid() {
+
+    // The field names refer to RFC 4122 section 4.1.2
+
+    return sprintf('%04x%04x-%04x-%03x4-%04x-%04x%04x%04x',
+        mt_rand(0, 65535), mt_rand(0, 65535), // 32 bits for "time_low"
+        mt_rand(0, 65535), // 16 bits for "time_mid"
+        mt_rand(0, 4095),  // 12 bits before the 0100 of (version) 4 for "time_hi_and_version"
+        bindec(substr_replace(sprintf('%016b', mt_rand(0, 65535)), '01', 6, 2)),
+            // 8 bits, the last two of which (positions 6 and 7) are 01, for "clk_seq_hi_res"
+            // (hence, the 2nd hex digit after the 3rd hyphen can only be 1, 5, 9 or d)
+            // 8 bits for "clk_seq_low"
+        mt_rand(0, 65535), mt_rand(0, 65535), mt_rand(0, 65535) // 48 bits for "node"
+    );
+  }
+}
+
+if ( !function_exists("translate") ) {
+  require_once("Translation.php");
+}
+
+ if ( !function_exists("clone") && version_compare(phpversion(), '5.0') < 0) {
+  /**
+  * PHP5 screws with the assignment operator changing so that $a = $b means that
+  * $a becomes a reference to $b.  There is a clone() that we can use in PHP5, so
+  * we have to emulate that for PHP4.  Bleargh.
+  */
+  eval( 'function clone($object) { return $object; }' );
+}
+
+if ( !function_exists("quoted_printable_encode") ) {
+  /**
+  * Process a string to fit the requirements of RFC2045 section 6.7.  Note that
+  * this works, but replaces more characters than the minimum set. For readability
+  * the spaces aren't encoded as =20 though.
+  */
+  function quoted_printable_encode($string) {
+    return preg_replace('/[^\r\n]{73}[^=\r\n]{2}/', "$0=\r\n", str_replace("%","=",str_replace("%20"," ",rawurlencode($string))));
+  }
+}
+
+
+if ( !function_exists("clean_by_regex") ) {
+  /**
+  * Clean a value by applying a regex to it.  If it is an array apply it to
+  * each element in the array recursively.  If it is an object we don't mess
+  * with it.
+  */
+  function clean_by_regex( $val, $regex ) {
+    if ( is_null($val) ) return null;
+    switch( $regex ) {
+      case 'int':     $regex = '#^\d+$#';     break;
+    }
+    if ( is_array($val) ) {
+      foreach( $val AS $k => $v ) {
+        $val[$k] = clean_by_regex($v,$regex);
+      }
+    }
+    else if ( ! is_object($val) ) {
+      if ( preg_match( $regex, $val, $matches) ) {
+        $val = $matches[0];
+      }
+      else {
+        $val = '';
+      }
+    }
+    return $val;
+  }
+}
+
+
+if ( !function_exists("param_to_global") ) {
+  /**
+  * Convert a parameter to a global.  We first look in _POST and then in _GET,
+  * and if they passed in a bunch of valid characters, we will make sure the
+  * incoming is cleaned to only match that set.
+  *
+  * @param string $varname The name of the global variable to put the answer in
+  * @param string $match_regex The part of the parameter matching this regex will be returned
+  * @param string $alias1  An alias for the name that we should look for first.
+  * @param    "    ...     More aliases, in the order which they should be examined.  $varname will be appended to the end.
+  */
+  function param_to_global( ) {
+    $args = func_get_args();
+
+    $varname = array_shift($args);
+    $GLOBALS[$varname] = null;
+
+    $match_regex = null;
+    $argc = func_num_args();
+    if ( $argc > 1 ) {
+      $match_regex = array_shift($args);
+    }
+
+    $args[] = $varname;
+    foreach( $args AS $k => $name ) {
+      if ( isset($_POST[$name]) ) {
+        $result = $_POST[$name];
+        break;
+      }
+      else if ( isset($_GET[$name]) ) {
+        $result = $_GET[$name];
+        break;
+      }
+    }
+    if ( !isset($result) ) return null;
+
+    if ( isset($match_regex) ) {
+      $result = clean_by_regex( $result, $match_regex );
+    }
+
+    $GLOBALS[$varname] = $result;
+    return $result;
+  }
+}
+
+
+if ( !function_exists("get_fields") ) {
+  /**
+  * @var array $_AWL_field_cache is a cache of the field names for a table
+  */
+  $_AWL_field_cache = array();
+
+
+  /**
+  * Get the names of the fields for a particular table
+  * @param string $tablename The name of the table.
+  * @return array of string The public fields in the table.
+  */
+  function get_fields( $tablename ) {
+    global $_AWL_field_cache;
+
+    if ( !isset($_AWL_field_cache[$tablename]) ) {
+      dbg_error_log( "DataUpdate", ":get_fields: Loaded fields for table '$tablename'" );
+      $sql = "SELECT f.attname, t.typname FROM pg_attribute f ";
+      $sql .= "JOIN pg_class c ON ( f.attrelid = c.oid ) ";
+      $sql .= "JOIN pg_type t ON ( f.atttypid = t.oid ) ";
+      $sql .= "WHERE relname = ? AND attnum >= 0 order by f.attnum;";
+      $qry = new PgQuery( $sql, $tablename );
+      $qry->Exec("DataUpdate");
+      $fields = array();
+      while( $row = $qry->Fetch() ) {
+        $fields["$row->attname"] = $row->typname;
+      }
+      $_AWL_field_cache[$tablename] = $fields;
+    }
+    return $_AWL_field_cache[$tablename];
+  }
+}
+
diff --git a/caldav/awl/Translation.php b/caldav/awl/Translation.php
new file mode 100644 (file)
index 0000000..913f3aa
--- /dev/null
@@ -0,0 +1,105 @@
+<?php
+/**
+* Functions involved in translating with gettext
+* @package awl
+* @subpackage   Translation
+* @author    Andrew McMillan <andrew@catalyst.net.nz>
+* @copyright Catalyst IT Ltd
+* @license   http://gnu.org/copyleft/gpl.html GNU GPL v2
+*/
+if ( !function_exists("i18n") ) {
+  /**
+  * Mark a string as being internationalized.  This is a semaphore method; it
+  * does nothing but it allows us to easily identify strings that require
+  * translation.  Generally this is used to mark strings that will be stored
+  * in the database (like descriptions of permissions).
+  *
+  * AWL uses GNU gettext for internationalization (i18n) and localization (l10n) of
+  * text presented to the user. Gettext needs to know about all places involving strings,
+  * that must be translated. Mark any place, where localization at runtime shall take place
+  * by using the function translate().
+  *
+  * In the help I have used 'xlate' rather than 'translate' and 'x18n' rather than 'i18n'
+  * so that the tools skip this particular file for translation :-)
+  *
+  * E.g. instead of:
+  *   print 'TEST to be displayed in different languages';
+  * use:
+  *   print xlate('TEST to be displayed in different languages');
+  * and you are all set for pure literals. The translation teams will receive that literal
+  * string as a job to translate and will translate it (when the message is clear enough).
+  * At runtime the message is then localized when printed.
+  * The input string can contain a hint to assist translators:
+  *   print xlate('TT <!-- abbreviation for Translation Test -->');
+  * The hint portion of the string will not be printed.
+  *
+  * But consider this case:
+  *   $message_to_be_localized = 'TEST to be displayed in different languages';
+  *   print xlate($message_to_be_localized);
+  *
+  * The translate() function is called in the right place for runtime handling, but there
+  * is no message at gettext preprocessing time to be given to the translation teams,
+  * just a variable name. Translation of the variable name would break the code! So all
+  * places potentially feeding this variable have to be marked to be given to translation
+  * teams, but not translated at runtime!
+  *
+  * This method resolves all such cases. Simply mark the candidates:
+  *   $message_to_be_localized = x18n('TEST to be displayed in different languages');
+  *   print xlate($message_to_be_localized);
+  *
+  * @param string the value
+  * @return string the same value
+  */
+  function i18n($value) {
+    return $value;  /* Just pass the value through */
+  }
+}
+
+
+if ( !function_exists("translate") ) {
+  /**
+  * Convert a string in English to whatever this user's locale is
+  */
+  function translate( $en ) {
+    $xl = gettext($en);
+    dbg_error_log("I18N","Translated =%s= into =%s=", $en, $xl );
+    return $xl;
+  }
+}
+
+
+if ( !function_exists("init_gettext") ) {
+  /**
+  * Initialise our use of Gettext
+  */
+  function init_gettext( $domain, $location ) {
+    bindtextdomain( $domain, $location );
+    $codeset = bind_textdomain_codeset( $domain, "UTF-8" );
+    textdomain( $domain );
+    dbg_error_log("I18N","Bound domain =%s= to location =%s= using character set =%s=", $domain, $location, $codeset );
+  }
+}
+
+
+if ( !function_exists("awl_set_locale") ) {
+  /**
+  * Set the translation to the user's locale.  At this stage all we do is
+  * call the gettext function.
+  */
+  function awl_set_locale( $locale ) {
+    global $c;
+
+    if ( !is_array($locale) && ! preg_match('/^[a-z]{2}(_[A-Z]{2})?\./', $locale ) ) {
+      $locale = array( $locale, $locale.".UTF-8");
+    }
+    if ( $newlocale = setlocale( LC_ALL, $locale) ) {
+      dbg_error_log("I18N","Set locale to =%s=", $newlocale );
+      $c->current_locale = $newlocale;
+    }
+    else {
+      dbg_log_array("I18N","Unsupported locale: ", $locale, false );
+    }
+  }
+}
+
+?>
\ No newline at end of file
diff --git a/caldav/awl/XMLElement.php b/caldav/awl/XMLElement.php
new file mode 100644 (file)
index 0000000..ffd4fce
--- /dev/null
@@ -0,0 +1,333 @@
+<?php
+/**
+* A class to assist with construction of XML documents
+*
+* @package   awl
+* @subpackage   XMLElement
+* @author    Andrew McMillan <andrew@mcmillan.net.nz>
+* @copyright Catalyst .Net Ltd, Morphoss Ltd <http://www.morphoss.com/>
+* @license   http://gnu.org/copyleft/gpl.html GNU GPL v2
+*/
+require_once("AWLUtilities.php");
+
+/**
+* A class for XML elements which may have attributes, or contain
+* other XML sub-elements
+*
+* @package   awl
+*/
+class XMLElement {
+  var $tagname;
+  var $xmlns;
+  var $attributes;
+  var $content;
+  var $_parent;
+
+  /**
+  * Constructor - nothing fancy as yet.
+  *
+  * @param string $tagname The tag name of the new element
+  * @param mixed $content Either a string of content, or an array of sub-elements
+  * @param array $attributes An array of attribute name/value pairs
+  * @param array $xmlns An XML namespace specifier
+  */
+  function XMLElement( $tagname, $content=false, $attributes=false, $xmlns=null ) {
+    $this->tagname=$tagname;
+    if ( gettype($content) == "object" ) {
+      // Subtree to be parented here
+      $this->content = array(&$content);
+    }
+    else {
+      // Array or text
+      $this->content = $content;
+    }
+    $this->attributes = $attributes;
+    if ( isset($this->attributes['xmlns']) ) {  // Oversimplification to be removed
+      $this->xmlns = $this->attributes['xmlns'];
+    }
+    if ( isset($xmlns) ) {
+      $this->xmlns = $xmlns;
+    }
+  }
+
+
+  /**
+  * Count the number of elements
+  * @return int The number of elements
+  */
+  function CountElements( ) {
+    if ( $this->content === false ) return 0;
+    if ( is_array($this->content) ) return count($this->content);
+    if ( $this->content == '' ) return 0;
+    return 1;
+  }
+
+  /**
+  * Set an element attribute to a value
+  *
+  * @param string The attribute name
+  * @param string The attribute value
+  */
+  function SetAttribute($k,$v) {
+    if ( gettype($this->attributes) != "array" ) $this->attributes = array();
+    $this->attributes[$k] = $v;
+    if ( strtolower($k) == 'xmlns' ) {
+      $this->xmlns = $v;
+    }
+  }
+
+  /**
+  * Set the whole content to a value
+  *
+  * @param mixed The element content, which may be text, or an array of sub-elements
+  */
+  function SetContent($v) {
+    $this->content = $v;
+  }
+
+  /**
+  * Accessor for the tag name
+  *
+  * @return string The tag name of the element
+  */
+  function GetTag() {
+    return $this->tagname;
+  }
+
+  /**
+  * Accessor for the full-namespaced tag name
+  *
+  * @return string The tag name of the element, prefixed by the namespace
+  */
+  function GetNSTag() {
+    return $this->xmlns . ':' . $this->tagname;
+  }
+
+  /**
+  * Accessor for a single attribute
+  * @param string $attr The name of the attribute.
+  * @return string The value of that attribute of the element
+  */
+  function GetAttribute( $attr ) {
+    if ( isset($this->attributes[$attr]) ) return $this->attributes[$attr];
+  }
+
+  /**
+  * Accessor for the attributes
+  *
+  * @return array The attributes of this element
+  */
+  function GetAttributes() {
+    return $this->attributes;
+  }
+
+  /**
+  * Accessor for the content
+  *
+  * @return array The content of this element
+  */
+  function GetContent() {
+    return $this->content;
+  }
+
+  /**
+  * Return an array of elements matching the specified tag
+  *
+  * @return array The XMLElements within the tree which match this tag
+  */
+  function GetElements( $tag, $recursive=false ) {
+    $elements = array();
+    if ( gettype($this->content) == "array" ) {
+      foreach( $this->content AS $k => $v ) {
+        if ( $v->tagname == $tag ) {
+          $elements[] = $v;
+        }
+        if ( $recursive ) {
+          $elements = $elements + $v->GetElements($tag,true);
+        }
+      }
+    }
+    return $elements;
+  }
+
+
+  /**
+  * Return an array of elements matching the specified path
+  *
+  * @return array The XMLElements within the tree which match this tag
+  */
+  function GetPath( $path ) {
+    $elements = array();
+    // printf( "Querying within '%s' for path '%s'\n", $this->tagname, $path );
+    if ( !preg_match( '#(/)?([^/]+)(/?.*)$#', $path, $matches ) ) return $elements;
+    // printf( "Matches: %s -- %s -- %s\n", $matches[1], $matches[2], $matches[3] );
+    if ( $matches[2] == '*' || strtolower($matches[2]) == strtolower($this->tagname) ) {
+      if ( $matches[3] == '' ) {
+        /**
+        * That is the full path
+        */
+        $elements[] = $this;
+      }
+      else if ( gettype($this->content) == "array" ) {
+        /**
+        * There is more to the path, so we recurse into that sub-part
+        */
+        foreach( $this->content AS $k => $v ) {
+          $elements = array_merge( $elements, $v->GetPath($matches[3]) );
+        }
+      }
+    }
+
+    if ( $matches[1] != '/' && gettype($this->content) == "array" ) {
+      /**
+      * If our input $path was not rooted, we recurse further
+      */
+      foreach( $this->content AS $k => $v ) {
+        $elements = array_merge( $elements, $v->GetPath($path) );
+      }
+    }
+    // printf( "Found %d within '%s' for path '%s'\n", count($elements), $this->tagname, $path );
+    return $elements;
+  }
+
+
+  /**
+  * Add a sub-element
+  *
+  * @param object An XMLElement to be appended to the array of sub-elements
+  */
+  function AddSubTag(&$v) {
+    if ( gettype($this->content) != "array" ) $this->content = array();
+    $this->content[] =& $v;
+    return count($this->content);
+  }
+
+  /**
+  * Add a new sub-element
+  *
+  * @param string The tag name of the new element
+  * @param mixed Either a string of content, or an array of sub-elements
+  * @param array An array of attribute name/value pairs
+  *
+  * @return objectref A reference to the new XMLElement
+  */
+  function &NewElement( $tagname, $content=false, $attributes=false, $xmlns=null ) {
+    if ( gettype($this->content) != "array" ) $this->content = array();
+    $element =/*&*/ new XMLElement($tagname,$content,$attributes,$xmlns);
+    $this->content[] =/*&*/ $element;
+    return $element;
+  }
+
+
+  /**
+  * Render just the internal content
+  *
+  * @return string The content of this element, as a string without this element wrapping it.
+  */
+  function RenderContent($indent=0, $nslist=null ) {
+    $r = "";
+    if ( is_array($this->content) ) {
+      /**
+      * Render the sub-elements with a deeper indent level
+      */
+      $r .= "\n";
+      foreach( $this->content AS $k => $v ) {
+        if ( is_object($v) ) {
+          $r .= $v->Render($indent+1, "", $nslist);
+        }
+      }
+      $r .= substr("                        ",0,$indent);
+    }
+    else {
+      /**
+      * Render the content, with special characters escaped
+      *
+      */
+      $r .= htmlspecialchars($this->content, ENT_NOQUOTES );
+    }
+    return $r;
+  }
+
+
+  /**
+  * Render the document tree into (nicely formatted) XML
+  *
+  * @param int The indenting level for the pretty formatting of the element
+  */
+  function Render($indent=0, $xmldef="", $nslist=null) {
+    $r = ( $xmldef == "" ? "" : $xmldef."\n");
+
+    $attr = "";
+    $tagname = $this->tagname;
+    if ( gettype($this->attributes) == "array" ) {
+      /**
+      * Render the element attribute values
+      */
+      foreach( $this->attributes AS $k => $v ) {
+        if ( preg_match('#^xmlns(:?(.+))?$#', $k, $matches ) ) {
+          if ( !isset($nslist) ) $nslist = array();
+          $prefix = (isset($matches[2]) ? $matches[2] : '');
+          if ( isset($nslist[$v]) && $nslist[$v] == $prefix ) continue; // No need to include in list as it's in a wrapping element
+          $nslist[$v] = $prefix;
+          if ( !isset($this->xmlns) ) $this->xmlns = $v;
+        }
+        $attr .= sprintf( ' %s="%s"', $k, htmlspecialchars($v) );
+      }
+    }
+    if ( isset($this->xmlns) && isset($nslist[$this->xmlns]) && $nslist[$this->xmlns] != '' ) {
+      $tagname = $nslist[$this->xmlns] . ':' . $tagname;
+    }
+
+    $r .= substr("                        ",0,$indent) . '<' . $tagname . $attr;
+
+    if ( (is_array($this->content) && count($this->content) > 0) || (!is_array($this->content) && strlen($this->content) > 0) ) {
+      $r .= ">";
+      $r .= $this->RenderContent($indent,$nslist);
+      $r .= '</' . $tagname.">\n";
+    }
+    else {
+      $r .= "/>\n";
+    }
+    return $r;
+  }
+}
+
+
+/**
+* Rebuild an XML tree in our own style from the parsed XML tags using
+* a tail-recursive approach.
+*
+* @param array $xmltags An array of XML tags we get from using the PHP XML parser
+* @param intref &$start_from A pointer to our current integer offset into $xmltags
+* @return mixed Either a single XMLElement, or an array of XMLElement objects.
+*/
+function BuildXMLTree( $xmltags, &$start_from ) {
+  $content = array();
+
+  if ( !isset($start_from) ) $start_from = 0;
+
+  for( $i=0; $i < 50000 && isset($xmltags[$start_from]); $i++) {
+    $tagdata = $xmltags[$start_from++];
+    if ( !isset($tagdata) || !isset($tagdata['tag']) || !isset($tagdata['type']) ) break;
+    if ( $tagdata['type'] == "close" ) break;
+    $attributes = ( isset($tagdata['attributes']) ? $tagdata['attributes'] : false );
+    if ( $tagdata['type'] == "open" ) {
+      $subtree = BuildXMLTree( $xmltags, $start_from );
+      $content[] = new XMLElement($tagdata['tag'], $subtree, $attributes );
+    }
+    else if ( $tagdata['type'] == "complete" ) {
+      $value = ( isset($tagdata['value']) ? $tagdata['value'] : false );
+      $content[] = new XMLElement($tagdata['tag'], $value, $attributes );
+    }
+  }
+
+  /**
+  * If there is only one element, return it directly, otherwise return the
+  * array of them
+  */
+  if ( count($content) == 1 ) {
+    return $content[0];
+  }
+  return $content;
+}
+
diff --git a/caldav/awl/iCalendar.php b/caldav/awl/iCalendar.php
new file mode 100644 (file)
index 0000000..055d120
--- /dev/null
@@ -0,0 +1,1633 @@
+<?php
+/**
+* A Class for handling iCalendar data.
+*
+* When parsed the underlying structure is roughly as follows:
+*
+*   iCalendar( array(iCalComponent), array(iCalProp) )
+*
+* each iCalComponent is similarly structured:
+*
+*   iCalComponent( array(iCalComponent), array(iCalProp) )
+*
+* Once parsed, $ical->component will point to the wrapping VCALENDAR component of
+* the iCalendar.  This will be fine for simple iCalendar usage as sampled below,
+* but more complex iCalendar such as a VEVENT with RRULE which has repeat overrides
+* will need quite a bit more thought to process correctly.
+*
+* @example
+* To create a new iCalendar from several data values:
+*   $ical = new iCalendar( array('DTSTART' => $dtstart, 'SUMMARY' => $summary, 'DURATION' => $duration ) );
+*
+* @example
+* To render it as an iCalendar string:
+*   echo $ical->Render();
+*
+* @example
+* To render just the VEVENTs in the iCalendar with a restricted list of properties:
+*   echo $ical->Render( false, 'VEVENT', array( 'DTSTART', 'DURATION', 'DTEND', 'RRULE', 'SUMMARY') );
+*
+* @example
+* To parse an existing iCalendar string for manipulation:
+*   $ical = new iCalendar( array('icalendar' => $icalendar_text ) );
+*
+* @example
+* To clear any 'VALARM' components in an iCalendar object
+*   $ical->component->ClearComponents('VALARM');
+*
+* @example
+* To replace any 'RRULE' property in an iCalendar object
+*   $ical->component->SetProperties( 'RRULE', $rrule_definition );
+*
+* @package awl
+* @subpackage iCalendar
+* @author Andrew McMillan <andrew@mcmillan.net.nz>
+* @copyright Catalyst IT Ltd, Morphoss Ltd <http://www.morphoss.com/>
+* @license   http://gnu.org/copyleft/gpl.html GNU GPL v2 or later
+*
+*/
+require_once("XMLElement.php");
+
+/**
+* A Class for representing properties within an iCalendar
+*
+* @package awl
+*/
+class iCalProp {
+  /**#@+
+   * @access private
+   */
+
+  /**
+   * The name of this property
+   *
+   * @var string
+   */
+  var $name;
+
+  /**
+   * An array of parameters to this property, represented as key/value pairs.
+   *
+   * @var array
+   */
+  var $parameters;
+
+  /**
+   * The value of this property.
+   *
+   * @var string
+   */
+  var $content;
+
+  /**
+   * The original value that this was parsed from, if that's the way it happened.
+   *
+   * @var string
+   */
+  var $rendered;
+
+  /**#@-*/
+
+  /**
+   * The constructor parses the incoming string, which is formatted as per RFC2445 as a
+   *   propname[;param1=pval1[; ... ]]:propvalue
+   * however we allow ourselves to assume that the RFC2445 content unescaping has already
+   * happened when iCalComponent::ParseFrom() called iCalComponent::UnwrapComponent().
+   *
+   * @param string $propstring The string from the iCalendar which contains this property.
+   */
+  function iCalProp( $propstring = null ) {
+    $this->name = "";
+    $this->content = "";
+    $this->parameters = array();
+    unset($this->rendered);
+    if ( $propstring != null && gettype($propstring) == 'string' ) {
+      $this->ParseFrom($propstring);
+    }
+  }
+
+
+  /**
+   * The constructor parses the incoming string, which is formatted as per RFC2445 as a
+   *   propname[;param1=pval1[; ... ]]:propvalue
+   * however we allow ourselves to assume that the RFC2445 content unescaping has already
+   * happened when iCalComponent::ParseFrom() called iCalComponent::UnwrapComponent().
+   *
+   * @param string $propstring The string from the iCalendar which contains this property.
+   */
+  function ParseFrom( $propstring ) {
+    $this->rendered = (strlen($propstring) < 72 ? $propstring : null);  // Only pre-rendered if we didn't unescape it
+    $pos = strpos( $propstring, ':');
+    $start = substr( $propstring, 0, $pos);
+
+    $unescaped = str_replace( '\\n', "\n", substr( $propstring, $pos + 1));
+    $unescaped = str_replace( '\\N', "\n", $unescaped);
+    $this->content = preg_replace( "/\\\\([,;:\"\\\\])/", '$1', $unescaped);
+
+    $parameters = explode(';',$start);
+    $this->name = array_shift( $parameters );
+    $this->parameters = array();
+    foreach( $parameters AS $k => $v ) {
+      $pos = strpos($v,'=');
+      $name = substr( $v, 0, $pos);
+      $value = substr( $v, $pos + 1);
+      $this->parameters[$name] = $value;
+    }
+    dbg_error_log("iCalendar", " iCalProp::ParseFrom found '%s' = '%s' with %d parameters", $this->name, $this->content, count($this->parameters) );
+  }
+
+
+  /**
+   * Get/Set name property
+   *
+   * @param string $newname [optional] A new name for the property
+   *
+   * @return string The name for the property.
+   */
+  function Name( $newname = null ) {
+    if ( $newname != null ) {
+      $this->name = $newname;
+      if ( isset($this->rendered) ) unset($this->rendered);
+      dbg_error_log("iCalendar", " iCalProp::Name(%s)", $this->name );
+    }
+    return $this->name;
+  }
+
+
+  /**
+   * Get/Set the content of the property
+   *
+   * @param string $newvalue [optional] A new value for the property
+   *
+   * @return string The value of the property.
+   */
+  function Value( $newvalue = null ) {
+    if ( $newvalue != null ) {
+      $this->content = $newvalue;
+      if ( isset($this->rendered) ) unset($this->rendered);
+    }
+    return $this->content;
+  }
+
+
+  /**
+   * Get/Set parameters in their entirety
+   *
+   * @param array $newparams An array of new parameter key/value pairs
+   *
+   * @return array The current array of parameters for the property.
+   */
+  function Parameters( $newparams = null ) {
+    if ( $newparams != null ) {
+      $this->parameters = $newparams;
+      if ( isset($this->rendered) ) unset($this->rendered);
+    }
+    return $this->parameters;
+  }
+
+
+  /**
+   * Test if our value contains a string
+   *
+   * @param string $search The needle which we shall search the haystack for.
+   *
+   * @return string The name for the property.
+   */
+  function TextMatch( $search ) {
+    if ( isset($this->content) ) return strstr( $this->content, $search );
+    return false;
+  }
+
+
+  /**
+   * Get the value of a parameter
+   *
+   * @param string $name The name of the parameter to retrieve the value for
+   *
+   * @return string The value of the parameter
+   */
+  function GetParameterValue( $name ) {
+    if ( isset($this->parameters[$name]) ) return $this->parameters[$name];
+  }
+
+  /**
+   * Set the value of a parameter
+   *
+   * @param string $name The name of the parameter to set the value for
+   *
+   * @param string $value The value of the parameter
+   */
+  function SetParameterValue( $name, $value ) {
+    if ( isset($this->rendered) ) unset($this->rendered);
+    $this->parameters[$name] = $value;
+  }
+
+  /**
+  * Render the set of parameters as key1=value1[;key2=value2[; ...]] with
+  * any colons or semicolons escaped.
+  */
+  function RenderParameters() {
+    $rendered = "";
+    foreach( $this->parameters AS $k => $v ) {
+      $escaped = preg_replace( "/([;:\"])/", '\\\\$1', $v);
+      $rendered .= sprintf( ";%s=%s", $k, $escaped );
+    }
+    return $rendered;
+  }
+
+
+  /**
+  * Render a suitably escaped RFC2445 content string.
+  */
+  function Render() {
+    // If we still have the string it was parsed in from, it hasn't been screwed with
+    // and we can just return that without modification.
+    if ( isset($this->rendered) ) return $this->rendered;
+
+    $property = preg_replace( '/[;].*$/', '', $this->name );
+    $escaped = $this->content;
+    switch( $property ) {
+        /** Content escaping does not apply to these properties culled from RFC2445 */
+      case 'ATTACH':                case 'GEO':                       case 'PERCENT-COMPLETE':      case 'PRIORITY':
+      case 'DURATION':              case 'FREEBUSY':                  case 'TZOFFSETFROM':          case 'TZOFFSETTO':
+      case 'TZURL':                 case 'ATTENDEE':                  case 'ORGANIZER':             case 'RECURRENCE-ID':
+      case 'URL':                   case 'EXRULE':                    case 'SEQUENCE':              case 'CREATED':
+      case 'RRULE':                 case 'REPEAT':                    case 'TRIGGER':
+        break;
+
+      case 'COMPLETED':             case 'DTEND':
+      case 'DUE':                   case 'DTSTART':
+      case 'DTSTAMP':               case 'LAST-MODIFIED':
+      case 'CREATED':               case 'EXDATE':
+      case 'RDATE':
+        if ( isset($this->parameters['VALUE']) && $this->parameters['VALUE'] == 'DATE' ) {
+          $escaped = substr( $escaped, 0, 8);
+        }
+        break;
+
+        /** Content escaping applies by default to other properties */
+      default:
+        $escaped = str_replace( '\\', '\\\\', $escaped);
+        $escaped = preg_replace( '/\r?\n/', '\\n', $escaped);
+        $escaped = preg_replace( "/([,;\"])/", '\\\\$1', $escaped);
+    }
+    $property = sprintf( "%s%s:", $this->name, $this->RenderParameters() );
+    if ( (strlen($property) + strlen($escaped)) <= 72 ) {
+      $this->rendered = $property . $escaped;
+    }
+    else if ( (strlen($property) + strlen($escaped)) > 72 && (strlen($property) < 72) && (strlen($escaped) < 72) ) {
+      $this->rendered = $property . " \r\n " . $escaped;
+    }
+    else {
+      $this->rendered = wordwrap( $property . $escaped, 72, " \r\n ", true );
+    }
+    return $this->rendered;
+  }
+
+}
+
+
+/**
+* A Class for representing components within an iCalendar
+*
+* @package awl
+*/
+class iCalComponent {
+  /**#@+
+   * @access private
+   */
+
+  /**
+   * The type of this component, such as 'VEVENT', 'VTODO', 'VTIMEZONE', etc.
+   *
+   * @var string
+   */
+  var $type;
+
+  /**
+   * An array of properties, which are iCalProp objects
+   *
+   * @var array
+   */
+  var $properties;
+
+  /**
+   * An array of (sub-)components, which are iCalComponent objects
+   *
+   * @var array
+   */
+  var $components;
+
+  /**
+   * The rendered result (or what was originally parsed, if there have been no changes)
+   *
+   * @var array
+   */
+  var $rendered;
+
+  /**#@-*/
+
+  /**
+  * A basic constructor
+  */
+  function iCalComponent( $content = null ) {
+    $this->type = "";
+    $this->properties = array();
+    $this->components = array();
+    $this->rendered = "";
+    if ( $content != null && gettype($content) == 'string' ) {
+      $this->ParseFrom($content);
+    }
+  }
+
+
+  /**
+  * Apply standard properties for a VCalendar
+  * @param array $extra_properties Key/value pairs of additional properties
+  */
+  function VCalendar( $extra_properties = null ) {
+    $this->SetType('VCALENDAR');
+    $this->AddProperty('PRODID', '-//davical.org//NONSGML AWL Calendar//EN');
+    $this->AddProperty('VERSION', '2.0');
+    $this->AddProperty('CALSCALE', 'GREGORIAN');
+    if ( is_array($extra_properties) ) {
+      foreach( $extra_properties AS $k => $v ) {
+        $this->AddProperty($k,$v);
+      }
+    }
+  }
+
+  /**
+  * Collect an array of all parameters of our properties which are the specified type
+  * Mainly used for collecting the full variety of references TZIDs
+  */
+  function CollectParameterValues( $parameter_name ) {
+    $values = array();
+    foreach( $this->components AS $k => $v ) {
+      $also = $v->CollectParameterValues($parameter_name);
+      $values = array_merge( $values, $also );
+    }
+    foreach( $this->properties AS $k => $v ) {
+      $also = $v->GetParameterValue($parameter_name);
+      if ( isset($also) && $also != "" ) {
+        dbg_error_log( "iCalendar", "::CollectParameterValues(%s) : Found '%s'", $parameter_name, $also);
+        $values[$also] = 1;
+      }
+    }
+    return $values;
+  }
+
+
+  /**
+  * Parse the text $content into sets of iCalProp & iCalComponent within this iCalComponent
+  * @param string $content The raw RFC2445-compliant iCalendar component, including BEGIN:TYPE & END:TYPE
+  */
+  function ParseFrom( $content ) {
+    $this->rendered = $content;
+    $content = $this->UnwrapComponent($content);
+
+    $lines = preg_split('/\r?\n/', $content );
+
+    $type = false;
+    $subtype = false;
+    $finish = null;
+    $subfinish = null;
+    foreach( $lines AS $k => $v ) {
+      if ( preg_match('/^\s*$/', $v ) ) continue;
+      dbg_error_log( "iCalendar",  "::ParseFrom: Parsing line: $v");
+      if ( $type === false ) {
+        if ( preg_match( '/^BEGIN:(.+)$/', $v, $matches ) ) {
+          // We have found the start of the main component
+          $type = $matches[1];
+          $finish = "END:$type";
+          $this->type = $type;
+          dbg_error_log( "iCalendar", "::ParseFrom: Start component of type '%s'", $type);
+        }
+        else {
+          dbg_error_log( "iCalendar", "::ParseFrom: Ignoring crap before start of component");
+          unset($lines[$k]);  // The content has crap before the start
+          if ( $v != "" ) $this->rendered = null;
+        }
+      }
+      else if ( $type == null ) {
+        dbg_error_log( "iCalendar", "::ParseFrom: Ignoring crap after end of component");
+        unset($lines[$k]);  // The content has crap after the end
+        if ( $v != "" ) $this->rendered = null;
+      }
+      else if ( $v == $finish ) {
+        dbg_error_log( "iCalendar", "::ParseFrom: End of component");
+        $type = null;  // We have reached the end of our component
+      }
+      else {
+        if ( $subtype === false && preg_match( '/^BEGIN:(.+)$/', $v, $matches ) ) {
+          // We have found the start of a sub-component
+          $subtype = $matches[1];
+          $subfinish = "END:$subtype";
+          $subcomponent = "$v\r\n";
+          dbg_error_log( "iCalendar", "::ParseFrom: Found a subcomponent '%s'", $subtype);
+        }
+        else if ( $subtype ) {
+          // We are inside a sub-component
+          $subcomponent .= $this->WrapComponent($v);
+          if ( $v == $subfinish ) {
+            dbg_error_log( "iCalendar", "::ParseFrom: End of subcomponent '%s'", $subtype);
+            // We have found the end of a sub-component
+            $this->components[] = new iCalComponent($subcomponent);
+            $subtype = false;
+          }
+          else
+            dbg_error_log( "iCalendar", "::ParseFrom: Inside a subcomponent '%s'", $subtype );
+        }
+        else {
+          dbg_error_log( "iCalendar", "::ParseFrom: Parse property of component");
+          // It must be a normal property line within a component.
+          $this->properties[] = new iCalProp($v);
+        }
+      }
+    }
+  }
+
+
+  /**
+    * This unescapes the (CRLF + linear space) wrapping specified in RFC2445. According
+    * to RFC2445 we should always end with CRLF but the CalDAV spec says that normalising
+    * XML parsers often muck with it and may remove the CR.  We accept either case.
+    */
+  function UnwrapComponent( $content ) {
+    return preg_replace('/\r?\n[ \t]/', '', $content );
+  }
+
+  /**
+    * This imposes the (CRLF + linear space) wrapping specified in RFC2445. According
+    * to RFC2445 we should always end with CRLF but the CalDAV spec says that normalising
+    * XML parsers often muck with it and may remove the CR.  We output RFC2445 compliance.
+    *
+    * In order to preserve pre-existing wrapping in the component, we split the incoming
+    * string on line breaks before running wordwrap over each component of that.
+    */
+  function WrapComponent( $content ) {
+    $strs = preg_split( "/\r?\n/", $content );
+    $wrapped = "";
+    foreach ($strs as $str) {
+      $wrapped .= wordwrap($str, 73, " \r\n ") . "\r\n";
+    }
+    return $wrapped;
+  }
+
+  /**
+  * Return the type of component which this is
+  */
+  function GetType() {
+    return $this->type;
+  }
+
+
+  /**
+  * Set the type of component which this is
+  */
+  function SetType( $type ) {
+    if ( isset($this->rendered) ) unset($this->rendered);
+    $this->type = $type;
+    return $this->type;
+  }
+
+
+  /**
+  * Get all properties, or the properties matching a particular type
+  */
+  function GetProperties( $type = null ) {
+    $properties = array();
+    foreach( $this->properties AS $k => $v ) {
+      if ( $type == null || $v->Name() == $type ) {
+        $properties[$k] = $v;
+      }
+    }
+    return $properties;
+  }
+
+
+  /**
+  * Get the value of the first property matching the name. Obviously this isn't
+  * so useful for properties which may occur multiply, but most don't.
+  *
+  * @param string $type The type of property we are after.
+  * @return string The value of the property, or null if there was no such property.
+  */
+  function GetPValue( $type ) {
+    foreach( $this->properties AS $k => $v ) {
+      if ( $v->Name() == $type ) return $v->Value();
+    }
+    return null;
+  }
+
+
+  /**
+  * Get the value of the specified parameter for the first property matching the
+  * name. Obviously this isn't so useful for properties which may occur multiply, but most don't.
+  *
+  * @param string $type The type of property we are after.
+  * @param string $type The name of the parameter we are after.
+  * @return string The value of the parameter for the property, or null in the case that there was no such property, or no such parameter.
+  */
+  function GetPParamValue( $type, $parameter_name ) {
+    foreach( $this->properties AS $k => $v ) {
+      if ( $v->Name() == $type ) return $v->GetParameterValue($parameter_name);
+    }
+    return null;
+  }
+
+
+  /**
+  * Clear all properties, or the properties matching a particular type
+  * @param string $type The type of property - omit for all properties
+  */
+  function ClearProperties( $type = null ) {
+    if ( $type != null ) {
+      // First remove all the existing ones of that type
+      foreach( $this->properties AS $k => $v ) {
+        if ( $v->Name() == $type ) {
+          unset($this->properties[$k]);
+          if ( isset($this->rendered) ) unset($this->rendered);
+        }
+      }
+      $this->properties = array_values($this->properties);
+    }
+    else {
+      if ( isset($this->rendered) ) unset($this->rendered);
+      $this->properties = array();
+    }
+  }
+
+
+  /**
+  * Set all properties, or the ones matching a particular type
+  */
+  function SetProperties( $new_properties, $type = null ) {
+    if ( isset($this->rendered) && count($new_properties) > 0 ) unset($this->rendered);
+    $this->ClearProperties($type);
+    foreach( $new_properties AS $k => $v ) {
+      $this->AddProperty($v);
+    }
+  }
+
+
+  /**
+  * Adds a new property
+  *
+  * @param iCalProp $new_property The new property to append to the set, or a string with the name
+  * @param string $value The value of the new property (default: param 1 is an iCalProp with everything
+  * @param array $parameters The key/value parameter pairs (default: none, or param 1 is an iCalProp with everything)
+  */
+  function AddProperty( $new_property, $value = null, $parameters = null ) {
+    if ( isset($this->rendered) ) unset($this->rendered);
+    if ( isset($value) && gettype($new_property) == 'string' ) {
+      $new_prop = new iCalProp();
+      $new_prop->Name($new_property);
+      $new_prop->Value($value);
+      if ( $parameters != null ) $new_prop->Parameters($parameters);
+      dbg_error_log("iCalendar"," Adding new property '%s'", $new_prop->Render() );
+      $this->properties[] = $new_prop;
+    }
+    else if ( gettype($new_property) ) {
+      $this->properties[] = $new_property;
+    }
+  }
+
+
+  /**
+  * Get all sub-components, or at least get those matching a type
+  * @return array an array of the sub-components
+  */
+  function &FirstNonTimezone( $type = null ) {
+    foreach( $this->components AS $k => $v ) {
+      if ( $v->GetType() != 'VTIMEZONE' ) return $this->components[$k];
+    }
+    $result = false;
+    return $result;
+  }
+
+
+  /**
+  * Return true if the person identified by the email address is down as an
+  * organizer for this meeting.
+  * @param string $email The e-mail address of the person we're seeking.
+  * @return boolean true if we found 'em, false if we didn't.
+  */
+  function IsOrganizer( $email ) {
+    if ( !preg_match( '#^mailto:#', $email ) ) $email = 'mailto:$email';
+    $props = $this->GetPropertiesByPath('!VTIMEZONE/ORGANIZER');
+    foreach( $props AS $k => $prop ) {
+      if ( $prop->Value() == $email ) return true;
+    }
+    return false;
+  }
+
+
+  /**
+  * Return true if the person identified by the email address is down as an
+  * attendee or organizer for this meeting.
+  * @param string $email The e-mail address of the person we're seeking.
+  * @return boolean true if we found 'em, false if we didn't.
+  */
+  function IsAttendee( $email ) {
+    if ( !preg_match( '#^mailto:#', $email ) ) $email = 'mailto:$email';
+    if ( $this->IsOrganizer($email) ) return true; /** an organizer is an attendee, as far as we're concerned */
+    $props = $this->GetPropertiesByPath('!VTIMEZONE/ATTENDEE');
+    foreach( $props AS $k => $prop ) {
+      if ( $prop->Value() == $email ) return true;
+    }
+    return false;
+  }
+
+
+  /**
+  * Get all sub-components, or at least get those matching a type, or failling to match,
+  * should the second parameter be set to false.
+  *
+  * @param string $type The type to match (default: All)
+  * @param boolean $normal_match Set to false to invert the match (default: true)
+  * @return array an array of the sub-components
+  */
+  function GetComponents( $type = null, $normal_match = true ) {
+    $components = $this->components;
+    if ( $type != null ) {
+      foreach( $components AS $k => $v ) {
+        if ( ($v->GetType() != $type) === $normal_match ) {
+          unset($components[$k]);
+        }
+      }
+      $components = array_values($components);
+    }
+    return $components;
+  }
+
+
+  /**
+  * Clear all components, or the components matching a particular type
+  * @param string $type The type of component - omit for all components
+  */
+  function ClearComponents( $type = null ) {
+    if ( $type != null ) {
+      // First remove all the existing ones of that type
+      foreach( $this->components AS $k => $v ) {
+        if ( $v->GetType() == $type ) {
+          unset($this->components[$k]);
+          if ( isset($this->rendered) ) unset($this->rendered);
+        }
+        else {
+          if ( ! $this->components[$k]->ClearComponents($type) ) {
+            if ( isset($this->rendered) ) unset($this->rendered);
+          }
+        }
+      }
+      return isset($this->rendered);
+    }
+    else {
+      if ( isset($this->rendered) ) unset($this->rendered);
+      $this->components = array();
+    }
+  }
+
+
+  /**
+  * Sets some or all sub-components of the component to the supplied new components
+  *
+  * @param array of iCalComponent $new_components The new components to replace the existing ones
+  * @param string $type The type of components to be replaced.  Defaults to null, which means all components will be replaced.
+  */
+  function SetComponents( $new_component, $type = null ) {
+    if ( isset($this->rendered) ) unset($this->rendered);
+    if ( count($new_component) > 0 ) $this->ClearComponents($type);
+    foreach( $new_component AS $k => $v ) {
+      $this->components[] = $v;
+    }
+  }
+
+
+  /**
+  * Adds a new subcomponent
+  *
+  * @param iCalComponent $new_component The new component to append to the set
+  */
+  function AddComponent( $new_component ) {
+    if ( is_array($new_component) && count($new_component) == 0 ) return;
+    if ( isset($this->rendered) ) unset($this->rendered);
+    if ( is_array($new_component) ) {
+      foreach( $new_component AS $k => $v ) {
+        $this->components[] = $v;
+      }
+    }
+    else {
+      $this->components[] = $new_component;
+    }
+  }
+
+
+  /**
+  * Mask components, removing any that are not of the types in the list
+  * @param array $keep An array of component types to be kept
+  */
+  function MaskComponents( $keep ) {
+    foreach( $this->components AS $k => $v ) {
+      if ( ! in_array( $v->GetType(), $keep ) ) {
+        unset($this->components[$k]);
+        if ( isset($this->rendered) ) unset($this->rendered);
+      }
+      else {
+        $v->MaskComponents($keep);
+      }
+    }
+  }
+
+
+  /**
+  * Mask properties, removing any that are not in the list
+  * @param array $keep An array of property names to be kept
+  * @param array $component_list An array of component types to check within
+  */
+  function MaskProperties( $keep, $component_list=null ) {
+    foreach( $this->components AS $k => $v ) {
+      $v->MaskProperties($keep, $component_list);
+    }
+
+    if ( !isset($component_list) || in_array($this->GetType(),$component_list) ) {
+      foreach( $this->components AS $k => $v ) {
+        if ( ! in_array( $v->GetType(), $keep ) ) {
+          unset($this->components[$k]);
+          if ( isset($this->rendered) ) unset($this->rendered);
+        }
+      }
+    }
+  }
+
+
+  /**
+  * Clone this component (and subcomponents) into a confidential version of it.  A confidential
+  * event will be scrubbed of any identifying characteristics other than time/date, repeat, uid
+  * and a summary which is just a translated 'Busy'.
+  */
+  function CloneConfidential() {
+    $confidential = clone($this);
+    $keep_properties = array( 'DTSTAMP', 'DTSTART', 'RRULE', 'DURATION', 'DTEND', 'UID', 'CLASS', 'TRANSP' );
+    $resource_components = array( 'VEVENT', 'VTODO', 'VJOURNAL' );
+    $confidential->MaskComponents(array( 'VTIMEZONE', 'VEVENT', 'VTODO', 'VJOURNAL' ));
+    $confidential->MaskProperties($keep_properties, $resource_components );
+    if ( in_array( $confidential->GetType(), $resource_components ) ) {
+      $confidential->AddProperty( 'SUMMARY', translate('Busy') );
+    }
+    foreach( $confidential->components AS $k => $v ) {
+      if ( in_array( $v->GetType(), $resource_components ) ) {
+        $v->AddProperty( 'SUMMARY', translate('Busy') );
+      }
+    }
+
+    return $confidential;
+  }
+
+
+  /**
+  *  Renders the component, possibly restricted to only the listed properties
+  */
+  function Render( $restricted_properties = null) {
+
+    $unrestricted = (!isset($restricted_properties) || count($restricted_properties) == 0);
+
+    if ( isset($this->rendered) && $unrestricted )
+      return $this->rendered;
+
+    $rendered = "BEGIN:$this->type\r\n";
+    foreach( $this->properties AS $k => $v ) {
+      if ( $unrestricted || isset($restricted_properties[$v]) ) $rendered .= $v->Render() . "\r\n";
+    }
+    foreach( $this->components AS $v ) {   $rendered .= $v->Render();  }
+    $rendered .= "END:$this->type\r\n";
+//    $rendered = $this->WrapComponent($rendered);
+
+    if ( $unrestricted ) $this->rendered = $rendered;
+
+    return $rendered;
+  }
+
+  /**
+  * Return an array of properties matching the specified path
+  *
+  * @return array An array of iCalProp within the tree which match the path given, in the form
+  *  [/]COMPONENT[/...]/PROPERTY in a syntax kind of similar to our poor man's XML queries. We
+  *  also allow COMPONENT and PROPERTY to be !COMPONENT and !PROPERTY for ++fun.
+  *
+  * @note At some point post PHP4 this could be re-done with an iterator, which should be more efficient for common use cases.
+  */
+  function GetPropertiesByPath( $path ) {
+    $properties = array();
+    dbg_error_log( "iCalendar", "GetPropertiesByPath: Querying within '%s' for path '%s'", $this->type, $path );
+    if ( !preg_match( '#(/?)(!?)([^/]+)(/?.*)$#', $path, $matches ) ) return $properties;
+
+    $adrift = ($matches[1] == '');
+    $normal = ($matches[2] == '');
+    $ourtest = $matches[3];
+    $therest = $matches[4];
+    dbg_error_log( "iCalendar", "GetPropertiesByPath: Matches: %s -- %s -- %s -- %s\n", $matches[1], $matches[2], $matches[3], $matches[4] );
+    if ( $ourtest == '*' || (($ourtest == $this->type) === $normal) && $therest != '' ) {
+      if ( preg_match( '#^/(!?)([^/]+)$#', $therest, $matches ) ) {
+        $normmatch = ($matches[1] =='');
+        $proptest  = $matches[2];
+        foreach( $this->properties AS $k => $v ) {
+          if ( $proptest = '*' || (($v->Name() == $proptest) === $normmatch ) ) {
+            $properties[] = $v;
+          }
+        }
+      }
+      else {
+        /**
+        * There is more to the path, so we recurse into that sub-part
+        */
+        foreach( $this->components AS $k => $v ) {
+          $properties = array_merge( $properties, $v->GetPropertiesByPath($therest) );
+        }
+      }
+    }
+
+    if ( $adrift ) {
+      /**
+      * Our input $path was not rooted, so we recurse further
+      */
+      foreach( $this->components AS $k => $v ) {
+        $properties = array_merge( $properties, $v->GetPropertiesByPath($path) );
+      }
+    }
+    dbg_error_log("iCalendar", "GetPropertiesByPath: Found %d within '%s' for path '%s'\n", count($properties), $this->type, $path );
+    return $properties;
+  }
+
+}
+
+/**
+************************************************************************************
+* Pretty much everything below here is deprecated and should be avoided in favour
+* of using, improving and enhancing the more sensible structures above.
+************************************************************************************
+*/
+
+/**
+* A Class for handling Events on a calendar
+*
+* @package awl
+*/
+class iCalendar {
+  /**#@+
+  * @access private
+  */
+
+  /**
+  * The component-ised version of the iCalendar
+  * @var component iCalComponent
+  */
+  var $component;
+
+  /**
+  * An array of arbitrary properties, containing arbitrary arrays of arbitrary properties
+  * @var properties array
+  */
+  var $properties;
+
+  /**
+  * An array of the lines of this iCalendar resource
+  * @var lines array
+  */
+  var $lines;
+
+  /**
+  * The typical location name for the standard timezone such as "Pacific/Auckland"
+  * @var tz_locn string
+  */
+  var $tz_locn;
+
+  /**
+  * The type of iCalendar data VEVENT/VTODO/VJOURNAL
+  * @var type string
+  */
+  var $type;
+
+  /**#@-*/
+
+  /**
+  * The constructor takes an array of args.  If there is an element called 'icalendar'
+  * then that will be parsed into the iCalendar object.  Otherwise the array elements
+  * are converted into properties of the iCalendar object directly.
+  */
+  function iCalendar( $args ) {
+    global $c;
+
+    $this->tz_locn = "";
+    if ( !isset($args) || !(is_array($args) || is_object($args)) ) return;
+    if ( is_object($args) ) {
+      settype($args,'array');
+    }
+
+    $this->component = new iCalComponent();
+    if ( isset($args['icalendar']) ) {
+      $this->component->ParseFrom($args['icalendar']);
+      $this->lines = preg_split('/\r?\n/', $args['icalendar'] );
+      $this->SaveTimeZones();
+      $first =& $this->component->FirstNonTimezone();
+      if ( $first ) {
+        $this->type = $first->GetType();
+        $this->properties = $first->GetProperties();
+      }
+      else {
+        $this->properties = array();
+      }
+      $this->properties['VCALENDAR'] = array('***ERROR*** This class is being referenced in an unsupported way!');
+      return;
+    }
+
+    if ( isset($args['type'] ) ) {
+      $this->type = $args['type'];
+      unset( $args['type'] );
+    }
+    else {
+      $this->type = 'VEVENT';  // Default to event
+    }
+    $this->component->SetType('VCALENDAR');
+    $this->component->SetProperties(
+        array(
+          new iCalProp('PRODID:-//davical.org//NONSGML AWL Calendar//EN'),
+          new iCalProp('VERSION:2.0'),
+          new iCalProp('CALSCALE:GREGORIAN')
+        )
+    );
+    $first = new iCalComponent();
+    $first->SetType($this->type);
+    $this->properties = array();
+
+    foreach( $args AS $k => $v ) {
+      dbg_error_log( "iCalendar", ":Initialise: %s to >>>%s<<<", $k, $v );
+      $property = new iCalProp();
+      $property->Name($k);
+      $property->Value($v);
+      $this->properties[] = $property;
+    }
+    $first->SetProperties($this->properties);
+    $this->component->SetComponents( array($first) );
+
+    $this->properties['VCALENDAR'] = array('***ERROR*** This class is being referenced in an unsupported way!');
+
+    /**
+    * @todo Need to handle timezones!!!
+    */
+    if ( $this->tz_locn == "" ) {
+      $this->tz_locn = $this->Get("tzid");
+      if ( (!isset($this->tz_locn) || $this->tz_locn == "") && isset($c->local_tzid) ) {
+        $this->tz_locn = $c->local_tzid;
+      }
+    }
+  }
+
+
+  /**
+  * Save any timezones by TZID in the PostgreSQL database for future re-use.
+  */
+  function SaveTimeZones() {
+    global $c;
+
+    $this->tzid_list = array_keys($this->component->CollectParameterValues('TZID'));
+    if ( ! isset($this->tzid) && count($this->tzid_list) > 0 ) {
+      dbg_error_log( "icalendar", "::TZID_List[0] = '%s', count=%d", $this->tzid_list[0], count($this->tzid_list) );
+      $this->tzid = $this->tzid_list[0];
+    }
+
+    $timezones = $this->component->GetComponents('VTIMEZONE');
+    if ( $timezones === false || count($timezones) == 0 ) return;
+    $this->vtimezone = $timezones[0]->Render();  // Backward compatibility
+
+    $tzid = $this->Get('TZID');
+    if ( isset($c->save_time_zone_defs) && $c->save_time_zone_defs ) {
+      foreach( $timezones AS $k => $tz ) {
+        $tzid = $tz->GetPValue('TZID');
+
+        $qry = new PgQuery( "SELECT tz_locn FROM time_zone WHERE tz_id = ?;", $tzid );
+        if ( $qry->Exec('iCalendar') && $qry->rows == 1 ) {
+          $row = $qry->Fetch();
+          if ( !isset($first_tzid) ) $first_tzid = $row->tz_locn;
+          continue;
+        }
+
+        if ( $tzid != "" && $qry->rows == 0 ) {
+
+          $tzname = $tz->GetPValue('X-LIC-LOCATION');
+          if ( !isset($tzname) ) {
+            /**
+            * Try and convert the TZID to a string like "Pacific/Auckland" if possible.
+            */
+            $tzname = preg_replace('#^(.*[^a-z])?([a-z]+/[a-z]+)$#i','$2',$tzid );
+          }
+
+          $qry2 = new PgQuery( "INSERT INTO time_zone (tz_id, tz_locn, tz_spec) VALUES( ?, ?, ? );",
+                                      $tzid, $tzname, $tz->Render() );
+          $qry2->Exec("iCalendar");
+        }
+      }
+    }
+    if ( ! isset($this->tzid) && isset($first_tzid) ) $this->tzid = $first_tzid;
+
+    if ( (!isset($this->tz_locn) || $this->tz_locn == '') && isset($first_tzid) && $first_tzid != '' ) {
+      $tzname = preg_replace('#^(.*[^a-z])?([a-z]+/[a-z]+)$#i','$2', $first_tzid );
+      if ( preg_match( '#\S+/\S+#', $tzname) ) {
+        $this->tz_locn = $tzname;
+      }
+      dbg_error_log( "icalendar", " TZCrap1: TZID '%s', Location '%s', Perhaps: %s", $tzid, $this->tz_locn, $tzname );
+    }
+
+    if ( (!isset($this->tz_locn) || $this->tz_locn == "") && isset($c->local_tzid) ) {
+      $this->tz_locn = $c->local_tzid;
+    }
+    if ( ! isset($this->tzid) && isset($this->tz_locn) ) $this->tzid = $this->tz_locn;
+  }
+
+
+  /**
+  * An array of property names that we should always want when rendering an iCalendar
+  *
+  * @deprecated This function is deprecated and will be removed eventually.
+  * @todo Remove this function.
+  */
+  function DefaultPropertyList() {
+    dbg_error_log( "LOG", " iCalendar: Call to deprecated method '%s'", 'DefaultPropertyList' );
+    return array( "UID" => 1, "DTSTAMP" => 1, "DTSTART" => 1, "DURATION" => 1,
+                  "LAST-MODIFIED" => 1,"CLASS" => 1, "TRANSP" => 1, "SEQUENCE" => 1,
+                  "DUE" => 1, "SUMMARY" => 1, "RRULE" => 1 );
+  }
+
+  /**
+  * A function to extract the contents of a BEGIN:SOMETHING to END:SOMETHING (perhaps multiply)
+  * and return just that bit (or, of course, those bits :-)
+  *
+  * @var string The type of thing(s) we want returned.
+  * @var integer The number of SOMETHINGS we want to get.
+  *
+  * @return string A string from BEGIN:SOMETHING to END:SOMETHING, possibly multiple of these
+  *
+  * @deprecated This function is deprecated and will be removed eventually.
+  * @todo Remove this function.
+  */
+  function JustThisBitPlease( $type, $count=1 ) {
+    dbg_error_log( "LOG", " iCalendar: Call to deprecated method '%s'", 'JustThisBitPlease' );
+    $answer = "";
+    $intags = false;
+    $start = "BEGIN:$type";
+    $finish = "END:$type";
+    dbg_error_log( "iCalendar", ":JTBP: Looking for %d subsets of type %s", $count, $type );
+    reset($this->lines);
+    foreach( $this->lines AS $k => $v ) {
+      if ( !$intags && $v == $start ) {
+        $answer .= $v . "\n";
+        $intags = true;
+      }
+      else if ( $intags && $v == $finish ) {
+        $answer .= $v . "\n";
+        $intags = false;
+      }
+      else if ( $intags ) {
+        $answer .= $v . "\n";
+      }
+    }
+    return $answer;
+  }
+
+
+  /**
+  * Function to parse lines from BEGIN:SOMETHING to END:SOMETHING into a nested array structure
+  *
+  * @var string The "SOMETHING" from the BEGIN:SOMETHING line we just met
+  * @return arrayref An array of the things we found between (excluding) the BEGIN & END, some of which might be sub-arrays
+  *
+  * @deprecated This function is deprecated and will be removed eventually.
+  * @todo Remove this function.
+  */
+  function &ParseSomeLines( $type ) {
+    dbg_error_log( "LOG", " iCalendar: Call to deprecated method '%s'", 'ParseSomeLines' );
+    $props = array();
+    $properties =& $props;
+    while( isset($this->lines[$this->_current_parse_line]) ) {
+      $i = $this->_current_parse_line++;
+      $line =& $this->lines[$i];
+      dbg_error_log( "iCalendar", ":Parse:%s LINE %03d: >>>%s<<<", $type, $i, $line );
+      if ( $this->parsing_vtimezone ) {
+        $this->vtimezone .= $line."\n";
+      }
+      if ( preg_match( '/^(BEGIN|END):([^:]+)$/', $line, $matches ) ) {
+        if ( $matches[1] == 'END' && $matches[2] == $type ) {
+          if ( $type == 'VTIMEZONE' ) {
+            $this->parsing_vtimezone = false;
+          }
+          return $properties;
+        }
+        else if( $matches[1] == 'END' ) {
+          dbg_error_log("ERROR"," iCalendar: parse error: Unexpected END:%s when we were looking for END:%s", $matches[2], $type );
+          return $properties;
+        }
+        else if( $matches[1] == 'BEGIN' ) {
+          $subtype = $matches[2];
+          if ( $subtype == 'VTIMEZONE' ) {
+            $this->parsing_vtimezone = true;
+            $this->vtimezone = $line."\n";
+          }
+          if ( !isset($properties['INSIDE']) ) $properties['INSIDE'] = array();
+          $properties['INSIDE'][] = $subtype;
+          if ( !isset($properties[$subtype]) ) $properties[$subtype] = array();
+          $properties[$subtype][] = $this->ParseSomeLines($subtype);
+        }
+      }
+      else {
+        // Parse the property
+        @list( $property, $value ) = preg_split('/:/', $line, 2 );
+        if ( strpos( $property, ';' ) > 0 ) {
+          $parameterlist = preg_split('/;/', $property );
+          $property = array_shift($parameterlist);
+          foreach( $parameterlist AS $pk => $pv ) {
+            if ( $pv == "VALUE=DATE" ) {
+              $value .= 'T000000';
+            }
+            elseif ( preg_match('/^([^;:=]+)=([^;:=]+)$/', $pv, $matches) ) {
+              switch( $matches[1] ) {
+                case 'TZID': $properties['TZID'] = $matches[2];  break;
+                default:
+                  dbg_error_log( "icalendar", " FYI: Ignoring Resource '%s', Property '%s', Parameter '%s', Value '%s'", $type, $property, $matches[1], $matches[2] );
+              }
+            }
+          }
+        }
+        if ( $this->parsing_vtimezone && (!isset($this->tz_locn) || $this->tz_locn == "") && $property == 'X-LIC-LOCATION' ) {
+          $this->tz_locn = $value;
+        }
+        $properties[strtoupper($property)] = $this->RFC2445ContentUnescape($value);
+      }
+    }
+    return $properties;
+  }
+
+
+  /**
+  * Build the iCalendar object from a text string which is a single iCalendar resource
+  *
+  * @var string The RFC2445 iCalendar resource to be parsed
+  *
+  * @deprecated This function is deprecated and will be removed eventually.
+  * @todo Remove this function.
+  */
+  function BuildFromText( $icalendar ) {
+    dbg_error_log( "LOG", " iCalendar: Call to deprecated method '%s'", 'BuildFromText' );
+    /**
+     * This unescapes the (CRLF + linear space) wrapping specified in RFC2445. According
+     * to RFC2445 we should always end with CRLF but the CalDAV spec says that normalising
+     * XML parsers often muck with it and may remove the CR.
+     */
+    $icalendar = preg_replace('/\r?\n[ \t]/', '', $icalendar );
+
+    $this->lines = preg_split('/\r?\n/', $icalendar );
+
+    $this->_current_parse_line = 0;
+    $this->properties = $this->ParseSomeLines('');
+
+    /**
+    * Our 'type' is the type of non-timezone inside a VCALENDAR
+    */
+    if ( isset($this->properties['VCALENDAR'][0]['INSIDE']) ) {
+      foreach ( $this->properties['VCALENDAR'][0]['INSIDE']  AS $k => $v ) {
+        if ( $v == 'VTIMEZONE' ) continue;
+        $this->type = $v;
+        break;
+      }
+    }
+
+  }
+
+
+  /**
+  * Returns a content string with the RFC2445 escaping removed
+  *
+  * @param string $escaped The incoming string to be escaped.
+  * @return string The string with RFC2445 content escaping removed.
+  *
+  * @deprecated This function is deprecated and will be removed eventually.
+  * @todo Remove this function.
+  */
+  function RFC2445ContentUnescape( $escaped ) {
+    dbg_error_log( "LOG", " iCalendar: Call to deprecated method '%s'", 'RFC2445ContentUnescape' );
+    $unescaped = str_replace( '\\n', "\n", $escaped);
+    $unescaped = str_replace( '\\N', "\n", $unescaped);
+    $unescaped = preg_replace( "/\\\\([,;:\"\\\\])/", '$1', $unescaped);
+    return $unescaped;
+  }
+
+
+
+  /**
+  * Do what must be done with time zones from on file.  Attempt to turn
+  * them into something that PostgreSQL can understand...
+  *
+  * @deprecated This function is deprecated and will be removed eventually.
+  * @todo Remove this function.
+  */
+  function DealWithTimeZones() {
+    global $c;
+
+    dbg_error_log( "LOG", " iCalendar: Call to deprecated method '%s'", 'DealWithTimeZones' );
+    $tzid = $this->Get('TZID');
+    if ( isset($c->save_time_zone_defs) && $c->save_time_zone_defs ) {
+      $qry = new PgQuery( "SELECT tz_locn FROM time_zone WHERE tz_id = ?;", $tzid );
+      if ( $qry->Exec('iCalendar') && $qry->rows == 1 ) {
+        $row = $qry->Fetch();
+        $this->tz_locn = $row->tz_locn;
+      }
+      dbg_error_log( "icalendar", " TZCrap2: TZID '%s', DB Rows=%d, Location '%s'", $tzid, $qry->rows, $this->tz_locn );
+    }
+
+    if ( (!isset($this->tz_locn) || $this->tz_locn == '') && $tzid != '' ) {
+      /**
+      * In case there was no X-LIC-LOCATION defined, let's hope there is something in the TZID
+      * that we can use.  We are looking for a string like "Pacific/Auckland" if possible.
+      */
+      $tzname = preg_replace('#^(.*[^a-z])?([a-z]+/[a-z]+)$#i','$1',$tzid );
+      /**
+      * Unfortunately this kind of thing will never work well :-(
+      *
+      if ( strstr( $tzname, ' ' ) ) {
+        $words = preg_split('/\s/', $tzname );
+        $tzabbr = '';
+        foreach( $words AS $i => $word ) {
+          $tzabbr .= substr( $word, 0, 1);
+        }
+        $this->tz_locn = $tzabbr;
+      }
+      */
+      if ( preg_match( '#\S+/\S+#', $tzname) ) {
+        $this->tz_locn = $tzname;
+      }
+      dbg_error_log( "icalendar", " TZCrap3: TZID '%s', Location '%s', Perhaps: %s", $tzid, $this->tz_locn, $tzname );
+    }
+
+    if ( $tzid != '' && isset($c->save_time_zone_defs) && $c->save_time_zone_defs && $qry->rows != 1 && isset($this->vtimezone) && $this->vtimezone != "" ) {
+      $qry2 = new PgQuery( "INSERT INTO time_zone (tz_id, tz_locn, tz_spec) VALUES( ?, ?, ? );",
+                                   $tzid, $this->tz_locn, $this->vtimezone );
+      $qry2->Exec("iCalendar");
+    }
+
+    if ( (!isset($this->tz_locn) || $this->tz_locn == "") && isset($c->local_tzid) ) {
+      $this->tz_locn = $c->local_tzid;
+    }
+  }
+
+
+  /**
+  * Get the value of a property in the first non-VTIMEZONE
+  */
+  function Get( $key ) {
+    if ( strtoupper($key) == 'TZID' ) {
+      // backward compatibility hack
+      dbg_error_log( "icalendar", " Get(TZID): TZID '%s', Location '%s'", (isset($this->tzid)?$this->tzid:"[not set]"), $this->tz_locn );
+      if ( isset($this->tzid) ) return $this->tzid;
+      return $this->tz_locn;
+    }
+    /**
+    * The property we work on is the first non-VTIMEZONE we find.
+    */
+    $component =& $this->component->FirstNonTimezone();
+    if ( $component === false ) return null;
+    return $component->GetPValue(strtoupper($key));
+  }
+
+
+  /**
+  * Set the value of a property
+  */
+  function Set( $key, $value ) {
+    if ( $value == "" ) return;
+    $key = strtoupper($key);
+    $property = new iCalProp();
+    $property->Name($key);
+    $property->Value($value);
+    if (isset($this->component->rendered) ) unset( $this->component->rendered );
+    $component =& $this->component->FirstNonTimezone();
+    $component->SetProperties( array($property), $key);
+    return $this->Get($key);
+  }
+
+
+  /**
+  * Add a new property/value, regardless of whether it exists already
+  *
+  * @param string $key The property key
+  * @param string $value The property value
+  * @param string $parameters Any parameters to set for the property, as an array of key/value pairs
+  */
+  function Add( $key, $value, $parameters = null ) {
+    if ( $value == "" ) return;
+    $key = strtoupper($key);
+    $property = new iCalProp();
+    $property->Name($key);
+    $property->Value($value);
+    if ( isset($parameters) && is_array($parameters) ) {
+      $property->parameters = $parameters;
+    }
+    $component =& $this->component->FirstNonTimezone();
+    $component->AddProperty($property);
+    if (isset($this->component->rendered) ) unset( $this->component->rendered );
+  }
+
+
+  /**
+  * Because I screwed up with the name originally...
+  * @DEPRECATED
+  * todo:: Remove this function after June 2008.
+  */
+  function Put( $key, $value ) {
+    dbg_error_log( "LOG", " iCalendar: Call to deprecated method '%s'", 'Put' );
+    $this->Set($key,$value);
+  }
+
+  /**
+  * Returns a PostgreSQL Date Format string suitable for returning HTTP (RFC2068) dates
+  * Preferred is "Sun, 06 Nov 1994 08:49:37 GMT" so we do that.
+  */
+  function HttpDateFormat() {
+    return "'Dy, DD Mon IYYY HH24:MI:SS \"GMT\"'";
+  }
+
+
+  /**
+  * Returns a PostgreSQL Date Format string suitable for returning iCal dates
+  */
+  function SqlDateFormat() {
+    return "'YYYYMMDD\"T\"HH24MISS'";
+  }
+
+
+  /**
+  * Returns a PostgreSQL Date Format string suitable for returning dates which
+  * have been cast to UTC
+  */
+  function SqlUTCFormat() {
+    return "'YYYYMMDD\"T\"HH24MISS\"Z\"'";
+  }
+
+
+  /**
+  * Returns a PostgreSQL Date Format string suitable for returning iCal durations
+  *  - this doesn't work for negative intervals, but events should not have such!
+  */
+  function SqlDurationFormat() {
+    return "'\"PT\"HH24\"H\"MI\"M\"'";
+  }
+
+  /**
+  * Returns a suitably escaped RFC2445 content string.
+  *
+  * @param string $name The incoming name[;param] prefixing the string.
+  * @param string $value The incoming string to be escaped.
+  *
+  * @deprecated This function is deprecated and will be removed eventually.
+  * @todo Remove this function.
+  */
+  function RFC2445ContentEscape( $name, $value ) {
+    dbg_error_log( "LOG", " iCalendar: Call to deprecated method '%s'", 'RFC2445ContentEscape' );
+    $property = preg_replace( '/[;].*$/', '', $name );
+    switch( $property ) {
+        /** Content escaping does not apply to these properties culled from RFC2445 */
+      case 'ATTACH':                case 'GEO':                       case 'PERCENT-COMPLETE':      case 'PRIORITY':
+      case 'COMPLETED':             case 'DTEND':                     case 'DUE':                   case 'DTSTART':
+      case 'DURATION':              case 'FREEBUSY':                  case 'TZOFFSETFROM':          case 'TZOFFSETTO':
+      case 'TZURL':                 case 'ATTENDEE':                  case 'ORGANIZER':             case 'RECURRENCE-ID':
+      case 'URL':                   case 'EXDATE':                    case 'EXRULE':                case 'RDATE':
+      case 'RRULE':                 case 'REPEAT':                    case 'TRIGGER':               case 'CREATED':
+      case 'DTSTAMP':               case 'LAST-MODIFIED':             case 'SEQUENCE':
+        break;
+
+        /** Content escaping applies by default to other properties */
+      default:
+        $value = str_replace( '\\', '\\\\', $value);
+        $value = preg_replace( '/\r?\n/', '\\n', $value);
+        $value = preg_replace( "/([,;:\"])/", '\\\\$1', $value);
+    }
+    $result = wordwrap("$name:$value", 73, " \r\n ", true ) . "\r\n";
+    return $result;
+  }
+
+  /**
+   * Return all sub-components of the given type, which are part of the
+   * component we pass in as an array of lines.
+   *
+   * @param array $component The component to be parsed
+   * @param string $type The type of sub-components to be extracted
+   * @param int $count The number of sub-components to extract (default: 9999)
+   *
+   * @return array The sub-component lines
+   */
+  function ExtractSubComponent( $component, $type, $count=9999 ) {
+    $answer = array();
+    $intags = false;
+    $start = "BEGIN:$type";
+    $finish = "END:$type";
+    dbg_error_log( "iCalendar", ":ExtractSubComponent: Looking for %d subsets of type %s", $count, $type );
+    reset($component);
+    foreach( $component AS $k => $v ) {
+      if ( !$intags && $v == $start ) {
+        $answer[] = $v;
+        $intags = true;
+      }
+      else if ( $intags && $v == $finish ) {
+        $answer[] = $v;
+        $intags = false;
+      }
+      else if ( $intags ) {
+        $answer[] = $v;
+      }
+    }
+    return $answer;
+  }
+
+
+  /**
+   * Extract a particular property from the provided component.  In doing so we
+   * assume that the content was unescaped when iCalComponent::ParseFrom()
+   * called iCalComponent::UnwrapComponent().
+   *
+   * @param array $component An array of lines of this component
+   * @param string $type The type of parameter
+   *
+   * @return array An array of iCalProperty objects
+   */
+  function ExtractProperty( $component, $type, $count=9999 ) {
+    $answer = array();
+    dbg_error_log( "iCalendar", ":ExtractProperty: Looking for %d properties of type %s", $count, $type );
+    reset($component);
+    foreach( $component AS $k => $v ) {
+      if ( preg_match( "/$type"."[;:]/i", $v ) ) {
+        $answer[] = new iCalProp($v);
+        dbg_error_log( "iCalendar", ":ExtractProperty: Found property %s", $type );
+        if ( --$count < 1 ) return $answer;
+      }
+    }
+    return $answer;
+  }
+
+
+  /**
+   * Applies the filter conditions, possibly recursively, to the value which will be either
+   * a single property, or an array of lines of the component under test.
+   *
+   * @todo Eventually we need to handle all of these possibilities, which will mean writing
+   * several routines:
+   *  - Get Property from Component
+   *  - Get Parameter from Property
+   *  - Test TimeRange
+   * For the moment we will leave these, until there is a perceived need.
+   *
+   * @param array $filter An array of XMLElement defining the filter(s)
+   * @param mixed $value Either a string which is the single property, or an array of lines, for the component.
+   * @return boolean Whether the filter passed / failed.
+   */
+  function ApplyFilter( $filter, $value ) {
+    foreach( $filter AS $k => $v ) {
+      $tag = $v->GetTag();
+      $value_type = gettype($value);
+      $value_defined = (isset($value) && $value_type == 'string') || ($value_type == 'array' && count($value) > 0 );
+      if ( $tag == 'urn:ietf:params:xml:ns:caldav:is-not-defined' && $value_defined ) {
+        dbg_error_log( "iCalendar", ":ApplyFilter: Value is set ('%s'), want unset, for filter %s", count($value), $tag );
+        return false;
+      }
+      elseif ( $tag == 'urn:ietf:params:xml:ns:caldav:is-defined' && !$value_defined ) {
+        dbg_error_log( "iCalendar", ":ApplyFilter: Want value, but it is not set for filter %s", $tag );
+        return false;
+      }
+      else {
+        switch( $tag ) {
+          case 'urn:ietf:params:xml:ns:caldav:time-range':
+            /** todo:: While this is unimplemented here at present, most time-range tests should occur at the SQL level. */
+            break;
+          case 'urn:ietf:params:xml:ns:caldav:text-match':
+            $search = $v->GetContent();
+            // In this case $value will either be a string, or an array of iCalProp objects
+            // since TEXT-MATCH does not apply to COMPONENT level - only property/parameter
+            if ( gettype($value) != 'string' ) {
+              if ( gettype($value) == 'array' ) {
+                $match = false;
+                foreach( $value AS $k1 => $v1 ) {
+                  // $v1 could be an iCalProp object
+                  if ( $match = $v1->TextMatch($search)) break;
+                }
+              }
+              else {
+                dbg_error_log( "iCalendar", ":ApplyFilter: TEXT-MATCH will only work on strings or arrays of iCalProp.  %s unsupported", gettype($value) );
+                return true;  // We return _true_ in this case, so the client sees the item
+              }
+            }
+            else {
+              $match = strstr( $value, $search[0] );
+            }
+            $negate = $v->GetAttribute("negate-condition");
+            if ( isset($negate) && strtolower($negate) == "yes" && $match ) {
+              dbg_error_log( "iCalendar", ":ApplyFilter: TEXT-MATCH of %s'%s' against '%s'", (isset($negate) && strtolower($negate) == "yes"?'!':''), $search, $value );
+              return false;
+            }
+            break;
+          case 'urn:ietf:params:xml:ns:caldav:comp-filter':
+            $subfilter = $v->GetContent();
+            $component = $this->ExtractSubComponent($value,$v->GetAttribute("name"));
+            if ( ! $this->ApplyFilter($subfilter,$component) ) return false;
+            break;
+          case 'urn:ietf:params:xml:ns:caldav:prop-filter':
+            $subfilter = $v->GetContent();
+            $properties = $this->ExtractProperty($value,$v->GetAttribute("name"));
+            if ( ! $this->ApplyFilter($subfilter,$properties) ) return false;
+            break;
+          case 'urn:ietf:params:xml:ns:caldav:param-filter':
+            $subfilter = $v->GetContent();
+            $parameter = $this->ExtractParameter($value,$v->GetAttribute("NAME"));
+            if ( ! $this->ApplyFilter($subfilter,$parameter) ) return false;
+            break;
+        }
+      }
+    }
+    return true;
+  }
+
+  /**
+   * Test a PROP-FILTER or COMP-FILTER and return a true/false
+   * COMP-FILTER (is-defined | is-not-defined | (time-range?, prop-filter*, comp-filter*))
+   * PROP-FILTER (is-defined | is-not-defined | ((time-range | text-match)?, param-filter*))
+   *
+   * @param array $filter An array of XMLElement defining the filter
+   *
+   * @return boolean Whether or not this iCalendar passes the test
+   */
+  function TestFilter( $filters ) {
+
+    foreach( $filters AS $k => $v ) {
+      $tag = $v->GetTag();
+      $name = $v->GetAttribute("name");
+      $filter = $v->GetContent();
+      if ( $tag == "urn:ietf:params:xml:ns:caldav:prop-filter" ) {
+        $value = $this->ExtractProperty($this->lines,$name);
+      }
+      else {
+        $value = $this->ExtractSubComponent($this->lines,$v->GetAttribute("name"));
+      }
+      if ( count($value) == 0 ) unset($value);
+      if ( ! $this->ApplyFilter($filter,$value) ) return false;
+    }
+    return true;
+  }
+
+  /**
+  * Returns the header we always use at the start of our iCalendar resources
+  *
+  * @deprecated This function is deprecated and will be removed eventually.
+  * @todo Remove this function.
+  */
+  function iCalHeader() {
+    dbg_error_log( "LOG", " iCalendar: Call to deprecated method '%s'", 'iCalHeader' );
+    return <<<EOTXT
+BEGIN:VCALENDAR\r
+PRODID:-//davical.org//NONSGML AWL Calendar//EN\r
+VERSION:2.0\r
+
+EOTXT;
+  }
+
+
+
+  /**
+  * Returns the footer we always use at the finish of our iCalendar resources
+  *
+  * @deprecated This function is deprecated and will be removed eventually.
+  * @todo Remove this function.
+  */
+  function iCalFooter() {
+    dbg_error_log( "LOG", " iCalendar: Call to deprecated method '%s'", 'iCalFooter' );
+    return "END:VCALENDAR\r\n";
+  }
+
+
+  /**
+  * Render the iCalendar object as a text string which is a single VEVENT (or other)
+  *
+  * @param boolean $as_calendar Whether or not to wrap the event in a VCALENDAR
+  * @param string $type The type of iCalendar object (VEVENT, VTODO, VFREEBUSY etc.)
+  * @param array $restrict_properties The names of the properties we want in our rendered result.
+  */
+  function Render( $as_calendar = true, $type = null, $restrict_properties = null ) {
+    if ( $as_calendar ) {
+      return $this->component->Render();
+    }
+    else {
+      $components = $this->component->GetComponents($type);
+      $rendered = "";
+      foreach( $components AS $k => $v ) {
+        $rendered .= $v->Render($restrict_properties);
+      }
+      return $rendered;
+    }
+  }
+
+}
diff --git a/caldav/caldav-client.php b/caldav/caldav-client.php
new file mode 100644 (file)
index 0000000..9269bb9
--- /dev/null
@@ -0,0 +1,532 @@
+<?php
+/**
+* A Class for connecting to a caldav server
+*
+* @package   awl
+* removed curl - now using fsockopen
+* changed 2009 by Andres Obrero - Switzerland andres@obrero.ch
+*
+* @subpackage   caldav
+* @author Andrew McMillan <debian@mcmillan.net.nz>
+* @copyright Andrew McMillan
+* @license   http://gnu.org/copyleft/gpl.html GNU GPL v2
+*/
+
+
+/**
+* A class for accessing DAViCal via CalDAV, as a client
+*
+* @package   awl
+*/
+class CalDAVClient {
+  /**
+  * Server, username, password, calendar
+  *
+  * @var string
+  */
+  var $base_url, $user, $pass, $calendar, $entry, $protocol, $server, $port;
+
+  /**
+  * The useragent which is send to the caldav server
+  *
+  * @var string
+  */
+  var $user_agent = 'DAViCalClient';
+
+  var $headers = array();
+  var $body = "";
+  var $requestMethod = "GET";
+  var $httpRequest = ""; // for debugging http headers sent
+  var $xmlRequest = ""; // for debugging xml sent
+  var $httpResponse = ""; // for debugging http headers received
+  var $xmlResponse = ""; // for debugging xml received
+
+  /**
+  * Constructor, initialises the class
+  *
+  * @param string $base_url  The URL for the calendar server
+  * @param string $user      The name of the user logging in
+  * @param string $pass      The password for that user
+  * @param string $calendar  The name of the calendar (not currently used)
+  */
+  function CalDAVClient( $base_url, $user, $pass, $calendar ) {
+    $this->user = $user;
+    $this->pass = $pass;
+    $this->calendar = $calendar;
+    $this->headers = array();
+
+    if ( preg_match( '#^(https?)://([a-z0-9.-]+)(:([0-9]+))?(/.*)$#', $base_url, $matches ) ) {
+      $this->server = $matches[2];
+      $this->base_url = $matches[5];
+      if ( $matches[1] == 'https' ) {
+        $this->protocol = 'ssl';
+        $this->port = 443;
+      }
+      else {
+        $this->protocol = 'tcp';
+        $this->port = 80;
+      }
+      if ( $matches[4] != '' ) {
+        $this->port = intval($matches[4]);
+      }
+    }
+    else {
+      trigger_error("Invalid URL: '".$base_url."'", E_USER_ERROR);
+    }
+  }
+
+  /**
+  * Adds an If-Match or If-None-Match header
+  *
+  * @param bool $match to Match or Not to Match, that is the question!
+  * @param string $etag The etag to match / not match against.
+  */
+  function SetMatch( $match, $etag = '*' ) {
+    $this->headers[] = sprintf( "%s-Match: \"%s\"", ($match ? "If" : "If-None"), $etag);
+  }
+
+  /**
+  * Add a Depth: header.  Valid values are 1 or infinity
+  *
+  * @param int $depth  The depth, default to infinity
+  */
+  function SetDepth( $depth = 'infinity' ) {
+    $this->headers[] = "Depth: ". ($depth == 1 ? "1" : "infinity" );
+  }
+
+  /**
+  * Add a Depth: header.  Valid values are 1 or infinity
+  *
+  * @param int $depth  The depth, default to infinity
+  */
+  function SetUserAgent( $user_agent = null ) {
+    if ( !isset($user_agent) ) $user_agent = $this->user_agent;
+    $this->user_agent = $user_agent;
+  }
+
+  /**
+  * Add a Content-type: header.
+  *
+  * @param int $type  The content type
+  */
+  function SetContentType( $type ) {
+    $this->headers[] = "Content-type: $type";
+  }
+
+  /**
+  * Split response into httpResponse and xmlResponse
+  *
+  * @param string Response from server
+   */
+  function ParseResponse( $response ) {
+      $pos = strpos($response, '<?xml');
+      if ($pos == false) {
+        $this->httpResponse = trim($response);
+      }
+      else {
+        $this->httpResponse = trim(substr($response, 0, $pos));
+        $this->xmlResponse = trim(substr($response, $pos));
+      }
+  }
+
+  /**
+   * Output http request headers
+   *
+   * @return HTTP headers
+   */
+  function GetHttpRequest() {
+      return $this->httpRequest;
+  }
+  /**
+   * Output http response headers
+   *
+   * @return HTTP headers
+   */
+  function GetHttpResponse() {
+      return $this->httpResponse;
+  }
+  /**
+   * Output xml request
+   *
+   * @return raw xml
+   */
+  function GetXmlRequest() {
+      return $this->xmlRequest;
+  }
+  /**
+   * Output xml response
+   *
+   * @return raw xml
+   */
+  function GetXmlResponse() {
+      return $this->xmlResponse;
+  }
+
+  /**
+  * Send a request to the server
+  *
+  * @param string $relative_url The URL to make the request to, relative to $base_url
+  *
+  * @return string The content of the response from the server
+  */
+  function DoRequest( $relative_url = "" ) {
+    if(!defined("_FSOCK_TIMEOUT")){ define("_FSOCK_TIMEOUT", 10); }
+    $headers = array();
+
+    $headers[] = $this->requestMethod." ". $this->base_url . $relative_url . " HTTP/1.1";
+    $headers[] = "Authorization: Basic ".base64_encode($this->user .":". $this->pass );
+    $headers[] = "Host: ".$this->server .":".$this->port;
+
+    foreach( $this->headers as $ii => $head ) {
+      $headers[] = $head;
+    }
+    $headers[] = "Content-Length: " . strlen($this->body);
+    $headers[] = "User-Agent: " . $this->user_agent;
+    $headers[] = 'Connection: close';
+    $this->httpRequest = join("\r\n",$headers);
+    $this->xmlRequest = $this->body;
+
+    $fip = fsockopen( $this->protocol . '://' . $this->server, $this->port, $errno, $errstr, _FSOCK_TIMEOUT); //error handling?
+    if ( !(get_resource_type($fip) == 'stream') ) return false;
+    if ( !fwrite($fip, $this->httpRequest."\r\n\r\n".$this->body) ) { fclose($fip); return false; }
+    $rsp = "";
+    while( !feof($fip) ) { $rsp .= fgets($fip,8192); }
+    fclose($fip);
+
+    $this->headers = array();  // reset the headers array for our next request
+    $this->ParseResponse($rsp);
+    return $rsp;
+  }
+
+
+  /**
+  * Send an OPTIONS request to the server
+  *
+  * @param string $relative_url The URL to make the request to, relative to $base_url
+  *
+  * @return array The allowed options
+  */
+  function DoOptionsRequest( $relative_url = "" ) {
+    $this->requestMethod = "OPTIONS";
+    $this->body = "";
+    $headers = $this->DoRequest($relative_url);
+    $options_header = preg_replace( '/^.*Allow: ([a-z, ]+)\r?\n.*/is', '$1', $headers );
+    $options = array_flip( preg_split( '/[, ]+/', $options_header ));
+    return $options;
+  }
+
+
+
+  /**
+  * Send an XML request to the server (e.g. PROPFIND, REPORT, MKCALENDAR)
+  *
+  * @param string $method The method (PROPFIND, REPORT, etc) to use with the request
+  * @param string $xml The XML to send along with the request
+  * @param string $relative_url The URL to make the request to, relative to $base_url
+  *
+  * @return array An array of the allowed methods
+  */
+  function DoXMLRequest( $request_method, $xml, $relative_url = '' ) {
+    $this->body = $xml;
+    $this->requestMethod = $request_method;
+    $this->SetContentType("text/xml");
+    return $this->DoRequest($relative_url);
+  }
+
+
+
+  /**
+  * Get a single item from the server.
+  *
+  * @param string $relative_url The part of the URL after the calendar
+  */
+  function DoGETRequest( $relative_url ) {
+    $this->body = "";
+    $this->requestMethod = "GET";
+    return $this->DoRequest( $relative_url );
+  }
+
+  /**
+  * PUT a text/icalendar resource, returning the etag
+  *
+  * @param string $relative_url The URL to make the request to, relative to $base_url
+  * @param string $icalendar The iCalendar resource to send to the server
+  * @param string $etag The etag of an existing resource to be overwritten, or '*' for a new resource.
+  *
+  * @return string The content of the response from the server
+  */
+  function DoPUTRequest( $relative_url, $icalendar, $etag = null ) {
+    $this->body = $icalendar;
+
+    $this->requestMethod = "PUT";
+    if ( $etag != null ) {
+      $this->SetMatch( ($etag != '*'), $etag );
+    }
+    $this->SetContentType("text/calendar");
+    $headers = $this->DoRequest($relative_url);
+
+    /**
+    * RSCDS will always return the real etag on PUT.  Other CalDAV servers may need
+    * more work, but we are assuming we are running against RSCDS in this case.
+    */
+    preg_match('/^HTTP\/1\.[01]+\s+(\d+)\s+/i', $headers, $match);
+    if ($match) {
+        //print htmlentities($this->GetXmlResponse())."<br/>";
+        $xml = simplexml_load_string($this->GetXmlResponse());
+        $xml->registerXPathNamespace('e', 'DAV:');
+        $elems = $xml->xpath('//e:error');
+        $error = $elems[0];
+        switch ($match[1]) {
+            case 412:
+                //echo "$error<br/>";
+                preg_match('/^"?[^"]+"+([0-9a-zA-Z]+)"+.*"+([0-9a-zA-Z]+)"+/', $error, $m);
+                //print_r($m);
+                if ($m) {
+                    if (strcasecmp($m[1], $m[2]) !== 0) {
+                        $error = "Remote changes discovered.\n";
+                        $error .= "Enter changes again after reload\n";
+                    }
+                }
+                $etag = array($match[1] => "$error");
+                //print_r($etag);
+                break;
+            default: break;
+        }
+    }
+    else
+        $etag = preg_replace( '/^.*Etag: "?([^"\r\n]+)"?\r?\n.*/is', '$1', $headers );
+    return $etag;
+  }
+
+
+  /**
+  * DELETE a text/icalendar resource
+  *
+  * @param string $relative_url The URL to make the request to, relative to $base_url
+  * @param string $etag The etag of an existing resource to be deleted, or '*' for any resource at that URL.
+  *
+  * @return int The HTTP Result Code for the DELETE
+  */
+  function DoDELETERequest( $relative_url, $etag = null ) {
+    $this->body = "";
+
+    $this->requestMethod = "DELETE";
+    if ( $etag != null ) {
+      $this->SetMatch( true, $etag );
+    }
+    $this->DoRequest($relative_url);
+    return $this->resultcode;
+  }
+
+
+  /**
+  * Given XML for a calendar query, return an array of the events (/todos) in the
+  * response.  Each event in the array will have a 'href', 'etag' and '$response_type'
+  * part, where the 'href' is relative to the calendar and the '$response_type' contains the
+  * definition of the calendar data in iCalendar format.
+  *
+  * @param string $filter XML fragment which is the <filter> element of a calendar-query
+  * @param string $relative_url The URL relative to the base_url specified when the calendar was opened.  Default ''.
+  * @param string $report_type Used as a name for the array element containing the calendar data. @deprecated
+  *
+  * @return array An array of the relative URLs, etags, and events from the server.  Each element of the array will
+  *               be an array with 'href', 'etag' and 'data' elements, corresponding to the URL, the server-supplied
+  *               etag (which only varies when the data changes) and the calendar data in iCalendar format.
+  */
+  function DoCalendarQuery( $filter, $relative_url = '' ) {
+
+    $xml = <<<EOXML
+<?xml version="1.0" encoding="utf-8" ?>
+<C:calendar-query xmlns:D="DAV:" xmlns:C="urn:ietf:params:xml:ns:caldav">
+  <D:prop>
+    <C:calendar-data/>
+    <D:getetag/>
+  </D:prop>
+$filter
+</C:calendar-query>
+EOXML;
+
+    $this->DoXMLRequest( 'REPORT', $xml, $relative_url );
+    $xml_parser = xml_parser_create_ns('UTF-8');
+    $this->xml_tags = array();
+    xml_parser_set_option ( $xml_parser, XML_OPTION_SKIP_WHITE, 1 );
+    xml_parse_into_struct( $xml_parser, $this->xmlResponse, $this->xml_tags );
+    xml_parser_free($xml_parser);
+
+    $report = array();
+    foreach( $this->xml_tags as $k => $v ) {
+      switch( $v['tag'] ) {
+        case 'DAV::RESPONSE':
+          if ( $v['type'] == 'open' ) {
+            $response = array();
+          }
+          elseif ( $v['type'] == 'close' ) {
+            $report[] = $response;
+          }
+          break;
+        case 'DAV::HREF':
+          $response['href'] = basename( $v['value'] );
+          break;
+        case 'DAV::GETETAG':
+          $response['etag'] = preg_replace('/^"?([^"]+)"?/', '$1', $v['value']);
+          break;
+        case 'URN:IETF:PARAMS:XML:NS:CALDAV:CALENDAR-DATA':
+          $response['data'] = $v['value'];
+          break;
+      }
+    }
+    return $report;
+  }
+
+
+  /**
+  * Get the events in a range from $start to $finish.  The dates should be in the
+  * format yyyymmddThhmmssZ and should be in GMT.  The events are returned as an
+  * array of event arrays.  Each event array will have a 'href', 'etag' and 'event'
+  * part, where the 'href' is relative to the calendar and the event contains the
+  * definition of the event in iCalendar format.
+  *
+  * @param timestamp $start The start time for the period
+  * @param timestamp $finish The finish time for the period
+  * @param string    $relative_url The URL relative to the base_url specified when the calendar was opened.  Default ''.
+  *
+  * @return array An array of the relative URLs, etags, and events, returned from DoCalendarQuery() @see DoCalendarQuery()
+  */
+  function GetEvents( $start = null, $finish = null, $relative_url = '' ) {
+    $filter = "";
+    if ( isset($start) && isset($finish) )
+        $range = "<C:time-range start=\"$start\" end=\"$finish\"/>";
+    else
+        $range = '';
+
+    $filter = <<<EOFILTER
+  <C:filter>
+    <C:comp-filter name="VCALENDAR">
+      <C:comp-filter name="VEVENT">
+        $range
+      </C:comp-filter>
+    </C:comp-filter>
+  </C:filter>
+EOFILTER;
+
+    return $this->DoCalendarQuery($filter, $relative_url);
+  }
+
+
+  /**
+  * Get the todo's in a range from $start to $finish.  The dates should be in the
+  * format yyyymmddThhmmssZ and should be in GMT.  The events are returned as an
+  * array of event arrays.  Each event array will have a 'href', 'etag' and 'event'
+  * part, where the 'href' is relative to the calendar and the event contains the
+  * definition of the event in iCalendar format.
+  *
+  * @param timestamp $start The start time for the period
+  * @param timestamp $finish The finish time for the period
+  * @param boolean   $completed Whether to include completed tasks
+  * @param boolean   $cancelled Whether to include cancelled tasks
+  * @param string    $relative_url The URL relative to the base_url specified when the calendar was opened.  Default ''.
+  *
+  * @return array An array of the relative URLs, etags, and events, returned from DoCalendarQuery() @see DoCalendarQuery()
+  */
+  function GetTodos( $start, $finish, $completed = false, $cancelled = false, $relative_url = "" ) {
+
+    if ( $start && $finish ) {
+$time_range = <<<EOTIME
+                <C:time-range start="$start" end="$finish"/>
+EOTIME;
+    }
+
+    // Warning!  May contain traces of double negatives...
+    $neg_cancelled = ( $cancelled === true ? "no" : "yes" );
+    $neg_completed = ( $cancelled === true ? "no" : "yes" );
+
+    $filter = <<<EOFILTER
+  <C:filter>
+    <C:comp-filter name="VCALENDAR">
+          <C:comp-filter name="VTODO">
+                <C:prop-filter name="STATUS">
+                        <C:text-match negate-condition="$neg_completed">COMPLETED</C:text-match>
+                </C:prop-filter>
+                <C:prop-filter name="STATUS">
+                        <C:text-match negate-condition="$neg_cancelled">CANCELLED</C:text-match>
+                </C:prop-filter>$time_range
+          </C:comp-filter>
+    </C:comp-filter>
+  </C:filter>
+EOFILTER;
+
+    return $this->DoCalendarQuery($filter, $relative_url);
+  }
+
+
+  /**
+  * Get the calendar entry by UID
+  *
+  * @param uid
+  * @param string    $relative_url The URL relative to the base_url specified when the calendar was opened.  Default ''.
+  *
+  * @return array An array of the relative URL, etag, and calendar data returned from DoCalendarQuery() @see DoCalendarQuery()
+  */
+  function GetEntryByUid( $uid, $relative_url = '' ) {
+    $filter = "";
+    if ( $uid ) {
+      $filter = <<<EOFILTER
+  <C:filter>
+    <C:comp-filter name="VCALENDAR">
+          <C:comp-filter name="VEVENT">
+                <C:prop-filter name="UID">
+                        <C:text-match icollation="i;octet">$uid</C:text-match>
+                </C:prop-filter>
+          </C:comp-filter>
+    </C:comp-filter>
+  </C:filter>
+EOFILTER;
+    }
+
+    return $this->DoCalendarQuery($filter, $relative_url);
+  }
+
+
+  /**
+  * Get the calendar entry by HREF
+  *
+  * @param string    $href         The href from a call to GetEvents or GetTodos etc.
+  * @param string    $relative_url The URL relative to the base_url specified when the calendar was opened.  Default ''.
+  *
+  * @return string The iCalendar of the calendar entry
+  */
+  function GetEntryByHref( $href, $relative_url = '' ) {
+    return $this->DoGETRequest( $relative_url . $href );
+  }
+
+}
+
+//$cal = new CalDAVClient( "https://www.google.com/calendar/dav/[mail id]/events/", "uid", "pwd", "calendar" );
+//$cal = new CalDAVClient( "http://calendar.datanom.net/caldav.php/[uid]/home", "uid", "pwd", "calendar" );
+//$cal->SetDepth(1);
+/*$folder_xml = $cal->DoXMLRequest("PROPFIND",
+'<?xml version="1.0" encoding="utf-8" ?>
+    <propfind xmlns="DAV:">
+    <prop>
+        <getcontentlength/>
+        <getcontenttype/>
+        <resourcetype/>
+        <getetag/>
+    </prop>
+</propfind>' );*/
+//print_r($folder_xml);
+
+
+/*$events = $cal->GetEvents("20100616T000000Z", "20100616T235959Z");
+foreach ( $events AS $k => $event ) {
+   print_r( $event['data'] );
+}*/
+
+
+/*
+print "Debug information\n";
+print "Headers sent:\n".$cal->GetHttpRequest()."\n".
+    "XML sent:\n".$cal->GetXmlRequest()."\n".
+    "Headers received:\n".$cal->GetHttpResponse()."\n".
+    "XML received:\n".$cal->GetXmlResponse()."\n";
+ */
\ No newline at end of file
diff --git a/caldav/caldavresource.class.php b/caldav/caldavresource.class.php
new file mode 100644 (file)
index 0000000..4c1214e
--- /dev/null
@@ -0,0 +1,304 @@
+<?php
+
+/* $Id$ */
+    final class VTYPE {
+        const VEVENT    = 1;
+        const VTODO     = 2;
+        const VJOURNAL  = 3;
+        const VFREEBUSY = 4;
+        const VTIMEZONE = 5;
+        const VALARM    = 6;
+        private $value;
+
+        public function __construct($value) {
+            switch ($value) {
+                case 'VEVENT': $this->value = self::VEVENT; break;
+                case 'VTODO': $this->value = self::VTODO; break;
+                case 'VJOURNAL': $this->value = self::VJOURNAL; break;
+                case 'VFREEBUSY': $this->value = self::VFREEBUSY; break;
+                case 'VTIMEZONE': $this->value = self::VTIMEZONE; break;
+                case 'VALARM': $this->value = self::VALARM; break;
+                case self::VEVENT:
+                case self::VTODO:
+                case self::VJOURNAL:
+                case self::VFREEBUSY:
+                case self::VTIMEZONE:
+                case self::VALARM: $this->value = $value; break;
+                default: throw new Exception ("$value: Invalid VTYPE");
+            }
+        }
+
+        private function __clone() {}
+
+        public function ordinal() {
+            return $this->value;
+        }
+
+        public function __toString() {
+            switch ($this->value) {
+                case self::VEVENT: return 'VEVENT'; break;
+                case self::VTODO: return 'VTODO'; break;
+                case self::VJOURNAL: return 'VJOURNAL'; break;
+                case self::VFREEBUSY: return 'VFREEBUSY'; break;
+                case self::VTIMEZONE: return 'VTIMEZONE'; break;
+                case self::VALARM: return 'VALARM'; break;
+            }
+        }
+
+    }
+
+    abstract class CaldavRessource
+        implements ArrayAccess, IteratorAggregate {
+
+        private   $client;
+
+        function __construct($url, $uid = '', $pwd = '', $cal = '') {
+            if (empty($url))
+                throw new Exception("Missing URL");
+            $this->client = new CalDAVClient($url, $uid, $pwd, $cal);
+        }
+
+        /**
+         * abstract functions to be implemented by sub classes
+         */
+        abstract function update($url, $etag = NULL);
+        abstract function newComponent($c_type);
+        abstract function getComponents($start, $end);
+        abstract function delete($url, $etag = NULL);
+
+        protected function callServer($method, $param = array()) {
+            $error = TRUE;
+            $msg = "Unknown error";
+
+            if (! is_array($param))
+                throw new Exception("Parameters must be inclosed in an array");
+            switch (strtolower($method)) {
+                case 'getevents':
+                    if (count($param) != 2) {
+                        $error = TRUE;
+                        $msg = "Expected 2 parameters";
+                        break;
+                    }
+                    if ($this->isDateTime($param[0]) &&
+                        $this->isDateTime($param[1])) {
+                        $res = $this->client->GetEvents($param[0], $param[1]);
+                        $error = FALSE;
+                    }
+                    else {
+                        $msg = "[${param[0]},${param[1]}]: Invalid DateTime";
+                        $error = TRUE;
+                    }
+                    break;
+                case 'getbyuid':
+                    if (count($param) != 1) {
+                        $error = TRUE;
+                        $msg = "Expected 1 parameter";
+                        break;
+                    }
+                    $res = $this->client->GetEntryByUid($param[0]);
+                    $error = FALSE;
+                    break;
+                case 'put':
+                       if (count($param) < 2 || count($param) > 3) {
+                                               $error = TRUE;
+                                               $msg = "Syntax: URL, CalDAV_resource[, ETag]";
+                                               break;
+                                       }
+                                       if (count($param) == 2)
+                                               $res = $this->client->DoPUTRequest($param[0], $param[1]);
+                                       else
+                                               $res = $this->client->DoPUTRequest($param[0], $param[1], $param[2]);
+                                       $error = FALSE;
+                       break;
+                case 'delete':
+                       if (count($param) < 1 || count($param) > 2) {
+                                               $error = TRUE;
+                                               $msg = "Syntax: URL[, ETag]";
+                                               break;
+                                       }
+                                       if (count($param) == 1)
+                                               $res = $this->client->DoDELETERequest($param[0]);
+                                       else
+                                               $res = $this->client->DoDELETERequest($param[0], $param[1]);
+                                       $error = FALSE;
+                       break;
+                default:
+                    throw new Exception("$method: Unknown method");
+            }
+            if ($error)
+                throw new Exception($msg);
+            else
+                return $res;
+        }
+
+        static function isDateTime($var) {
+            return (preg_match("/^([0-9]{8})T([0-9]{6})Z?$/", $var) > 0);
+        }
+
+        /**
+         * Returned date-time will always be in UTC
+         */
+        static function timestamp2ICal($ts, $localtime = TRUE) {
+            $ts = (int) $ts;
+            if ($ts < 0)
+                throw new Exception("$ts: invalid timestamp");
+            if ($localtime) {
+                $date = date('Ymd', $ts);
+                $time = date('His', $ts);
+                $res = sprintf("%sT%s", $date, $time);
+            }
+            else {
+                $date = gmdate('Ymd', $ts);
+                $time = gmdate('His', $ts);
+                $res = sprintf("%sT%sZ", $date, $time);
+            }
+            return $res;
+        }
+
+        static function iCal2Timestamp($ical) {
+            if (! self::isDateTime($ical)) {
+                // test for badly formed all-day event
+                //print "$ical";
+                $res = preg_match("/^([0-9]{4})([0-9]{2})([0-9]{2})$/",
+                    $ical, $parts);
+                if ($res == 0)
+                    throw new Exception("$ical: invalid CalDAV Date-Time");
+                else {
+                    $timepart = array('00', '00', '00');
+                    $parts = array_merge($parts, $timepart);
+                }
+            }
+            else {
+                $date = "([0-9]{4})([0-9]{2})([0-9]{2})";
+                $time = "([0-9]{2})([0-9]{2})([0-9]{2})";
+                preg_match("/^${date}T${time}(Z?)$/", $ical, $parts);
+            }
+            if (count($parts) == 8 && ! empty($parts[7]))
+                return gmmktime($parts[4], $parts[5], $parts[6],
+                    $parts[2], $parts[3], $parts[1]);
+            else
+                return mktime($parts[4], $parts[5], $parts[6],
+                    $parts[2], $parts[3], $parts[1]);
+        }
+
+        private static function down_hour($date) {
+            //print "$date<br/>";
+            if (! self::isDateTime($date)) {
+                // test for badly formed all-day event
+                $res = preg_match("/^([0-9]{4})([0-9]{2})([0-9]{2})$/",
+                    $date, $parts);
+                if ($res == 0)
+                    throw new Exception("$date: invalid CalDAV Date-Time");
+                else {
+                    array_shift($parts);
+                    $timepart = array('T', '00', '00', '00');
+                    $parts = array_merge($parts, $timepart);
+                    return implode('', $parts);
+                }
+            }
+            else {
+                $a = explode('T', $date);
+                $a[1] = substr_replace($a[1], '0000', 2);
+                return $a[0].'T'.$a[1];
+            }
+        }
+
+        static function fix_allday_event(&$date_a, &$date_b) {
+            //print "$date_a : $date_b<br/>";
+            if ($date_a == $date_b) {
+                if (! self::isDateTime($date_a) && ! self::isDateTime($date_b)) {
+                    $res1 = preg_match("/^([0-9]{4})([0-9]{2})([0-9]{2})$/",
+                        $date_a);
+                    $res2 = preg_match("/^([0-9]{4})([0-9]{2})([0-9]{2})$/",
+                        $date_b);
+                    if ($res1 == 0 || $res2 == 0)
+                        throw new Exception("$date_a, $date_b: invalid CalDAV Date-Time");
+                    else {
+                        $date_a .= "T000000";
+                        $date_b .= "T235959";
+                    }
+                }
+                else {
+                    preg_match("/^([0-9]{4}[0-9]{2}[0-9]{2})T([0-9]{6})$/",
+                        $date_a, $part_a);
+                    preg_match("/^([0-9]{4}[0-9]{2}[0-9]{2})T([0-9]{6})$/",
+                        $date_b, $part_b);
+                    $date_a = $part_a[1]."T000000";
+                    $date_b = $part_b[1]."T235959";
+                    //print "$date_a : $date_b<br/>";
+                }
+                //print "$date_a : $date_b<br/>";
+            }
+        }
+
+        static function datecmp($date_a, $date_b) {
+            $date_a = self::iCal2Timestamp($date_a);
+            $date_b = self::iCal2Timestamp(self::down_hour($date_b));
+            if ($date_a < $date_b)
+                $res = -1;
+            else if ($date_a > $date_b)
+                $res = 1;
+            else
+                $res = 0;
+            return $res;
+        }
+
+        private static function intcmpstr($a_str, $b_str) {
+            $a = (int) $a_str;
+            $b = (int) $b_str;
+            //print "$a:$b<br/>";
+            if ($a > $b)
+                return 1;
+            else if ($a < $b)
+                return -1;
+            else
+                return 0;
+        }
+
+        static function cmpdate($date_a, $date_b) {
+            $datepart = explode('T', $date_a);
+            $d_a = $datepart[0];
+            $datepart = explode('T', $date_b);
+            $d_b = $datepart[0];
+            $y_cmp = self::intcmpstr(substr($d_a, 0, 4), substr($d_b, 0, 4));
+            if ($y_cmp == 0) {
+                $m_cmp = self::intcmpstr(substr($d_a, 4, 2), substr($d_b, 4, 2));
+                if ($m_cmp == 0) {
+                    return self::intcmpstr(substr($d_a, 6, 2), substr($d_b, 6, 2));
+                }
+                return $m_cmp;
+            }
+            return $y_cmp;
+        }
+
+        static function cmptime($time_a, $time_b) {
+            $timepart = explode('T', $time_a);
+            $t_a = $timepart[1];
+            $timepart = explode('T', $time_b);
+            $t_b = $timepart[1];
+            //print "$t_a:$t_b<br/>";
+            $h_cmp = self::intcmpstr(substr($t_a, 0, 2), substr($t_b, 0, 2));
+            if ($h_cmp == 0) {
+                $m_cmp = self::intcmpstr(substr($t_a, 2, 2), substr($t_b, 2, 2));
+                if ($m_cmp == 0) {
+                    return self::intcmpstr(substr($t_a, 4, 2), substr($t_b, 4, 2));
+                }
+                return $m_cmp;
+            }
+            return $h_cmp;
+        }
+
+        static function allDayEvent($time_a, $time_b) {
+            //echo $time_a.':'.$time_b.'<br/>';
+            $a = explode('T', $time_a);
+            if (count($a) < 2)
+                array_push($a, '0000');
+            $b = explode('T', $time_b);
+            if (count($b) < 2)
+                array_push($b, '0000');
+            $t = strtotime($time_b) - 3600;
+            $t = date("Ymd\THm", $t);
+            return (self::cmpdate($time_a, $t) == 0 &&
+                    $a[1] == '0000' && $b[1] == '0000');
+        }
+    }
diff --git a/caldav/calendar.class.php b/caldav/calendar.class.php
new file mode 100644 (file)
index 0000000..7fdbd01
--- /dev/null
@@ -0,0 +1,317 @@
+<?php
+/* $Id$ */
+
+    require_once 'caldav-client.php';
+    require_once 'awl/iCalendar.php';
+    require_once 'caldavresource.class.php';
+    require_once 'rruleparser.class.php';
+    require_once 'vevent.class.php';
+    require_once 'icomponent.class.php';
+
+    class CalendarIterator implements Iterator {
+        private $list;
+
+        function __construct(array $list) {
+            $this->list = $list;
+        }
+
+        function current() {
+            return current($this->list);
+        }
+
+        function next() {
+            next($this->list);
+        }
+
+        function key() {
+            return key($this->list);
+        }
+
+        function rewind() {
+            reset($this->list);
+        }
+
+        function valid() {
+            $obj = current($this->list);
+            return ($obj !== FALSE);
+        }
+
+    }
+
+    class Calendar extends CaldavRessource {
+
+        private $calendar;
+
+       function __construct($url, $uid = '', $pwd = '', $cal = '') {
+           //file_put_contents('/tmp/dump', "$url\n$uid\n$pwd\n$cal\n", FILE_APPEND);
+            if (empty($url))
+                throw new Exception("Missing URL");
+            parent::__construct($url, $uid, $pwd, $cal);
+        }
+
+        private function setComponent(VTYPE $type, array $item, $new = FALSE) {
+            switch ($type) {
+                case VTYPE::VEVENT:
+                    $ical = new VEvent(
+                        $item['etag'], $item['href'],
+                        $type, $item['ical'], $new);
+                    break;
+                default:
+                    throw new Exception(
+                        "$thisType: Unsupported iCalendar component");
+            }
+            $this->calendar[$item['etag']] = $ical;
+            //var_dump($this->calendar[$item['etag']]);
+            //print "-------------------------------<br/>";
+        }
+
+        private function setResource($etag, $resource) {
+            if ($resource === NULL)
+                unset($this->calendar[$etag]);
+            else if (isset($this->calendar[$etag]))
+                $this->calendar[$etag]->setResource($resource);
+            else {
+                $type = new VTYPE($this->getType($resource));
+                $this->setComponent($type, array(
+                    'etag' => $etag,
+                    'href' => NULL,
+                    'ical' => $resource),
+                    TRUE
+                );
+            }
+        }
+
+        private function getType(iCalendar $iCalendar) {
+            $components = $iCalendar->component->GetComponents();
+            // Find VCalender component
+            foreach($components as $type) {
+                try {
+                    $vtype = new VTYPE($type->GetType());
+                    if ($vtype->ordinal() != VTYPE::VTIMEZONE)
+                        break;
+                }
+                catch (Exception $ex) {}
+            }
+            return $vtype;
+        }
+
+        private function wrapCalendar($component) {
+            $cal = "BEGIN:VCALENDAR\r\n";
+            $cal .= "PRODID:-//datanom.net//NONSGML WEBCAL Calendar//EN\r\n";
+            $cal .= "VERSION:2.0\r\n";
+            $cal .= "CALSCALE:GREGORIAN\r\n";
+            $cal .= $component;
+            $cal .= "END:VCALENDAR\r\n";
+            
+            return $cal;
+        }
+        
+        function getComponents($start, $end) {
+            $this->calendar = array();
+
+            if (! $this->isDateTime($start) || ! $this->isDateTime($end))
+                throw new Exception("[$start:$end]: Invalid DateTime format");
+            //print "$start:$end<br/>";
+            //file_put_contents('/tmp/dump', "$start, $end\n", FILE_APPEND);
+            $events = $this->callServer('getEvents', array($start, $end));
+            //var_export($events, FALSE);
+            //file_put_contents('/tmp/dump', var_export($events, TRUE), FILE_APPEND);
+            foreach ($events as $k => $event) {
+                $iCalendar = new iCalendar(
+                    array('icalendar' => $event['data']));
+                $vtype = $this->getType($iCalendar);
+                $this->setComponent($vtype, array(
+                    'etag' => $event['etag'],
+                    'href' => $event['href'],
+                    'ical' => $iCalendar
+                    )
+                );
+            }
+        }
+
+        function newComponent($c_type) {
+            switch (strtoupper($c_type)) {
+                case 'VEVENT': $type = 'VEVENT'; break;
+                default:
+                    throw new Exception(
+                        "$thisType: Unsupported iCalendar component");
+            }
+            $start = gmdate("Ymd\THm\Z");
+            $end = strtotime($start) + (60*60);
+            $end = gmdate("Ymd\THm\Z", $end);
+            //echo "$start:$end<br/>";
+            $uid = sha1(microtime() . $start . $end);
+            $iCalendar = new iCalendar(array(
+                    'type' => $type,
+                    'DTSTART' => $start,
+                    'DTEND' => $end,
+                    'UID' => $uid
+                )
+            );
+            $vtype = $this->getType($iCalendar);
+            $etag = sha1("This is a new component");
+            $this->setComponent($vtype, array(
+                'etag' => $etag,
+                'href' => NULL,
+                'ical' => $iCalendar
+                )
+            );
+            return $this->calendar[$etag];
+        }
+/*
+        function reload($start, $end) {
+            $res = $this->update();
+            if (count($res) < 1) {
+                $this->getComponents($start, $end);
+            }
+            return $res;
+        }
+*/
+        private function updateEvent($url, $etag) {
+            $res = array();
+            $resource = $this->calendar[$etag];
+            if ($resource && $resource->isDirty()) {
+                // update (call put)
+                $component = $resource->getBaseComponent();
+                //print "$etag: update\n";
+                $uid = $component->GetPValue('UID');
+                $ical = $this->wrapCalendar($component->Render());
+                //echo "$uid<br/>".nl2br($ical)."$etag<br/>";
+                $url = $resource->getUrl();
+                if ($url) {
+                    $newEtag = $this->callServer('put', 
+                            array("$uid.ics", $ical, $etag));
+                }
+                else {
+                    $newEtag = $this->callServer('put', 
+                            array("$uid.ics", $ical));
+                }
+                if (is_array($newEtag))
+                    array_push($res, $newEtag);
+                else {
+                    $resource->setEtag($newEtag);
+                }
+            }
+            return $res;
+        }
+                
+        function update($url, $etag = NULL) {
+            //var_dump($this->calendar);
+            if (! $etag) {
+                foreach($this->calendar as $id => $resource) {
+                    //var_dump($resource);
+                    $thisUrl = $resource->getUrl();
+                    if ($thisUrl && strcasecmp($url, $thisUrl) == 0) {
+                        $etag = $id;
+                        break;
+                    }
+                }
+            }
+            if ($etag)
+                $res = $this->updateEvent($url, $etag);
+            else
+                $res = array($url => 'Event does not exist');
+            return $res;
+        }
+
+        function delete($url, $etag = NULL) {
+            if ($etag) {
+                $res = $this->callServer('delete', array($url, $etag));
+            }
+            else {
+                $res = $this->callServer('delete', array($url));
+            }
+            return $res;
+        }
+
+        // inherited abstract methods from parent
+        function offsetExists($etag) {
+            return (is_object($this->calendar[$etag]) &&
+                $this->calendar[$etag] instanceof IComponent);
+        }
+
+        function offsetGet($etag) {
+            if ($this->offsetExists($etag))
+                return $this->calendar[$etag]->getResource();
+        }
+
+        function offsetSet($etag, $ical) {
+            $this->setResource($etag, $ical);
+        }
+
+        function offsetUnset($etag) {
+            $this->setResource($etag, NULL);
+        }
+
+        function getIterator() {
+            return new CalendarIterator($this->calendar);
+        }
+
+    }
+/*
+$cal = new Calendar(
+    'http://calendar.datanom.net/caldav.php/mir/home/',
+    'uid',
+    'pwd'
+);
+$cal->getComponents("20030830T000000Z","20031201T000000Z");
+//print_r($cal);
+$i = 0;
+foreach($cal as $obj) {
+    $i++;
+    print "========= [$i] =========\n";
+    //print_r($obj);
+    //print_r ($obj->getAlarm());
+    print_r($obj->getActiveDates("20031014T000000Z","20031114T000000Z"));
+    //print "{$obj->isUTCTime()}\n";
+    //$obj->getActiveDates();
+}
+print "Found $i event(s)\n";
+
+//print_r ($cal->getUrlByEtag($cal->getEtagFromUid('KOrganizer-1670268771.406')));
+$time = time();
+print "time: $time\n";
+$dt = $cal->timestamp2ICal($time, TRUE);
+print "dt: $dt\n";
+$time = $cal->iCal2Timestamp($dt);
+print "time: $time\n";
+$dt = $cal->timestamp2ICal($time, FALSE);
+print "dt: $dt\n";
+$time = $cal->iCal2Timestamp(substr($dt, 0, strpos($dt, 'T')));
+$dt = $cal->timestamp2ICal($time, TRUE);
+print "dt: $dt\n";
+$r = new RRuleParser(
+    'FREQ=HOURLY;INTERVAL=3;UNTIL=20070101T170000Z',
+    '20070101T090000Z', '20070101T090000Z');
+$r = new RRuleParser(
+    'FREQ=WEEKLY;COUNT=12;INTERVAL=2',
+    '20070101T140000Z', '20070101T120000Z');
+print "$r\n";
+print_r($r->getEventDates('20070301T140000Z','20070501T140000Z'));
+$r = new RRuleParser(
+    'FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=-1',
+    '20070101T000100Z', '20070101T001100Z');
+//DTSTART;TZID=US-Eastern:19970105T083000
+print "$r\n";
+$r = new RRuleParser(
+    'FREQ=YEARLY;INTERVAL=2;BYMONTH=1;BYDAY=SU;BYHOUR=8,9;BYMINUTE=30',
+    '20070101T000100Z', '20070101T001100Z');
+print "$r\n";
+print_r ($r->getEventDates('20060101T000100Z', '20060101T001100Z'));
+$r = new RRuleParser(
+    'FREQ=DAILY;COUNT=10;INTERVAL=2',
+    '20070101T000100Z', '20070101T001100Z');
+print "$r\n";
+//foreach ($cal as $obj)
+//    var_dump($obj->getBaseComponent());
+//$bak = $cal['3ba46312e910765bf7059a53909d149b'];
+//print_r($bak);
+//print_r(new Icalendar(array('SUMMARY' => 'test')));
+//$cal['3ba46312e910765bf7059a53909d149b'] = new Icalendar(array('SUMMARY' => 'test'));
+//print_r($cal['3ba46312e910765bf7059a53909d149b']);
+//unset($cal['3ba46312e910765bf7059a53909d149b']);
+//var_dump($cal['3ba46312e910765bf7059a53909d149b']);
+//$cal['3ba46312e910765bf7059a53909d149b'] = $bak;
+//var_dump($cal['3ba46312e910765bf7059a53909d149b']);
+//$cal->update();
+//print_r($cal['3ba46312e910765bf7059a53909d149b']);*/
diff --git a/caldav/ical.class.php b/caldav/ical.class.php
new file mode 100644 (file)
index 0000000..644a629
--- /dev/null
@@ -0,0 +1,648 @@
+<?php
+/* $Id$ */
+
+$msg = <<<EOF
+BEGIN:VCALENDAR
+PRODID:-//davical.org//NONSGML AWL Calendar//EN
+VERSION:2.0
+CALSCALE:GREGORIAN
+BEGIN:VEVENT
+DTSTAMP:20070417T195323Z
+ORGANIZER:MAILTO:
+X-PILOTID:12451851
+X-PILOTSTAT:0
+CREATED:20050814T160951Z
+UID:libkcal-1123041440.757
+SEQUENCE:0
+LAST-MODIFIED:20070124T213448Z
+SUMMARY:møde carl christian\, hovedopgave
+CLASS:PUBLIC
+PRIORITY:3
+RRULE:FREQ=DAILY;UNTIL=20020528T153000Z;INTERVAL=14
+EXDATE;VALUE=DATE:20020402
+DTSTART:20020219T163000Z
+DTEND:20020219T180000Z
+TRANSP:OPAQUE
+BEGIN:VALARM
+DESCRIPTION:
+ACTION:DISPLAY
+TRIGGER;VALUE=DURATION:-P1D
+END:VALARM
+END:VEVENT
+END:VCALENDAR
+BEGIN:VCALENDAR
+PRODID:-//davical.org//NONSGML AWL Calendar//EN
+VERSION:2.0
+CALSCALE:GREGORIAN
+BEGIN:VEVENT
+DTSTAMP:20070417T195323Z
+ORGANIZER:MAILTO:
+X-PILOTID:12451851
+X-PILOTSTAT:0
+CREATED:20050814T160951Z
+UID:libkcal-1123041440.757
+SEQUENCE:0
+LAST-MODIFIED:20070124T213448Z
+SUMMARY:event number 2
+CLASS:PUBLIC
+PRIORITY:3
+RRULE:FREQ=DAILY;UNTIL=20020528T153000Z;INTERVAL=14
+EXDATE;VALUE=DATE:20020402
+DTSTART:20020219T163000Z
+DTEND:20020219T180000Z
+TRANSP:OPAQUE
+BEGIN:VALARM
+DESCRIPTION:
+ACTION:DISPLAY
+TRIGGER;VALUE=DURATION:-P1D
+END:VALARM
+END:VEVENT
+END:VCALENDAR
+EOF;
+
+require_once 'caldav-client.php';
+
+interface ICalendar {
+    function getProperty($name);
+    function setProperty($name, $value);
+    function getAllProperties();
+    function setAllProperties(array $assoc_array);
+    function isChanged();
+}
+
+class VEvent implements ICalendar {
+    private $attendee;
+    private $class;
+    private $created;
+    private $dtstamp;
+    private $description;
+    private $dtend;
+    private $dtstart;
+    private $duration;
+    private $exdate;
+    private $lastmodified;
+    private $location;
+    private $organizer;
+    private $priority;
+    private $rrule;
+    private $sequence;
+    private $status;
+    private $summary;
+    private $transp;
+    private $uid;
+    private $xprop = array();
+    // Properties must not contain hypens in their name
+    private static $translate = array(
+        'lastmodified' => 'last-modified',
+        'xprop' => 'x-prop'
+    );
+    private $changed;
+
+    function __construct($values = array()) {
+        if (! is_array($values))
+            throw new Exception("Attribute to constructor must be an array");
+        $this->setProperties($values);
+        $this->changed = FALSE;
+    }
+
+    private function setProperties(array $props) {
+        foreach ($props as $k => $v) {
+            switch (strtoupper($k)) {
+                case 'DTSTAMP':
+                    $this->dtstamp = $v;
+                    break;
+                case 'ORGANIZER':
+                    $this->organizer = $v;
+                    break;
+                case 'CREATED':
+                    $this->created = $v;
+                    break;
+                case 'UID':
+                    $this->uid = $v;
+                    break;
+                case 'SEQUENCE':
+                    $this->sequence = $v;
+                    break;
+                case 'LAST-MODIFIED':
+                    $this->lastmodified = $v;
+                    break;
+                case 'SUMMARY':
+                    $this->summary = $v;
+                    break;
+                case 'CLASS':
+                    $this->class = $v;
+                    break;
+                case 'PRIORITY':
+                    $this->priority = $v;
+                    break;
+                case 'RRULE':
+                    $this->rrule = $v;
+                    break;
+                case 'EXDATE':
+                    $this->exdate = $v;
+                    break;
+                case 'DTSTART':
+                    $this->dtstart = $v;
+                    break;
+                case 'DTEND':
+                    $this->dtend = $v;
+                    break;
+                case 'TRANSP':
+                    $this->transp = $v;
+                    break;
+                case 'ATTENDEE':
+                    $this->attendee = $v;
+                    break;
+                case 'DURATION':
+                    $this->duration = $v;
+                    break;
+                case 'LOCATION':
+                    $this->location = $v;
+                    break;
+                case 'STATUS':
+                    $this->status = $v;
+                    break;
+                case 'DESCRIPTION':
+                    $this->description = $v;
+                    break;
+                default:
+                    if (($k[0] == 'x' || $k[0] == 'X') && $k[1] == '-')
+                        $this->xprop[$k] = $v;
+                    else
+                        throw new Exception("[$k,$v]: Unknown attribute");
+            }
+        }
+    }
+
+    public function getProperty($name) {
+        $prop = strtolower($name);
+        if (($trans = array_search($prop, self::$translate)) !== false)
+            $prop = $trans;
+        if (! property_exists($this, $prop)) {
+            // Support for PHP 5 < 5.3
+            $obj = new ReflectionClass(get_class($this));
+            if (! $obj->hasProperty($prop))
+                throw new Exception("$name: Unknown property");
+        }
+        return $this->$prop;
+    }
+
+    public function setProperty($name, $value) {
+        $prop = strtolower($name);
+        if (($trans = array_search($prop, self::$translate)) !== false)
+            $prop = $trans;
+        if (! property_exists($this, $prop)) {
+            // Support for PHP 5 < 5.3
+            $obj = new ReflectionClass(get_class($this));
+            if (! $obj->hasProperty($prop))
+                throw new Exception("$name: Unknown property");
+        }
+        $this->$prop = $value;
+        $this->changed = TRUE;
+    }
+
+    function getAllProperties() {
+        $props = array();
+        $p = get_object_vars($this);
+        foreach ($p as $k => $v){
+            if ($k == 'changed')
+                continue;
+            if (array_key_exists($k, self::$translate))
+                $k = strtoupper(self::$translate[$k]);
+            else
+                $k = strtoupper($k);
+            $props[$k] = $v;
+        }
+        return $props;
+    }
+
+    function setAllProperties(array $assoc_array) {
+        $props = array();
+        foreach ($assoc_array as $k => $v){
+            $k = strtolower($k);
+            if (is_array($v)) {
+                foreach ($v as $k1 => $v1)
+                    $props[$k1] = $v1;
+            }
+            else
+                $props[$k] = $v;
+        }
+        $this->setProperties($props);
+        $this->changed = TRUE;
+    }
+
+    function isChanged() {
+        return $this->changed;
+    }
+
+}
+
+class VAlarm implements ICalendar {
+    private $action;
+    private $description;
+    private $duration;
+    private $repeat;
+    private $trigger;
+    // Properties must not contain hypens in their name
+    private static $translate = array();
+    private $changed;
+
+    function __construct($values = array()) {
+        if (! is_array($values))
+            throw new Exception("Attribute to constructor must be an array");
+        $this->setProperties($values);
+        $this->changed = FALSE;
+    }
+
+    private function setProperties(array $props) {
+        foreach ($props as $k => $v) {
+            switch (strtoupper($k)) {
+                case 'ACTION':
+                    $this->action = $v;
+                    break;
+                case 'DESCRIPTION':
+                    $this->description = $v;
+                    break;
+                case 'DURATION':
+                    $this->duration = $v;
+                    break;
+                case 'REPEAT':
+                    $this->repeat = $v;
+                    break;
+                case 'TRIGGER':
+                    $this->trigger = $v;
+                    break;
+                default:
+                    throw new Exception("$k: Unknown attribute");
+            }
+        }
+    }
+
+    public function getProperty($name) {
+        $prop = strtolower($name);
+        if (($trans = array_search($prop, self::$translate)) !== false)
+            $prop = $trans;
+        if (! property_exists($this, $prop)) {
+            // Support for PHP 5 < 5.3
+            $obj = new ReflectionClass(get_class($this));
+            if (! $obj->hasProperty($prop))
+                throw new Exception("$name: Unknown property");
+        }
+        return $this->$prop;
+    }
+
+    public function setProperty($name, $value) {
+        $prop = strtolower($name);
+        if (($trans = array_search($prop, self::$translate)) !== false)
+            $prop = $trans;
+        if (! property_exists($this, $prop)) {
+            // Support for PHP 5 < 5.3
+            $obj = new ReflectionClass(get_class($this));
+            if (! $obj->hasProperty($prop))
+                throw new Exception("$name: Unknown property");
+        }
+        $this->$prop = $value;
+        $this->changed = TRUE;
+    }
+
+    function getAllProperties() {
+        $props = array();
+        $p = get_object_vars($this);
+        foreach ($p as $k => $v){
+            if ($k == 'changed')
+                continue;
+            if (array_key_exists($k, self::$translate))
+                $k = strtoupper(self::$translate[$k]);
+            else
+                $k = strtoupper($k);
+            $props[$k] = $v;
+        }
+        return $props;
+    }
+
+    function setAllProperties(array $assoc_array) {
+        $props = array();
+        foreach ($assoc_array as $k => $v){
+            $k = strtolower($k);
+            if (is_array($v)) {
+                foreach ($v as $k1 => $v1)
+                    $props[$k1] = $v1;
+            }
+            else
+                $props[$k] = $v;
+        }
+        $this->setProperties($props);
+        $this->changed = TRUE;
+    }
+
+    function isChanged() {
+        return $this->changed;
+    }
+
+}
+
+class VTimezone implements ICalendar {
+
+    private $tzid;
+    private $xprop;
+    private $standard;
+    private $daylight;
+
+    // Properties must not contain hypens in their name
+    private static $translate = array();
+    private $changed;
+
+    function __construct($values = array()) {
+        if (! is_array($values))
+            throw new Exception("Attribute to constructor must be an array");
+        $this->setProperties($values);
+        $this->changed = FALSE;
+    }
+
+    private function setProperties(array $props) {
+        foreach ($props as $k => $v) {
+            switch (strtoupper($k)) {
+                case 'TZID':
+                    $this->tzid = $v;
+                    break;
+                default:
+                    if (($k[0] == 'x' || $k[0] == 'X') && $k[1] == '-')
+                        $this->xprop[$k] = $v;
+                    else
+                        throw new Exception("[$k,$v]: Unknown attribute");
+            }
+        }
+    }
+
+    function getProperty($name){}
+    function setProperty($name, $value) {
+        $prop = strtolower($name);
+        if (($trans = array_search($prop, self::$translate)) !== false)
+            $prop = $trans;
+        if (! property_exists($this, $prop)) {
+            // Support for PHP 5 < 5.3
+            $obj = new ReflectionClass(get_class($this));
+            if (! $obj->hasProperty($prop))
+                throw new Exception("$name: Unknown property");
+        }
+        $this->$prop = $value;
+        if (
+            $this->$prop == 'standard' && ! isset($this->standard) ||
+            $this->$prop == 'daylight' && ! isset($this->daylight)
+        ) {
+            // we are constructing the object
+        }
+        else
+            $this->changed = TRUE;
+    }
+    function getAllProperties(){}
+    function setAllProperties(array $assoc_array){}
+    function isChanged(){}
+}
+
+class VCalendar {
+    private $calscale;
+    private $prodid;
+    private $version;
+    private $objects;
+
+    function __construct($values = array()) {
+        if (! is_array($values))
+            throw new Exception("Attribute to constructor must be an array");
+        $this->objects = array();
+        //print_r($values);
+        foreach ($values as $k => $v) {
+            switch (strtoupper($k)) {
+                case 'CALSCALE':
+                    $this->calscale = $v;
+                    break;
+                case 'PRODID':
+                    $this->prodid = $v;
+                    break;
+                case 'VERSION':
+                    $this->version = $v;
+                    break;
+                default:
+                    throw new Exception("$k: Unknown attribute");
+            }
+        }
+    }
+
+    public function addObject(ICalendar $object) {
+        $this->objects[get_class($object)] = $object;
+    }
+
+    public function getObject($name) {
+        if (isset($this->objects[$name]))
+            return $this->objects[$name];
+        else
+            return null;
+    }
+
+    public function getObjects() {
+        return $this->objects;
+    }
+
+}
+
+class IcalParserIterator implements Iterator {
+    private $icalParser;
+    private $pos;
+
+    function __construct(IcalParser $icalParser) {
+        if (! $icalParser instanceof IcalParser)
+            throw new Exception(get_class($icalParser) . ': Can only handle instances of class IcalParser');
+        $this->icalParser = $icalParser;
+        $this->pos = 0;
+    }
+
+    public function current() {
+        return $this->icalParser->getIcal($this->pos);
+    }
+
+    public function key() {
+        return $this->pos;
+    }
+
+    public function next() {
+        $this->pos++;
+    }
+
+    public function rewind() {
+        $this->pos = 0;
+    }
+
+    public function valid() {
+        return $this->icalParser->peek($this->pos);
+    }
+
+}
+
+class IcalParser implements IteratorAggregate {
+//    private $elements = array('VCALENDAR', 'VEVENT', 'VALARM', 'VTIMEZONE');
+//    private $subelem = array('VTIMEZONE' => array('STANDARD', 'DAYLIGHT'));
+    private $items;
+
+    function __construct($message = NULL) {
+        $this->items = array();
+        if ($message)
+            $this->setMessage($message);
+    }
+
+    function setMessage($message) {
+        $this->split_message($message);
+    }
+
+    private function remove_value(array &$array, $key) {
+        $tmp = array();
+        $val;
+
+        foreach ($array as $k => $v) {
+            if ($k == $key)
+                $val = $v;
+            else
+                $tmp[$k] = $v;
+        }
+        $array = $tmp;
+        return $val;
+    }
+
+    private function newObject($name, $props = array()) {
+        try {
+            $obj = new ReflectionClass($name);
+        }
+        catch (ReflectionException $ex) {
+            print $ex->getMessage();
+            throw new Exception("$name: Class not found");
+        }
+        $obj = NULL;
+        return new $name($props);
+    }
+
+    private function split_message($message) {
+        $data = null;
+        $elem_data = array();
+        $data_list = array();
+        $elem_list = array();
+
+        if (empty($message))
+            return;
+        //print "$message\n";
+        $lines = explode("\n", $message);
+        foreach ($lines as $line) {
+            $a = explode(":", $line);
+            //print_r($a);
+            $item = strtoupper($a[0]);
+            if (count($a) > 2) {
+                $elem = '';
+                for ($i = 1; $i < count($a); $i++) {
+                    if ($elem != '')
+                        $elem .= ':';
+                    $elem .= $a[$i];
+                }
+                //print "elem: $elem\n";
+            }
+            else
+                $elem = $a[1];
+            switch ($item) {
+                case 'BEGIN':
+                    /*if (! in_array($elem, $this->elements)) {
+                        $found = TRUE;
+                        if (($sub = end($elem_list)) !== FALSE) {
+                            if (! array_key_exists($sub, $this->subelem))
+                                $found = FALSE;
+                        }
+                        else
+                            $found = FALSE;
+                        reset($elem_list);
+                        if (! $found)
+                            break;
+                    }*/
+                    if (count($data) > 0) {
+                        if (count($elem_list) > 0) {
+                            /*$name = array_pop($elem_list);
+                            if (array_key_exists($name, $this->subelem)) {
+                                print "BEGIN: $name\n";
+                                print_r($data);
+                            }*/
+                            $elem_data[array_pop($elem_list)] = $data;
+                        }
+                        else
+                            $elem_data[$elem] = $data;
+                    }
+                    array_push($elem_list, $elem);
+                    $data = array();
+                    break;
+                case 'END':
+                    if (count($elem_list) > 0) {
+                        /*$name = array_pop($elem_list);
+                        if (array_key_exists($name, $this->subelem)) {
+                            print "END: $name\n";
+                            print_r($data);
+                        }*/
+                        $elem_data[array_pop($elem_list)] = $data;
+                    }
+                    $data = array();
+                    if ($elem == 'VCALENDAR' && count($elem_data) > 1) {
+                        array_push($data_list, $elem_data);
+                        $elem_data = array();
+                        $elem_list = array();
+                    }
+                    break;
+                default:
+                    if (! is_array($data))
+                        throw new Exception("Message is not valid [missing 'BEGIN']");
+                    if (($pos = strpos($item, ';')) !== false) {
+                        $head = substr($item, 0, $pos);
+                        $elem = substr($item, $pos + 1) . ';' . $elem;
+                        $item = $head;
+                        //print "Ny elem: $item:$elem\n";
+                    }
+                    $data[$item] = $elem;
+            }
+        }
+        //print_r($data_list);
+        foreach ($data_list as $item) {
+            $c = $this->remove_value($item, 'VCALENDAR');
+            $calendar = new VCalendar($c);
+            foreach ($item as $k => $v) {
+                if ($k == 'DAYLIGHT' || $k == 'STANDARD') {
+                    $object = $calendar->getObject('VTimezone');
+                    if (! is_object($object)) {
+                        $object = $this->newObject('VTimezone');
+                        $calendar->addObject($object);
+                    }
+                    $object->setProperty($k, $v);
+                }
+                else {
+                    if (($object = $calendar->getObject($k)) == NULL) {
+                        $object = $this->newObject($k, $v);
+                        $calendar->addObject($object);
+                    }
+                    else {
+                        $object->setAllProperties($v);
+                    }
+                }
+            }
+            array_push($this->items, $calendar);
+        }
+    }
+
+    public function getIterator() {
+        return new IcalParserIterator($this);
+    }
+
+    public function getIcal($id) {
+        if ($this->peek($id))
+            return $this->items[$id];
+        else
+            throw new Exception("Index out of bounds");
+    }
+
+    public function peek($id) {
+        return isset($this->items[$id]);
+    }
+
+}
+
+?>
diff --git a/caldav/icomponent.class.php b/caldav/icomponent.class.php
new file mode 100644 (file)
index 0000000..ff7eefb
--- /dev/null
@@ -0,0 +1,147 @@
+<?php
+/* $Id$ */
+
+    require_once 'caldavresource.class.php';
+
+    abstract class IComponent {
+
+        public  $type;
+        private $component;
+        private $url;
+        private $etag;
+        private $dirty;
+
+        function __construct($etag, $url, VTYPE $type,
+                             iCalendar $component, $new) {
+            $this->etag = $etag;
+            $this->url = $url;
+            $this->component = $component;
+            $this->type = $type;
+            $this->dirty = $new;
+        }
+
+        public function isDirty() {
+            return $this->dirty;
+        }
+
+        public function setDirty() {
+            $this->dirty = TRUE;
+        }
+        
+        public function getResource() {
+            return $this->component;
+        }
+
+        public function setResource(iCalendar $component) {
+            $this->component = $component;
+            $this->dirty = TRUE;
+        }
+
+        public function getBaseComponent() {
+            return $this->getComponent($this->type);
+        }
+
+        public function getUrl() {
+            return $this->url;
+        }
+
+        public function getEtag() {
+            return $this->etag;
+        }
+
+        public function setEtag($etag) {
+            $this->etag = $etag;
+        }
+        
+        public function getComponent($type) {
+            $ref = $this->component;
+            //print_r($ref);
+
+            if ($this->component === NULL)
+                $ical = NULL;
+            else if ($type instanceof VTYPE && $type->ordinal() == VTYPE::VTIMEZONE) {
+                $ical = $ref->component->GetComponents('VTIMEZONE');
+            }
+            else {
+                //$theType = sprintf("%s", $this->type);
+                //print "self: $theType\n";
+                $component = $ref->component->GetComponents($this->type);
+                //print_r($component);
+                if (! $type instanceof VTYPE)
+                    $type = new VTYPE($type);
+                //$theType = sprintf("%s", $type);
+                //print "instance: $theType\n";
+                if (count($component) > 0)
+                    $ical = $component[0];
+                if ($type->ordinal() != $this->type->ordinal() && $ical) {
+                    $ical = $ical->GetComponents($type);
+                }
+            }
+            return $ical;
+        }
+
+        public function isUTCTime() {
+            $event = $this->getBaseComponent();
+            $start = $event->GetPValue('DTSTART');
+            $end = $event->GetPValue('DTEND');
+
+            if (! ($start && $end))
+                throw new Exception("Not a valid iCal component");
+            return ($start[strlen($start) - 1] == 'Z' ||
+                    $nd[strlen($end) - 1] == 'Z');
+        }
+
+        public function getDetails() {
+            $event = $this->getBaseComponent();
+            $start = strtotime($event->GetPValue('DTSTART'));
+            $start = date("Y-m-d H:m", $start);
+            $end = strtotime($event->GetPValue('DTEND'));
+            $end = date("Y-m-d H:m", $end);
+            $title = $event->GetPValue('SUMMARY');
+            
+            return "$start-$end: $title";
+        }
+        
+        public function getTZID() {
+            $res = 'UTC';
+
+            if (! $this->isUTCTime()) {
+                $timezone = $this->getTimeZone();
+                if ($timezone) {
+                    $res = $timezone->GetPValue('TZID');
+                }
+                // timezone not given assume TZID = server's timezone
+                // servers default timezone is UTC
+            }
+            return $res;
+        }
+
+        function getTimeZone() {
+            $timezone = $this->getComponent(VTYPE::VTIMEZONE);
+            if ($timezone)
+                $timezone = $timezone[0];
+            return $timezone;
+        }
+
+        public function __toString() {
+            return $this->type->__toString();
+        }
+
+        /**
+         * The following functions should be overloaded in
+         * the child classes if the have specific functionality
+         */
+
+        function isActive($start, $end) {
+            return FALSE;
+        }
+
+        function getActiveDates() {
+            return array();
+        }
+
+        function getAlarm() {
+            return NULL;
+        }
+
+    }
diff --git a/caldav/rfc2445.html b/caldav/rfc2445.html
new file mode 100644 (file)
index 0000000..26a54b7
--- /dev/null
@@ -0,0 +1,8411 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xml:lang="en" xmlns="http://www.w3.org/1999/xhtml" lang="en"><head>
+
+
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+    <meta name="robots" content="index,follow">
+    <meta name="creator" content="rfcmarkup version 1.74">
+    <link rel="icon" href="http://tools.ietf.org/images/rfc.png" type="image/png">
+    <link rel="shortcut icon" href="http://tools.ietf.org/images/rfc.png" type="image/png">
+    <title>RFC 2445 - Internet Calendaring and Scheduling Core Object Specification (iCalendar)</title>
+    
+    <style type="text/css">
+       body {
+           margin: 0px 8px;
+            font-size: 1em;
+       }
+        h1, h2, h3, h4, h5, h6, .h1, .h2, .h3, .h4, .h5, .h6 {
+           font-weight: bold;
+            line-height: 0pt;
+            display: inline;
+            white-space: pre;
+            font-family: monospace;
+            font-size: 1em;
+           font-weight: bold;
+        }
+        pre {
+            font-size: 1em;
+            margin-top: 0px;
+            margin-bottom: 0px;
+        }
+       .pre {
+           white-space: pre;
+           font-family: monospace;
+       }
+       .header{
+           font-weight: bold;
+       }
+        .newpage {
+            page-break-before: always;
+        }
+        .invisible {
+            text-decoration: none;
+            color: white;
+        }
+        @media print {
+            body {
+                font-size: 10.5pt;
+            }
+            h1, h2, h3, h4, h5, h6 {
+                font-size: 10.5pt;
+            }
+        
+            a:link, a:visited {
+                color: inherit;
+                text-decoration: none;
+            }
+            .noprint {
+                display: none;
+            }
+        }
+       @media screen {
+           .grey, .grey a:link, .grey a:visited {
+               color: #777;
+           }
+            .docinfo {
+                background-color: #EEE;
+            }
+            .top {
+                border-top: 7px solid #EEE;
+            }
+            .bgwhite  { background-color: white; }
+            .bgred    { background-color: #F44; }
+            .bggrey   { background-color: #666; }
+            .bgbrown  { background-color: #840; }            
+            .bgorange { background-color: #FA0; }
+            .bgyellow { background-color: #EE0; }
+            .bgmagenta{ background-color: #F4F; }
+            .bgblue   { background-color: #66F; }
+            .bgcyan   { background-color: #4DD; }
+            .bggreen  { background-color: #4F4; }
+
+            .legend   { font-size: 90%; }
+            .cplate   { font-size: 70%; border: solid grey 1px; }
+       }
+    </style>
+
+    <script type="text/javascript"><!--
+    function addHeaderTags() {
+       var spans = document.getElementsByTagName("span");
+       for (var i=0; i < spans.length; i++) {
+           var elem = spans[i];
+           if (elem) {
+               var level = elem.getAttribute("class");
+                if (level == "h1" || level == "h2" || level == "h3" || level == "h4" || level == "h5" || level == "h6") {
+                    elem.innerHTML = "<"+level+">"+elem.innerHTML+"</"+level+">";              
+                }
+           }
+       }
+    }
+    var legend_html = "Colour legend:<br />      <table>         <tr><td>Unknown:</td>          <td><span class='cplate bgwhite'>&nbsp;&nbsp;&nbsp;&nbsp;</span></td></tr>         <tr><td>Draft:</td>            <td><span class='cplate bgred'>&nbsp;&nbsp;&nbsp;&nbsp;</span></td></tr>         <tr><td>Informational:</td>    <td><span class='cplate bgorange'>&nbsp;&nbsp;&nbsp;&nbsp;</span></td></tr>         <tr><td>Experimental:</td>     <td><span class='cplate bgyellow'>&nbsp;&nbsp;&nbsp;&nbsp;</span></td></tr>         <tr><td>Best Common Practice:</td><td><span class='cplate bgmagenta'>&nbsp;&nbsp;&nbsp;&nbsp;</span></td></tr>         <tr><td>Proposed Standard:</td><td><span class='cplate bgblue'>&nbsp;&nbsp;&nbsp;&nbsp;</span></td></tr>         <tr><td>Draft Standard:</td>   <td><span class='cplate bgcyan'>&nbsp;&nbsp;&nbsp;&nbsp;</span></td></tr>         <tr><td>Standard:</td>         <td><span class='cplate bggreen'>&nbsp;&nbsp;&nbsp;&nbsp;</span></td></tr>         <tr><td>Historic:</td>         <td><span class='cplate bggrey'>&nbsp;&nbsp;&nbsp;&nbsp;</span></td></tr>         <tr><td>Obsolete:</td>         <td><span class='cplate bgbrown'>&nbsp;&nbsp;&nbsp;&nbsp;</span></td></tr>     </table>";
+    function showElem(id) {
+        var elem = document.getElementById(id);
+        elem.innerHTML = eval(id+"_html");
+        elem.style.visibility='visible';
+    }
+    function hideElem(id) {
+        var elem = document.getElementById(id);
+        elem.style.visibility='hidden';        
+        elem.innerHTML = "";
+    }
+    // -->
+    </script>
+</head><body onload="addHeaderTags()">
+   <div style="height: 13px;">
+      <div onmouseover="this.style.cursor='pointer';" onclick="showElem('legend');" onmouseout="hideElem('legend')" style="height: 6px; position: absolute; cursor: pointer;" class="pre noprint docinfo bgblue" title="Click for colour legend.">                                                                        </div>
+      <div id="legend" class="docinfo noprint pre legend" style="border: 1px solid rgb(51, 68, 85); padding: 4px 9px 5px 7px; position: absolute; top: 4px; left: 4ex; visibility: hidden; background-color: white;" onmouseover="showElem('legend');" onmouseout="hideElem('legend');"></div>
+   </div>
+<span class="pre noprint docinfo top">[<a style="" href="http://tools.ietf.org/html/" title="Document search and retrieval page">RFCs/IDs</a>] [<a href="http://tools.ietf.org/rfc/rfc2445.txt" title="Plaintext version of this document">Plain</a>] [From <a href="http://tools.ietf.org/html/draft-ietf-calsch-ical">draft-ietf-calsch-ical</a>]                        </span><br>
+<span class="pre noprint docinfo">                                                                        </span><br>
+<span class="pre noprint docinfo">                                                       PROPOSED STANDARD</span><br>
+<span class="pre noprint docinfo">                                                                  <a style="" href="http://www.rfc-editor.org/errata_search.php?rfc=2445">Errata</a></span><br>
+<pre>Network Working Group                                         F. Dawson
+Request for Comments: 2445                                        Lotus
+Category: Standards Track                                  D. Stenerson
+                                                              Microsoft
+                                                          November 1998
+
+
+     <span class="h1"><h1>Internet Calendaring and Scheduling Core Object Specification</h1></span>
+                              <span class="h1"><h1>(iCalendar)</h1></span>
+
+Status of this Memo
+
+   This document specifies an Internet standards track protocol for the
+   Internet community, and requests discussion and suggestions for
+   improvements.  Please refer to the current edition of the "Internet
+   Official Protocol Standards" (STD 1) for the standardization state
+   and status of this protocol.  Distribution of this memo is unlimited.
+
+Copyright Notice
+
+   Copyright (C) The Internet Society (1998).  All Rights Reserved.
+
+Abstract
+
+   There is a clear need to provide and deploy interoperable calendaring
+   and scheduling services for the Internet. Current group scheduling
+   and Personal Information Management (PIM) products are being extended
+   for use across the Internet, today, in proprietary ways. This memo
+   has been defined to provide the definition of a common format for
+   openly exchanging calendaring and scheduling information across the
+   Internet.
+
+   This memo is formatted as a registration for a MIME media type per
+   [<a href="http://tools.ietf.org/html/rfc2048" title="&quot;Multipurpose Internet Mail Extensions (MIME) - Part Four: Registration Procedures&quot;">RFC 2048</a>]. However, the format in this memo is equally applicable
+   for use outside of a MIME message content type.
+
+   The proposed media type value is 'text/calendar'. This string would
+   label a media type containing calendaring and scheduling information
+   encoded as text characters formatted in a manner outlined below.
+
+   This MIME media type provides a standard content type for capturing
+   calendar event, to-do and journal entry information. It also can be
+   used to convey free/busy time information. The content type is
+   suitable as a MIME message entity that can be transferred over MIME
+   based email systems, using HTTP or some other Internet transport. In
+
+
+
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                     [Page 1]</span>
+</pre><pre class="newpage"><a name="page-2" id="page-2" href="#page-2" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+   addition, the content type is useful as an object for interactions
+   between desktop applications using the operating system clipboard,
+   drag/drop or file systems capabilities.
+
+   This memo is based on the earlier work of the vCalendar specification
+   for the exchange of personal calendaring and scheduling information.
+   In order to avoid confusion with this referenced work, this memo is
+   to be known as the iCalendar specification.
+
+   This memo defines the format for specifying iCalendar object methods.
+   An iCalendar object method is a set of usage constraints for the
+   iCalendar object. For example, these methods might define scheduling
+   messages that request an event be scheduled, reply to an event
+   request, send a cancellation notice for an event, modify or replace
+   the definition of an event, provide a counter proposal for an
+   original event request, delegate an event request to another
+   individual, request free or busy time, reply to a free or busy time
+   request, or provide similar scheduling messages for a to-do or
+   journal entry calendar component. The iCalendar Transport-indendent
+   Interoperability Protocol (iTIP) defined in [<a href="#ref-ITIP" title="&quot;iCalendar Transport-Independent Interoperability Protocol (iTIP) : Scheduling Events, Busy Time, To-dos and Journal Entries&quot;">ITIP</a>] is one such
+   scheduling protocol.
+
+Table of Contents
+
+   <a href="#section-1">1</a> Introduction.....................................................<a href="#page-5">5</a>
+   <a href="#section-2">2</a> Basic Grammar and Conventions....................................<a href="#page-6">6</a>
+    <a href="#section-2.1">2.1</a> Formatting Conventions .......................................<a href="#page-7">7</a>
+    <a href="#section-2.2">2.2</a> Related Memos ................................................<a href="#page-8">8</a>
+    <a href="#section-2.3">2.3</a> International Considerations .................................<a href="#page-8">8</a>
+   <a href="#section-3">3</a> Registration Information.........................................<a href="#page-8">8</a>
+    <a href="#section-3.1">3.1</a> Content Type .................................................<a href="#page-8">8</a>
+    <a href="#section-3.2">3.2</a> Parameters ...................................................<a href="#page-9">9</a>
+    <a href="#section-3.3">3.3</a> Content Header Fields .......................................<a href="#page-10">10</a>
+    <a href="#section-3.4">3.4</a> Encoding Considerations .....................................<a href="#page-10">10</a>
+    <a href="#section-3.5">3.5</a> Security Considerations .....................................<a href="#page-10">10</a>
+    <a href="#section-3.6">3.6</a> Interoperability Considerations .............................<a href="#page-11">11</a>
+    <a href="#section-3.7">3.7</a> Applications Which Use This Media Type ......................<a href="#page-11">11</a>
+    <a href="#section-3.8">3.8</a> Additional Information ......................................<a href="#page-11">11</a>
+    <a href="#section-3.9">3.9</a> Magic Numbers ...............................................<a href="#page-11">11</a>
+    <a href="#section-3.10">3.10</a> File Extensions ............................................<a href="#page-11">11</a>
+    <a href="#section-3.11">3.11</a> Contact for Further Information: ...........................<a href="#page-12">12</a>
+    <a href="#section-3.12">3.12</a> Intended Usage .............................................<a href="#page-12">12</a>
+    <a href="#section-3.13">3.13</a> Authors/Change Controllers .................................<a href="#page-12">12</a>
+   <a href="#section-4">4</a> iCalendar Object Specification..................................<a href="#page-13">13</a>
+    <a href="#section-4.1">4.1</a> Content Lines ...............................................<a href="#page-13">13</a>
+     <a href="#section-4.1.1">4.1.1</a> List and Field Separators ................................<a href="#page-16">16</a>
+     <a href="#section-4.1.2">4.1.2</a> Multiple Values ..........................................<a href="#page-16">16</a>
+     <a href="#section-4.1.3">4.1.3</a> Binary Content ...........................................<a href="#page-16">16</a>
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                     [Page 2]</span>
+</pre><pre class="newpage"><a name="page-3" id="page-3" href="#page-3" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+     <a href="#section-4.1.4">4.1.4</a> Character Set ............................................<a href="#page-17">17</a>
+    <a href="#section-4.2">4.2</a> Property Parameters .........................................<a href="#page-17">17</a>
+     <a href="#section-4.2.1">4.2.1</a> Alternate Text Representation ............................<a href="#page-18">18</a>
+     <a href="#section-4.2.2">4.2.2</a> Common Name ..............................................<a href="#page-19">19</a>
+     <a href="#section-4.2.3">4.2.3</a> Calendar User Type .......................................<a href="#page-20">20</a>
+     <a href="#section-4.2.4">4.2.4</a> Delegators ...............................................<a href="#page-20">20</a>
+     <a href="#section-4.2.5">4.2.5</a> Delegatees ...............................................<a href="#page-21">21</a>
+     <a href="#section-4.2.6">4.2.6</a> Directory Entry Reference ................................<a href="#page-21">21</a>
+     <a href="#section-4.2.7">4.2.7</a> Inline Encoding ..........................................<a href="#page-22">22</a>
+     <a href="#section-4.2.8">4.2.8</a> Format Type ..............................................<a href="#page-23">23</a>
+     <a href="#section-4.2.9">4.2.9</a> Free/Busy Time Type ......................................<a href="#page-23">23</a>
+     <a href="#section-4.2.10">4.2.10</a> Language ................................................<a href="#page-24">24</a>
+     <a href="#section-4.2.11">4.2.11</a> Group or List Membership ................................<a href="#page-25">25</a>
+     <a href="#section-4.2.12">4.2.12</a> Participation Status ....................................<a href="#page-25">25</a>
+     <a href="#section-4.2.13">4.2.13</a> Recurrence Identifier Range .............................<a href="#page-27">27</a>
+     <a href="#section-4.2.14">4.2.14</a> Alarm Trigger Relationship ..............................<a href="#page-27">27</a>
+     <a href="#section-4.2.15">4.2.15</a> Relationship Type .......................................<a href="#page-28">28</a>
+     <a href="#section-4.2.16">4.2.16</a> Participation Role ......................................<a href="#page-29">29</a>
+     <a href="#section-4.2.17">4.2.17</a> RSVP Expectation ........................................<a href="#page-29">29</a>
+     <a href="#section-4.2.18">4.2.18</a> Sent By .................................................<a href="#page-30">30</a>
+     <a href="#section-4.2.19">4.2.19</a> Time Zone Identifier ....................................<a href="#page-30">30</a>
+     <a href="#section-4.2.20">4.2.20</a> Value Data Types ........................................<a href="#page-32">32</a>
+    <a href="#section-4.3">4.3</a> Property Value Data Types ...................................<a href="#page-32">32</a>
+     <a href="#section-4.3.1">4.3.1</a> Binary ...................................................<a href="#page-33">33</a>
+     <a href="#section-4.3.2">4.3.2</a> Boolean ..................................................<a href="#page-33">33</a>
+     <a href="#section-4.3.3">4.3.3</a> Calendar User Address ....................................<a href="#page-34">34</a>
+     <a href="#section-4.3.4">4.3.4</a> Date .....................................................<a href="#page-34">34</a>
+     <a href="#section-4.3.5">4.3.5</a> Date-Time ................................................<a href="#page-35">35</a>
+     <a href="#section-4.3.6">4.3.6</a> Duration .................................................<a href="#page-37">37</a>
+     <a href="#section-4.3.7">4.3.7</a> Float ....................................................<a href="#page-38">38</a>
+     <a href="#section-4.3.8">4.3.8</a> Integer ..................................................<a href="#page-38">38</a>
+     <a href="#section-4.3.9">4.3.9</a> Period of Time ...........................................<a href="#page-39">39</a>
+     <a href="#section-4.3.10">4.3.10</a> Recurrence Rule .........................................<a href="#page-40">40</a>
+     <a href="#section-4.3.11">4.3.11</a> Text ....................................................<a href="#page-45">45</a>
+     <a href="#section-4.3.12">4.3.12</a> Time ....................................................<a href="#page-47">47</a>
+     <a href="#section-4.3.13">4.3.13</a> URI .....................................................<a href="#page-49">49</a>
+     <a href="#section-4.3.14">4.3.14</a> UTC Offset ..............................................<a href="#page-49">49</a>
+    <a href="#section-4.4">4.4</a> iCalendar Object ............................................<a href="#page-50">50</a>
+    <a href="#section-4.5">4.5</a> Property ....................................................<a href="#page-51">51</a>
+    <a href="#section-4.6">4.6</a> Calendar Components .........................................<a href="#page-51">51</a>
+     <a href="#section-4.6.1">4.6.1</a> Event Component ..........................................<a href="#page-52">52</a>
+     <a href="#section-4.6.2">4.6.2</a> To-do Component ..........................................<a href="#page-55">55</a>
+     <a href="#section-4.6.3">4.6.3</a> Journal Component ........................................<a href="#page-56">56</a>
+     <a href="#section-4.6.4">4.6.4</a> Free/Busy Component ......................................<a href="#page-58">58</a>
+     <a href="#section-4.6.5">4.6.5</a> Time Zone Component ......................................<a href="#page-60">60</a>
+     <a href="#section-4.6.6">4.6.6</a> Alarm Component ..........................................<a href="#page-67">67</a>
+    <a href="#section-4.7">4.7</a> Calendar Properties .........................................<a href="#page-73">73</a>
+     <a href="#section-4.7.1">4.7.1</a> Calendar Scale ...........................................<a href="#page-73">73</a>
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                     [Page 3]</span>
+</pre><pre class="newpage"><a name="page-4" id="page-4" href="#page-4" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+     <a href="#section-4.7.2">4.7.2</a> Method ...................................................<a href="#page-74">74</a>
+     <a href="#section-4.7.3">4.7.3</a> Product Identifier .......................................<a href="#page-75">75</a>
+     <a href="#section-4.7.4">4.7.4</a> Version ..................................................<a href="#page-76">76</a>
+    <a href="#section-4.8">4.8</a> Component Properties ........................................<a href="#page-77">77</a>
+     <a href="#section-4.8.1">4.8.1</a> Descriptive Component Properties .........................<a href="#page-77">77</a>
+       <a href="#section-4.8.1.1">4.8.1.1</a> Attachment ...........................................<a href="#page-77">77</a>
+       <a href="#section-4.8.1.2">4.8.1.2</a> Categories ...........................................<a href="#page-78">78</a>
+       <a href="#section-4.8.1.3">4.8.1.3</a> Classification .......................................<a href="#page-79">79</a>
+       <a href="#section-4.8.1.4">4.8.1.4</a> Comment ..............................................<a href="#page-80">80</a>
+       <a href="#section-4.8.1.5">4.8.1.5</a> Description ..........................................<a href="#page-81">81</a>
+       <a href="#section-4.8.1.6">4.8.1.6</a> Geographic Position ..................................<a href="#page-82">82</a>
+       <a href="#section-4.8.1.7">4.8.1.7</a> Location .............................................<a href="#page-84">84</a>
+       <a href="#section-4.8.1.8">4.8.1.8</a> Percent Complete .....................................<a href="#page-85">85</a>
+       <a href="#section-4.8.1.9">4.8.1.9</a> Priority .............................................<a href="#page-85">85</a>
+       <a href="#section-4.8.1.10">4.8.1.10</a> Resources ...........................................<a href="#page-87">87</a>
+       <a href="#section-4.8.1.11">4.8.1.11</a> Status ..............................................<a href="#page-88">88</a>
+       <a href="#section-4.8.1.12">4.8.1.12</a> Summary .............................................<a href="#page-89">89</a>
+     <a href="#section-4.8.2">4.8.2</a> Date and Time Component Properties .......................<a href="#page-90">90</a>
+       <a href="#section-4.8.2.1">4.8.2.1</a> Date/Time Completed ..................................<a href="#page-90">90</a>
+       <a href="#section-4.8.2.2">4.8.2.2</a> Date/Time End ........................................<a href="#page-91">91</a>
+       <a href="#section-4.8.2.3">4.8.2.3</a> Date/Time Due ........................................<a href="#page-92">92</a>
+       <a href="#section-4.8.2.4">4.8.2.4</a> Date/Time Start ......................................<a href="#page-93">93</a>
+       <a href="#section-4.8.2.5">4.8.2.5</a> Duration .............................................<a href="#page-94">94</a>
+       <a href="#section-4.8.2.6">4.8.2.6</a> Free/Busy Time .......................................<a href="#page-95">95</a>
+       <a href="#section-4.8.2.7">4.8.2.7</a> Time Transparency ....................................<a href="#page-96">96</a>
+     <a href="#section-4.8.3">4.8.3</a> Time Zone Component Properties ...........................<a href="#page-97">97</a>
+       <a href="#section-4.8.3.1">4.8.3.1</a> Time Zone Identifier .................................<a href="#page-97">97</a>
+       <a href="#section-4.8.3.2">4.8.3.2</a> Time Zone Name .......................................<a href="#page-98">98</a>
+       <a href="#section-4.8.3.3">4.8.3.3</a> Time Zone Offset From ................................<a href="#page-99">99</a>
+       <a href="#section-4.8.3.4">4.8.3.4</a> Time Zone Offset To .................................<a href="#page-100">100</a>
+       <a href="#section-4.8.3.5">4.8.3.5</a> Time Zone URL .......................................<a href="#page-101">101</a>
+     <a href="#section-4.8.4">4.8.4</a> Relationship Component Properties .......................<a href="#page-102">102</a>
+       <a href="#section-4.8.4.1">4.8.4.1</a> Attendee ............................................<a href="#page-102">102</a>
+       <a href="#section-4.8.4.2">4.8.4.2</a> Contact .............................................<a href="#page-104">104</a>
+       <a href="#section-4.8.4.3">4.8.4.3</a> Organizer ...........................................<a href="#page-106">106</a>
+       <a href="#section-4.8.4.4">4.8.4.4</a> Recurrence ID .......................................<a href="#page-107">107</a>
+       <a href="#section-4.8.4.5">4.8.4.5</a> Related To ..........................................<a href="#page-109">109</a>
+       <a href="#section-4.8.4.6">4.8.4.6</a> Uniform Resource Locator ............................<a href="#page-110">110</a>
+       <a href="#section-4.8.4.7">4.8.4.7</a> Unique Identifier ...................................<a href="#page-111">111</a>
+     <a href="#section-4.8.5">4.8.5</a> Recurrence Component Properties .........................<a href="#page-112">112</a>
+       <a href="#section-4.8.5.1">4.8.5.1</a> Exception Date/Times ................................<a href="#page-112">112</a>
+       <a href="#section-4.8.5.2">4.8.5.2</a> Exception Rule ......................................<a href="#page-114">114</a>
+       <a href="#section-4.8.5.3">4.8.5.3</a> Recurrence Date/Times ...............................<a href="#page-115">115</a>
+       <a href="#section-4.8.5.4">4.8.5.4</a> Recurrence Rule .....................................<a href="#page-117">117</a>
+     <a href="#section-4.8.6">4.8.6</a> Alarm Component Properties ..............................<a href="#page-126">126</a>
+       <a href="#section-4.8.6.1">4.8.6.1</a> Action ..............................................<a href="#page-126">126</a>
+       <a href="#section-4.8.6.2">4.8.6.2</a> Repeat Count ........................................<a href="#page-126">126</a>
+       <a href="#section-4.8.6.3">4.8.6.3</a> Trigger .............................................<a href="#page-127">127</a>
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                     [Page 4]</span>
+</pre><pre class="newpage"><a name="page-5" id="page-5" href="#page-5" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+     <a href="#section-4.8.7">4.8.7</a> Change Management Component Properties ..................<a href="#page-129">129</a>
+       <a href="#section-4.8.7.1">4.8.7.1</a> Date/Time Created ...................................<a href="#page-129">129</a>
+       <a href="#section-4.8.7.2">4.8.7.2</a> Date/Time Stamp .....................................<a href="#page-130">130</a>
+       <a href="#section-4.8.7.3">4.8.7.3</a> Last Modified .......................................<a href="#page-131">131</a>
+       <a href="#section-4.8.7.4">4.8.7.4</a> Sequence Number .....................................<a href="#page-131">131</a>
+     <a href="#section-4.8.8">4.8.8</a> Miscellaneous Component Properties ......................<a href="#page-133">133</a>
+       <a href="#section-4.8.8.1">4.8.8.1</a> Non-standard Properties .............................<a href="#page-133">133</a>
+       <a href="#section-4.8.8.2">4.8.8.2</a> Request Status ......................................<a href="#page-134">134</a>
+   <a href="#section-5">5</a> iCalendar Object Examples......................................<a href="#page-136">136</a>
+   <a href="#section-6">6</a> Recommended Practices..........................................<a href="#page-140">140</a>
+   <a href="#section-7">7</a> Registration of Content Type Elements..........................<a href="#page-141">141</a>
+    <a href="#section-7.1">7.1</a> Registration of New and Modified iCalendar Object Methods ..<a href="#page-141">141</a>
+    <a href="#section-7.2">7.2</a> Registration of New Properties .............................<a href="#page-141">141</a>
+     <a href="#section-7.2.1">7.2.1</a> Define the property .....................................<a href="#page-142">142</a>
+     <a href="#section-7.2.2">7.2.2</a> Post the Property definition ............................<a href="#page-143">143</a>
+     <a href="#section-7.2.3">7.2.3</a> Allow a comment period ..................................<a href="#page-143">143</a>
+     <a href="#section-7.2.4">7.2.4</a> Submit the property for approval ........................<a href="#page-143">143</a>
+    <a href="#section-7.3">7.3</a> Property Change Control ....................................<a href="#page-143">143</a>
+   <a href="#section-8">8</a> References.....................................................<a href="#page-144">144</a>
+   <a href="#section-9">9</a> Acknowledgments................................................<a href="#page-145">145</a>
+   <a href="#section-10">10</a> Authors' and Chairs' Addresses................................<a href="#page-146">146</a>
+   <a href="#section-11">11</a> Full Copyright Statement......................................<a href="#page-148">148</a>
+
+<span class="h2"><h2><a name="section-1">1</a> Introduction</h2></span>
+
+   The use of calendaring and scheduling has grown considerably in the
+   last decade. Enterprise and inter-enterprise business has become
+   dependent on rapid scheduling of events and actions using this
+   information technology. However, the longer term growth of
+   calendaring and scheduling, is currently limited by the lack of
+   Internet standards for the message content types that are central to
+   these knowledgeware applications. This memo is intended to progress
+   the level of interoperability possible between dissimilar calendaring
+   and scheduling applications. This memo defines a MIME content type
+   for exchanging electronic calendaring and scheduling information. The
+   Internet Calendaring and Scheduling Core Object Specification, or
+   iCalendar, allows for the capture and exchange of information
+   normally stored within a calendaring and scheduling application; such
+   as a Personal Information Manager (PIM) or a Group Scheduling
+   product.
+
+   The iCalendar format is suitable as an exchange format between
+   applications or systems. The format is defined in terms of a MIME
+   content type. This will enable the object to be exchanged using
+   several transports, including but not limited to SMTP, HTTP, a file
+   system, desktop interactive protocols such as the use of a memory-
+   based clipboard or drag/drop interactions, point-to-point
+   asynchronous communication, wired-network transport, or some form of
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                     [Page 5]</span>
+</pre><pre class="newpage"><a name="page-6" id="page-6" href="#page-6" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+   unwired transport such as infrared might also be used.
+
+   The memo also provides for the definition of iCalendar object methods
+   that will map this content type to a set of messages for supporting
+   calendaring and scheduling operations such as requesting, replying
+   to, modifying, and canceling meetings or appointments, to-dos and
+   journal entries. The iCalendar object methods can be used to define
+   other calendaring and scheduling operations such a requesting for and
+   replying with free/busy time data. Such a scheduling protocol is
+   defined in the iCalendar Transport-independent Interoperability
+   Protocol (iTIP) defined in [<a href="#ref-ITIP" title="&quot;iCalendar Transport-Independent Interoperability Protocol (iTIP) : Scheduling Events, Busy Time, To-dos and Journal Entries&quot;">ITIP</a>].
+
+   The memo also includes a formal grammar for the content type based on
+   the Internet ABNF defined in [<a href="http://tools.ietf.org/html/rfc2234" title="&quot;Augmented BNF for Syntax Specifications: ABNF&quot;">RFC 2234</a>]. This ABNF is required for
+   the implementation of parsers and to serve as the definitive
+   reference when ambiguities or questions arise in interpreting the
+   descriptive prose definition of the memo.
+
+<span class="h2"><h2><a name="section-2">2</a> Basic Grammar and Conventions</h2></span>
+
+   The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+   "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY" and
+   "OPTIONAL" in this document are to be interoperated as described in
+   [<a href="http://tools.ietf.org/html/rfc2119" title="&quot;Key words for use in RFCs to Indicate Requirement Levels&quot;">RFC 2119</a>].
+
+   This memo makes use of both a descriptive prose and a more formal
+   notation for defining the calendaring and scheduling format.
+
+   The notation used in this memo is the ABNF notation of [<a href="http://tools.ietf.org/html/rfc2234" title="&quot;Augmented BNF for Syntax Specifications: ABNF&quot;">RFC 2234</a>].
+   Readers intending on implementing this format defined in this memo
+   should be familiar with this notation in order to properly interpret
+   the specifications of this memo.
+
+   All numeric and hexadecimal values used in this memo are given in
+   decimal notation.
+
+   All names of properties, property parameters, enumerated property
+   values and property parameter values are case-insensitive. However,
+   all other property values are case-sensitive, unless otherwise
+   stated.
+
+        Note: All indented editorial notes, such as this one, are
+        intended to provide the reader with additional information. The
+        information is not essential to the building of an
+        implementation conformant with this memo. The information is
+        provided to highlight a particular feature or characteristic of
+        the memo.
+
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                     [Page 6]</span>
+</pre><pre class="newpage"><a name="page-7" id="page-7" href="#page-7" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+   The format for the iCalendar object is based on the syntax of the
+   [<a href="http://tools.ietf.org/html/rfc2425" title="&quot;A MIME Content-Type for Directory Information&quot;">RFC 2425</a>] content type. While the iCalendar object is not a profile
+   of the [<a href="http://tools.ietf.org/html/rfc2425" title="&quot;A MIME Content-Type for Directory Information&quot;">RFC 2425</a>] content type, it does reuse a number of the
+   elements from the [<a href="http://tools.ietf.org/html/rfc2425" title="&quot;A MIME Content-Type for Directory Information&quot;">RFC 2425</a>] specification.
+
+<span class="h3"><h3><a name="section-2.1">2.1</a> Formatting Conventions</h3></span>
+
+   The mechanisms defined in this memo are defined in prose. Many of the
+   terms used to describe these have common usage that is different than
+   the standards usage of this memo. In order to reference within this
+   memo elements of the calendaring and scheduling model, core object
+   (this memo) or interoperability protocol [<a href="#ref-ITIP" title="&quot;iCalendar Transport-Independent Interoperability Protocol (iTIP) : Scheduling Events, Busy Time, To-dos and Journal Entries&quot;">ITIP</a>] some formatting
+   conventions have been used. Calendaring and scheduling roles are
+   referred to in quoted-strings of text with the first character of
+   each word in upper case. For example, "Organizer" refers to a role of
+   a "Calendar User" within the scheduling protocol defined by [<a href="#ref-ITIP" title="&quot;iCalendar Transport-Independent Interoperability Protocol (iTIP) : Scheduling Events, Busy Time, To-dos and Journal Entries&quot;">ITIP</a>].
+   Calendar components defined by this memo are referred to with
+   capitalized, quoted-strings of text. All calendar components start
+   with the letter "V". For example, "VEVENT" refers to the event
+   calendar component, "VTODO" refers to the to-do calendar component
+   and "VJOURNAL" refers to the daily journal calendar component.
+   Scheduling methods defined by [<a href="#ref-ITIP" title="&quot;iCalendar Transport-Independent Interoperability Protocol (iTIP) : Scheduling Events, Busy Time, To-dos and Journal Entries&quot;">ITIP</a>] are referred to with
+   capitalized, quoted-strings of text. For example, "REQUEST" refers to
+   the method for requesting a scheduling calendar component be created
+   or modified, "REPLY" refers to the method a recipient of a request
+   uses to update their status with the "Organizer" of the calendar
+   component.
+
+   The properties defined by this memo are referred to with capitalized,
+   quoted-strings of text, followed by the word "property". For example,
+   "ATTENDEE" property refers to the iCalendar property used to convey
+   the calendar address of a calendar user. Property parameters defined
+   by this memo are referred to with lowercase, quoted-strings of text,
+   followed by the word "parameter". For example, "value" parameter
+   refers to the iCalendar property parameter used to override the
+   default data type for a property value. Enumerated values defined by
+   this memo are referred to with capitalized text, either alone or
+   followed by the word "value". For example, the "MINUTELY" value can
+   be used with the "FREQ" component of the "RECUR" data type to specify
+   repeating components based on an interval of one minute or more.
+
+
+
+
+
+
+
+
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                     [Page 7]</span>
+</pre><pre class="newpage"><a name="page-8" id="page-8" href="#page-8" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+<span class="h3"><h3><a name="section-2.2">2.2</a> Related Memos</h3></span>
+
+   Implementers will need to be familiar with several other memos that,
+   along with this memo, form a framework for Internet calendaring and
+   scheduling standards. This memo, [<a href="#ref-ICAL">ICAL</a>], specifies a core
+   specification of objects, data types, properties and property
+   parameters.
+
+   [<a name="ref-ITIP" id="ref-ITIP">ITIP</a>] - specifies an interoperability protocol for scheduling
+   between different implementations;
+
+   [<a name="ref-IMIP" id="ref-IMIP">IMIP</a>] specifies an Internet email binding for [<a href="#ref-ITIP" title="&quot;iCalendar Transport-Independent Interoperability Protocol (iTIP) : Scheduling Events, Busy Time, To-dos and Journal Entries&quot;">ITIP</a>].
+
+   This memo does not attempt to repeat the specification of concepts or
+   definitions from these other memos. Where possible, references are
+   made to the memo that provides for the specification of these
+   concepts or definitions.
+
+<span class="h3"><h3><a name="section-2.3">2.3</a> International Considerations</h3></span>
+
+   In the rest of this document, descriptions of characters are of the
+   form "character name (codepoint)", where "codepoint" is from the US-
+   ASCII character set. The "character name" is the authoritative
+   description; (codepoint) is a reference to that character in US-ASCII
+   or US-ASCII compatible sets (for example the ISO-8859-x family, UTF-
+   8, ISO-2022-xx, KOI8-R). If a non-US-ASCII compatible character set
+   is used, appropriate code-point from that character set MUST be
+   chosen instead. Use of non-US-ASCII-compatible character sets is NOT
+   recommended.
+
+<span class="h2"><h2><a name="section-3">3</a>  Registration Information</h2></span>
+
+   The Calendaring and Scheduling Core Object Specification is intended
+   for use as a MIME content type. However, the implementation of the
+   memo is in no way limited solely as a MIME content type.
+
+<span class="h3"><h3><a name="section-3.1">3.1</a> Content Type</h3></span>
+
+   The following text is intended to register this memo as the MIME
+   content type "text/calendar".
+
+     To: ietf-types@uninett.no
+
+     Subject: Registration of MIME content type text/calendar.
+
+     MIME media type name: text
+
+     MIME subtype name: calendar
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                     [Page 8]</span>
+</pre><pre class="newpage"><a name="page-9" id="page-9" href="#page-9" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+<span class="h3"><h3><a name="section-3.2">3.2</a> Parameters</h3></span>
+
+   Required parameters: none
+
+   Optional parameters: charset, method, component and optinfo
+
+   The "charset" parameter is defined in [<a href="http://tools.ietf.org/html/rfc2046" title="&quot; Multipurpose Internet Mail Extensions (MIME) - Part Two: Media Types&quot;">RFC 2046</a>] for other body
+   parts. It is used to identify the default character set used within
+   the body part.
+
+   The "method" parameter is used to convey the iCalendar object method
+   or transaction semantics for the calendaring and scheduling
+   information. It also is an identifier for the restricted set of
+   properties and values that the iCalendar object consists of. The
+   parameter is to be used as a guide for applications interpreting the
+   information contained within the body part. It SHOULD NOT be used to
+   exclude or require particular pieces of information unless the
+   identified method definition specifically calls for this behavior.
+   Unless specifically forbidden by a particular method definition, a
+   text/calendar content type can contain any set of properties
+   permitted by the Calendaring and Scheduling Core Object
+   Specification. The "method" parameter MUST be the same value as that
+   specified in the "METHOD" component property in the iCalendar object.
+   If one is present, the other MUST also be present.
+
+   The value for the "method" parameter is defined as follows:
+
+        method  = 1*(ALPHA / DIGIT / "-")
+        ; IANA registered iCalendar object method
+
+   The "component" parameter conveys the type of iCalendar calendar
+   component within the body part. If the iCalendar object contains more
+   than one calendar component type, then multiple component parameters
+   MUST be specified.
+
+   The value for the "component" parameter is defined as follows:
+
+        component       = ("VEVENT" / "VTODO" / "VJOURNAL" / "VFREEBUSY"
+                        / "VTIMEZONE" / x-name / iana-token)
+
+   The "optinfo" parameter conveys optional information about the
+   iCalendar object within the body part. This parameter can only
+   specify semantics already specified by the iCalendar object and that
+   can be otherwise determined by parsing the body part. In addition,
+   the optional information specified by this parameter MUST be
+   consistent with that information specified by the iCalendar object.
+   For example, it can be used to convey the "Attendee" response status
+   to a meeting request. The parameter value consists of a string value.
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                     [Page 9]</span>
+</pre><pre class="newpage"><a name="page-10" id="page-10" href="#page-10" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+   The parameter can be specified multiple times.
+
+   This parameter MAY only specify semantics already specified by the
+   iCalendar object and that can be otherwise determined by parsing the
+   body part.
+
+   The value for the "optinfo" parameter is defined as follows:
+
+        optinfo = infovalue / qinfovalue
+
+        infovalue       = iana-token / x-name
+
+        qinfovalue      = DQUOTE (infovalue) DQUOTE
+
+<span class="h3"><h3><a name="section-3.3">3.3</a> Content Header Fields</h3></span>
+
+   Optional content header fields: Any header fields defined by [RFC
+   2045].
+
+<span class="h3"><h3><a name="section-3.4">3.4</a> Encoding Considerations</h3></span>
+
+   This MIME content type can contain 8bit characters, so the use of
+   quoted-printable or BASE64 MIME content-transfer-encodings might be
+   necessary when iCalendar objects are transferred across protocols
+   restricted to the 7bit repertoire. Note that a text valued property
+   in the content entity can also have content encoding of special
+   characters using a BACKSLASH character (US-ASCII decimal 92)
+   escapement technique. This means that content values can end up
+   encoded twice.
+
+<span class="h3"><h3><a name="section-3.5">3.5</a> Security Considerations</h3></span>
+
+   SPOOFING - - In this memo, the "Organizer" is the only person
+   authorized to make changes to an existing "VEVENT", "VTODO",
+   "VJOURNAL" calendar component and redistribute the updates to the
+   "Attendees". An iCalendar object that maliciously changes or cancels
+   an existing "VEVENT", "VTODO" or "VJOURNAL" or "VFREEBUSY" calendar
+   component might be constructed by someone other than the "Organizer"
+   and sent to the "Attendees". In addition in this memo, other than the
+   "Organizer", an "Attendee" of a "VEVENT", "VTODO", "VJOURNAL"
+   calendar component is the only other person authorized to update any
+   parameter associated with their "ATTENDEE" property and send it to
+   the "Organizer". An iCalendar object that maliciously changes the
+   "ATTENDEE" parameters can be constructed by someone other than the
+   real "Attendee" and sent to the "Organizer".
+
+
+
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                    [Page 10]</span>
+</pre><pre class="newpage"><a name="page-11" id="page-11" href="#page-11" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+   PROCEDURAL ALARMS - - An iCalendar object can be created that
+   contains a "VEVENT" and "VTODO" calendar component with "VALARM"
+   calendar components. The "VALARM" calendar component can be of type
+   PROCEDURE and can have an attachment containing some sort of
+   executable program. Implementations that incorporate these types of
+   alarms are subject to any virus or malicious attack that might occur
+   as a result of executing the attachment.
+
+   ATTACHMENTS - - An iCalendar object can include references to Uniform
+   Resource Locators that can be programmed resources.
+
+   Implementers and users of this memo should be aware of the network
+   security implications of accepting and parsing such information. In
+   addition, the security considerations observed by implementations of
+   electronic mail systems should be followed for this memo.
+
+<span class="h3"><h3><a name="section-3.6">3.6</a> Interoperability Considerations</h3></span>
+
+   This MIME content type is intended to define a common format for
+   conveying calendaring and scheduling information between different
+   systems. It is heavily based on the earlier [<a href="#ref-VCAL" title="&quot;vCalendar - The Electronic Calendaring and Scheduling Exchange Format&quot;">VCAL</a>] industry
+   specification.
+
+<span class="h3"><h3><a name="section-3.7">3.7</a> Applications Which Use This Media Type</h3></span>
+
+   This content-type is designed for widespread use by Internet
+   calendaring and scheduling applications. In addition, applications in
+   the workflow and document management area might find this content-
+   type applicable. The [<a href="#ref-ITIP" title="&quot;iCalendar Transport-Independent Interoperability Protocol (iTIP) : Scheduling Events, Busy Time, To-dos and Journal Entries&quot;">ITIP</a>] and [<a href="#ref-IMIP" title="&quot;iCalendar Message-based Interoperability Protocol (IMIP)&quot;">IMIP</a>] Internet protocols directly
+   use this content-type also. Future work on an Internet calendar
+   access protocol will utilize this content-type too.
+
+<span class="h3"><h3><a name="section-3.8">3.8</a> Additional Information</h3></span>
+
+   This memo defines this content-type.
+
+<span class="h3"><h3><a name="section-3.9">3.9</a> Magic Numbers</h3></span>
+
+   None.
+
+<span class="h3"><h3><a name="section-3.10">3.10</a> File Extensions</h3></span>
+
+   The file extension of "ics" is to be used to designate a file
+   containing (an arbitrary set of) calendaring and scheduling
+   information consistent with this MIME content type.
+
+
+
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                    [Page 11]</span>
+</pre><pre class="newpage"><a name="page-12" id="page-12" href="#page-12" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+   The file extension of "ifb" is to be used to designate a file
+   containing free or busy time information consistent with this MIME
+   content type.
+
+   Macintosh file type codes: The file type code of "iCal" is to be used
+   in Apple MacIntosh operating system environments to designate a file
+   containing calendaring and scheduling information consistent with
+   this MIME media type.
+
+   The file type code of "iFBf" is to be used in Apple MacIntosh
+   operating system environments to designate a file containing free or
+   busy time information consistent with this MIME media type.
+
+<span class="h3"><h3><a name="section-3.11">3.11</a> Contact for Further Information:</h3></span>
+
+   Frank Dawson
+   6544 Battleford Drive
+   Raleigh, NC 27613-3502
+   919-676-9515 (Telephone)
+   919-676-9564 (Data/Facsimile)
+   Frank_Dawson@Lotus.com (Internet Mail)
+
+   Derik Stenerson
+   One Microsoft Way
+   Redmond, WA  98052-6399
+   425-936-5522 (Telephone)
+   425-936-7329 (Facsimile)
+   deriks@microsoft.com (Internet Mail)
+
+<span class="h3"><h3><a name="section-3.12">3.12</a> Intended Usage</h3></span>
+
+   COMMON
+
+<span class="h3"><h3><a name="section-3.13">3.13</a> Authors/Change Controllers</h3></span>
+
+   Frank Dawson
+   6544 Battleford Drive
+   Raleigh, NC 27613-3502
+   919-676-9515 (Telephone)
+   919-676-9564 (Data/Facsimile)
+   Frank_Dawson@Lotus.com (Internet Mail)
+
+   Derik Stenerson
+   One Microsoft Way
+   Redmond, WA  98052-6399
+   425-936-5522 (Telephone)
+   425-936-7329 (Facsimile)
+   deriks@microsoft.com (Internet Mail)
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                    [Page 12]</span>
+</pre><pre class="newpage"><a name="page-13" id="page-13" href="#page-13" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+<span class="h2"><h2><a name="section-4">4</a> iCalendar Object Specification</h2></span>
+
+   The following sections define the details of a Calendaring and
+   Scheduling Core Object Specification. This information is intended to
+   be an integral part of the MIME content type registration. In
+   addition, this information can be used independent of such content
+   registration. In particular, this memo has direct applicability for
+   use as a calendaring and scheduling exchange format in file-, memory-
+   or network-based transport mechanisms.
+
+<span class="h3"><h3><a name="section-4.1">4.1</a> Content Lines</h3></span>
+
+   The iCalendar object is organized into individual lines of text,
+   called content lines. Content lines are delimited by a line break,
+   which is a CRLF sequence (US-ASCII decimal 13, followed by US-ASCII
+   decimal 10).
+
+   Lines of text SHOULD NOT be longer than 75 octets, excluding the line
+   break. Long content lines SHOULD be split into a multiple line
+   representations using a line "folding" technique. That is, a long
+   line can be split between any two characters by inserting a CRLF
+   immediately followed by a single linear white space character (i.e.,
+   SPACE, US-ASCII decimal 32 or HTAB, US-ASCII decimal 9). Any sequence
+   of CRLF followed immediately by a single linear white space character
+   is ignored (i.e., removed) when processing the content type.
+
+   For example the line:
+
+     DESCRIPTION:This is a long description that exists on a long line.
+
+   Can be represented as:
+
+     DESCRIPTION:This is a lo
+      ng description
+       that exists on a long line.
+
+   The process of moving from this folded multiple line representation
+   to its single line representation is called "unfolding". Unfolding is
+   accomplished by removing the CRLF character and the linear white
+   space character that immediately follows.
+
+   When parsing a content line, folded lines MUST first be unfolded
+   according to the unfolding procedure described above. When generating
+   a content line, lines longer than 75 octets SHOULD be folded
+   according to the folding procedure described above.
+
+
+
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                    [Page 13]</span>
+</pre><pre class="newpage"><a name="page-14" id="page-14" href="#page-14" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+   The content information associated with an iCalendar object is
+   formatted using a syntax similar to that defined by [<a href="http://tools.ietf.org/html/rfc2425" title="&quot;A MIME Content-Type for Directory Information&quot;">RFC 2425</a>]. That
+   is, the content information consists of CRLF-separated content lines.
+
+   The following notation defines the lines of content in an iCalendar
+   object:
+
+     contentline        = name *(";" param ) ":" value CRLF
+        ; This ABNF is just a general definition for an initial parsing
+        ; of the content line into its property name, parameter list,
+        ; and value string
+
+     ; When parsing a content line, folded lines MUST first
+        ; be unfolded according to the unfolding procedure
+        ; described above. When generating a content line, lines
+        ; longer than 75 octets SHOULD be folded according to
+        ; the folding procedure described above.
+
+     name               = x-name / iana-token
+
+     iana-token = 1*(ALPHA / DIGIT / "-")
+     ; iCalendar identifier registered with IANA
+
+     x-name             = "X-" [vendorid "-"] 1*(ALPHA / DIGIT / "-")
+     ; Reservered for experimental use. Not intended for use in
+     ; released products.
+
+     vendorid   = 3*(ALPHA / DIGIT)     ;Vendor identification
+
+     param              = param-name "=" param-value
+                          *("," param-value)
+        ; Each property defines the specific ABNF for the parameters
+        ; allowed on the property. Refer to specific properties for
+        ; precise parameter ABNF.
+
+     param-name = iana-token / x-token
+
+     param-value        = paramtext / quoted-string
+
+     paramtext  = *SAFE-CHAR
+
+     value      = *VALUE-CHAR
+
+     quoted-string      = DQUOTE *QSAFE-CHAR DQUOTE
+
+     NON-US-ASCII       = %x80-F8
+     ; Use restricted by charset parameter
+     ; on outer MIME object (UTF-8 preferred)
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                    [Page 14]</span>
+</pre><pre class="newpage"><a name="page-15" id="page-15" href="#page-15" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+     QSAFE-CHAR = WSP / %x21 / %x23-7E / NON-US-ASCII
+     ; Any character except CTLs and DQUOTE
+
+     SAFE-CHAR  = WSP / %x21 / %x23-2B / %x2D-39 / %x3C-7E
+                / NON-US-ASCII
+     ; Any character except CTLs, DQUOTE, ";", ":", ","
+
+     VALUE-CHAR = WSP / %x21-7E / NON-US-ASCII
+     ; Any textual character
+
+     CR = %x0D
+     ; carriage return
+
+     LF = %x0A
+     ; line feed
+
+     CRLF       = CR LF
+     ; Internet standard newline
+
+     CTL        = %x00-08 / %x0A-1F / %x7F
+        ; Controls
+
+     ALPHA      = %x41-5A / %x61-7A   ; A-Z / a-z
+
+     DIGIT      = %x30-39
+        ; 0-9
+
+     DQUOTE     = %x22
+        ; Quotation Mark
+
+     WSP        = SPACE / HTAB
+
+     SPACE      = %x20
+
+     HTAB       = %x09
+
+   The property value component of a content line has a format that is
+   property specific. Refer to the section describing each property for
+   a definition of this format.
+
+   All names of properties, property parameters, enumerated property
+   values and property parameter values are case-insensitive. However,
+   all other property values are case-sensitive, unless otherwise
+   stated.
+
+
+
+
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                    [Page 15]</span>
+</pre><pre class="newpage"><a name="page-16" id="page-16" href="#page-16" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+<span class="h4"><h4><a name="section-4.1.1">4.1.1</a> List and Field Separators</h4></span>
+
+   Some properties and parameters allow a list of values. Values in a
+   list of values MUST be separated by a COMMA character (US-ASCII
+   decimal 44). There is no significance to the order of values in a
+   list. For those parameter values (such as those that specify URI
+   values) that are specified in quoted-strings, the individual quoted-
+   strings are separated by a COMMA character (US-ASCII decimal 44).
+
+   Some property values are defined in terms of multiple parts. These
+   structured property values MUST have their value parts separated by a
+   SEMICOLON character (US-ASCII decimal 59).
+
+   Some properties allow a list of parameters. Each property parameter
+   in a list of property parameters MUST be separated by a SEMICOLON
+   character (US-ASCII decimal 59).
+
+   Property parameters with values containing a COLON, a SEMICOLON or a
+   COMMA character MUST be placed in quoted text.
+
+   For example, in the following properties a SEMICOLON is used to
+   separate property parameters from each other, and a COMMA is used to
+   separate property values in a value list.
+
+     ATTENDEE;RSVP=TRUE;ROLE=REQ-PARTICIPANT:MAILTO:
+      jsmith@host.com
+
+     RDATE;VALUE=DATE:19970304,19970504,19970704,19970904
+
+<span class="h4"><h4><a name="section-4.1.2">4.1.2</a> Multiple Values</h4></span>
+
+   Some properties defined in the iCalendar object can have multiple
+   values. The general rule for encoding multi-valued items is to simply
+   create a new content line for each value, including the property
+   name. However, it should be noted that some properties support
+   encoding multiple values in a single property by separating the
+   values with a COMMA character (US-ASCII decimal 44). Individual
+   property definitions should be consulted for determining whether a
+   specific property allows multiple values and in which of these two
+   forms.
+
+<span class="h4"><h4><a name="section-4.1.3">4.1.3</a> Binary Content</h4></span>
+
+   Binary content information in an iCalendar object SHOULD be
+   referenced using a URI within a property value. That is the binary
+   content information SHOULD be placed in an external MIME entity that
+   can be referenced by a URI from within the iCalendar object. In
+   applications where this is not feasible, binary content information
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                    [Page 16]</span>
+</pre><pre class="newpage"><a name="page-17" id="page-17" href="#page-17" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+   can be included within an iCalendar object, but only after first
+   encoding it into text using the "BASE64" encoding method defined in
+   [<a href="http://tools.ietf.org/html/rfc2045" title="&quot; Multipurpose Internet Mail Extensions (MIME) - Part One: Format of Internet Message Bodies&quot;">RFC 2045</a>]. Inline binary contact SHOULD only be used in applications
+   whose special circumstances demand that an iCalendar object be
+   expressed as a single entity. A property containing inline binary
+   content information MUST specify the "ENCODING" property parameter.
+   Binary content information placed external to the iCalendar object
+   MUST be referenced by a uniform resource identifier (URI).
+
+   The following example specifies an "ATTACH" property that references
+   an attachment external to the iCalendar object with a URI reference:
+
+     ATTACH:http://xyz.com/public/quarterly-report.doc
+
+   The following example specifies an "ATTACH" property with inline
+   binary encoded content information:
+
+     ATTACH;FMTTYPE=image/basic;ENCODING=BASE64;VALUE=BINARY:
+      MIICajCCAdOgAwIBAgICBEUwDQYJKoZIhvcNAQEEBQAwdzELMAkGA1U
+      EBhMCVVMxLDAqBgNVBAoTI05ldHNjYXBlIENvbW11bmljYXRpb25zIE
+        &lt;...remainder of "BASE64" encoded binary data...&gt;
+
+<span class="h4"><h4><a name="section-4.1.4">4.1.4</a> Character Set</h4></span>
+
+   There is not a property parameter to declare the character set used
+   in a property value. The default character set for an iCalendar
+   object is UTF-8 as defined in [<a href="http://tools.ietf.org/html/rfc2279" title="&quot;UTF-8, a transformation format of ISO 10646&quot;">RFC 2279</a>].
+
+   The "charset" Content-Type parameter can be used in MIME transports
+   to specify any other IANA registered character set.
+
+<span class="h3"><h3><a name="section-4.2">4.2</a> Property Parameters</h3></span>
+
+   A property can have attributes associated with it. These "property
+   parameters" contain meta-information about the property or the
+   property value. Property parameters are provided to specify such
+   information as the location of an alternate text representation for a
+   property value, the language of a text property value, the data type
+   of the property value and other attributes.
+
+   Property parameter values that contain the COLON (US-ASCII decimal
+   58), SEMICOLON (US-ASCII decimal 59) or COMMA (US-ASCII decimal 44)
+   character separators MUST be specified as quoted-string text values.
+   Property parameter values MUST NOT contain the DOUBLE-QUOTE (US-ASCII
+   decimal 22) character. The DOUBLE-QUOTE (US-ASCII decimal 22)
+   character is used as a delimiter for parameter values that contain
+   restricted characters or URI text. For example:
+
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                    [Page 17]</span>
+</pre><pre class="newpage"><a name="page-18" id="page-18" href="#page-18" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+     DESCRIPTION;ALTREP="http://www.wiz.org":The Fall'98 Wild Wizards
+       Conference - - Las Vegas, NV, USA
+
+   Property parameter values that are not in quoted strings are case
+   insensitive.
+
+   The general property parameters defined by this memo are defined by
+   the following notation:
+
+     parameter  = altrepparam           ; Alternate text representation
+                / cnparam               ; Common name
+                / cutypeparam           ; Calendar user type
+                / delfromparam          ; Delegator
+                / deltoparam            ; Delegatee
+                / dirparam              ; Directory entry
+                / encodingparam         ; Inline encoding
+                / fmttypeparam          ; Format type
+                / fbtypeparam           ; Free/busy time type
+                / languageparam         ; Language for text
+                / memberparam           ; Group or list membership
+                / partstatparam         ; Participation status
+                / rangeparam            ; Recurrence identifier range
+                / trigrelparam          ; Alarm trigger relationship
+                / reltypeparam          ; Relationship type
+                / roleparam             ; Participation role
+                / rsvpparam             ; RSVP expectation
+                / sentbyparam           ; Sent by
+                / tzidparam             ; Reference to time zone object
+                / valuetypeparam        ; Property value data type
+                / ianaparam
+        ; Some other IANA registered iCalendar parameter.
+                / xparam
+        ; A non-standard, experimental parameter.
+
+     ianaparam  = iana-token "=" param-value *("," param-value)
+
+     xparam     =x-name "=" param-value *("," param-value)
+
+<span class="h4"><h4><a name="section-4.2.1">4.2.1</a> Alternate Text Representation</h4></span>
+
+   Parameter Name: ALTREP
+
+   Purpose: To specify an alternate text representation for the property
+   value.
+
+   Format Definition: The property parameter is defined by the following
+   notation:
+
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                    [Page 18]</span>
+</pre><pre class="newpage"><a name="page-19" id="page-19" href="#page-19" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+     altrepparam        = "ALTREP" "=" DQUOTE uri DQUOTE
+
+   Description: The parameter specifies a URI that points to an
+   alternate representation for a textual property value. A property
+   specifying this parameter MUST also include a value that reflects the
+   default representation of the text value. The individual URI
+   parameter values MUST each be specified in a quoted-string.
+
+   Example:
+
+     DESCRIPTION;ALTREP="CID:&lt;part3.msg.970415T083000@host.com&gt;":Project
+       XYZ Review Meeting will include the following agenda items: (a)
+       Market Overview, (b) Finances, (c) Project Management
+
+   The "ALTREP" property parameter value might point to a "text/html"
+   content portion.
+
+     Content-Type:text/html
+     Content-Id:&lt;part3.msg.970415T083000@host.com&gt;
+
+     &lt;html&gt;&lt;body&gt;
+     &lt;p&gt;&lt;b&gt;Project XYZ Review Meeting&lt;/b&gt; will include the following
+     agenda items:&lt;ol&gt;&lt;li&gt;Market
+     Overview&lt;/li&gt;&lt;li&gt;Finances&lt;/li&gt;&lt;li&gt;Project Management&lt;/li&gt;&lt;/ol&gt;&lt;/p&gt;
+     &lt;/body&gt;&lt;/html&gt;
+
+<span class="h4"><h4><a name="section-4.2.2">4.2.2</a> Common Name</h4></span>
+
+   Parameter Name: CN
+
+   Purpose: To specify the common name to be associated with the
+   calendar user specified by the property.
+
+   Format Definition: The property parameter is defined by the following
+   notation:
+
+     cnparam    = "CN" "=" param-value
+
+   Description: This parameter can be specified on properties with a
+   CAL-ADDRESS value type. The parameter specifies the common name to be
+   associated with the calendar user specified by the property. The
+   parameter value is text. The parameter value can be used for display
+   text to be associated with the calendar address specified by the
+   property.
+
+
+
+
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                    [Page 19]</span>
+</pre><pre class="newpage"><a name="page-20" id="page-20" href="#page-20" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+   Example:
+
+     ORGANIZER;CN="John Smith":MAILTO:jsmith@host.com
+
+<span class="h4"><h4><a name="section-4.2.3">4.2.3</a> Calendar User Type</h4></span>
+
+   Parameter Name: CUTYPE
+
+   Purpose: To specify the type of calendar user specified by the
+   property.
+
+   Format Definition: The property parameter is defined by the following
+   notation:
+
+     cutypeparam        = "CUTYPE" "="
+                         ("INDIVIDUAL"          ; An individual
+                        / "GROUP"               ; A group of individuals
+                        / "RESOURCE"            ; A physical resource
+                        / "ROOM"                ; A room resource
+                        / "UNKNOWN"             ; Otherwise not known
+                        / x-name                ; Experimental type
+                        / iana-token)           ; Other IANA registered
+                                                ; type
+     ; Default is INDIVIDUAL
+
+   Description: This parameter can be specified on properties with a
+   CAL-ADDRESS value type. The parameter identifies the type of calendar
+   user specified by the property. If not specified on a property that
+   allows this parameter, the default is INDIVIDUAL.
+
+   Example:
+
+     ATTENDEE;CUTYPE=GROUP:MAILTO:ietf-calsch@imc.org
+
+<span class="h4"><h4><a name="section-4.2.4">4.2.4</a> Delegators</h4></span>
+
+   Parameter Name: DELEGATED-FROM
+
+   Purpose: To specify the calendar users that have delegated their
+   participation to the calendar user specified by the property.
+
+   Format Definition: The property parameter is defined by the following
+   notation:
+
+     delfromparam       = "DELEGATED-FROM" "=" DQUOTE cal-address DQUOTE
+                          *("," DQUOTE cal-address DQUOTE)
+
+
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                    [Page 20]</span>
+</pre><pre class="newpage"><a name="page-21" id="page-21" href="#page-21" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+   Description: This parameter can be specified on properties with a
+   CAL-ADDRESS value type. This parameter can be specified on a property
+   that has a value type of calendar address. This parameter specifies
+   those calendar uses that have delegated their participation in a
+   group scheduled event or to-do to the calendar user specified by the
+   property. The value MUST be a MAILTO URI as defined in [<a href="http://tools.ietf.org/html/rfc1738" title="&quot;Uniform Resource Locators (URL)&quot;">RFC 1738</a>].
+   The individual calendar address parameter values MUST each be
+   specified in a quoted-string.
+
+   Example:
+
+     ATTENDEE;DELEGATED-FROM="MAILTO:jsmith@host.com":MAILTO:
+      jdoe@host.com
+
+<span class="h4"><h4><a name="section-4.2.5">4.2.5</a> Delegatees</h4></span>
+
+   Parameter Name: DELEGATED-TO
+
+   Purpose: To specify the calendar users to whom the calendar user
+   specified by the property has delegated participation.
+
+   Format Definition: The property parameter is defined by the following
+   notation:
+
+     deltoparam = "DELEGATED-TO" "=" DQUOTE cal-address DQUOTE
+                  *("," DQUOTE cal-address DQUOTE)
+
+   Description: This parameter can be specified on properties with a
+   CAL-ADDRESS value type. This parameter specifies those calendar users
+   whom have been delegated participation in a group scheduled event or
+   to-do by the calendar user specified by the property. The value MUST
+   be a MAILTO URI as defined in [<a href="http://tools.ietf.org/html/rfc1738" title="&quot;Uniform Resource Locators (URL)&quot;">RFC 1738</a>]. The individual calendar
+   address parameter values MUST each be specified in a quoted-string.
+
+   Example:
+
+     ATTENDEE;DELEGATED-TO="MAILTO:jdoe@host.com","MAILTO:jqpublic@
+      host.com":MAILTO:jsmith@host.com
+
+<span class="h4"><h4><a name="section-4.2.6">4.2.6</a> Directory Entry Reference</h4></span>
+
+   Parameter Name: DIR
+
+   Purpose: To specify reference to a directory entry associated with
+   the calendar user specified by the property.
+
+   Format Definition: The property parameter is defined by the following
+   notation:
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                    [Page 21]</span>
+</pre><pre class="newpage"><a name="page-22" id="page-22" href="#page-22" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+     dirparam   = "DIR" "=" DQUOTE uri DQUOTE
+
+   Description: This parameter can be specified on properties with a
+   CAL-ADDRESS value type. The parameter specifies a reference to the
+   directory entry associated with the calendar user specified by the
+   property. The parameter value is a URI. The individual URI parameter
+   values MUST each be specified in a quoted-string.
+
+   Example:
+
+     ORGANIZER;DIR="ldap://host.com:6666/o=eDABC%20Industries,c=3DUS??
+      (cn=3DBJim%20Dolittle)":MAILTO:jimdo@host1.com
+
+<span class="h4"><h4><a name="section-4.2.7">4.2.7</a> Inline Encoding</h4></span>
+
+   Parameter Name: ENCODING
+
+   Purpose: To specify an alternate inline encoding for the property
+   value.
+
+   Format Definition: The property parameter is defined by the following
+   notation:
+
+     encodingparam      = "ENCODING" "="
+                          ("8BIT"
+        ; "8bit" text encoding is defined in [<a href="http://tools.ietf.org/html/rfc2045" title="&quot; Multipurpose Internet Mail Extensions (MIME) - Part One: Format of Internet Message Bodies&quot;">RFC 2045</a>]
+                        / "BASE64"
+        ; "BASE64" binary encoding format is defined in [<a href="http://tools.ietf.org/html/rfc2045" title="&quot; Multipurpose Internet Mail Extensions (MIME) - Part One: Format of Internet Message Bodies&quot;">RFC 2045</a>]
+                        / iana-token
+        ; Some other IANA registered iCalendar encoding type
+                        / x-name)
+        ; A non-standard, experimental encoding type
+
+   Description: The property parameter identifies the inline encoding
+   used in a property value. The default encoding is "8BIT",
+   corresponding to a property value consisting of text. The "BASE64"
+   encoding type corresponds to a property value encoded using the
+   "BASE64" encoding defined in [<a href="http://tools.ietf.org/html/rfc2045" title="&quot; Multipurpose Internet Mail Extensions (MIME) - Part One: Format of Internet Message Bodies&quot;">RFC 2045</a>].
+
+   If the value type parameter is ";VALUE=BINARY", then the inline
+   encoding parameter MUST be specified with the value
+   ";ENCODING=BASE64".
+
+
+
+
+
+
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                    [Page 22]</span>
+</pre><pre class="newpage"><a name="page-23" id="page-23" href="#page-23" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+   Example:
+
+     ATTACH;FMTYPE=IMAGE/JPEG;ENCODING=BASE64;VALUE=BINARY:MIICajC
+      CAdOgAwIBAgICBEUwDQYJKoZIhvcNAQEEBQAwdzELMAkGA1UEBhMCVVMxLDA
+      qBgNVBAoTI05ldHNjYXBlIENvbW11bmljYXRpb25zIENvcnBvcmF0aW9uMRw
+      &lt;...remainder of "BASE64" encoded binary data...&gt;
+
+<span class="h4"><h4><a name="section-4.2.8">4.2.8</a> Format Type</h4></span>
+
+   Parameter Name: FMTTYPE
+
+   Purpose: To specify the content type of a referenced object.
+
+   Format Definition: The property parameter is defined by the following
+   notation:
+
+     fmttypeparam       = "FMTTYPE" "=" iana-token
+                                        ; A IANA registered content type
+                                     / x-name
+                                        ; A non-standard content type
+
+   Description: This parameter can be specified on properties that are
+   used to reference an object. The parameter specifies the content type
+   of the referenced object. For example, on the "ATTACH" property, a
+   FTP type URI value does not, by itself, necessarily convey the type
+   of content associated with the resource. The parameter value MUST be
+   the TEXT for either an IANA registered content type or a non-standard
+   content type.
+
+     Example:
+
+      ATTACH;FMTTYPE=application/binary:ftp://domain.com/pub/docs/
+       agenda.doc
+
+<span class="h4"><h4><a name="section-4.2.9">4.2.9</a> Free/Busy Time Type</h4></span>
+
+   Parameter Name: FBTYPE
+
+   Purpose: To specify the free or busy time type.
+
+   Format Definition: The property parameter is defined by the following
+   notation:
+
+     fbtypeparam        = "FBTYPE" "=" ("FREE" / "BUSY"
+                        / "BUSY-UNAVAILABLE" / "BUSY-TENTATIVE"
+                        / x-name
+        ; Some experimental iCalendar data type.
+                        / iana-token)
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                    [Page 23]</span>
+</pre><pre class="newpage"><a name="page-24" id="page-24" href="#page-24" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+        ; Some other IANA registered iCalendar data type.
+
+   Description: The parameter specifies the free or busy time type. The
+   value FREE indicates that the time interval is free for scheduling.
+   The value BUSY indicates that the time interval is busy because one
+   or more events have been scheduled for that interval. The value
+   BUSY-UNAVAILABLE indicates that the time interval is busy and that
+   the interval can not be scheduled. The value BUSY-TENTATIVE indicates
+   that the time interval is busy because one or more events have been
+   tentatively scheduled for that interval. If not specified on a
+   property that allows this parameter, the default is BUSY.
+
+   Example: The following is an example of this parameter on a FREEBUSY
+   property.
+
+     FREEBUSY;FBTYPE=BUSY:19980415T133000Z/19980415T170000Z
+
+<span class="h4"><h4><a name="section-4.2.10">4.2.10</a> Language</h4></span>
+
+   Parameter Name: LANGUAGE
+
+   Purpose: To specify the language for text values in a property or
+   property parameter.
+
+   Format Definition: The property parameter is defined by the following
+   notation:
+
+     languageparam =    "LANGUAGE" "=" language
+
+     language = &lt;Text identifying a language, as defined in [<a href="http://tools.ietf.org/html/rfc1766" title="&quot;Tags for the Identification of Languages&quot;">RFC 1766</a>]&gt;
+
+   Description: This parameter can be specified on properties with a
+   text value type. The parameter identifies the language of the text in
+   the property or property parameter value. The value of the "language"
+   property parameter is that defined in [<a href="http://tools.ietf.org/html/rfc1766" title="&quot;Tags for the Identification of Languages&quot;">RFC 1766</a>].
+
+   For transport in a MIME entity, the Content-Language header field can
+   be used to set the default language for the entire body part.
+   Otherwise, no default language is assumed.
+
+   Example:
+
+     SUMMARY;LANGUAGE=us-EN:Company Holiday Party
+
+     LOCATION;LANGUAGE=en:Germany
+     LOCATION;LANGUAGE=no:Tyskland
+
+
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                    [Page 24]</span>
+</pre><pre class="newpage"><a name="page-25" id="page-25" href="#page-25" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+   The following example makes use of the Quoted-Printable encoding in
+   order to represent non-ASCII characters.
+
+     LOCATION;LANGUAGE=da:K=F8benhavn
+     LOCATION;LANGUAGE=en:Copenhagen
+
+<span class="h4"><h4><a name="section-4.2.11">4.2.11</a>  Group or List Membership</h4></span>
+
+   Parameter Name: MEMBER
+
+   Purpose: To specify the group or list membership of the calendar user
+   specified by the property.
+
+   Format Definition: The property parameter is defined by the following
+   notation:
+
+     memberparam        = "MEMBER" "=" DQUOTE cal-address DQUOTE
+                          *("," DQUOTE cal-address DQUOTE)
+
+   Description: This parameter can be specified on properties with a
+   CAL-ADDRESS value type. The parameter identifies the groups or list
+   membership for the calendar user specified by the property. The
+   parameter value either a single calendar address in a quoted-string
+   or a COMMA character (US-ASCII decimal 44) list of calendar
+   addresses, each in a quoted-string. The individual calendar address
+   parameter values MUST each be specified in a quoted-string.
+
+   Example:
+
+     ATTENDEE;MEMBER="MAILTO:ietf-calsch@imc.org":MAILTO:jsmith@host.com
+
+     ATTENDEE;MEMBER="MAILTO:projectA@host.com","MAILTO:projectB@host.
+      com":MAILTO:janedoe@host.com
+
+<span class="h4"><h4><a name="section-4.2.12">4.2.12</a> Participation Status</h4></span>
+
+   Parameter Name: PARTSTAT
+
+   Purpose: To specify the participation status for the calendar user
+   specified by the property.
+
+   Format Definition: The property parameter is defined by the following
+   notation:
+
+     partstatparam      = "PARTSTAT" "="
+                         ("NEEDS-ACTION"        ; Event needs action
+                        / "ACCEPTED"            ; Event accepted
+                        / "DECLINED"            ; Event declined
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                    [Page 25]</span>
+</pre><pre class="newpage"><a name="page-26" id="page-26" href="#page-26" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+                        / "TENTATIVE"           ; Event tentatively
+                                                ; accepted
+                        / "DELEGATED"           ; Event delegated
+                        / x-name                ; Experimental status
+                        / iana-token)           ; Other IANA registered
+                                                ; status
+     ; These are the participation statuses for a "VEVENT". Default is
+     ; NEEDS-ACTION
+     partstatparam      /= "PARTSTAT" "="
+                         ("NEEDS-ACTION"        ; To-do needs action
+                        / "ACCEPTED"            ; To-do accepted
+                        / "DECLINED"            ; To-do declined
+                        / "TENTATIVE"           ; To-do tentatively
+                                                ; accepted
+                        / "DELEGATED"           ; To-do delegated
+                        / "COMPLETED"           ; To-do completed.
+                                                ; COMPLETED property has
+                                                ;date/time completed.
+                        / "IN-PROCESS"          ; To-do in process of
+                                                ; being completed
+                        / x-name                ; Experimental status
+                        / iana-token)           ; Other IANA registered
+                                                ; status
+     ; These are the participation statuses for a "VTODO". Default is
+     ; NEEDS-ACTION
+
+     partstatparam      /= "PARTSTAT" "="
+                         ("NEEDS-ACTION"        ; Journal needs action
+                        / "ACCEPTED"            ; Journal accepted
+                        / "DECLINED"            ; Journal declined
+                        / x-name                ; Experimental status
+                        / iana-token)           ; Other IANA registered
+                                                ; status
+     ; These are the participation statuses for a "VJOURNAL". Default is
+     ; NEEDS-ACTION
+
+   Description: This parameter can be specified on properties with a
+   CAL-ADDRESS value type. The parameter identifies the participation
+   status for the calendar user specified by the property value. The
+   parameter values differ depending on whether they are associated with
+   a group scheduled "VEVENT", "VTODO" or "VJOURNAL". The values MUST
+   match one of the values allowed for the given calendar component. If
+   not specified on a property that allows this parameter, the default
+   value is NEEDS-ACTION.
+
+   Example:
+
+     ATTENDEE;PARTSTAT=DECLINED:MAILTO:jsmith@host.com
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                    [Page 26]</span>
+</pre><pre class="newpage"><a name="page-27" id="page-27" href="#page-27" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+<span class="h4"><h4><a name="section-4.2.13">4.2.13</a>  Recurrence Identifier Range</h4></span>
+
+   Parameter Name: RANGE
+
+   Purpose: To specify the effective range of recurrence instances from
+   the instance specified by the recurrence identifier specified by the
+   property.
+
+   Format Definition: The property parameter is defined by the following
+   notation:
+
+     rangeparam = "RANGE" "=" ("THISANDPRIOR"
+        ; To specify all instances prior to the recurrence identifier
+                / "THISANDFUTURE")
+        ; To specify the instance specified by the recurrence identifier
+        ; and all subsequent recurrence instances
+
+   Description: The parameter can be specified on a property that
+   specifies a recurrence identifier. The parameter specifies the
+   effective range of recurrence instances that is specified by the
+   property. The effective range is from the recurrence identified
+   specified by the property. If this parameter is not specified an
+   allowed property, then the default range is the single instance
+   specified by the recurrence identifier value of the property. The
+   parameter value can be "THISANDPRIOR" to indicate a range defined by
+   the recurrence identified value of the property and all prior
+   instances. The parameter value can also be "THISANDFUTURE" to
+   indicate a range defined by the recurrence identifier and all
+   subsequent instances.
+
+   Example:
+
+     RECURRENCE-ID;RANGE=THISANDPRIOR:19980401T133000Z
+
+<span class="h4"><h4><a name="section-4.2.14">4.2.14</a> Alarm Trigger Relationship</h4></span>
+
+   Parameter Name: RELATED
+
+   Purpose: To specify the relationship of the alarm trigger with
+   respect to the start or end of the calendar component.
+
+   Format Definition: The property parameter is defined by the following
+   notation:
+
+     trigrelparam       = "RELATED" "="
+                         ("START"       ; Trigger off of start
+                        / "END")        ; Trigger off of end
+
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                    [Page 27]</span>
+</pre><pre class="newpage"><a name="page-28" id="page-28" href="#page-28" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+   Description: The parameter can be specified on properties that
+   specify an alarm trigger with a DURATION value type. The parameter
+   specifies whether the alarm will trigger relative to the start or end
+   of the calendar component. The parameter value START will set the
+   alarm to trigger off the start of the calendar component; the
+   parameter value END will set the alarm to trigger off the end of the
+   calendar component. If the parameter is not specified on an allowable
+   property, then the default is START.
+
+   Example:
+
+     TRIGGER;RELATED=END:PT5M
+
+<span class="h4"><h4><a name="section-4.2.15">4.2.15</a> Relationship Type</h4></span>
+
+   Parameter Name: RELTYPE
+
+   Purpose: To specify the type of hierarchical relationship associated
+   with the calendar component specified by the property.
+
+   Format Definition: The property parameter is defined by the following
+   notation:
+
+     reltypeparam       = "RELTYPE" "="
+                         ("PARENT"      ; Parent relationship. Default.
+                        / "CHILD"       ; Child relationship
+                        / "SIBLING      ; Sibling relationship
+                        / iana-token    ; Some other IANA registered
+                                        ; iCalendar relationship type
+                        / x-name)       ; A non-standard, experimental
+                                        ; relationship type
+
+   Description: This parameter can be specified on a property that
+   references another related calendar. The parameter specifies the
+   hierarchical relationship type of the calendar component referenced
+   by the property. The parameter value can be PARENT, to indicate that
+   the referenced calendar component is a superior of calendar
+   component; CHILD to indicate that the referenced calendar component
+   is a subordinate of the calendar component; SIBLING to indicate that
+   the referenced calendar component is a peer of the calendar
+   component. If this parameter is not specified on an allowable
+   property, the default relationship type is PARENT.
+
+   Example:
+
+     RELATED-TO;RELTYPE=SIBLING:&lt;19960401-080045-4000F192713@host.com&gt;
+
+
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                    [Page 28]</span>
+</pre><pre class="newpage"><a name="page-29" id="page-29" href="#page-29" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+<span class="h4"><h4><a name="section-4.2.16">4.2.16</a> Participation Role</h4></span>
+
+   Parameter Name: ROLE
+
+   Purpose: To specify the participation role for the calendar user
+   specified by the property.
+
+   Format Definition: The property parameter is defined by the following
+   notation:
+
+     roleparam  = "ROLE" "="
+                 ("CHAIR"               ; Indicates chair of the
+                                        ; calendar entity
+                / "REQ-PARTICIPANT"     ; Indicates a participant whose
+                                        ; participation is required
+                / "OPT-PARTICIPANT"     ; Indicates a participant whose
+                                        ; participation is optional
+                / "NON-PARTICIPANT"     ; Indicates a participant who is
+                                        ; copied for information
+                                        ; purposes only
+                / x-name                ; Experimental role
+                / iana-token)           ; Other IANA role
+     ; Default is REQ-PARTICIPANT
+
+   Description: This parameter can be specified on properties with a
+   CAL-ADDRESS value type. The parameter specifies the participation
+   role for the calendar user specified by the property in the group
+   schedule calendar component. If not specified on a property that
+   allows this parameter, the default value is REQ-PARTICIPANT.
+
+   Example:
+
+     ATTENDEE;ROLE=CHAIR:MAILTO:mrbig@host.com
+
+<span class="h4"><h4><a name="section-4.2.17">4.2.17</a>  RSVP Expectation</h4></span>
+
+   Parameter Name: RSVP
+
+   Purpose: To specify whether there is an expectation of a favor of a
+   reply from the calendar user specified by the property value.
+
+   Format Definition: The property parameter is defined by the following
+   notation:
+
+     rsvpparam = "RSVP" "=" ("TRUE" / "FALSE")
+     ; Default is FALSE
+
+
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                    [Page 29]</span>
+</pre><pre class="newpage"><a name="page-30" id="page-30" href="#page-30" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+   Description: This parameter can be specified on properties with a
+   CAL-ADDRESS value type. The parameter identifies the expectation of a
+   reply from the calendar user specified by the property value. This
+   parameter is used by the "Organizer" to request a participation
+   status reply from an "Attendee" of a group scheduled event or to-do.
+   If not specified on a property that allows this parameter, the
+   default value is FALSE.
+
+   Example:
+
+     ATTENDEE;RSVP=TRUE:MAILTO:jsmith@host.com
+
+<span class="h4"><h4><a name="section-4.2.18">4.2.18</a>  Sent By</h4></span>
+
+   Parameter Name: SENT-BY
+
+   Purpose: To specify the calendar user that is acting on behalf of the
+   calendar user specified by the property.
+
+   Format Definition: The property parameter is defined by the following
+   notation:
+
+     sentbyparam        = "SENT-BY" "=" DQUOTE cal-address DQUOTE
+
+   Description: This parameter can be specified on properties with a
+   CAL-ADDRESS value type. The parameter specifies the calendar user
+   that is acting on behalf of the calendar user specified by the
+   property. The parameter value MUST be a MAILTO URI as defined in [RFC
+   1738]. The individual calendar address parameter values MUST each be
+   specified in a quoted-string.
+
+   Example:
+
+     ORGANIZER;SENT-BY:"MAILTO:sray@host.com":MAILTO:jsmith@host.com
+
+<span class="h4"><h4><a name="section-4.2.19">4.2.19</a> Time Zone Identifier</h4></span>
+
+   Parameter Name: TZID
+
+   Purpose: To specify the identifier for the time zone definition for a
+   time component in the property value.
+
+   Format Definition: This property parameter is defined by the
+   following notation:
+
+     tzidparam  = "TZID" "=" [<a href="#ref-tzidprefix">tzidprefix</a>] paramtext CRLF
+
+     tzidprefix = "/"
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                    [Page 30]</span>
+</pre><pre class="newpage"><a name="page-31" id="page-31" href="#page-31" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+   Description: The parameter MUST be specified on the "DTSTART",
+   "DTEND", "DUE", "EXDATE" and "RDATE" properties when either a DATE-
+   TIME or TIME value type is specified and when the value is not either
+   a UTC or a "floating" time. Refer to the DATE-TIME or TIME value type
+   definition for a description of UTC and "floating time" formats. This
+   property parameter specifies a text value which uniquely identifies
+   the "VTIMEZONE" calendar component to be used when evaluating the
+   time portion of the property. The value of the TZID property
+   parameter will be equal to the value of the TZID property for the
+   matching time zone definition. An individual "VTIMEZONE" calendar
+   component MUST be specified for each unique "TZID" parameter value
+   specified in the iCalendar object.
+
+   The parameter MUST be specified on properties with a DATE-TIME value
+   if the DATE-TIME is not either a UTC or a "floating" time.
+
+   The presence of the SOLIDUS character (US-ASCII decimal 47) as a
+   prefix, indicates that this TZID represents a unique ID in a globally
+   defined time zone registry (when such registry is defined).
+
+        Note: This document does not define a naming convention for time
+        zone identifiers. Implementers may want to use the naming
+        conventions defined in existing time zone specifications such as
+        the public-domain Olson database [<a href="#ref-TZ" title="&quot;ftp://elsie.nci.nih.gov/pub/&quot;">TZ</a>]. The specification of
+        globally unique time zone identifiers is not addressed by this
+        document and is left for future study.
+
+   The following are examples of this property parameter:
+
+     DTSTART;TZID=US-Eastern:19980119T020000
+
+     DTEND;TZID=US-Eastern:19980119T030000
+
+   The TZID property parameter MUST NOT be applied to DATE-TIME or TIME
+   properties whose time values are specified in UTC.
+
+   The use of local time in a DATE-TIME or TIME value without the TZID
+   property parameter is to be interpreted as a local time value,
+   regardless of the existence of "VTIMEZONE" calendar components in the
+   iCalendar object.
+
+   For more information see the sections on the data types DATE-TIME and
+   TIME.
+
+
+
+
+
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                    [Page 31]</span>
+</pre><pre class="newpage"><a name="page-32" id="page-32" href="#page-32" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+<span class="h4"><h4><a name="section-4.2.20">4.2.20</a> Value Data Types</h4></span>
+
+   Parameter Name: VALUE
+
+   Purpose: To explicitly specify the data type format for a property
+   value.
+
+   Format Definition: The "VALUE" property parameter is defined by the
+   following notation:
+
+     valuetypeparam = "VALUE" "=" valuetype
+
+     valuetype  = ("BINARY"
+                / "BOOLEAN"
+                / "CAL-ADDRESS"
+                / "DATE"
+                / "DATE-TIME"
+                / "DURATION"
+                / "FLOAT"
+                / "INTEGER"
+                / "PERIOD"
+                / "RECUR"
+                / "TEXT"
+                / "TIME"
+                / "URI"
+                / "UTC-OFFSET"
+                / x-name
+                ; Some experimental iCalendar data type.
+                / iana-token)
+                ; Some other IANA registered iCalendar data type.
+
+   Description: The parameter specifies the data type and format of the
+   property value. The property values MUST be of a single value type.
+   For example, a "RDATE" property cannot have a combination of DATE-
+   TIME and TIME value types.
+
+   If the property's value is the default value type, then this
+   parameter need not be specified. However, if the property's default
+   value type is overridden by some other allowable value type, then
+   this parameter MUST be specified.
+
+<span class="h3"><h3><a name="section-4.3">4.3</a> Property Value Data Types</h3></span>
+
+   The properties in an iCalendar object are strongly typed. The
+   definition of each property restricts the value to be one of the
+   value data types, or simply value types, defined in this section. The
+   value type for a property will either be specified implicitly as the
+   default value type or will be explicitly specified with the "VALUE"
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                    [Page 32]</span>
+</pre><pre class="newpage"><a name="page-33" id="page-33" href="#page-33" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+   parameter. If the value type of a property is one of the alternate
+   valid types, then it MUST be explicitly specified with the "VALUE"
+   parameter.
+
+<span class="h4"><h4><a name="section-4.3.1">4.3.1</a>   Binary</h4></span>
+
+   Value Name: BINARY
+
+   Purpose: This value type is used to identify properties that contain
+   a character encoding of inline binary data. For example, an inline
+   attachment of an object code might be included in an iCalendar
+   object.
+
+   Formal Definition: The value type is defined by the following
+   notation:
+
+     binary     = *(4b-char) [<a href="#ref-b-end">b-end</a>]
+     ; A "BASE64" encoded character string, as defined by [<a href="http://tools.ietf.org/html/rfc2045" title="&quot; Multipurpose Internet Mail Extensions (MIME) - Part One: Format of Internet Message Bodies&quot;">RFC 2045</a>].
+
+     b-end      = (2b-char "==") / (3b-char "=")
+
+     b-char = ALPHA / DIGIT / "+" / "/"
+
+   Description: Property values with this value type MUST also include
+   the inline encoding parameter sequence of ";ENCODING=BASE64". That
+   is, all inline binary data MUST first be character encoded using the
+   "BASE64" encoding method defined in [<a href="http://tools.ietf.org/html/rfc2045" title="&quot; Multipurpose Internet Mail Extensions (MIME) - Part One: Format of Internet Message Bodies&quot;">RFC 2045</a>]. No additional content
+   value encoding (i.e., BACKSLASH character encoding) is defined for
+   this value type.
+
+   Example: The following is an abridged example of a "BASE64" encoded
+   binary value data.
+
+     ATTACH;VALUE=BINARY;ENCODING=BASE64:MIICajCCAdOgAwIBAgICBEUwDQY
+      JKoZIhvcNAQEEBQAwdzELMAkGA1UEBhMCVVMxLDAqBgNVBAoTI05ldHNjYXBlI
+      ENvbW11bmljYXRpb25zIENvcnBvcmF0aW9uMRwwGgYDVQQLExNJbmZv
+        &lt;...remainder of "BASE64" encoded binary data...&gt;
+
+<span class="h4"><h4><a name="section-4.3.2">4.3.2</a>   Boolean</h4></span>
+
+   Value Name: BOOLEAN
+
+   Purpose: This value type is used to identify properties that contain
+   either a "TRUE" or "FALSE" Boolean value.
+
+   Formal Definition: The value type is defined by the following
+   notation:
+
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                    [Page 33]</span>
+</pre><pre class="newpage"><a name="page-34" id="page-34" href="#page-34" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+     boolean    = "TRUE" / "FALSE"
+
+   Description: These values are case insensitive text. No additional
+   content value encoding (i.e., BACKSLASH character encoding) is
+   defined for this value type.
+
+   Example: The following is an example of a hypothetical property that
+   has a BOOLEAN value type:
+
+   GIBBERISH:TRUE
+
+<span class="h4"><h4><a name="section-4.3.3">4.3.3</a>   Calendar User Address</h4></span>
+
+   Value Name: CAL-ADDRESS
+
+   Purpose: This value type is used to identify properties that contain
+   a calendar user address.
+
+   Formal Definition: The value type is as defined by the following
+   notation:
+
+     cal-address        = uri
+
+   Description: The value is a URI as defined by [<a href="http://tools.ietf.org/html/rfc1738" title="&quot;Uniform Resource Locators (URL)&quot;">RFC 1738</a>] or any other
+   IANA registered form for a URI. When used to address an Internet
+   email transport address for a calendar user, the value MUST be a
+   MAILTO URI, as defined by [<a href="http://tools.ietf.org/html/rfc1738" title="&quot;Uniform Resource Locators (URL)&quot;">RFC 1738</a>]. No additional content value
+   encoding (i.e., BACKSLASH character encoding) is defined for this
+   value type.
+
+   Example:
+
+     ATTENDEE:MAILTO:jane_doe@host.com
+
+<span class="h4"><h4><a name="section-4.3.4">4.3.4</a> Date</h4></span>
+
+   Value Name: DATE
+
+   Purpose: This value type is used to identify values that contain a
+   calendar date.
+
+   Formal Definition: The value type is defined by the following
+   notation:
+
+     date               = date-value
+
+     date-value         = date-fullyear date-month date-mday
+     date-fullyear      = 4DIGIT
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                    [Page 34]</span>
+</pre><pre class="newpage"><a name="page-35" id="page-35" href="#page-35" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+     date-month         = 2DIGIT        ;01-12
+     date-mday          = 2DIGIT        ;01-28, 01-29, 01-30, 01-31
+                                        ;based on month/year
+
+   Description: If the property permits, multiple "date" values are
+   specified as a COMMA character (US-ASCII decimal 44) separated list
+   of values. The format for the value type is expressed as the [ISO
+   8601] complete representation, basic format for a calendar date. The
+   textual format specifies a four-digit year, two-digit month, and
+   two-digit day of the month. There are no separator characters between
+   the year, month and day component text.
+
+   No additional content value encoding (i.e., BACKSLASH character
+   encoding) is defined for this value type.
+
+   Example: The following represents July 14, 1997:
+
+     19970714
+
+<span class="h4"><h4><a name="section-4.3.5">4.3.5</a>   Date-Time</h4></span>
+
+   Value Name: DATE-TIME
+
+   Purpose: This value type is used to identify values that specify a
+   precise calendar date and time of day.
+
+   Formal Definition: The value type is defined by the following
+   notation:
+
+     date-time  = date "T" time ;As specified in the date and time
+                                ;value definitions
+
+   Description: If the property permits, multiple "date-time" values are
+   specified as a COMMA character (US-ASCII decimal 44) separated list
+   of values. No additional content value encoding (i.e., BACKSLASH
+   character encoding) is defined for this value type.
+
+   The "DATE-TIME" data type is used to identify values that contain a
+   precise calendar date and time of day. The format is based on the
+   [ISO 8601] complete representation, basic format for a calendar date
+   and time of day. The text format is a concatenation of the "date",
+   followed by the LATIN CAPITAL LETTER T character (US-ASCII decimal
+   84) time designator, followed by the "time" format.
+
+   The "DATE-TIME" data type expresses time values in three forms:
+
+   The form of date and time with UTC offset MUST NOT be used. For
+   example, the following is not valid for a date-time value:
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                    [Page 35]</span>
+</pre><pre class="newpage"><a name="page-36" id="page-36" href="#page-36" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+     DTSTART:19980119T230000-0800       ;Invalid time format
+
+   FORM #1: DATE WITH LOCAL TIME
+
+   The date with local time form is simply a date-time value that does
+   not contain the UTC designator nor does it reference a time zone. For
+   example, the following represents Janurary 18, 1998, at 11 PM:
+
+     DTSTART:19980118T230000
+
+   Date-time values of this type are said to be "floating" and are not
+   bound to any time zone in particular. They are used to represent the
+   same hour, minute, and second value regardless of which time zone is
+   currently being observed. For example, an event can be defined that
+   indicates that an individual will be busy from 11:00 AM to 1:00 PM
+   every day, no matter which time zone the person is in. In these
+   cases, a local time can be specified. The recipient of an iCalendar
+   object with a property value consisting of a local time, without any
+   relative time zone information, SHOULD interpret the value as being
+   fixed to whatever time zone the ATTENDEE is in at any given moment.
+   This means that two ATTENDEEs, in different time zones, receiving the
+   same event definition as a floating time, may be participating in the
+   event at different actual times. Floating time SHOULD only be used
+   where that is the reasonable behavior.
+
+   In most cases, a fixed time is desired. To properly communicate a
+   fixed time in a property value, either UTC time or local time with
+   time zone reference MUST be specified.
+
+   The use of local time in a DATE-TIME value without the TZID property
+   parameter is to be interpreted as floating time, regardless of the
+   existence of "VTIMEZONE" calendar components in the iCalendar object.
+
+   FORM #2: DATE WITH UTC TIME
+
+   The date with UTC time, or absolute time, is identified by a LATIN
+   CAPITAL LETTER Z suffix character (US-ASCII decimal 90), the UTC
+   designator, appended to the time value. For example, the following
+   represents January 19, 1998, at 0700 UTC:
+
+     DTSTART:19980119T070000Z
+
+   The TZID property parameter MUST NOT be applied to DATE-TIME
+   properties whose time values are specified in UTC.
+
+   FORM #3: DATE WITH LOCAL TIME AND TIME ZONE REFERENCE
+
+
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                    [Page 36]</span>
+</pre><pre class="newpage"><a name="page-37" id="page-37" href="#page-37" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+   The date and local time with reference to time zone information is
+   identified by the use the TZID property parameter to reference the
+   appropriate time zone definition. TZID is discussed in detail in the
+   section on Time Zone. For example, the following represents 2 AM in
+   New York on Janurary 19, 1998:
+
+          DTSTART;TZID=US-Eastern:19980119T020000
+
+   Example: The following represents July 14, 1997, at 1:30 PM in New
+   York City in each of the three time formats, using the "DTSTART"
+   property.
+
+     DTSTART:19970714T133000            ;Local time
+     DTSTART:19970714T173000Z           ;UTC time
+     DTSTART;TZID=US-Eastern:19970714T133000    ;Local time and time
+                        ; zone reference
+
+   A time value MUST ONLY specify 60 seconds when specifying the
+   periodic "leap second" in the time value. For example:
+
+     COMPLETED:19970630T235960Z
+
+<span class="h4"><h4><a name="section-4.3.6">4.3.6</a>   Duration</h4></span>
+
+   Value Name: DURATION
+
+   Purpose: This value type is used to identify properties that contain
+   a duration of time.
+
+   Formal Definition: The value type is defined by the following
+   notation:
+
+     dur-value  = (["+"] / "-") "P" (dur-date / dur-time / dur-week)
+
+     dur-date   = dur-day [<a href="#ref-dur-time">dur-time</a>]
+     dur-time   = "T" (dur-hour / dur-minute / dur-second)
+     dur-week   = 1*DIGIT "W"
+     dur-hour   = 1*DIGIT "H" [<a href="#ref-dur-minute">dur-minute</a>]
+     dur-minute = 1*DIGIT "M" [<a href="#ref-dur-second">dur-second</a>]
+     dur-second = 1*DIGIT "S"
+     dur-day    = 1*DIGIT "D"
+
+   Description: If the property permits, multiple "duration" values are
+   specified by a COMMA character (US-ASCII decimal 44) separated list
+   of values. The format is expressed as the [ISO 8601] basic format for
+   the duration of time. The format can represent durations in terms of
+   weeks, days, hours, minutes, and seconds.
+
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                    [Page 37]</span>
+</pre><pre class="newpage"><a name="page-38" id="page-38" href="#page-38" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+   No additional content value encoding (i.e., BACKSLASH character
+   encoding) are defined for this value type.
+
+   Example: A duration of 15 days, 5 hours and 20 seconds would be:
+
+     P15DT5H0M20S
+
+   A duration of 7 weeks would be:
+
+     P7W
+
+<span class="h4"><h4><a name="section-4.3.7">4.3.7</a>   Float</h4></span>
+
+   Value Name: FLOAT
+
+   Purpose: This value type is used to identify properties that contain
+   a real number value.
+
+   Formal Definition: The value type is defined by the following
+   notation:
+
+     float      = (["+"] / "-") 1*DIGIT ["." 1*DIGIT]
+
+   Description: If the property permits, multiple "float" values are
+   specified by a COMMA character (US-ASCII decimal 44) separated list
+   of values.
+
+   No additional content value encoding (i.e., BACKSLASH character
+   encoding) is defined for this value type.
+
+   Example:
+
+     1000000.0000001
+     1.333
+     -3.14
+
+<span class="h4"><h4><a name="section-4.3.8">4.3.8</a> Integer</h4></span>
+
+     Value Name:INTEGER
+
+     Purpose: This value type is used to identify properties that contain
+     a signed integer value.
+
+     Formal Definition: The value type is defined by the following
+     notation:
+
+     integer    = (["+"] / "-") 1*DIGIT
+
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                    [Page 38]</span>
+</pre><pre class="newpage"><a name="page-39" id="page-39" href="#page-39" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+     Description: If the property permits, multiple "integer" values are
+     specified by a COMMA character (US-ASCII decimal 44) separated list
+     of values. The valid range for "integer" is -2147483648 to
+     2147483647. If the sign is not specified, then the value is assumed
+     to be positive.
+
+     No additional content value encoding (i.e., BACKSLASH character
+     encoding) is defined for this value type.
+
+     Example:
+
+     1234567890
+     -1234567890
+     +1234567890
+     432109876
+
+<span class="h4"><h4><a name="section-4.3.9">4.3.9</a> Period of Time</h4></span>
+
+   Value Name: PERIOD
+
+   Purpose: This value type is used to identify values that contain a
+   precise period of time.
+
+   Formal Definition: The data type is defined by the following
+   notation:
+
+     period     = period-explicit / period-start
+
+     period-explicit = date-time "/" date-time
+     ; [ISO 8601] complete representation basic format for a period of
+     ; time consisting of a start and end. The start MUST be before the
+     ; end.
+
+     period-start = date-time "/" dur-value
+     ; [ISO 8601] complete representation basic format for a period of
+     ; time consisting of a start and positive duration of time.
+
+   Description: If the property permits, multiple "period" values are
+   specified by a COMMA character (US-ASCII decimal 44) separated list
+   of values. There are two forms of a period of time. First, a period
+   of time is identified by its start and its end. This format is
+   expressed as the [ISO 8601] complete representation, basic format for
+   "DATE-TIME" start of the period, followed by a SOLIDUS character
+   (US-ASCII decimal 47), followed by the "DATE-TIME" of the end of the
+   period. The start of the period MUST be before the end of the period.
+   Second, a period of time can also be defined by a start and a
+   positive duration of time. The format is expressed as the [ISO 8601]
+   complete representation, basic format for the "DATE-TIME" start of
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                    [Page 39]</span>
+</pre><pre class="newpage"><a name="page-40" id="page-40" href="#page-40" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+   the period, followed by a SOLIDUS character (US-ASCII decimal 47),
+   followed by the [ISO 8601] basic format for "DURATION" of the period.
+
+   Example: The period starting at 18:00:00 UTC, on January 1, 1997 and
+   ending at 07:00:00 UTC on January 2, 1997 would be:
+
+     19970101T180000Z/19970102T070000Z
+
+   The period start at 18:00:00 on January 1, 1997 and lasting 5 hours
+   and 30 minutes would be:
+
+     19970101T180000Z/PT5H30M
+
+   No additional content value encoding (i.e., BACKSLASH character
+   encoding) is defined for this value type.
+
+<span class="h4"><h4><a name="section-4.3.10">4.3.10</a> Recurrence Rule</h4></span>
+
+   Value Name: RECUR
+
+   Purpose: This value type is used to identify properties that contain
+   a recurrence rule specification.
+
+   Formal Definition: The value type is defined by the following
+   notation:
+
+     recur      = "FREQ"=freq *(
+
+                ; either UNTIL or COUNT may appear in a 'recur',
+                ; but UNTIL and COUNT MUST NOT occur in the same 'recur'
+
+                ( ";" "UNTIL" "=" enddate ) /
+                ( ";" "COUNT" "=" 1*DIGIT ) /
+
+                ; the rest of these keywords are optional,
+                ; but MUST NOT occur more than once
+
+                ( ";" "INTERVAL" "=" 1*DIGIT )          /
+                ( ";" "BYSECOND" "=" byseclist )        /
+                ( ";" "BYMINUTE" "=" byminlist )        /
+                ( ";" "BYHOUR" "=" byhrlist )           /
+                ( ";" "BYDAY" "=" bywdaylist )          /
+                ( ";" "BYMONTHDAY" "=" bymodaylist )    /
+                ( ";" "BYYEARDAY" "=" byyrdaylist )     /
+                ( ";" "BYWEEKNO" "=" bywknolist )       /
+                ( ";" "BYMONTH" "=" bymolist )          /
+                ( ";" "BYSETPOS" "=" bysplist )         /
+                ( ";" "WKST" "=" weekday )              /
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                    [Page 40]</span>
+</pre><pre class="newpage"><a name="page-41" id="page-41" href="#page-41" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+                ( ";" x-name "=" text )
+                )
+
+     freq       = "SECONDLY" / "MINUTELY" / "HOURLY" / "DAILY"
+                / "WEEKLY" / "MONTHLY" / "YEARLY"
+
+     enddate    = date
+     enddate    =/ date-time            ;An UTC value
+
+     byseclist  = seconds / ( seconds *("," seconds) )
+
+     seconds    = 1DIGIT / 2DIGIT       ;0 to 59
+
+     byminlist  = minutes / ( minutes *("," minutes) )
+
+     minutes    = 1DIGIT / 2DIGIT       ;0 to 59
+
+     byhrlist   = hour / ( hour *("," hour) )
+
+     hour       = 1DIGIT / 2DIGIT       ;0 to 23
+
+     bywdaylist = weekdaynum / ( weekdaynum *("," weekdaynum) )
+
+     weekdaynum = [([<a href="#ref-plus">plus</a>] ordwk / minus ordwk)] weekday
+
+     plus       = "+"
+
+     minus      = "-"
+
+     ordwk      = 1DIGIT / 2DIGIT       ;1 to 53
+
+     weekday    = "SU" / "MO" / "TU" / "WE" / "TH" / "FR" / "SA"
+     ;Corresponding to SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY,
+     ;FRIDAY, SATURDAY and SUNDAY days of the week.
+
+     bymodaylist = monthdaynum / ( monthdaynum *("," monthdaynum) )
+
+     monthdaynum = ([<a href="#ref-plus">plus</a>] ordmoday) / (minus ordmoday)
+
+     ordmoday   = 1DIGIT / 2DIGIT       ;1 to 31
+
+     byyrdaylist = yeardaynum / ( yeardaynum *("," yeardaynum) )
+
+     yeardaynum = ([<a href="#ref-plus">plus</a>] ordyrday) / (minus ordyrday)
+
+     ordyrday   = 1DIGIT / 2DIGIT / 3DIGIT      ;1 to 366
+
+     bywknolist = weeknum / ( weeknum *("," weeknum) )
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                    [Page 41]</span>
+</pre><pre class="newpage"><a name="page-42" id="page-42" href="#page-42" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+     weeknum    = ([<a href="#ref-plus">plus</a>] ordwk) / (minus ordwk)
+
+     bymolist   = monthnum / ( monthnum *("," monthnum) )
+
+     monthnum   = 1DIGIT / 2DIGIT       ;1 to 12
+
+     bysplist   = setposday / ( setposday *("," setposday) )
+
+     setposday  = yeardaynum
+
+   Description: If the property permits, multiple "recur" values are
+   specified by a COMMA character (US-ASCII decimal 44) separated list
+   of values. The value type is a structured value consisting of a list
+   of one or more recurrence grammar parts. Each rule part is defined by
+   a NAME=VALUE pair. The rule parts are separated from each other by
+   the SEMICOLON character (US-ASCII decimal 59). The rule parts are not
+   ordered in any particular sequence. Individual rule parts MUST only
+   be specified once.
+
+   The FREQ rule part identifies the type of recurrence rule. This rule
+   part MUST be specified in the recurrence rule. Valid values include
+   SECONDLY, to specify repeating events based on an interval of a
+   second or more; MINUTELY, to specify repeating events based on an
+   interval of a minute or more; HOURLY, to specify repeating events
+   based on an interval of an hour or more; DAILY, to specify repeating
+   events based on an interval of a day or more; WEEKLY, to specify
+   repeating events based on an interval of a week or more; MONTHLY, to
+   specify repeating events based on an interval of a month or more; and
+   YEARLY, to specify repeating events based on an interval of a year or
+   more.
+
+   The INTERVAL rule part contains a positive integer representing how
+   often the recurrence rule repeats. The default value is "1", meaning
+   every second for a SECONDLY rule, or every minute for a MINUTELY
+   rule, every hour for an HOURLY rule, every day for a DAILY rule,
+   every week for a WEEKLY rule, every month for a MONTHLY rule and
+   every year for a YEARLY rule.
+
+   The UNTIL rule part defines a date-time value which bounds the
+   recurrence rule in an inclusive manner. If the value specified by
+   UNTIL is synchronized with the specified recurrence, this date or
+   date-time becomes the last instance of the recurrence. If specified
+   as a date-time value, then it MUST be specified in an UTC time
+   format. If not present, and the COUNT rule part is also not present,
+   the RRULE is considered to repeat forever.
+
+   The COUNT rule part defines the number of occurrences at which to
+   range-bound the recurrence. The "DTSTART" property value, if
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                    [Page 42]</span>
+</pre><pre class="newpage"><a name="page-43" id="page-43" href="#page-43" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+   specified, counts as the first occurrence.
+
+   The BYSECOND rule part specifies a COMMA character (US-ASCII decimal
+   44) separated list of seconds within a minute. Valid values are 0 to
+   59. The BYMINUTE rule part specifies a COMMA character (US-ASCII
+   decimal 44) separated list of minutes within an hour. Valid values
+   are 0 to 59. The BYHOUR rule part specifies a COMMA character (US-
+   ASCII decimal 44) separated list of hours of the day. Valid values
+   are 0 to 23.
+
+   The BYDAY rule part specifies a COMMA character (US-ASCII decimal 44)
+   separated list of days of the week; MO indicates Monday; TU indicates
+   Tuesday; WE indicates Wednesday; TH indicates Thursday; FR indicates
+   Friday; SA indicates Saturday; SU indicates Sunday.
+
+   Each BYDAY value can also be preceded by a positive (+n) or negative
+   (-n) integer. If present, this indicates the nth occurrence of the
+   specific day within the MONTHLY or YEARLY RRULE. For example, within
+   a MONTHLY rule, +1MO (or simply 1MO) represents the first Monday
+   within the month, whereas -1MO represents the last Monday of the
+   month. If an integer modifier is not present, it means all days of
+   this type within the specified frequency. For example, within a
+   MONTHLY rule, MO represents all Mondays within the month.
+
+   The BYMONTHDAY rule part specifies a COMMA character (ASCII decimal
+   44) separated list of days of the month. Valid values are 1 to 31 or
+   -31 to -1. For example, -10 represents the tenth to the last day of
+   the month.
+
+   The BYYEARDAY rule part specifies a COMMA character (US-ASCII decimal
+   44) separated list of days of the year. Valid values are 1 to 366 or
+   -366 to -1. For example, -1 represents the last day of the year
+   (December 31st) and -306 represents the 306th to the last day of the
+   year (March 1st).
+
+   The BYWEEKNO rule part specifies a COMMA character (US-ASCII decimal
+   44) separated list of ordinals specifying weeks of the year. Valid
+   values are 1 to 53 or -53 to -1. This corresponds to weeks according
+   to week numbering as defined in [ISO 8601]. A week is defined as a
+   seven day period, starting on the day of the week defined to be the
+   week start (see WKST). Week number one of the calendar year is the
+   first week which contains at least four (4) days in that calendar
+   year. This rule part is only valid for YEARLY rules. For example, 3
+   represents the third week of the year.
+
+        Note: Assuming a Monday week start, week 53 can only occur when
+        Thursday is January 1 or if it is a leap year and Wednesday is
+        January 1.
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                    [Page 43]</span>
+</pre><pre class="newpage"><a name="page-44" id="page-44" href="#page-44" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+   The BYMONTH rule part specifies a COMMA character (US-ASCII decimal
+   44) separated list of months of the year. Valid values are 1 to 12.
+
+   The WKST rule part specifies the day on which the workweek starts.
+   Valid values are MO, TU, WE, TH, FR, SA and SU. This is significant
+   when a WEEKLY RRULE has an interval greater than 1, and a BYDAY rule
+   part is specified. This is also significant when in a YEARLY RRULE
+   when a BYWEEKNO rule part is specified. The default value is MO.
+
+   The BYSETPOS rule part specifies a COMMA character (US-ASCII decimal
+   44) separated list of values which corresponds to the nth occurrence
+   within the set of events specified by the rule. Valid values are 1 to
+   366 or -366 to -1. It MUST only be used in conjunction with another
+   BYxxx rule part. For example "the last work day of the month" could
+   be represented as:
+
+     RRULE:FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=-1
+
+   Each BYSETPOS value can include a positive (+n) or negative (-n)
+   integer. If present, this indicates the nth occurrence of the
+   specific occurrence within the set of events specified by the rule.
+
+   If BYxxx rule part values are found which are beyond the available
+   scope (ie, BYMONTHDAY=30 in February), they are simply ignored.
+
+   Information, not contained in the rule, necessary to determine the
+   various recurrence instance start time and dates are derived from the
+   Start Time (DTSTART) entry attribute. For example,
+   "FREQ=YEARLY;BYMONTH=1" doesn't specify a specific day within the
+   month or a time. This information would be the same as what is
+   specified for DTSTART.
+
+   BYxxx rule parts modify the recurrence in some manner. BYxxx rule
+   parts for a period of time which is the same or greater than the
+   frequency generally reduce or limit the number of occurrences of the
+   recurrence generated. For example, "FREQ=DAILY;BYMONTH=1" reduces the
+   number of recurrence instances from all days (if BYMONTH tag is not
+   present) to all days in January. BYxxx rule parts for a period of
+   time less than the frequency generally increase or expand the number
+   of occurrences of the recurrence. For example,
+   "FREQ=YEARLY;BYMONTH=1,2" increases the number of days within the
+   yearly recurrence set from 1 (if BYMONTH tag is not present) to 2.
+
+   If multiple BYxxx rule parts are specified, then after evaluating the
+   specified FREQ and INTERVAL rule parts, the BYxxx rule parts are
+   applied to the current set of evaluated occurrences in the following
+   order: BYMONTH, BYWEEKNO, BYYEARDAY, BYMONTHDAY, BYDAY, BYHOUR,
+   BYMINUTE, BYSECOND and BYSETPOS; then COUNT and UNTIL are evaluated.
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                    [Page 44]</span>
+</pre><pre class="newpage"><a name="page-45" id="page-45" href="#page-45" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+   Here is an example of evaluating multiple BYxxx rule parts.
+
+     DTSTART;TZID=US-Eastern:19970105T083000
+     RRULE:FREQ=YEARLY;INTERVAL=2;BYMONTH=1;BYDAY=SU;BYHOUR=8,9;
+      BYMINUTE=30
+
+   First, the "INTERVAL=2" would be applied to "FREQ=YEARLY" to arrive
+   at "every other year". Then, "BYMONTH=1" would be applied to arrive
+   at "every January, every other year". Then, "BYDAY=SU" would be
+   applied to arrive at "every Sunday in January, every other year".
+   Then, "BYHOUR=8,9" would be applied to arrive at "every Sunday in
+   January at 8 AM and 9 AM, every other year". Then, "BYMINUTE=30"
+   would be applied to arrive at "every Sunday in January at 8:30 AM and
+   9:30 AM, every other year". Then, lacking information from RRULE, the
+   second is derived from DTSTART, to end up in "every Sunday in January
+   at 8:30:00 AM and 9:30:00 AM, every other year". Similarly, if the
+   BYMINUTE, BYHOUR, BYDAY, BYMONTHDAY or BYMONTH rule part were
+   missing, the appropriate minute, hour, day or month would have been
+   retrieved from the "DTSTART" property.
+
+   No additional content value encoding (i.e., BACKSLASH character
+   encoding) is defined for this value type.
+
+   Example: The following is a rule which specifies 10 meetings which
+   occur every other day:
+
+     FREQ=DAILY;COUNT=10;INTERVAL=2
+
+   There are other examples specified in the "RRULE" specification.
+
+<span class="h4"><h4><a name="section-4.3.11">4.3.11</a> Text</h4></span>
+
+   Value Name: TEXT
+
+   Purpose This value type is used to identify values that contain human
+   readable text.
+
+   Formal Definition: The character sets supported by this revision of
+   iCalendar are UTF-8 and US ASCII thereof. The applicability to other
+   character sets is for future work. The value type is defined by the
+   following notation.
+
+     text       = *(TSAFE-CHAR / ":" / DQUOTE / ESCAPED-CHAR)
+     ; Folded according to description above
+
+     ESCAPED-CHAR = "\\" / "\;" / "\," / "\N" / "\n")
+        ; \\ encodes \, \N or \n encodes newline
+        ; \; encodes ;, \, encodes ,
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                    [Page 45]</span>
+</pre><pre class="newpage"><a name="page-46" id="page-46" href="#page-46" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+     TSAFE-CHAR = %x20-21 / %x23-2B / %x2D-39 / %x3C-5B
+                  %x5D-7E / NON-US-ASCII
+        ; Any character except CTLs not needed by the current
+        ; character set, DQUOTE, ";", ":", "\", ","
+
+     Note: Certain other character sets may require modification of the
+     above definitions, but this is beyond the scope of this document.
+
+   Description: If the property permits, multiple "text" values are
+   specified by a COMMA character (US-ASCII decimal 44) separated list
+   of values.
+
+   The language in which the text is represented can be controlled by
+   the "LANGUAGE" property parameter.
+
+   An intentional formatted text line break MUST only be included in a
+   "TEXT" property value by representing the line break with the
+   character sequence of BACKSLASH (US-ASCII decimal 92), followed by a
+   LATIN SMALL LETTER N (US-ASCII decimal 110) or a LATIN CAPITAL LETTER
+   N (US-ASCII decimal 78), that is "\n" or "\N".
+
+   The "TEXT" property values may also contain special characters that
+   are used to signify delimiters, such as a COMMA character for lists
+   of values or a SEMICOLON character for structured values. In order to
+   support the inclusion of these special characters in "TEXT" property
+   values, they MUST be escaped with a BACKSLASH character. A BACKSLASH
+   character (US-ASCII decimal 92) in a "TEXT" property value MUST be
+   escaped with another BACKSLASH character. A COMMA character in a
+   "TEXT" property value MUST be escaped with a BACKSLASH character
+   (US-ASCII decimal 92). A SEMICOLON character in a "TEXT" property
+   value MUST be escaped with a BACKSLASH character (US-ASCII decimal
+   92).  However, a COLON character in a "TEXT" property value SHALL NOT
+   be escaped with a BACKSLASH character.Example: A multiple line value
+   of:
+
+     Project XYZ Final Review
+     Conference Room - 3B
+     Come Prepared.
+
+   would be represented as:
+
+     Project XYZ Final Review\nConference Room - 3B\nCome Prepared.
+
+
+
+
+
+
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                    [Page 46]</span>
+</pre><pre class="newpage"><a name="page-47" id="page-47" href="#page-47" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+<span class="h4"><h4><a name="section-4.3.12">4.3.12</a> Time</h4></span>
+
+   Value Name: TIME
+
+   Purpose: This value type is used to identify values that contain a
+   time of day.
+
+   Formal Definition: The data type is defined by the following
+   notation:
+
+     time               = time-hour time-minute time-second [<a href="#ref-time-utc">time-utc</a>]
+
+     time-hour          = 2DIGIT        ;00-23
+     time-minute        = 2DIGIT        ;00-59
+     time-second        = 2DIGIT        ;00-60
+     ;The "60" value is used to account for "leap" seconds.
+
+     time-utc   = "Z"
+
+   Description: If the property permits, multiple "time" values are
+   specified by a COMMA character (US-ASCII decimal 44) separated list
+   of values. No additional content value encoding (i.e., BACKSLASH
+   character encoding) is defined for this value type.
+
+   The "TIME" data type is used to identify values that contain a time
+   of day. The format is based on the [ISO 8601] complete
+   representation, basic format for a time of day. The text format
+   consists of a two-digit 24-hour of the day (i.e., values 0-23), two-
+   digit minute in the hour (i.e., values 0-59), and two-digit seconds
+   in the minute (i.e., values 0-60). The seconds value of 60 MUST only
+   to be used to account for "leap" seconds. Fractions of a second are
+   not supported by this format.
+
+   In parallel to the "DATE-TIME" definition above, the "TIME" data type
+   expresses time values in three forms:
+
+   The form of time with UTC offset MUST NOT be used. For example, the
+   following is NOT VALID for a time value:
+
+     230000-0800        ;Invalid time format
+
+   FORM #1 LOCAL TIME
+
+   The local time form is simply a time value that does not contain the
+   UTC designator nor does it reference a time zone. For example, 11:00
+   PM:
+
+     230000
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                    [Page 47]</span>
+</pre><pre class="newpage"><a name="page-48" id="page-48" href="#page-48" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+   Time values of this type are said to be "floating" and are not bound
+   to any time zone in particular. They are used to represent the same
+   hour, minute, and second value regardless of which time zone is
+   currently being observed. For example, an event can be defined that
+   indicates that an individual will be busy from 11:00 AM to 1:00 PM
+   every day, no matter which time zone the person is in. In these
+   cases, a local time can be specified. The recipient of an iCalendar
+   object with a property value consisting of a local time, without any
+   relative time zone information, SHOULD interpret the value as being
+   fixed to whatever time zone the ATTENDEE is in at any given moment.
+   This means that two ATTENDEEs may participate in the same event at
+   different UTC times; floating time SHOULD only be used where that is
+   reasonable behavior.
+
+   In most cases, a fixed time is desired. To properly communicate a
+   fixed time in a property value, either UTC time or local time with
+   time zone reference MUST be specified.
+
+   The use of local time in a TIME value without the TZID property
+   parameter is to be interpreted as a local time value, regardless of
+   the existence of "VTIMEZONE" calendar components in the iCalendar
+   object.
+
+   FORM #2: UTC TIME
+
+   UTC time, or absolute time, is identified by a LATIN CAPITAL LETTER Z
+   suffix character (US-ASCII decimal 90), the UTC designator, appended
+   to the time value. For example, the following represents 07:00 AM
+   UTC:
+
+     070000Z
+
+   The TZID property parameter MUST NOT be applied to TIME properties
+   whose time values are specified in UTC.
+
+   FORM #3: LOCAL TIME AND TIME ZONE REFERENCE
+
+   The local time with reference to time zone information form is
+   identified by the use the TZID property parameter to reference the
+   appropriate time zone definition. TZID is discussed in detail in the
+   section on Time Zone.
+
+   Example: The following represents 8:30 AM in New York in Winter, five
+   hours behind UTC, in each of the three formats using the "X-
+   TIMEOFDAY" non-standard property:
+
+
+
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                    [Page 48]</span>
+</pre><pre class="newpage"><a name="page-49" id="page-49" href="#page-49" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+     X-TIMEOFDAY:083000
+
+     X-TIMEOFDAY:133000Z
+
+     X-TIMEOFDAY;TZID=US-Eastern:083000
+
+<span class="h4"><h4><a name="section-4.3.13">4.3.13</a> URI</h4></span>
+
+   Value Name: URI
+
+   Purpose: This value type is used to identify values that contain a
+   uniform resource identifier (URI) type of reference to the property
+   value.
+
+   Formal Definition: The data type is defined by the following
+   notation:
+
+     uri        = &lt;As defined by any IETF RFC&gt;
+
+   Description: This data type might be used to reference binary
+   information, for values that are large, or otherwise undesirable to
+   include directly in the iCalendar object.
+
+   The URI value formats in <a href="http://tools.ietf.org/html/rfc1738">RFC 1738</a>, <a href="http://tools.ietf.org/html/rfc2111">RFC 2111</a> and any other IETF
+   registered value format can be specified.
+
+   Any IANA registered URI format can be used. These include, but are
+   not limited to, those defined in <a href="http://tools.ietf.org/html/rfc1738">RFC 1738</a> and <a href="http://tools.ietf.org/html/rfc2111">RFC 2111</a>.
+
+   When a property parameter value is a URI value type, the URI MUST be
+   specified as a quoted-string value.
+
+   No additional content value encoding (i.e., BACKSLASH character
+   encoding) is defined for this value type.
+
+   Example: The following is a URI for a network file:
+
+     <a href="http://host1.com/my-report.txt">http://host1.com/my-report.txt</a>
+
+<span class="h4"><h4><a name="section-4.3.14">4.3.14</a> UTC Offset</h4></span>
+
+   Value Name: UTC-OFFSET
+
+   Purpose: This value type is used to identify properties that contain
+   an offset from UTC to local time.
+
+   Formal Definition: The data type is defined by the following
+   notation:
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                    [Page 49]</span>
+</pre><pre class="newpage"><a name="page-50" id="page-50" href="#page-50" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+     utc-offset = time-numzone  ;As defined above in time data type
+
+     time-numzone       = ("+" / "-") time-hour time-minute [time-
+     second]
+
+   Description: The PLUS SIGN character MUST be specified for positive
+   UTC offsets (i.e., ahead of UTC). The value of "-0000" and "-000000"
+   are not allowed. The time-second, if present, may not be 60; if
+   absent, it defaults to zero.
+
+   No additional content value encoding (i.e., BACKSLASH character
+   encoding) is defined for this value type.
+
+   Example: The following UTC offsets are given for standard time for
+   New York (five hours behind UTC) and Geneva (one hour ahead of UTC):
+
+     -0500
+
+     +0100
+
+<span class="h3"><h3><a name="section-4.4">4.4</a> iCalendar Object</h3></span>
+
+   The Calendaring and Scheduling Core Object is a collection of
+   calendaring and scheduling information. Typically, this information
+   will consist of a single iCalendar object. However, multiple
+   iCalendar objects can be sequentially grouped together. The first
+   line and last line of the iCalendar object MUST contain a pair of
+   iCalendar object delimiter strings. The syntax for an iCalendar
+   object is as follows:
+
+     icalobject = 1*("BEGIN" ":" "VCALENDAR" CRLF
+                  icalbody
+                  "END" ":" "VCALENDAR" CRLF)
+
+   The following is a simple example of an iCalendar object:
+
+     BEGIN:VCALENDAR
+     VERSION:2.0
+     PRODID:-//hacksw/handcal//NONSGML v1.0//EN
+     BEGIN:VEVENT
+     DTSTART:19970714T170000Z
+     DTEND:19970715T035959Z
+     SUMMARY:Bastille Day Party
+     END:VEVENT
+     END:VCALENDAR
+
+
+
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                    [Page 50]</span>
+</pre><pre class="newpage"><a name="page-51" id="page-51" href="#page-51" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+<span class="h3"><h3><a name="section-4.5">4.5</a> Property</h3></span>
+
+   A property is the definition of an individual attribute describing a
+   calendar or a calendar component. A property takes the form defined
+   by the "contentline" notation defined in <a href="#section-4.1.1">section 4.1.1</a>.
+
+   The following is an example of a property:
+
+     DTSTART:19960415T133000Z
+
+   This memo imposes no ordering of properties within an iCalendar
+   object.
+
+   Property names, parameter names and enumerated parameter values are
+   case insensitive. For example, the property name "DUE" is the same as
+   "due" and "Due", DTSTART;TZID=US-Eastern:19980714T120000 is the same
+   as DtStart;TzID=US-Eastern:19980714T120000.
+
+<span class="h3"><h3><a name="section-4.6">4.6</a> Calendar Components</h3></span>
+
+   The body of the iCalendar object consists of a sequence of calendar
+   properties and one or more calendar components. The calendar
+   properties are attributes that apply to the calendar as a whole. The
+   calendar components are collections of properties that express a
+   particular calendar semantic. For example, the calendar component can
+   specify an event, a to-do, a journal entry, time zone information, or
+   free/busy time information, or an alarm.
+
+   The body of the iCalendar object is defined by the following
+   notation:
+
+     icalbody   = calprops component
+
+     calprops   = 2*(
+
+                ; 'prodid' and 'version' are both REQUIRED,
+                ; but MUST NOT occur more than once
+
+                prodid /version /
+
+                ; 'calscale' and 'method' are optional,
+                ; but MUST NOT occur more than once
+
+                calscale        /
+                method          /
+
+                x-prop
+
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                    [Page 51]</span>
+</pre><pre class="newpage"><a name="page-52" id="page-52" href="#page-52" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+                )
+
+     component  = 1*(eventc / todoc / journalc / freebusyc /
+                / timezonec / iana-comp / x-comp)
+
+     iana-comp  = "BEGIN" ":" iana-token CRLF
+
+                  1*contentline
+
+                  "END" ":" iana-token CRLF
+
+     x-comp     = "BEGIN" ":" x-name CRLF
+
+                  1*contentline
+
+                  "END" ":" x-name CRLF
+
+   An iCalendar object MUST include the "PRODID" and "VERSION" calendar
+   properties. In addition, it MUST include at least one calendar
+   component. Special forms of iCalendar objects are possible to publish
+   just busy time (i.e., only a "VFREEBUSY" calendar component) or time
+   zone (i.e., only a "VTIMEZONE" calendar component) information. In
+   addition, a complex iCalendar object is possible that is used to
+   capture a complete snapshot of the contents of a calendar (e.g.,
+   composite of many different calendar components). More commonly, an
+   iCalendar object will consist of just a single "VEVENT", "VTODO" or
+   "VJOURNAL" calendar component.
+
+<span class="h4"><h4><a name="section-4.6.1">4.6.1</a> Event Component</h4></span>
+
+   Component Name: "VEVENT"
+
+   Purpose: Provide a grouping of component properties that describe an
+   event.
+
+   Format Definition: A "VEVENT" calendar component is defined by the
+   following notation:
+
+     eventc     = "BEGIN" ":" "VEVENT" CRLF
+                  eventprop *alarmc
+                  "END" ":" "VEVENT" CRLF
+
+     eventprop  = *(
+
+                ; the following are optional,
+                ; but MUST NOT occur more than once
+
+                class / created / description / dtstart / geo /
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                    [Page 52]</span>
+</pre><pre class="newpage"><a name="page-53" id="page-53" href="#page-53" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+                last-mod / location / organizer / priority /
+                dtstamp / seq / status / summary / transp /
+                uid / url / recurid /
+
+                ; either 'dtend' or 'duration' may appear in
+                ; a 'eventprop', but 'dtend' and 'duration'
+                ; MUST NOT occur in the same 'eventprop'
+
+                dtend / duration /
+
+                ; the following are optional,
+                ; and MAY occur more than once
+
+                attach / attendee / categories / comment /
+                contact / exdate / exrule / rstatus / related /
+                resources / rdate / rrule / x-prop
+
+                )
+
+   Description: A "VEVENT" calendar component is a grouping of component
+   properties, and possibly including "VALARM" calendar components, that
+   represents a scheduled amount of time on a calendar. For example, it
+   can be an activity; such as a one-hour long, department meeting from
+   8:00 AM to 9:00 AM, tomorrow. Generally, an event will take up time
+   on an individual calendar. Hence, the event will appear as an opaque
+   interval in a search for busy time. Alternately, the event can have
+   its Time Transparency set to "TRANSPARENT" in order to prevent
+   blocking of the event in searches for busy time.
+
+   The "VEVENT" is also the calendar component used to specify an
+   anniversary or daily reminder within a calendar. These events have a
+   DATE value type for the "DTSTART" property instead of the default
+   data type of DATE-TIME. If such a "VEVENT" has a "DTEND" property, it
+   MUST be specified as a DATE value also. The anniversary type of
+   "VEVENT" can span more than one date (i.e, "DTEND" property value is
+   set to a calendar date after the "DTSTART" property value).
+
+   The "DTSTART" property for a "VEVENT" specifies the inclusive start
+   of the event. For recurring events, it also specifies the very first
+   instance in the recurrence set. The "DTEND" property for a "VEVENT"
+   calendar component specifies the non-inclusive end of the event. For
+   cases where a "VEVENT" calendar component specifies a "DTSTART"
+   property with a DATE data type but no "DTEND" property, the events
+   non-inclusive end is the end of the calendar date specified by the
+   "DTSTART" property. For cases where a "VEVENT" calendar component
+   specifies a "DTSTART" property with a DATE-TIME data type but no
+   "DTEND" property, the event ends on the same calendar date and time
+   of day specified by the "DTSTART" property.
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                    [Page 53]</span>
+</pre><pre class="newpage"><a name="page-54" id="page-54" href="#page-54" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+   The "VEVENT" calendar component cannot be nested within another
+   calendar component. However, "VEVENT" calendar components can be
+   related to each other or to a "VTODO" or to a "VJOURNAL" calendar
+   component with the "RELATED-TO" property.
+
+   Example: The following is an example of the "VEVENT" calendar
+   component used to represent a meeting that will also be opaque to
+   searches for busy time:
+
+     BEGIN:VEVENT
+     UID:19970901T130000Z-123401@host.com
+     DTSTAMP:19970901T1300Z
+     DTSTART:19970903T163000Z
+     DTEND:19970903T190000Z
+     SUMMARY:Annual Employee Review
+     CLASS:PRIVATE
+     CATEGORIES:BUSINESS,HUMAN RESOURCES
+     END:VEVENT
+
+   The following is an example of the "VEVENT" calendar component used
+   to represent a reminder that will not be opaque, but rather
+   transparent, to searches for busy time:
+
+     BEGIN:VEVENT
+     UID:19970901T130000Z-123402@host.com
+     DTSTAMP:19970901T1300Z
+     DTSTART:19970401T163000Z
+     DTEND:19970402T010000Z
+     SUMMARY:Laurel is in sensitivity awareness class.
+     CLASS:PUBLIC
+     CATEGORIES:BUSINESS,HUMAN RESOURCES
+     TRANSP:TRANSPARENT
+     END:VEVENT
+
+   The following is an example of the "VEVENT" calendar component used
+   to represent an anniversary that will occur annually. Since it takes
+   up no time, it will not appear as opaque in a search for busy time;
+   no matter what the value of the "TRANSP" property indicates:
+
+     BEGIN:VEVENT
+     UID:19970901T130000Z-123403@host.com
+     DTSTAMP:19970901T1300Z
+     DTSTART:19971102
+     SUMMARY:Our Blissful Anniversary
+     CLASS:CONFIDENTIAL
+     CATEGORIES:ANNIVERSARY,PERSONAL,SPECIAL OCCASION
+     RRULE:FREQ=YEARLY
+     END:VEVENT
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                    [Page 54]</span>
+</pre><pre class="newpage"><a name="page-55" id="page-55" href="#page-55" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+<span class="h4"><h4><a name="section-4.6.2">4.6.2</a> To-do Component</h4></span>
+
+   Component Name: VTODO
+
+   Purpose: Provide a grouping of calendar properties that describe a
+   to-do.
+
+   Formal Definition: A "VTODO" calendar component is defined by the
+   following notation:
+
+     todoc      = "BEGIN" ":" "VTODO" CRLF
+                  todoprop *alarmc
+                  "END" ":" "VTODO" CRLF
+
+     todoprop   = *(
+
+                ; the following are optional,
+                ; but MUST NOT occur more than once
+
+                class / completed / created / description / dtstamp /
+                dtstart / geo / last-mod / location / organizer /
+                percent / priority / recurid / seq / status /
+                summary / uid / url /
+
+                ; either 'due' or 'duration' may appear in
+                ; a 'todoprop', but 'due' and 'duration'
+                ; MUST NOT occur in the same 'todoprop'
+
+                due / duration /
+
+                ; the following are optional,
+                ; and MAY occur more than once
+                attach / attendee / categories / comment / contact /
+                exdate / exrule / rstatus / related / resources /
+                rdate / rrule / x-prop
+
+                )
+
+   Description: A "VTODO" calendar component is a grouping of component
+   properties and possibly "VALARM" calendar components that represent
+   an action-item or assignment. For example, it can be used to
+   represent an item of work assigned to an individual; such as "turn in
+   travel expense today".
+
+   The "VTODO" calendar component cannot be nested within another
+   calendar component. However, "VTODO" calendar components can be
+   related to each other or to a "VTODO" or to a "VJOURNAL" calendar
+   component with the "RELATED-TO" property.
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                    [Page 55]</span>
+</pre><pre class="newpage"><a name="page-56" id="page-56" href="#page-56" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+   A "VTODO" calendar component without the "DTSTART" and "DUE" (or
+   "DURATION") properties specifies a to-do that will be associated with
+   each successive calendar date, until it is completed.
+
+   Example: The following is an example of a "VTODO" calendar component:
+
+     BEGIN:VTODO
+     UID:19970901T130000Z-123404@host.com
+     DTSTAMP:19970901T1300Z
+     DTSTART:19970415T133000Z
+     DUE:19970416T045959Z
+     SUMMARY:1996 Income Tax Preparation
+     CLASS:CONFIDENTIAL
+     CATEGORIES:FAMILY,FINANCE
+     PRIORITY:1
+     STATUS:NEEDS-ACTION
+     END:VTODO
+
+<span class="h4"><h4><a name="section-4.6.3">4.6.3</a> Journal Component</h4></span>
+
+   Component Name: VJOURNAL
+
+   Purpose: Provide a grouping of component properties that describe a
+   journal entry.
+
+   Formal Definition: A "VJOURNAL" calendar component is defined by the
+   following notation:
+
+     journalc   = "BEGIN" ":" "VJOURNAL" CRLF
+                  jourprop
+                  "END" ":" "VJOURNAL" CRLF
+
+     jourprop   = *(
+
+                ; the following are optional,
+                ; but MUST NOT occur more than once
+
+                class / created / description / dtstart / dtstamp /
+                last-mod / organizer / recurid / seq / status /
+                summary / uid / url /
+
+                ; the following are optional,
+                ; and MAY occur more than once
+
+                attach / attendee / categories / comment /
+                contact / exdate / exrule / related / rdate /
+                rrule / rstatus / x-prop
+
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                    [Page 56]</span>
+</pre><pre class="newpage"><a name="page-57" id="page-57" href="#page-57" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+                )
+
+   Description: A "VJOURNAL" calendar component is a grouping of
+   component properties that represent one or more descriptive text
+   notes associated with a particular calendar date. The "DTSTART"
+   property is used to specify the calendar date that the journal entry
+   is associated with. Generally, it will have a DATE value data type,
+   but it can also be used to specify a DATE-TIME value data type.
+   Examples of a journal entry include a daily record of a legislative
+   body or a journal entry of individual telephone contacts for the day
+   or an ordered list of accomplishments for the day. The "VJOURNAL"
+   calendar component can also be used to associate a document with a
+   calendar date.
+
+   The "VJOURNAL" calendar component does not take up time on a
+   calendar. Hence, it does not play a role in free or busy time
+   searches - - it is as though it has a time transparency value of
+   TRANSPARENT. It is transparent to any such searches.
+
+   The "VJOURNAL" calendar component cannot be nested within another
+   calendar component. However, "VJOURNAL" calendar components can be
+   related to each other or to a "VEVENT" or to a "VTODO" calendar
+   component, with the "RELATED-TO" property.
+
+   Example: The following is an example of the "VJOURNAL" calendar
+   component:
+
+     BEGIN:VJOURNAL
+     UID:19970901T130000Z-123405@host.com
+     DTSTAMP:19970901T1300Z
+     DTSTART;VALUE=DATE:19970317
+     SUMMARY:Staff meeting minutes
+     DESCRIPTION:1. Staff meeting: Participants include Joe\, Lisa
+       and Bob. Aurora project plans were reviewed. There is currently
+       no budget reserves for this project. Lisa will escalate to
+       management. Next meeting on Tuesday.\n
+       2. Telephone Conference: ABC Corp. sales representative called
+       to discuss new printer. Promised to get us a demo by Friday.\n
+       3. Henry Miller (Handsoff Insurance): Car was totaled by tree.
+       Is looking into a loaner car. 654-2323 (tel).
+     END:VJOURNAL
+
+
+
+
+
+
+
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                    [Page 57]</span>
+</pre><pre class="newpage"><a name="page-58" id="page-58" href="#page-58" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+<span class="h4"><h4><a name="section-4.6.4">4.6.4</a> Free/Busy Component</h4></span>
+
+   Component Name: VFREEBUSY
+
+   Purpose: Provide a grouping of component properties that describe
+   either a request for free/busy time, describe a response to a request
+   for free/busy time or describe a published set of busy time.
+
+   Formal Definition: A "VFREEBUSY" calendar component is defined by the
+   following notation:
+
+     freebusyc  = "BEGIN" ":" "VFREEBUSY" CRLF
+                  fbprop
+                  "END" ":" "VFREEBUSY" CRLF
+
+     fbprop     = *(
+
+                ; the following are optional,
+                ; but MUST NOT occur more than once
+
+                contact / dtstart / dtend / duration / dtstamp /
+                organizer / uid / url /
+
+                ; the following are optional,
+                ; and MAY occur more than once
+
+                attendee / comment / freebusy / rstatus / x-prop
+
+                )
+
+   Description: A "VFREEBUSY" calendar component is a grouping of
+   component properties that represents either a request for, a reply to
+   a request for free or busy time information or a published set of
+   busy time information.
+
+   When used to request free/busy time information, the "ATTENDEE"
+   property specifies the calendar users whose free/busy time is being
+   requested; the "ORGANIZER" property specifies the calendar user who
+   is requesting the free/busy time; the "DTSTART" and "DTEND"
+   properties specify the window of time for which the free/busy time is
+   being requested; the "UID" and "DTSTAMP" properties are specified to
+   assist in proper sequencing of multiple free/busy time requests.
+
+   When used to reply to a request for free/busy time, the "ATTENDEE"
+   property specifies the calendar user responding to the free/busy time
+   request; the "ORGANIZER" property specifies the calendar user that
+   originally requested the free/busy time; the "FREEBUSY" property
+   specifies the free/busy time information (if it exists); and the
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                    [Page 58]</span>
+</pre><pre class="newpage"><a name="page-59" id="page-59" href="#page-59" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+   "UID" and "DTSTAMP" properties are specified to assist in proper
+   sequencing of multiple free/busy time replies.
+
+   When used to publish busy time, the "ORGANIZER" property specifies
+   the calendar user associated with the published busy time; the
+   "DTSTART" and "DTEND" properties specify an inclusive time window
+   that surrounds the busy time information; the "FREEBUSY" property
+   specifies the published busy time information; and the "DTSTAMP"
+   property specifies the date/time that iCalendar object was created.
+
+   The "VFREEBUSY" calendar component cannot be nested within another
+   calendar component. Multiple "VFREEBUSY" calendar components can be
+   specified within an iCalendar object. This permits the grouping of
+   Free/Busy information into logical collections, such as monthly
+   groups of busy time information.
+
+   The "VFREEBUSY" calendar component is intended for use in iCalendar
+   object methods involving requests for free time, requests for busy
+   time, requests for both free and busy, and the associated replies.
+
+   Free/Busy information is represented with the "FREEBUSY" property.
+   This property provides a terse representation of time periods. One or
+   more "FREEBUSY" properties can be specified in the "VFREEBUSY"
+   calendar component.
+
+   When present in a "VFREEBUSY" calendar component, the "DTSTART" and
+   "DTEND" properties SHOULD be specified prior to any "FREEBUSY"
+   properties. In a free time request, these properties can be used in
+   combination with the "DURATION" property to represent a request for a
+   duration of free time within a specified window of time.
+
+   The recurrence properties ("RRULE", "EXRULE", "RDATE", "EXDATE") are
+   not permitted within a "VFREEBUSY" calendar component. Any recurring
+   events are resolved into their individual busy time periods using the
+   "FREEBUSY" property.
+
+   Example: The following is an example of a "VFREEBUSY" calendar
+   component used to request free or busy time information:
+
+     BEGIN:VFREEBUSY
+     ORGANIZER:MAILTO:jane_doe@host1.com
+     ATTENDEE:MAILTO:john_public@host2.com
+     DTSTART:19971015T050000Z
+     DTEND:19971016T050000Z
+     DTSTAMP:19970901T083000Z
+     END:VFREEBUSY
+
+
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                    [Page 59]</span>
+</pre><pre class="newpage"><a name="page-60" id="page-60" href="#page-60" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+   The following is an example of a "VFREEBUSY" calendar component used
+   to reply to the request with busy time information:
+
+     BEGIN:VFREEBUSY
+     ORGANIZER:MAILTO:jane_doe@host1.com
+     ATTENDEE:MAILTO:john_public@host2.com
+     DTSTAMP:19970901T100000Z
+     FREEBUSY;VALUE=PERIOD:19971015T050000Z/PT8H30M,
+      19971015T160000Z/PT5H30M,19971015T223000Z/PT6H30M
+     URL:http://host2.com/pub/busy/jpublic-01.ifb
+     COMMENT:This iCalendar file contains busy time information for
+       the next three months.
+     END:VFREEBUSY
+
+   The following is an example of a "VFREEBUSY" calendar component used
+   to publish busy time information.
+
+     BEGIN:VFREEBUSY
+     ORGANIZER:jsmith@host.com
+     DTSTART:19980313T141711Z
+     DTEND:19980410T141711Z
+     FREEBUSY:19980314T233000Z/19980315T003000Z
+     FREEBUSY:19980316T153000Z/19980316T163000Z
+     FREEBUSY:19980318T030000Z/19980318T040000Z
+     URL:http://www.host.com/calendar/busytime/jsmith.ifb
+     END:VFREEBUSY
+
+<span class="h4"><h4><a name="section-4.6.5">4.6.5</a> Time Zone Component</h4></span>
+
+   Component Name: VTIMEZONE
+
+   Purpose: Provide a grouping of component properties that defines a
+   time zone.
+
+   Formal Definition: A "VTIMEZONE" calendar component is defined by the
+   following notation:
+
+     timezonec  = "BEGIN" ":" "VTIMEZONE" CRLF
+
+                  2*(
+
+                  ; 'tzid' is required, but MUST NOT occur more
+                  ; than once
+
+                tzid /
+
+                  ; 'last-mod' and 'tzurl' are optional,
+                but MUST NOT occur more than once
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                    [Page 60]</span>
+</pre><pre class="newpage"><a name="page-61" id="page-61" href="#page-61" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+                last-mod / tzurl /
+
+                  ; one of 'standardc' or 'daylightc' MUST occur
+                ..; and each MAY occur more than once.
+
+                standardc / daylightc /
+
+                ; the following is optional,
+                ; and MAY occur more than once
+
+                  x-prop
+
+                  )
+
+                  "END" ":" "VTIMEZONE" CRLF
+
+     standardc  = "BEGIN" ":" "STANDARD" CRLF
+
+                  tzprop
+
+                  "END" ":" "STANDARD" CRLF
+
+     daylightc  = "BEGIN" ":" "DAYLIGHT" CRLF
+
+                  tzprop
+
+                  "END" ":" "DAYLIGHT" CRLF
+
+     tzprop     = 3*(
+
+                ; the following are each REQUIRED,
+                ; but MUST NOT occur more than once
+
+                dtstart / tzoffsetto / tzoffsetfrom /
+
+                ; the following are optional,
+                ; and MAY occur more than once
+
+                comment / rdate / rrule / tzname / x-prop
+
+                )
+
+   Description: A time zone is unambiguously defined by the set of time
+   measurement rules determined by the governing body for a given
+   geographic area. These rules describe at a minimum the base  offset
+   from UTC for the time zone, often referred to as the Standard Time
+   offset. Many locations adjust their Standard Time forward or backward
+   by one hour, in order to accommodate seasonal changes in number of
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                    [Page 61]</span>
+</pre><pre class="newpage"><a name="page-62" id="page-62" href="#page-62" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+   daylight hours, often referred to as Daylight  Saving Time. Some
+   locations adjust their time by a fraction of an hour. Standard Time
+   is also known as Winter Time. Daylight Saving Time is also known as
+   Advanced Time, Summer Time, or Legal Time in certain countries. The
+   following table shows the changes in time zone rules in effect for
+   New York City starting from 1967. Each line represents a description
+   or rule for a particular observance.
+
+     Effective Observance Rule
+
+     Date       (Date/Time)             Offset  Abbreviation
+
+     1967-*     last Sun in Oct, 02:00  -0500   EST
+
+     1967-1973  last Sun in Apr, 02:00  -0400   EDT
+
+     1974-1974  Jan 6,  02:00           -0400   EDT
+
+     1975-1975  Feb 23, 02:00           -0400   EDT
+
+     1976-1986  last Sun in Apr, 02:00  -0400   EDT
+
+     1987-*     first Sun in Apr, 02:00 -0400   EDT
+
+        Note: The specification of a global time zone registry is not
+        addressed by this document and is left for future study.
+        However, implementers may find the Olson time zone database [<a href="#ref-TZ" title="&quot;ftp://elsie.nci.nih.gov/pub/&quot;">TZ</a>]
+        a useful reference. It is an informal, public-domain collection
+        of time zone information, which is currently being maintained by
+        volunteer Internet participants, and is used in several
+        operating systems. This database contains current and historical
+        time zone information for a wide variety of locations around the
+        globe; it provides a time zone identifier for every unique time
+        zone rule set in actual use since 1970, with historical data
+        going back to the introduction of standard time.
+
+   Interoperability between two calendaring and scheduling applications,
+   especially for recurring events, to-dos or journal entries, is
+   dependent on the ability to capture and convey date and time
+   information in an unambiguous format. The specification of current
+   time zone information is integral to this behavior.
+
+   If present, the "VTIMEZONE" calendar component defines the set of
+   Standard Time and Daylight Saving Time observances (or rules) for a
+   particular time zone for a given interval of time. The "VTIMEZONE"
+   calendar component cannot be nested within other calendar components.
+   Multiple "VTIMEZONE" calendar components can exist in an iCalendar
+   object. In this situation, each "VTIMEZONE" MUST represent a unique
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                    [Page 62]</span>
+</pre><pre class="newpage"><a name="page-63" id="page-63" href="#page-63" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+   time zone definition. This is necessary for some classes of events,
+   such as airline flights, that start in one time zone and end in
+   another.
+
+   The "VTIMEZONE" calendar component MUST be present if the iCalendar
+   object contains an RRULE that generates dates on both sides of a time
+   zone shift (e.g. both in Standard Time and Daylight Saving Time)
+   unless the iCalendar object intends to convey a floating time (See
+   the section "4.1.10.11 Time" for proper interpretation of floating
+   time). It can be present if the iCalendar object does not contain
+   such a RRULE. In addition, if a RRULE is present, there MUST be valid
+   time zone information for all recurrence instances.
+
+   The "VTIMEZONE" calendar component MUST include the "TZID" property
+   and at least one definition of a standard or daylight component. The
+   standard or daylight component MUST include the "DTSTART",
+   "TZOFFSETFROM" and "TZOFFSETTO" properties.
+
+   An individual "VTIMEZONE" calendar component MUST be specified for
+   each unique "TZID" parameter value specified in the iCalendar object.
+
+   Each "VTIMEZONE" calendar component consists of a collection of one
+   or more sub-components that describe the rule for a particular
+   observance (either a Standard Time or a Daylight Saving Time
+   observance). The "STANDARD" sub-component consists of a collection of
+   properties that describe Standard Time. The "DAYLIGHT" sub-component
+   consists of a collection of properties that describe Daylight Saving
+   Time. In general this collection of properties consists of:
+
+        - the first onset date-time for the observance
+
+        - the last onset date-time for the observance, if a last onset
+          is known.
+
+        - the offset to be applied for the observance
+
+        - a rule that describes the day and time when the observance
+          takes effect
+
+        - an optional name for the observance
+
+   For a given time zone, there may be multiple unique definitions of
+   the observances over a period of time. Each observance is described
+   using either a "STANDARD" or "DAYLIGHT" sub-component. The collection
+   of these sub-components is used to describe the time zone for a given
+   period of time. The offset to apply at any given time is found by
+   locating the observance that has the last onset date and time before
+   the time in question, and using the offset value from that
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                    [Page 63]</span>
+</pre><pre class="newpage"><a name="page-64" id="page-64" href="#page-64" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+   observance.
+
+   The top-level properties in a "VTIMEZONE" calendar component are:
+
+   The mandatory "TZID" property is a text value that uniquely
+   identifies the VTIMZONE calendar component within the scope of an
+   iCalendar object.
+
+   The optional "LAST-MODIFIED" property is a UTC value that specifies
+   the date and time that this time zone definition was last updated.
+
+   The optional "TZURL" property is url value that points to a published
+   VTIMEZONE definition. TZURL SHOULD refer to a resource that is
+   accessible by anyone who might need to interpret the object. This
+   SHOULD NOT normally be a file: URL or other URL that is not widely-
+   accessible.
+
+   The collection of properties that are used to define the STANDARD and
+   DAYLIGHT sub-components include:
+
+   The mandatory "DTSTART" property gives the effective onset date and
+   local time for the time zone sub-component definition. "DTSTART" in
+   this usage MUST be specified as a local DATE-TIME value.
+
+   The mandatory "TZOFFSETFROM" property gives the UTC offset which is
+   in use when the onset of this time zone observance begins.
+   "TZOFFSETFROM" is combined with "DTSTART" to define the effective
+   onset for the time zone sub-component definition. For example, the
+   following represents the time at which the observance of Standard
+   Time took effect in Fall 1967 for New York City:
+
+     DTSTART:19671029T020000
+
+     TZOFFSETFROM:-0400
+
+   The mandatory "TZOFFSETTO " property gives the UTC offset for the
+   time zone sub-component (Standard Time or Daylight Saving Time) when
+   this observance is in use.
+
+   The optional "TZNAME" property is the customary name for the time
+   zone. It may be specified multiple times, to allow for specifying
+   multiple language variants of the time zone names. This could be used
+   for displaying dates.
+
+   If specified, the onset for the observance defined by the time zone
+   sub-component is defined by either the "RRULE" or "RDATE" property.
+   If neither is specified, only one sub-component can be specified in
+   the "VTIMEZONE" calendar component and it is assumed that the single
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                    [Page 64]</span>
+</pre><pre class="newpage"><a name="page-65" id="page-65" href="#page-65" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+   observance specified is always in effect.
+
+   The "RRULE" property defines the recurrence rule for the onset of the
+   observance defined by this time zone sub-component. Some specific
+   requirements for the usage of RRULE for this purpose include:
+
+        - If observance is known to have an effective end date, the
+        "UNTIL" recurrence rule parameter MUST be used to specify the
+        last valid onset of this observance (i.e., the UNTIL date-time
+        will be equal to the last instance generated by the recurrence
+        pattern). It MUST be specified in UTC time.
+
+        - The "DTSTART" and the "TZOFFSETTO" properties MUST be used
+        when generating the onset date-time values (instances) from the
+        RRULE.
+
+   Alternatively, the "RDATE" property can be used to define the onset
+   of the observance by giving the individual onset date and times.
+   "RDATE" in this usage MUST be specified as a local DATE-TIME value in
+   UTC time.
+
+   The optional "COMMENT" property is also allowed for descriptive
+   explanatory text.
+
+   Example: The following are examples of the "VTIMEZONE" calendar
+   component:
+
+   This is an example showing time zone information for the Eastern
+   United States using "RDATE" property. Note that this is only suitable
+   for a recurring event that starts on or later than April 6, 1997 at
+   03:00:00 EDT (i.e., the earliest effective transition date and time)
+   and ends no later than April 7, 1998 02:00:00 EST (i.e., latest valid
+   date and time for EST in this scenario). For example, this can be
+   used for a recurring event that occurs every Friday, 8am-9:00 AM,
+   starting June 1, 1997, ending December 31, 1997.
+
+     BEGIN:VTIMEZONE
+     TZID:US-Eastern
+     LAST-MODIFIED:19870101T000000Z
+     BEGIN:STANDARD
+     DTSTART:19971026T020000
+     RDATE:19971026T020000
+     TZOFFSETFROM:-0400
+     TZOFFSETTO:-0500
+     TZNAME:EST
+     END:STANDARD
+     BEGIN:DAYLIGHT
+     DTSTART:19971026T020000
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                    [Page 65]</span>
+</pre><pre class="newpage"><a name="page-66" id="page-66" href="#page-66" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+     RDATE:19970406T020000
+     TZOFFSETFROM:-0500
+     TZOFFSETTO:-0400
+     TZNAME:EDT
+     END:DAYLIGHT
+     END:VTIMEZONE
+
+   This is a simple example showing the current time zone rules for the
+   Eastern United States using a RRULE recurrence pattern. Note that
+   there is no effective end date to either of the Standard Time or
+   Daylight Time rules. This information would be valid for a recurring
+   event starting today and continuing indefinitely.
+
+     BEGIN:VTIMEZONE
+     TZID:US-Eastern
+     LAST-MODIFIED:19870101T000000Z
+     TZURL:http://zones.stds_r_us.net/tz/US-Eastern
+     BEGIN:STANDARD
+     DTSTART:19671029T020000
+     RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
+     TZOFFSETFROM:-0400
+     TZOFFSETTO:-0500
+     TZNAME:EST
+     END:STANDARD
+     BEGIN:DAYLIGHT
+     DTSTART:19870405T020000
+     RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4
+     TZOFFSETFROM:-0500
+     TZOFFSETTO:-0400
+     TZNAME:EDT
+     END:DAYLIGHT
+     END:VTIMEZONE
+
+   This is an example showing a fictitious set of rules for the Eastern
+   United States, where the Daylight Time rule has an effective end date
+   (i.e., after that date, Daylight Time is no longer observed).
+
+     BEGIN:VTIMEZONE
+     TZID:US--Fictitious-Eastern
+     LAST-MODIFIED:19870101T000000Z
+     BEGIN:STANDARD
+     DTSTART:19671029T020000
+     RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
+     TZOFFSETFROM:-0400
+     TZOFFSETTO:-0500
+     TZNAME:EST
+     END:STANDARD
+
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                    [Page 66]</span>
+</pre><pre class="newpage"><a name="page-67" id="page-67" href="#page-67" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+     BEGIN:DAYLIGHT
+     DTSTART:19870405T020000
+     RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4;UNTIL=19980404T070000Z
+     TZOFFSETFROM:-0500
+     TZOFFSETTO:-0400
+     TZNAME:EDT
+     END:DAYLIGHT
+     END:VTIMEZONE
+
+   This is an example showing a fictitious set of rules for the Eastern
+   United States, where the first Daylight Time rule has an effective
+   end date. There is a second Daylight Time rule that picks up where
+   the other left off.
+
+     BEGIN:VTIMEZONE
+     TZID:US--Fictitious-Eastern
+     LAST-MODIFIED:19870101T000000Z
+     BEGIN:STANDARD
+     DTSTART:19671029T020000
+     RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
+     TZOFFSETFROM:-0400
+     TZOFFSETTO:-0500
+     TZNAME:EST
+     END:STANDARD
+     BEGIN:DAYLIGHT
+     DTSTART:19870405T020000
+     RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4;UNTIL=19980404T070000Z
+     TZOFFSETFROM:-0500
+     TZOFFSETTO:-0400
+     TZNAME:EDT
+     END:DAYLIGHT
+     BEGIN:DAYLIGHT
+     DTSTART:19990424T020000
+     RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=4
+     TZOFFSETFROM:-0500
+     TZOFFSETTO:-0400
+     TZNAME:EDT
+     END:DAYLIGHT
+     END:VTIMEZONE
+
+<span class="h4"><h4><a name="section-4.6.6">4.6.6</a> Alarm Component</h4></span>
+
+   Component Name: VALARM
+
+   Purpose: Provide a grouping of component properties that define an
+   alarm.
+
+
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                    [Page 67]</span>
+</pre><pre class="newpage"><a name="page-68" id="page-68" href="#page-68" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+   Formal Definition: A "VALARM" calendar component is defined by the
+   following notation:
+
+          alarmc     = "BEGIN" ":" "VALARM" CRLF
+                       (audioprop / dispprop / emailprop / procprop)
+                       "END" ":" "VALARM" CRLF
+
+     audioprop  = 2*(
+
+                ; 'action' and 'trigger' are both REQUIRED,
+                ; but MUST NOT occur more than once
+
+                action / trigger /
+
+                ; 'duration' and 'repeat' are both optional,
+                ; and MUST NOT occur more than once each,
+                ; but if one occurs, so MUST the other
+
+                duration / repeat /
+
+                ; the following is optional,
+                ; but MUST NOT occur more than once
+
+                attach /
+
+                ; the following is optional,
+                ; and MAY occur more than once
+
+                x-prop
+
+                )
+
+
+
+     dispprop   = 3*(
+
+                ; the following are all REQUIRED,
+                ; but MUST NOT occur more than once
+
+                action / description / trigger /
+
+                ; 'duration' and 'repeat' are both optional,
+                ; and MUST NOT occur more than once each,
+                ; but if one occurs, so MUST the other
+
+                duration / repeat /
+
+                ; the following is optional,
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                    [Page 68]</span>
+</pre><pre class="newpage"><a name="page-69" id="page-69" href="#page-69" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+                ; and MAY occur more than once
+
+                *x-prop
+
+                )
+
+
+
+     emailprop  = 5*(
+
+                ; the following are all REQUIRED,
+                ; but MUST NOT occur more than once
+
+                action / description / trigger / summary
+
+                ; the following is REQUIRED,
+                ; and MAY occur more than once
+
+                attendee /
+
+                ; 'duration' and 'repeat' are both optional,
+                ; and MUST NOT occur more than once each,
+                ; but if one occurs, so MUST the other
+
+                duration / repeat /
+
+                ; the following are optional,
+                ; and MAY occur more than once
+
+                attach / x-prop
+
+                )
+
+
+
+     procprop   = 3*(
+
+                ; the following are all REQUIRED,
+                ; but MUST NOT occur more than once
+
+                action / attach / trigger /
+
+                ; 'duration' and 'repeat' are both optional,
+                ; and MUST NOT occur more than once each,
+                ; but if one occurs, so MUST the other
+
+                duration / repeat /
+
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                    [Page 69]</span>
+</pre><pre class="newpage"><a name="page-70" id="page-70" href="#page-70" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+                ; 'description' is optional,
+                ; and MUST NOT occur more than once
+
+                description /
+
+                ; the following is optional,
+                ; and MAY occur more than once
+
+                x-prop
+
+                )
+
+   Description: A "VALARM" calendar component is a grouping of component
+   properties that is a reminder or alarm for an event or a to-do. For
+   example, it may be used to define a reminder for a pending event or
+   an overdue to-do.
+
+   The "VALARM" calendar component MUST include the "ACTION" and
+   "TRIGGER" properties. The "ACTION" property further constrains the
+   "VALARM" calendar component in the following ways:
+
+   When the action is "AUDIO", the alarm can also include one and only
+   one "ATTACH" property, which MUST point to a sound resource, which is
+   rendered when the alarm is triggered.
+
+   When the action is "DISPLAY", the alarm MUST also include a
+   "DESCRIPTION" property, which contains the text to be displayed when
+   the alarm is triggered.
+
+   When the action is "EMAIL", the alarm MUST include a "DESCRIPTION"
+   property, which contains the text to be used as the message body, a
+   "SUMMARY" property, which contains the text to be used as the message
+   subject, and one or more "ATTENDEE" properties, which contain the
+   email address of attendees to receive the message. It can also
+   include one or more "ATTACH" properties, which are intended to be
+   sent as message attachments. When the alarm is triggered, the email
+   message is sent.
+
+   When the action is "PROCEDURE", the alarm MUST include one and only
+   one "ATTACH" property, which MUST point to a procedure resource,
+   which is invoked when the alarm is triggered.
+
+   The "VALARM" calendar component MUST only appear within either a
+   "VEVENT" or "VTODO" calendar component. "VALARM" calendar components
+   cannot be nested. Multiple mutually independent "VALARM" calendar
+   components can be specified for a single "VEVENT" or "VTODO" calendar
+   component.
+
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                    [Page 70]</span>
+</pre><pre class="newpage"><a name="page-71" id="page-71" href="#page-71" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+   The "TRIGGER" property specifies when the alarm will be triggered.
+   The "TRIGGER" property specifies a duration prior to the start of an
+   event or a to-do. The "TRIGGER" edge may be explicitly set to be
+   relative to the "START" or "END" of the event or to-do with the
+   "RELATED" parameter of the "TRIGGER" property. The "TRIGGER" property
+   value type can alternatively be set to an absolute calendar date and
+   time of day value.
+
+   In an alarm set to trigger on the "START" of an event or to-do, the
+   "DTSTART" property MUST be present in the associated event or to-do.
+   In an alarm in a "VEVENT" calendar component set to trigger on the
+   "END" of the event, either the "DTEND" property MUST be present, or
+   the "DTSTART" and "DURATION" properties MUST both be present. In an
+   alarm in a "VTODO" calendar component set to trigger on the "END" of
+   the to-do, either the "DUE" property MUST be present, or the
+   "DTSTART" and "DURATION" properties MUST both be present.
+
+   The alarm can be defined such that it triggers repeatedly. A
+   definition of an alarm with a repeating trigger MUST include both the
+   "DURATION" and "REPEAT" properties. The "DURATION" property specifies
+   the delay period, after which the alarm will repeat. The "REPEAT"
+   property specifies the number of additional repetitions that the
+   alarm will triggered. This repitition count is in addition to the
+   initial triggering of the alarm. Both of these properties MUST be
+   present in order to specify a repeating alarm. If one of these two
+   properties is absent, then the alarm will not repeat beyond the
+   initial trigger.
+
+   The "ACTION" property is used within the "VALARM" calendar component
+   to specify the type of action invoked when the alarm is triggered.
+   The "VALARM" properties provide enough information for a specific
+   action to be invoked. It is typically the responsibility of a
+   "Calendar User Agent" (CUA) to deliver the alarm in the specified
+   fashion. An "ACTION" property value of AUDIO specifies an alarm that
+   causes a sound to be played to alert the user; DISPLAY specifies an
+   alarm that causes a text message to be displayed to the user; EMAIL
+   specifies an alarm that causes an electronic email message to be
+   delivered to one or more email addresses; and PROCEDURE specifies an
+   alarm that causes a procedure to be executed. The "ACTION" property
+   MUST specify one and only one of these values.
+
+   In an AUDIO alarm, if the optional "ATTACH" property is included, it
+   MUST specify an audio sound resource. The intention is that the sound
+   will be played as the alarm effect. If an "ATTACH" property is
+   specified that does not refer to a sound resource, or if the
+   specified sound resource cannot be rendered (because its format is
+   unsupported, or because it cannot be retrieved), then the CUA or
+   other entity responsible for playing the sound may choose a fallback
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                    [Page 71]</span>
+</pre><pre class="newpage"><a name="page-72" id="page-72" href="#page-72" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+   action, such as playing a built-in default sound, or playing no sound
+   at all.
+
+   In a DISPLAY alarm, the intended alarm effect is for the text value
+   of the "DESCRIPTION" property to be displayed to the user.
+
+   In an EMAIL alarm, the intended alarm effect is for an email message
+   to be composed and delivered to all the addresses specified by the
+   "ATTENDEE" properties in the "VALARM" calendar component. The
+   "DESCRIPTION" property of the "VALARM" calendar component MUST be
+   used as the body text of the message, and the "SUMMARY" property MUST
+   be used as the subject text. Any "ATTACH" properties in the "VALARM"
+   calendar component SHOULD be sent as attachments to the message.
+
+   In a PROCEDURE alarm, the "ATTACH" property in the "VALARM" calendar
+   component MUST specify a procedure or program that is intended to be
+   invoked as the alarm effect. If the procedure or program is in a
+   format that cannot be rendered, then no procedure alarm will be
+   invoked. If the "DESCRIPTION" property is present, its value
+   specifies the argument string to be passed to the procedure or
+   program. "Calendar User Agents" that receive an iCalendar object with
+   this category of alarm, can disable or allow the "Calendar User" to
+   disable, or otherwise ignore this type of alarm. While a very useful
+   alarm capability, the PROCEDURE type of alarm SHOULD be treated by
+   the "Calendar User Agent" as a potential security risk.
+
+   Example: The following example is for a "VALARM" calendar component
+   that specifies an audio alarm that will sound at a precise time and
+   repeat 4 more times at 15 minute intervals:
+
+     BEGIN:VALARM
+     TRIGGER;VALUE=DATE-TIME:19970317T133000Z
+     REPEAT:4
+     DURATION:PT15M
+     ACTION:AUDIO
+     ATTACH;FMTTYPE=audio/basic:ftp://host.com/pub/sounds/bell-01.aud
+     END:VALARM
+
+   The following example is for a "VALARM" calendar component that
+   specifies a display alarm that will trigger 30 minutes before the
+   scheduled start of the event or the due date/time of the to-do it is
+   associated with and will repeat 2 more times at 15 minute intervals:
+
+     BEGIN:VALARM
+     TRIGGER:-PT30M
+     REPEAT:2
+     DURATION:PT15M
+     ACTION:DISPLAY
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                    [Page 72]</span>
+</pre><pre class="newpage"><a name="page-73" id="page-73" href="#page-73" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+     DESCRIPTION:Breakfast meeting with executive\n
+      team at 8:30 AM EST.
+     END:VALARM
+
+   The following example is for a "VALARM" calendar component that
+   specifies an email alarm that will trigger 2 days before the
+   scheduled due date/time of a to-do it is associated with. It does not
+   repeat. The email has a subject, body and attachment link.
+
+     BEGIN:VALARM
+     TRIGGER:-P2D
+     ACTION:EMAIL
+     ATTENDEE:MAILTO:john_doe@host.com
+     SUMMARY:*** REMINDER: SEND AGENDA FOR WEEKLY STAFF MEETING ***
+     DESCRIPTION:A draft agenda needs to be sent out to the attendees
+       to the weekly managers meeting (MGR-LIST). Attached is a
+       pointer the document template for the agenda file.
+     ATTACH;FMTTYPE=application/binary:http://host.com/templates/agen
+      da.doc
+     END:VALARM
+
+   The following example is for a "VALARM" calendar component that
+   specifies a procedural alarm that will trigger at a precise date/time
+   and will repeat 23 more times at one hour intervals. The alarm will
+   invoke a procedure file.
+
+     BEGIN:VALARM
+     TRIGGER;VALUE=DATE-TIME:19980101T050000Z
+     REPEAT:23
+     DURATION:PT1H
+     ACTION:PROCEDURE
+     ATTACH;FMTTYPE=application/binary:ftp://host.com/novo-
+      procs/felizano.exe
+     END:VALARM
+
+<span class="h3"><h3><a name="section-4.7">4.7</a> Calendar Properties</h3></span>
+
+   The Calendar Properties are attributes that apply to the iCalendar
+   object, as a whole. These properties do not appear within a calendar
+   component. They SHOULD be specified after the "BEGIN:VCALENDAR"
+   property and prior to any calendar component.
+
+<span class="h4"><h4><a name="section-4.7.1">4.7.1</a> Calendar Scale</h4></span>
+
+   Property Name: CALSCALE
+
+   Purpose: This property defines the calendar scale used for the
+   calendar information specified in the iCalendar object.
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                    [Page 73]</span>
+</pre><pre class="newpage"><a name="page-74" id="page-74" href="#page-74" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+   Value Type: TEXT
+
+   Property Parameters: Non-standard property parameters can be
+   specified on this property.
+
+   Conformance: Property can be specified in an iCalendar object. The
+   default value is "GREGORIAN".
+
+   Description: This memo is based on the Gregorian calendar scale. The
+   Gregorian calendar scale is assumed if this property is not specified
+   in the iCalendar object. It is expected that other calendar scales
+   will be defined in other specifications or by future versions of this
+   memo.
+
+   Format Definition: The property is defined by the following notation:
+
+     calscale   = "CALSCALE" calparam ":" calvalue CRLF
+
+     calparam   = *(";" xparam)
+
+     calvalue   = "GREGORIAN" / iana-token
+
+   Example: The following is an example of this property:
+
+     CALSCALE:GREGORIAN
+
+<span class="h4"><h4><a name="section-4.7.2">4.7.2</a> Method</h4></span>
+
+   Property Name: METHOD
+
+   Purpose: This property defines the iCalendar object method associated
+   with the calendar object.
+
+   Value Type: TEXT
+
+   Property Parameters: Non-standard property parameters can be
+   specified on this property.
+
+   Conformance: The property can be specified in an iCalendar object.
+
+   Description: When used in a MIME message entity, the value of this
+   property MUST be the same as the Content-Type "method" parameter
+   value. This property can only appear once within the iCalendar
+   object. If either the "METHOD" property or the Content-Type "method"
+   parameter is specified, then the other MUST also be specified.
+
+   No methods are defined by this specification. This is the subject of
+   other specifications, such as the iCalendar Transport-independent
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                    [Page 74]</span>
+</pre><pre class="newpage"><a name="page-75" id="page-75" href="#page-75" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+   Interoperability Protocol (iTIP) defined by [<a href="#ref-ITIP" title="&quot;iCalendar Transport-Independent Interoperability Protocol (iTIP) : Scheduling Events, Busy Time, To-dos and Journal Entries&quot;">ITIP</a>].
+
+   If this property is not present in the iCalendar object, then a
+   scheduling transaction MUST NOT be assumed. In such cases, the
+   iCalendar object is merely being used to transport a snapshot of some
+   calendar information; without the intention of conveying a scheduling
+   semantic.
+
+   Format Definition: The property is defined by the following notation:
+
+     method     = "METHOD" metparam ":" metvalue CRLF
+
+     metparam   = *(";" xparam)
+
+     metvalue   = iana-token
+
+   Example: The following is a hypothetical example of this property to
+   convey that the iCalendar object is a request for a meeting:
+
+     METHOD:REQUEST
+
+<span class="h4"><h4><a name="section-4.7.3">4.7.3</a> Product Identifier</h4></span>
+
+   Property Name: PRODID
+
+   Purpose: This property specifies the identifier for the product that
+   created the iCalendar object.
+
+   Value Type: TEXT
+
+   Property Parameters: Non-standard property parameters can be
+   specified on this property.
+
+   Conformance: The property MUST be specified once in an iCalendar
+   object.
+
+   Description: The vendor of the implementation SHOULD assure that this
+   is a globally unique identifier; using some technique such as an FPI
+   value, as defined in [ISO 9070].
+
+   This property SHOULD not be used to alter the interpretation of an
+   iCalendar object beyond the semantics specified in this memo. For
+   example, it is not to be used to further the understanding of non-
+   standard properties.
+
+   Format Definition: The property is defined by the following notation:
+
+     prodid     = "PRODID" pidparam ":" pidvalue CRLF
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                    [Page 75]</span>
+</pre><pre class="newpage"><a name="page-76" id="page-76" href="#page-76" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+     pidparam   = *(";" xparam)
+
+     pidvalue   = text
+     ;Any text that describes the product and version
+     ;and that is generally assured of being unique.
+
+   Example: The following is an example of this property. It does not
+   imply that English is the default language.
+
+     PRODID:-//ABC Corporation//NONSGML My Product//EN
+
+<span class="h4"><h4><a name="section-4.7.4">4.7.4</a> Version</h4></span>
+
+   Property Name: VERSION
+
+   Purpose: This property specifies the identifier corresponding to the
+   highest version number or the minimum and maximum range of the
+   iCalendar specification that is required in order to interpret the
+   iCalendar object.
+
+   Value Type: TEXT
+
+   Property Parameters: Non-standard property parameters can be
+   specified on this property.
+
+   Conformance: This property MUST be specified by an iCalendar object,
+   but MUST only be specified once.
+
+   Description: A value of "2.0" corresponds to this memo.
+
+   Format Definition: The property is defined by the following notation:
+
+     version    = "VERSION" verparam ":" vervalue CRLF
+
+     verparam   = *(";" xparam)
+
+     vervalue   = "2.0"         ;This memo
+                / maxver
+                / (minver ";" maxver)
+
+     minver     = &lt;A IANA registered iCalendar version identifier&gt;
+     ;Minimum iCalendar version needed to parse the iCalendar object
+
+     maxver     = &lt;A IANA registered iCalendar version identifier&gt;
+     ;Maximum iCalendar version needed to parse the iCalendar object
+
+   Example: The following is an example of this property:
+
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                    [Page 76]</span>
+</pre><pre class="newpage"><a name="page-77" id="page-77" href="#page-77" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+     VERSION:2.0
+
+<span class="h3"><h3><a name="section-4.8">4.8</a> Component Properties</h3></span>
+
+   The following properties can appear within calendar components, as
+   specified by each component property definition.
+
+<span class="h4"><h4><a name="section-4.8.1">4.8.1</a> Descriptive Component Properties</h4></span>
+
+   The following properties specify descriptive information about
+   calendar components.
+
+<span class="h5"><h5><a name="section-4.8.1.1">4.8.1.1</a> Attachment</h5></span>
+
+   Property Name: ATTACH
+
+   Purpose: The property provides the capability to associate a document
+   object with a calendar component.
+
+   Value Type: The default value type for this property is URI. The
+   value type can also be set to BINARY to indicate inline binary
+   encoded content information.
+
+   Property Parameters: Non-standard, inline encoding, format type and
+   value data type property parameters can be specified on this
+   property.
+
+   Conformance: The property can be specified in a "VEVENT", "VTODO",
+   "VJOURNAL" or "VALARM" calendar components.
+
+   Description: The property can be specified within "VEVENT", "VTODO",
+   "VJOURNAL", or "VALARM" calendar components. This property can be
+   specified multiple times within an iCalendar object.
+
+   Format Definition: The property is defined by the following notation:
+
+     attach     = "ATTACH" attparam ":" uri  CRLF
+
+     attach     =/ "ATTACH" attparam ";" "ENCODING" "=" "BASE64"
+                   ";" "VALUE" "=" "BINARY" ":" binary
+
+     attparam   = *(
+
+                ; the following is optional,
+                ; but MUST NOT occur more than once
+
+                (";" fmttypeparam) /
+
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                    [Page 77]</span>
+</pre><pre class="newpage"><a name="page-78" id="page-78" href="#page-78" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+                ; the following is optional,
+                ; and MAY occur more than once
+
+                (";" xparam)
+
+                )
+
+   Example: The following are examples of this property:
+
+     ATTACH:CID:jsmith.part3.960817T083000.xyzMail@host1.com
+
+     ATTACH;FMTTYPE=application/postscript:ftp://xyzCorp.com/pub/
+      reports/r-960812.ps
+
+<span class="h5"><h5><a name="section-4.8.1.2">4.8.1.2</a> Categories</h5></span>
+
+   Property Name: CATEGORIES
+
+   Purpose: This property defines the categories for a calendar
+   component.
+
+   Value Type: TEXT
+
+   Property Parameters: Non-standard and language property parameters
+   can be specified on this property.
+
+   Conformance: The property can be specified within "VEVENT", "VTODO"
+   or "VJOURNAL" calendar components.
+
+   Description: This property is used to specify categories or subtypes
+   of the calendar component. The categories are useful in searching for
+   a calendar component of a particular type and category. Within the
+   "VEVENT", "VTODO" or "VJOURNAL" calendar components, more than one
+   category can be specified as a list of categories separated by the
+   COMMA character (US-ASCII decimal 44).
+
+   Format Definition: The property is defined by the following notation:
+
+     categories = "CATEGORIES" catparam ":" text *("," text)
+                  CRLF
+
+     catparam   = *(
+
+                ; the following is optional,
+                ; but MUST NOT occur more than once
+
+                (";" languageparam ) /
+
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                    [Page 78]</span>
+</pre><pre class="newpage"><a name="page-79" id="page-79" href="#page-79" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+                ; the following is optional,
+                ; and MAY occur more than once
+
+                (";" xparam)
+
+                )
+
+   Example: The following are examples of this property:
+
+     CATEGORIES:APPOINTMENT,EDUCATION
+
+     CATEGORIES:MEETING
+
+<span class="h5"><h5><a name="section-4.8.1.3">4.8.1.3</a> Classification</h5></span>
+
+   Property Name: CLASS
+
+   Purpose: This property defines the access classification for a
+   calendar component.
+
+   Value Type: TEXT
+
+   Property Parameters: Non-standard property parameters can be
+   specified on this property.
+
+   Conformance: The property can be specified once in a "VEVENT",
+   "VTODO" or "VJOURNAL" calendar components.
+
+   Description: An access classification is only one component of the
+   general security system within a calendar application. It provides a
+   method of capturing the scope of the access the calendar owner
+   intends for information within an individual calendar entry. The
+   access classification of an individual iCalendar component is useful
+   when measured along with the other security components of a calendar
+   system (e.g., calendar user authentication, authorization, access
+   rights, access role, etc.). Hence, the semantics of the individual
+   access classifications cannot be completely defined by this memo
+   alone. Additionally, due to the "blind" nature of most exchange
+   processes using this memo, these access classifications cannot serve
+   as an enforcement statement for a system receiving an iCalendar
+   object. Rather, they provide a method for capturing the intention of
+   the calendar owner for the access to the calendar component.
+
+   Format Definition: The property is defined by the following notation:
+
+     class      = "CLASS" classparam ":" classvalue CRLF
+
+     classparam = *(";" xparam)
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                    [Page 79]</span>
+</pre><pre class="newpage"><a name="page-80" id="page-80" href="#page-80" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+     classvalue = "PUBLIC" / "PRIVATE" / "CONFIDENTIAL" / iana-token
+                / x-name
+     ;Default is PUBLIC
+
+   Example: The following is an example of this property:
+
+     CLASS:PUBLIC
+
+<span class="h5"><h5><a name="section-4.8.1.4">4.8.1.4</a> Comment</h5></span>
+
+   Property Name: COMMENT
+
+   Purpose: This property specifies non-processing information intended
+   to provide a comment to the calendar user.
+
+   Value Type: TEXT
+
+   Property Parameters: Non-standard, alternate text representation and
+   language property parameters can be specified on this property.
+
+   Conformance: This property can be specified in "VEVENT", "VTODO",
+   "VJOURNAL", "VTIMEZONE" or "VFREEBUSY" calendar components.
+
+   Description: The property can be specified multiple times.
+
+   Format Definition: The property is defined by the following notation:
+
+     comment    = "COMMENT" commparam ":" text CRLF
+
+     commparam  = *(
+
+                ; the following are optional,
+                ; but MUST NOT occur more than once
+
+                (";" altrepparam) / (";" languageparam) /
+
+                ; the following is optional,
+                ; and MAY occur more than once
+
+                (";" xparam)
+
+                )
+
+   Example: The following is an example of this property:
+
+     COMMENT:The meeting really needs to include both ourselves
+       and the customer. We can't hold this  meeting without them.
+       As a matter of fact\, the venue for the meeting ought to be at
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                    [Page 80]</span>
+</pre><pre class="newpage"><a name="page-81" id="page-81" href="#page-81" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+       their site. - - John
+
+   The data type for this property is TEXT.
+
+<span class="h5"><h5><a name="section-4.8.1.5">4.8.1.5</a> Description</h5></span>
+
+   Property Name: DESCRIPTION
+
+   Purpose: This property provides a more complete description of the
+   calendar component, than that provided by the "SUMMARY" property.
+
+   Value Type: TEXT
+
+   Property Parameters: Non-standard, alternate text representation and
+   language property parameters can be specified on this property.
+
+   Conformance: The property can be specified in the "VEVENT", "VTODO",
+   "VJOURNAL" or "VALARM" calendar components. The property can be
+   specified multiple times only within a "VJOURNAL" calendar component.
+
+   Description: This property is used in the "VEVENT" and "VTODO" to
+   capture lengthy textual decriptions associated with the activity.
+
+   This property is used in the "VJOURNAL" calendar component to capture
+   one more textual journal entries.
+
+   This property is used in the "VALARM" calendar component to capture
+   the display text for a DISPLAY category of alarm, to capture the body
+   text for an EMAIL category of alarm and to capture the argument
+   string for a PROCEDURE category of alarm.
+
+   Format Definition: The property is defined by the following notation:
+
+     description        = "DESCRIPTION" descparam ":" text CRLF
+
+     descparam  = *(
+
+                ; the following are optional,
+                ; but MUST NOT occur more than once
+
+                (";" altrepparam) / (";" languageparam) /
+
+                ; the following is optional,
+                ; and MAY occur more than once
+
+                (";" xparam)
+
+                )
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                    [Page 81]</span>
+</pre><pre class="newpage"><a name="page-82" id="page-82" href="#page-82" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+   Example: The following is an example of the property with formatted
+   line breaks in the property value:
+
+     DESCRIPTION:Meeting to provide technical review for "Phoenix"
+       design.\n Happy Face Conference Room. Phoenix design team
+       MUST attend this meeting.\n RSVP to team leader.
+
+   The following is an example of the property with folding of long
+   lines:
+
+     DESCRIPTION:Last draft of the new novel is to be completed
+       for the editor's proof today.
+
+<span class="h5"><h5><a name="section-4.8.1.6">4.8.1.6</a> Geographic Position</h5></span>
+
+   Property Name: GEO
+
+   Purpose: This property specifies information related to the global
+   position for the activity specified by a calendar component.
+
+   Value Type: FLOAT. The value MUST be two SEMICOLON separated FLOAT
+   values.
+
+   Property Parameters: Non-standard property parameters can be
+   specified on this property.
+
+   Conformance: This property can be specified in  "VEVENT" or "VTODO"
+   calendar components.
+
+   Description: The property value specifies latitude and longitude, in
+   that order (i.e., "LAT LON" ordering). The longitude represents the
+   location east or west of the prime meridian as a positive or negative
+   real number, respectively. The longitude and latitude values MAY be
+   specified up to six decimal places, which will allow for accuracy to
+   within one meter of geographical position. Receiving applications
+   MUST accept values of this precision and MAY truncate values of
+   greater precision.
+
+   Values for latitude and longitude shall be expressed as decimal
+   fractions of degrees. Whole degrees of latitude shall be represented
+   by a two-digit decimal number ranging from 0 through 90. Whole
+   degrees of longitude shall be represented by a decimal number ranging
+   from 0 through 180. When a decimal fraction of a degree is specified,
+   it shall be separated from the whole number of degrees by a decimal
+   point.
+
+
+
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                    [Page 82]</span>
+</pre><pre class="newpage"><a name="page-83" id="page-83" href="#page-83" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+   Latitudes north of the equator shall be specified by a plus sign (+),
+   or by the absence of a minus sign (-), preceding the digits
+   designating degrees. Latitudes south of the Equator shall be
+   designated by a minus sign (-) preceding the digits designating
+   degrees. A point on the Equator shall be assigned to the Northern
+   Hemisphere.
+
+   Longitudes east of the prime meridian shall be specified by a plus
+   sign (+), or by the absence of a minus sign (-), preceding the digits
+   designating degrees. Longitudes west of the meridian shall be
+   designated by minus sign (-) preceding the digits designating
+   degrees. A point on the prime meridian shall be assigned to the
+   Eastern Hemisphere. A point on the 180th meridian shall be assigned
+   to the Western Hemisphere. One exception to this last convention is
+   permitted. For the special condition of describing a band of latitude
+   around the earth, the East Bounding Coordinate data element shall be
+   assigned the value +180 (180) degrees.
+
+   Any spatial address with a latitude of +90 (90) or -90 degrees will
+   specify the position at the North or South Pole, respectively. The
+   component for longitude may have any legal value.
+
+   With the exception of the special condition described above, this
+   form is specified in Department of Commerce, 1986, Representation of
+   geographic point locations for information interchange (Federal
+   Information Processing Standard 70-1):  Washington,  Department of
+   Commerce, National Institute of Standards and Technology.
+
+   The simple formula for converting degrees-minutes-seconds into
+   decimal degrees is:
+
+     decimal = degrees + minutes/60 + seconds/3600.
+
+   Format Definition: The property is defined by the following notation:
+
+     geo        = "GEO" geoparam ":" geovalue CRLF
+
+     geoparam   = *(";" xparam)
+
+     geovalue   = float ";" float
+     ;Latitude and Longitude components
+
+   Example: The following is an example of this property:
+
+     GEO:37.386013;-122.082932
+
+
+
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                    [Page 83]</span>
+</pre><pre class="newpage"><a name="page-84" id="page-84" href="#page-84" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+<span class="h5"><h5><a name="section-4.8.1.7">4.8.1.7</a> Location</h5></span>
+
+   Property Name: LOCATION
+
+   Purpose: The property defines the intended venue for the activity
+   defined by a calendar component.
+
+   Value Type: TEXT
+
+   Property Parameters: Non-standard, alternate text representation and
+   language property parameters can be specified on this property.
+
+   Conformance: This property can be specified in "VEVENT" or "VTODO"
+   calendar component.
+
+   Description: Specific venues such as conference or meeting rooms may
+   be explicitly specified using this property. An alternate
+   representation may be specified that is a URI that points to
+   directory information with more structured specification of the
+   location. For example, the alternate representation may specify
+   either an LDAP URI pointing to an LDAP server entry or a CID URI
+   pointing to a MIME body part containing a vCard [<a href="http://tools.ietf.org/html/rfc2426" title="&quot;vCard MIME Directory Profile&quot;">RFC 2426</a>] for the
+   location.
+
+   Format Definition: The property is defined by the following notation:
+
+     location   = "LOCATION locparam ":" text CRLF
+
+     locparam   = *(
+
+                ; the following are optional,
+                ; but MUST NOT occur more than once
+
+                (";" altrepparam) / (";" languageparam) /
+
+                ; the following is optional,
+                ; and MAY occur more than once
+
+                (";" xparam)
+
+                )
+
+   Example: The following are some examples of this property:
+
+     LOCATION:Conference Room - F123, Bldg. 002
+
+     LOCATION;ALTREP="http://xyzcorp.com/conf-rooms/f123.vcf":
+      Conference Room - F123, Bldg. 002
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                    [Page 84]</span>
+</pre><pre class="newpage"><a name="page-85" id="page-85" href="#page-85" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+<span class="h5"><h5><a name="section-4.8.1.8">4.8.1.8</a> Percent Complete</h5></span>
+
+   Property Name: PERCENT-COMPLETE
+
+   Purpose: This property is used by an assignee or delegatee of a to-do
+   to convey the percent completion of a to-do to the Organizer.
+
+   Value Type: INTEGER
+
+   Property Parameters: Non-standard property parameters can be
+   specified on this property.
+
+   Conformance: This property can be specified in a "VTODO" calendar
+   component.
+
+   Description: The property value is a positive integer between zero
+   and one hundred. A value of "0" indicates the to-do has not yet been
+   started. A value of "100" indicates that the to-do has been
+   completed. Integer values in between indicate the percent partially
+   complete.
+
+   When a to-do is assigned to multiple individuals, the property value
+   indicates the percent complete for that portion of the to-do assigned
+   to the assignee or delegatee. For example, if a to-do is assigned to
+   both individuals "A" and "B". A reply from "A" with a percent
+   complete of "70" indicates that "A" has completed 70% of the to-do
+   assigned to them. A reply from "B" with a percent complete of "50"
+   indicates "B" has completed 50% of the to-do assigned to them.
+
+   Format Definition: The property is defined by the following notation:
+
+     percent = "PERCENT-COMPLETE" pctparam ":" integer CRLF
+
+     pctparam   = *(";" xparam)
+
+   Example: The following is an example of this property to show 39%
+   completion:
+
+     PERCENT-COMPLETE:39
+
+<span class="h5"><h5><a name="section-4.8.1.9">4.8.1.9</a> Priority</h5></span>
+
+   Property Name: PRIORITY
+
+   Purpose: The property defines the relative priority for a calendar
+   component.
+
+   Value Type: INTEGER
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                    [Page 85]</span>
+</pre><pre class="newpage"><a name="page-86" id="page-86" href="#page-86" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+   Property Parameters: Non-standard property parameters can be
+   specified on this property.
+
+   Conformance: The property can be specified in a "VEVENT" or "VTODO"
+   calendar component.
+
+   Description: The priority is specified as an integer in the range
+   zero to nine. A value of zero (US-ASCII decimal 48) specifies an
+   undefined priority. A value of one (US-ASCII decimal 49) is the
+   highest priority. A value of two (US-ASCII decimal 50) is the second
+   highest priority. Subsequent numbers specify a decreasing ordinal
+   priority. A value of nine (US-ASCII decimal 58) is the lowest
+   priority.
+
+   A CUA with a three-level priority scheme of "HIGH", "MEDIUM" and
+   "LOW" is mapped into this property such that a property value in the
+   range of one (US-ASCII decimal 49) to four (US-ASCII decimal 52)
+   specifies "HIGH" priority. A value of five (US-ASCII decimal 53) is
+   the normal or "MEDIUM" priority. A value in the range of six (US-
+   ASCII decimal 54) to nine (US-ASCII decimal 58) is "LOW" priority.
+
+   A CUA with a priority schema of "A1", "A2", "A3", "B1", "B2", ...,
+   "C3" is mapped into this property such that a property value of one
+   (US-ASCII decimal 49) specifies "A1", a property value of two (US-
+   ASCII decimal 50) specifies "A2", a property value of three (US-ASCII
+   decimal 51) specifies "A3", and so forth up to a property value of 9
+   (US-ASCII decimal 58) specifies "C3".
+
+   Other integer values are reserved for future use.
+
+   Within a "VEVENT" calendar component, this property specifies a
+   priority for the event. This property may be useful when more than
+   one event is scheduled for a given time period.
+
+   Within a "VTODO" calendar component, this property specified a
+   priority for the to-do. This property is useful in prioritizing
+   multiple action items for a given time period.
+
+   Format Definition: The property is specified by the following
+   notation:
+
+     priority   = "PRIORITY" prioparam ":" privalue CRLF
+     ;Default is zero
+
+     prioparam  = *(";" xparam)
+
+     privalue   = integer       ;Must be in the range [<a href="#ref-0..9">0..9</a>]
+        ; All other values are reserved for future use
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                    [Page 86]</span>
+</pre><pre class="newpage"><a name="page-87" id="page-87" href="#page-87" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+   The following is an example of a property with the highest priority:
+
+     PRIORITY:1
+
+   The following is an example of a property with a next highest
+   priority:
+
+     PRIORITY:2
+
+   Example: The following is an example of a property with no priority.
+   This is equivalent to not specifying the "PRIORITY" property:
+
+     PRIORITY:0
+
+<span class="h5"><h5><a name="section-4.8.1.10">4.8.1.10</a> Resources</h5></span>
+
+   Property Name: RESOURCES
+
+   Purpose: This property defines the equipment or resources anticipated
+   for an activity specified by a calendar entity..
+
+   Value Type: TEXT
+
+   Property Parameters: Non-standard, alternate text representation and
+   language property parameters can be specified on this property.
+
+   Conformance: This property can be specified in "VEVENT" or "VTODO"
+   calendar component.
+
+   Description: The property value is an arbitrary text. More than one
+   resource can be specified as a list of resources separated by the
+   COMMA character (US-ASCII decimal 44).
+
+   Format Definition: The property is defined by the following notation:
+
+     resources  = "RESOURCES" resrcparam ":" text *("," text) CRLF
+
+     resrcparam = *(
+
+                ; the following are optional,
+                ; but MUST NOT occur more than once
+
+                (";" altrepparam) / (";" languageparam) /
+
+                ; the following is optional,
+                ; and MAY occur more than once
+
+
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                    [Page 87]</span>
+</pre><pre class="newpage"><a name="page-88" id="page-88" href="#page-88" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+                (";" xparam)
+
+                )
+
+   Example: The following is an example of this property:
+
+     RESOURCES:EASEL,PROJECTOR,VCR
+
+     RESOURCES;LANGUAGE=fr:1 raton-laveur
+
+<span class="h5"><h5><a name="section-4.8.1.11">4.8.1.11</a> Status</h5></span>
+
+   Property Name: STATUS
+
+   Purpose: This property defines the overall status or confirmation for
+   the calendar component.
+
+   Value Type: TEXT
+
+   Property Parameters: Non-standard property parameters can be
+   specified on this property.
+
+   Conformance: This property can be specified in "VEVENT", "VTODO" or
+   "VJOURNAL" calendar components.
+
+   Description: In a group scheduled calendar component, the property is
+   used by the "Organizer" to provide a confirmation of the event to the
+   "Attendees". For example in a "VEVENT" calendar component, the
+   "Organizer" can indicate that a meeting is tentative, confirmed or
+   cancelled. In a "VTODO" calendar component, the "Organizer" can
+   indicate that an action item needs action, is completed, is in
+   process or being worked on, or has been cancelled. In a "VJOURNAL"
+   calendar component, the "Organizer" can indicate that a journal entry
+   is draft, final or has been cancelled or removed.
+
+   Format Definition: The property is defined by the following notation:
+
+     status     = "STATUS" statparam] ":" statvalue CRLF
+
+     statparam  = *(";" xparam)
+
+     statvalue  = "TENTATIVE"           ;Indicates event is
+                                        ;tentative.
+                / "CONFIRMED"           ;Indicates event is
+                                        ;definite.
+                / "CANCELLED"           ;Indicates event was
+                                        ;cancelled.
+        ;Status values for a "VEVENT"
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                    [Page 88]</span>
+</pre><pre class="newpage"><a name="page-89" id="page-89" href="#page-89" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+     statvalue  =/ "NEEDS-ACTION"       ;Indicates to-do needs action.
+                / "COMPLETED"           ;Indicates to-do completed.
+                / "IN-PROCESS"          ;Indicates to-do in process of
+                / "CANCELLED"           ;Indicates to-do was cancelled.
+        ;Status values for "VTODO".
+
+     statvalue  =/ "DRAFT"              ;Indicates journal is draft.
+                / "FINAL"               ;Indicates journal is final.
+                / "CANCELLED"           ;Indicates journal is removed.
+        ;Status values for "VJOURNAL".
+
+   Example: The following is an example of this property for a "VEVENT"
+   calendar component:
+
+     STATUS:TENTATIVE
+
+   The following is an example of this property for a "VTODO" calendar
+   component:
+
+     STATUS:NEEDS-ACTION
+
+   The following is an example of this property for a "VJOURNAL"
+   calendar component:
+
+     STATUS:DRAFT
+
+<span class="h5"><h5><a name="section-4.8.1.12">4.8.1.12</a> Summary</h5></span>
+
+   Property Name: SUMMARY
+
+   Purpose: This property defines a short summary or subject for the
+   calendar component.
+
+   Value Type: TEXT
+
+   Property Parameters: Non-standard, alternate text representation and
+   language property parameters can be specified on this property.
+
+   Conformance: The property can be specified in "VEVENT", "VTODO",
+   "VJOURNAL" or "VALARM" calendar components.
+
+   Description: This property is used in the "VEVENT", "VTODO" and
+   "VJOURNAL" calendar components to capture a short, one line summary
+   about the activity or journal entry.
+
+   This property is used in the "VALARM" calendar component to capture
+   the subject of an EMAIL category of alarm.
+
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                    [Page 89]</span>
+</pre><pre class="newpage"><a name="page-90" id="page-90" href="#page-90" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+   Format Definition: The property is defined by the following notation:
+
+     summary    = "SUMMARY" summparam ":" text CRLF
+
+     summparam  = *(
+
+                ; the following are optional,
+                ; but MUST NOT occur more than once
+
+                (";" altrepparam) / (";" languageparam) /
+
+                ; the following is optional,
+                ; and MAY occur more than once
+
+                (";" xparam)
+
+                )
+
+   Example: The following is an example of this property:
+
+     SUMMARY:Department Party
+
+<span class="h4"><h4><a name="section-4.8.2">4.8.2</a> Date and Time Component Properties</h4></span>
+
+   The following properties specify date and time related information in
+   calendar components.
+
+<span class="h5"><h5><a name="section-4.8.2.1">4.8.2.1</a> Date/Time Completed</h5></span>
+
+   Property Name: COMPLETED
+
+   Purpose: This property defines the date and time that a to-do was
+   actually completed.
+
+   Value Type: DATE-TIME
+
+   Property Parameters: Non-standard property parameters can be
+   specified on this property.
+
+   Conformance: The property can be specified in a "VTODO" calendar
+   component.
+
+   Description: The date and time MUST be in a UTC format.
+
+   Format Definition: The property is defined by the following notation:
+
+     completed  = "COMPLETED" compparam ":" date-time CRLF
+
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                    [Page 90]</span>
+</pre><pre class="newpage"><a name="page-91" id="page-91" href="#page-91" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+     compparam  = *(";" xparam)
+
+   Example: The following is an example of this property:
+
+     COMPLETED:19960401T235959Z
+
+<span class="h5"><h5><a name="section-4.8.2.2">4.8.2.2</a> Date/Time End</h5></span>
+
+   Property Name: DTEND
+
+   Purpose: This property specifies the date and time that a calendar
+   component ends.
+
+   Value Type: The default value type is DATE-TIME. The value type can
+   be set to a DATE value type.
+
+   Property Parameters: Non-standard, value data type, time zone
+   identifier property parameters can be specified on this property.
+
+   Conformance: This property can be specified in "VEVENT" or
+   "VFREEBUSY" calendar components.
+
+   Description: Within the "VEVENT" calendar component, this property
+   defines the date and time by which the event ends. The value MUST be
+   later in time than the value of the "DTSTART" property.
+
+   Within the "VFREEBUSY" calendar component, this property defines the
+   end date and time for the free or busy time information. The time
+   MUST be specified in the UTC time format. The value MUST be later in
+   time than the value of the "DTSTART" property.
+
+   Format Definition: The property is defined by the following notation:
+
+     dtend      = "DTEND" dtendparam":" dtendval CRLF
+
+     dtendparam = *(
+
+                ; the following are optional,
+                ; but MUST NOT occur more than once
+
+                (";" "VALUE" "=" ("DATE-TIME" / "DATE")) /
+                (";" tzidparam) /
+
+                ; the following is optional,
+                ; and MAY occur more than once
+
+
+
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                    [Page 91]</span>
+</pre><pre class="newpage"><a name="page-92" id="page-92" href="#page-92" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+                (";" xparam)
+
+                )
+
+
+
+     dtendval   = date-time / date
+     ;Value MUST match value type
+
+   Example: The following is an example of this property:
+
+     DTEND:19960401T235959Z
+
+     DTEND;VALUE=DATE:19980704
+
+<span class="h5"><h5><a name="section-4.8.2.3">4.8.2.3</a> Date/Time Due</h5></span>
+
+   Property Name: DUE
+
+   Purpose: This property defines the date and time that a to-do is
+   expected to be completed.
+
+   Value Type: The default value type is DATE-TIME. The value type can
+   be set to a DATE value type.
+
+   Property Parameters: Non-standard, value data type, time zone
+   identifier property parameters can be specified on this property.
+
+   Conformance: The property can be specified once in a "VTODO" calendar
+   component.
+
+   Description: The value MUST be a date/time equal to or after the
+   DTSTART value, if specified.
+
+   Format Definition: The property is defined by the following notation:
+
+     due        = "DUE" dueparam":" dueval CRLF
+
+     dueparam   = *(
+                ; the following are optional,
+                ; but MUST NOT occur more than once
+
+                (";" "VALUE" "=" ("DATE-TIME" / "DATE")) /
+                (";" tzidparam) /
+
+                ; the following is optional,
+                ; and MAY occur more than once
+
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                    [Page 92]</span>
+</pre><pre class="newpage"><a name="page-93" id="page-93" href="#page-93" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+                  *(";" xparam)
+
+                )
+
+
+
+     dueval     = date-time / date
+     ;Value MUST match value type
+
+   Example: The following is an example of this property:
+
+     DUE:19980430T235959Z
+
+<span class="h5"><h5><a name="section-4.8.2.4">4.8.2.4</a> Date/Time Start</h5></span>
+
+   Property Name: DTSTART
+
+   Purpose: This property specifies when the calendar component begins.
+
+   Value Type: The default value type is DATE-TIME. The time value MUST
+   be one of the forms defined for the DATE-TIME value type. The value
+   type can be set to a DATE value type.
+
+   Property Parameters: Non-standard, value data type, time zone
+   identifier property parameters can be specified on this property.
+
+   Conformance: This property can be specified in the "VEVENT", "VTODO",
+   "VFREEBUSY", or "VTIMEZONE" calendar components.
+
+   Description: Within the "VEVENT" calendar component, this property
+   defines the start date and time for the event. The property is
+   REQUIRED in "VEVENT" calendar components. Events can have a start
+   date/time but no end date/time. In that case, the event does not take
+   up any time.
+
+   Within the "VFREEBUSY" calendar component, this property defines the
+   start date and time for the free or busy time information. The time
+   MUST be specified in UTC time.
+
+   Within the "VTIMEZONE" calendar component, this property defines the
+   effective start date and time for a time zone specification. This
+   property is REQUIRED within each STANDARD and DAYLIGHT part included
+   in "VTIMEZONE" calendar components and MUST be specified as a local
+   DATE-TIME without the "TZID" property parameter.
+
+   Format Definition: The property is defined by the following notation:
+
+     dtstart    = "DTSTART" dtstparam ":" dtstval CRLF
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                    [Page 93]</span>
+</pre><pre class="newpage"><a name="page-94" id="page-94" href="#page-94" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+     dtstparam  = *(
+
+                ; the following are optional,
+                ; but MUST NOT occur more than once
+
+                (";" "VALUE" "=" ("DATE-TIME" / "DATE")) /
+                (";" tzidparam) /
+
+                ; the following is optional,
+                ; and MAY occur more than once
+
+                  *(";" xparam)
+
+                )
+
+
+
+     dtstval    = date-time / date
+     ;Value MUST match value type
+
+   Example: The following is an example of this property:
+
+     DTSTART:19980118T073000Z
+
+<span class="h5"><h5><a name="section-4.8.2.5">4.8.2.5</a> Duration</h5></span>
+
+   Property Name: DURATION
+
+   Purpose: The property specifies a positive duration of time.
+
+   Value Type: DURATION
+
+   Property Parameters: Non-standard property parameters can be
+   specified on this property.
+
+   Conformance: The property can be specified in "VEVENT", "VTODO",
+   "VFREEBUSY" or "VALARM" calendar components.
+
+   Description: In a "VEVENT" calendar component the property may be
+   used to specify a duration of the event, instead of an explicit end
+   date/time. In a "VTODO" calendar component the property may be used
+   to specify a duration for the to-do, instead of an explicit due
+   date/time. In a "VFREEBUSY" calendar component the property may be
+   used to specify the interval of free time being requested. In a
+   "VALARM" calendar component the property may be used to specify the
+   delay period prior to repeating an alarm.
+
+   Format Definition: The property is defined by the following notation:
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                    [Page 94]</span>
+</pre><pre class="newpage"><a name="page-95" id="page-95" href="#page-95" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+     duration   = "DURATION" durparam ":" dur-value CRLF
+                  ;consisting of a positive duration of time.
+
+     durparam   = *(";" xparam)
+
+   Example: The following is an example of this property that specifies
+   an interval of time of 1 hour and zero minutes and zero seconds:
+
+     DURATION:PT1H0M0S
+
+   The following is an example of this property that specifies an
+   interval of time of 15 minutes.
+
+     DURATION:PT15M
+
+<span class="h5"><h5><a name="section-4.8.2.6">4.8.2.6</a> Free/Busy Time</h5></span>
+
+   Property Name: FREEBUSY
+
+   Purpose: The property defines one or more free or busy time
+   intervals.
+
+   Value Type: PERIOD. The date and time values MUST be in an UTC time
+   format.
+
+   Property Parameters: Non-standard or free/busy time type property
+   parameters can be specified on this property.
+
+   Conformance: The property can be specified in a "VFREEBUSY" calendar
+   component.
+
+   Property Parameter: "FBTYPE" and non-standard parameters can be
+   specified on this property.
+
+   Description: These time periods can be specified as either a start
+   and end date-time or a start date-time and duration. The date and
+   time MUST be a UTC time format.
+
+   "FREEBUSY" properties within the "VFREEBUSY" calendar component
+   SHOULD be sorted in ascending order, based on start time and then end
+   time, with the earliest periods first.
+
+   The "FREEBUSY" property can specify more than one value, separated by
+   the COMMA character (US-ASCII decimal 44). In such cases, the
+   "FREEBUSY" property values SHOULD all be of the same "FBTYPE"
+   property parameter type (e.g., all values of a particular "FBTYPE"
+   listed together in a single property).
+
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                    [Page 95]</span>
+</pre><pre class="newpage"><a name="page-96" id="page-96" href="#page-96" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+   Format Definition: The property is defined by the following notation:
+
+     freebusy   = "FREEBUSY" fbparam ":" fbvalue
+                  CRLF
+
+     fbparam    = *(
+                ; the following is optional,
+                ; but MUST NOT occur more than once
+
+                (";" fbtypeparam) /
+
+                ; the following is optional,
+                ; and MAY occur more than once
+
+                (";" xparam)
+
+                )
+
+     fbvalue    = period *["," period]
+     ;Time value MUST be in the UTC time format.
+
+   Example: The following are some examples of this property:
+
+     FREEBUSY;FBTYPE=BUSY-UNAVAILABLE:19970308T160000Z/PT8H30M
+
+     FREEBUSY;FBTYPE=FREE:19970308T160000Z/PT3H,19970308T200000Z/PT1H
+
+     FREEBUSY;FBTYPE=FREE:19970308T160000Z/PT3H,19970308T200000Z/PT1H,
+      19970308T230000Z/19970309T000000Z
+
+<span class="h5"><h5><a name="section-4.8.2.7">4.8.2.7</a> Time Transparency</h5></span>
+
+   Property Name: TRANSP
+
+   Purpose: This property defines whether an event is transparent or not
+   to busy time searches.
+
+   Value Type: TEXT
+
+   Property Parameters: Non-standard property parameters can be
+   specified on this property.
+
+   Conformance: This property can be specified once in a "VEVENT"
+   calendar component.
+
+   Description: Time Transparency is the characteristic of an event that
+   determines whether it appears to consume time on a calendar. Events
+   that consume actual time for the individual or resource associated
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                    [Page 96]</span>
+</pre><pre class="newpage"><a name="page-97" id="page-97" href="#page-97" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+   with the calendar SHOULD be recorded as OPAQUE, allowing them to be
+   detected by free-busy time searches. Other events, which do not take
+   up the individual's (or resource's) time SHOULD be recorded as
+   TRANSPARENT, making them invisible to free-busy time searches.
+
+   Format Definition: The property is specified by the following
+   notation:
+
+     transp     = "TRANSP" tranparam ":" transvalue CRLF
+
+     tranparam  = *(";" xparam)
+
+     transvalue = "OPAQUE"      ;Blocks or opaque on busy time searches.
+                / "TRANSPARENT" ;Transparent on busy time searches.
+        ;Default value is OPAQUE
+
+   Example: The following is an example of this property for an event
+   that is transparent or does not block on free/busy time searches:
+
+     TRANSP:TRANSPARENT
+
+   The following is an example of this property for an event that is
+   opaque or blocks on free/busy time searches:
+
+     TRANSP:OPAQUE
+
+<span class="h4"><h4><a name="section-4.8.3">4.8.3</a> Time Zone Component Properties</h4></span>
+
+   The following properties specify time zone information in calendar
+   components.
+
+<span class="h5"><h5><a name="section-4.8.3.1">4.8.3.1</a> Time Zone Identifier</h5></span>
+
+   Property Name: TZID
+
+   Purpose: This property specifies the text value that uniquely
+   identifies the "VTIMEZONE" calendar component.
+
+   Value Type: TEXT
+
+   Property Parameters: Non-standard property parameters can be
+   specified on this property.
+
+   Conformance: This property MUST be specified in a "VTIMEZONE"
+   calendar component.
+
+
+
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                    [Page 97]</span>
+</pre><pre class="newpage"><a name="page-98" id="page-98" href="#page-98" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+   Description: This is the label by which a time zone calendar
+   component is referenced by any iCalendar properties whose data type
+   is either DATE-TIME or TIME and not intended to specify a UTC or a
+   "floating" time. The presence of the SOLIDUS character (US-ASCII
+   decimal 47) as a prefix, indicates that this TZID represents an
+   unique ID in a globally defined time zone registry (when such
+   registry is defined).
+
+        Note: This document does not define a naming convention for time
+        zone identifiers. Implementers may want to use the naming
+        conventions defined in existing time zone specifications such as
+        the public-domain Olson database [<a href="#ref-TZ" title="&quot;ftp://elsie.nci.nih.gov/pub/&quot;">TZ</a>]. The specification of
+        globally unique time zone identifiers is not addressed by this
+        document and is left for future study.
+
+   Format Definition: This property is defined by the following
+   notation:
+
+     tzid       = "TZID" tzidpropparam ":" [<a href="#ref-tzidprefix">tzidprefix</a>] text CRLF
+
+     tzidpropparam      = *(";" xparam)
+
+     ;tzidprefix        = "/"
+     ; Defined previously. Just listed here for reader convenience.
+
+   Example: The following are examples of non-globally unique time zone
+   identifiers:
+
+     TZID:US-Eastern
+
+     TZID:California-Los_Angeles
+
+   The following is an example of a fictitious globally unique time zone
+   identifier:
+
+     TZID:/US-New_York-New_York
+
+<span class="h5"><h5><a name="section-4.8.3.2">4.8.3.2</a> Time Zone Name</h5></span>
+
+   Property Name: TZNAME
+
+   Purpose: This property specifies the customary designation for a time
+   zone description.
+
+   Value Type: TEXT
+
+   Property Parameters: Non-standard and language property parameters
+   can be specified on this property.
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                    [Page 98]</span>
+</pre><pre class="newpage"><a name="page-99" id="page-99" href="#page-99" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+   Conformance: This property can be specified in a "VTIMEZONE" calendar
+   component.
+
+   Description: This property may be specified in multiple languages; in
+   order to provide for different language requirements.
+
+   Format Definition: This property is defined by the following
+   notation:
+
+     tzname     = "TZNAME" tznparam ":" text CRLF
+
+     tznparam   = *(
+
+                ; the following is optional,
+                ; but MUST NOT occur more than once
+
+                (";" languageparam) /
+
+                ; the following is optional,
+                ; and MAY occur more than once
+
+                (";" xparam)
+
+                )
+
+   Example: The following are example of this property:
+
+     TZNAME:EST
+
+   The following is an example of this property when two different
+   languages for the time zone name are specified:
+
+     TZNAME;LANGUAGE=en:EST
+     TZNAME;LANGUAGE=fr-CA:HNE
+
+<span class="h5"><h5><a name="section-4.8.3.3">4.8.3.3</a> Time Zone Offset From</h5></span>
+
+   Property Name: TZOFFSETFROM
+
+   Purpose: This property specifies the offset which is in use prior to
+   this time zone observance.
+
+   Value Type: UTC-OFFSET
+
+   Property Parameters: Non-standard property parameters can be
+   specified on this property.
+
+
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                    [Page 99]</span>
+</pre><pre class="newpage"><a name="page-100" id="page-100" href="#page-100" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+   Conformance: This property MUST be specified in a "VTIMEZONE"
+   calendar component.
+
+   Description: This property specifies the offset which is in use prior
+   to this time observance. It is used to calculate the absolute time at
+   which the transition to a given observance takes place. This property
+   MUST only be specified in a "VTIMEZONE" calendar component. A
+   "VTIMEZONE" calendar component MUST include this property. The
+   property value is a signed numeric indicating the number of hours and
+   possibly minutes from UTC. Positive numbers represent time zones east
+   of the prime meridian, or ahead of UTC. Negative numbers represent
+   time zones west of the prime meridian, or behind UTC.
+
+   Format Definition: The property is defined by the following notation:
+
+     tzoffsetfrom       = "TZOFFSETFROM" frmparam ":" utc-offset
+                          CRLF
+
+     frmparam   = *(";" xparam)
+
+   Example: The following are examples of this property:
+
+     TZOFFSETFROM:-0500
+
+     TZOFFSETFROM:+1345
+
+<span class="h5"><h5><a name="section-4.8.3.4">4.8.3.4</a> Time Zone Offset To</h5></span>
+
+   Property Name: TZOFFSETTO
+
+   Purpose: This property specifies the offset which is in use in this
+   time zone observance.
+
+   Value Type: UTC-OFFSET
+
+   Property Parameters: Non-standard property parameters can be
+   specified on this property.
+
+   Conformance: This property MUST be specified in a "VTIMEZONE"
+   calendar component.
+
+   Description: This property specifies the offset which is in use in
+   this time zone observance. It is used to calculate the absolute time
+   for the new observance. The property value is a signed numeric
+   indicating the number of hours and possibly minutes from UTC.
+   Positive numbers represent time zones east of the prime meridian, or
+   ahead of UTC. Negative numbers represent time zones west of the prime
+   meridian, or behind UTC.
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                   [Page 100]</span>
+</pre><pre class="newpage"><a name="page-101" id="page-101" href="#page-101" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+   Format Definition: The property is defined by the following notation:
+
+     tzoffsetto = "TZOFFSETTO" toparam ":" utc-offset CRLF
+
+     toparam    = *(";" xparam)
+
+   Example: The following are examples of this property:
+
+     TZOFFSETTO:-0400
+
+     TZOFFSETTO:+1245
+
+<span class="h5"><h5><a name="section-4.8.3.5">4.8.3.5</a> Time Zone URL</h5></span>
+
+   Property Name: TZURL
+
+   Purpose: The TZURL provides a means for a VTIMEZONE component to
+   point to a network location that can be used to retrieve an up-to-
+   date version of itself.
+
+   Value Type: URI
+
+   Property Parameters: Non-standard property parameters can be
+   specified on this property.
+
+   Conformance: This property can be specified in a "VTIMEZONE" calendar
+   component.
+
+   Description: The TZURL provides a means for a VTIMEZONE component to
+   point to a network location that can be used to retrieve an up-to-
+   date version of itself. This provides a hook to handle changes
+   government bodies impose upon time zone definitions. Retrieval of
+   this resource results in an iCalendar object containing a single
+   VTIMEZONE component and a METHOD property set to PUBLISH.
+
+   Format Definition: The property is defined by the following notation:
+
+     tzurl      = "TZURL" tzurlparam ":" uri CRLF
+
+     tzurlparam = *(";" xparam)
+
+   Example: The following is an example of this property:
+
+     TZURL:http://timezones.r.us.net/tz/US-California-Los_Angeles
+
+
+
+
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                   [Page 101]</span>
+</pre><pre class="newpage"><a name="page-102" id="page-102" href="#page-102" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+<span class="h4"><h4><a name="section-4.8.4">4.8.4</a> Relationship Component Properties</h4></span>
+
+   The following properties specify relationship information in calendar
+   components.
+
+<span class="h5"><h5><a name="section-4.8.4.1">4.8.4.1</a> Attendee</h5></span>
+
+   Property Name: ATTENDEE
+
+   Purpose: The property defines an "Attendee" within a calendar
+   component.
+
+   Value Type: CAL-ADDRESS
+
+   Property Parameters: Non-standard, language, calendar user type,
+   group or list membership, participation role, participation status,
+   RSVP expectation, delegatee, delegator, sent by, common name or
+   directory entry reference property parameters can be specified on
+   this property.
+
+   Conformance: This property MUST be specified in an iCalendar object
+   that specifies a group scheduled calendar entity. This property MUST
+   NOT be specified in an iCalendar object when publishing the calendar
+   information (e.g., NOT in an iCalendar object that specifies the
+   publication of a calendar user's busy time, event, to-do or journal).
+   This property is not specified in an iCalendar object that specifies
+   only a time zone definition or that defines calendar entities that
+   are not group scheduled entities, but are entities only on a single
+   user's calendar.
+
+   Description: The property MUST only be specified within calendar
+   components to specify participants, non-participants and the chair of
+   a group scheduled calendar entity. The property is specified within
+   an "EMAIL" category of the "VALARM" calendar component to specify an
+   email address that is to receive the email type of iCalendar alarm.
+
+   The property parameter CN is for the common or displayable name
+   associated with the calendar address; ROLE, for the intended role
+   that the attendee will have in the calendar component; PARTSTAT, for
+   the status of the attendee's participation; RSVP, for indicating
+   whether the favor of a reply is requested; CUTYPE, to indicate the
+   type of calendar user; MEMBER, to indicate the groups that the
+   attendee belongs to; DELEGATED-TO, to indicate the calendar users
+   that the original request was delegated to; and DELEGATED-FROM, to
+   indicate whom the request was delegated from; SENT-BY, to indicate
+   whom is acting on behalf of the ATTENDEE; and DIR, to indicate the
+   URI that points to the directory information corresponding to the
+   attendee. These property parameters can be specified on an "ATTENDEE"
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                   [Page 102]</span>
+</pre><pre class="newpage"><a name="page-103" id="page-103" href="#page-103" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+   property in either a "VEVENT", "VTODO" or "VJOURNAL" calendar
+   component. They MUST not be specified in an "ATTENDEE" property in a
+   "VFREEBUSY" or "VALARM" calendar component. If the LANGUAGE property
+   parameter is specified, the identified language applies to the CN
+   parameter.
+
+   A recipient delegated a request MUST inherit the RSVP and ROLE values
+   from the attendee that delegated the request to them.
+
+   Multiple attendees can be specified by including multiple "ATTENDEE"
+   properties within the calendar component.
+
+   Format Definition: The property is defined by the following notation:
+
+     attendee   = "ATTENDEE" attparam ":" cal-address CRLF
+
+     attparam   = *(
+
+                ; the following are optional,
+                ; but MUST NOT occur more than once
+
+                (";" cutypeparam) / (";"memberparam) /
+                (";" roleparam) / (";" partstatparam) /
+                (";" rsvpparam) / (";" deltoparam) /
+                (";" delfromparam) / (";" sentbyparam) /
+                (";"cnparam) / (";" dirparam) /
+                (";" languageparam) /
+
+                ; the following is optional,
+                ; and MAY occur more than once
+
+                (";" xparam)
+
+                )
+
+   Example: The following are examples of this property's use for a to-
+   do:
+
+     ORGANIZER:MAILTO:jsmith@host1.com
+     ATTENDEE;MEMBER="MAILTO:DEV-GROUP@host2.com":
+      MAILTO:joecool@host2.com
+     ATTENDEE;DELEGATED-FROM="MAILTO:immud@host3.com":
+      MAILTO:ildoit@host1.com
+
+   The following is an example of this property used for specifying
+   multiple attendees to an event:
+
+
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                   [Page 103]</span>
+</pre><pre class="newpage"><a name="page-104" id="page-104" href="#page-104" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+     ORGANIZER:MAILTO:jsmith@host1.com
+     ATTENDEE;ROLE=REQ-PARTICIPANT;PARTSTAT=TENTATIVE;CN=Henry Cabot
+      :MAILTO:hcabot@host2.com
+     ATTENDEE;ROLE=REQ-PARTICIPANT;DELEGATED-FROM="MAILTO:bob@host.com"
+      ;PARTSTAT=ACCEPTED;CN=Jane Doe:MAILTO:jdoe@host1.com
+
+   The following is an example of this property with a URI to the
+   directory information associated with the attendee:
+
+     ATTENDEE;CN=John Smith;DIR="ldap://host.com:6666/o=eDABC%
+      20Industries,c=3DUS??(cn=3DBJim%20Dolittle)":MAILTO:jimdo@
+      host1.com
+
+   The following is an example of this property with "delegatee" and
+   "delegator" information for an event:
+
+     ORGANIZER;CN=John Smith:MAILTO:jsmith@host.com
+     ATTENDEE;ROLE=REQ-PARTICIPANT;PARTSTAT=TENTATIVE;DELEGATED-FROM=
+      "MAILTO:iamboss@host2.com";CN=Henry Cabot:MAILTO:hcabot@
+      host2.com
+     ATTENDEE;ROLE=NON-PARTICIPANT;PARTSTAT=DELEGATED;DELEGATED-TO=
+      "MAILTO:hcabot@host2.com";CN=The Big Cheese:MAILTO:iamboss
+      @host2.com
+     ATTENDEE;ROLE=REQ-PARTICIPANT;PARTSTAT=ACCEPTED;CN=Jane Doe
+      :MAILTO:jdoe@host1.com
+
+   Example: The following is an example of this property's use when
+   another calendar user is acting on behalf of the "Attendee":
+
+     ATTENDEE;SENT-BY=MAILTO:jan_doe@host1.com;CN=John Smith:MAILTO:
+      jsmith@host1.com
+
+<span class="h5"><h5><a name="section-4.8.4.2">4.8.4.2</a> Contact</h5></span>
+
+   Property Name: CONTACT
+
+   Purpose: The property is used to represent contact information or
+   alternately a reference to contact information associated with the
+   calendar component.
+
+   Value Type: TEXT
+
+   Property Parameters: Non-standard, alternate text representation and
+   language property parameters can be specified on this property.
+
+   Conformance: The property can be specified in a "VEVENT", "VTODO",
+   "VJOURNAL" or "VFREEBUSY" calendar component.
+
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                   [Page 104]</span>
+</pre><pre class="newpage"><a name="page-105" id="page-105" href="#page-105" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+   Description: The property value consists of textual contact
+   information. An alternative representation for the property value can
+   also be specified that refers to a URI pointing to an alternate form,
+   such as a vCard [<a href="http://tools.ietf.org/html/rfc2426" title="&quot;vCard MIME Directory Profile&quot;">RFC 2426</a>], for the contact information.
+
+   Format Definition: The property is defined by the following notation:
+
+     contact    = "CONTACT" contparam ":" text CRLF
+
+     contparam  = *(
+                ; the following are optional,
+                ; but MUST NOT occur more than once
+
+                (";" altrepparam) / (";" languageparam) /
+
+                ; the following is optional,
+                ; and MAY occur more than once
+
+                (";" xparam)
+
+                )
+
+   Example: The following is an example of this property referencing
+   textual contact information:
+
+     CONTACT:Jim Dolittle\, ABC Industries\, +1-919-555-1234
+
+   The following is an example of this property with an alternate
+   representation of a LDAP URI to a directory entry containing the
+   contact information:
+
+     CONTACT;ALTREP="ldap://host.com:6666/o=3DABC%20Industries\,
+      c=3DUS??(cn=3DBJim%20Dolittle)":Jim Dolittle\, ABC Industries\,
+      +1-919-555-1234
+
+   The following is an example of this property with an alternate
+   representation of a MIME body part containing the contact
+   information, such as a vCard [<a href="http://tools.ietf.org/html/rfc2426" title="&quot;vCard MIME Directory Profile&quot;">RFC 2426</a>] embedded in a [<a href="#ref-MIME-DIR">MIME-DIR</a>]
+   content-type:
+
+     CONTACT;ALTREP="CID=&lt;part3.msg970930T083000SILVER@host.com&gt;":Jim
+       Dolittle\, ABC Industries\, +1-919-555-1234
+
+   The following is an example of this property referencing a network
+   resource, such as a vCard [<a href="http://tools.ietf.org/html/rfc2426" title="&quot;vCard MIME Directory Profile&quot;">RFC 2426</a>] object containing the contact
+   information:
+
+
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                   [Page 105]</span>
+</pre><pre class="newpage"><a name="page-106" id="page-106" href="#page-106" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+     CONTACT;ALTREP="http://host.com/pdi/jdoe.vcf":Jim
+       Dolittle\, ABC Industries\, +1-919-555-1234
+
+<span class="h5"><h5><a name="section-4.8.4.3">4.8.4.3</a> Organizer</h5></span>
+
+   Property Name: ORGANIZER
+
+   Purpose: The property defines the organizer for a calendar component.
+
+   Value Type: CAL-ADDRESS
+
+   Property Parameters: Non-standard, language, common name, directory
+   entry reference, sent by property parameters can be specified on this
+   property.
+
+   Conformance: This property MUST be specified in an iCalendar object
+   that specifies a group scheduled calendar entity. This property MUST
+   be specified in an iCalendar object that specifies the publication of
+   a calendar user's busy time. This property MUST NOT be specified in
+   an iCalendar object that specifies only a time zone definition or
+   that defines calendar entities that are not group scheduled entities,
+   but are entities only on a single user's calendar.
+
+   Description: The property is specified within the "VEVENT", "VTODO",
+   "VJOURNAL calendar components to specify the organizer of a group
+   scheduled calendar entity. The property is specified within the
+   "VFREEBUSY" calendar component to specify the calendar user
+   requesting the free or busy time. When publishing a "VFREEBUSY"
+   calendar component, the property is used to specify the calendar that
+   the published busy time came from.
+
+   The property has the property parameters CN, for specifying the
+   common or display name associated with the "Organizer", DIR, for
+   specifying a pointer to the directory information associated with the
+   "Organizer", SENT-BY, for specifying another calendar user that is
+   acting on behalf of the "Organizer". The non-standard parameters may
+   also be specified on this property. If the LANGUAGE property
+   parameter is specified, the identified language applies to the CN
+   parameter value.
+
+   Format Definition: The property is defined by the following notation:
+
+     organizer  = "ORGANIZER" orgparam ":"
+                  cal-address CRLF
+
+     orgparam   = *(
+
+                ; the following are optional,
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                   [Page 106]</span>
+</pre><pre class="newpage"><a name="page-107" id="page-107" href="#page-107" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+                ; but MUST NOT occur more than once
+
+                (";" cnparam) / (";" dirparam) / (";" sentbyparam) /
+                (";" languageparam) /
+
+                ; the following is optional,
+                ; and MAY occur more than once
+
+                (";" xparam)
+
+                )
+
+   Example: The following is an example of this property:
+
+     ORGANIZER;CN=John Smith:MAILTO:jsmith@host1.com
+
+   The following is an example of this property with a pointer to the
+   directory information associated with the organizer:
+
+     ORGANIZER;CN=JohnSmith;DIR="ldap://host.com:6666/o=3DDC%20Associ
+      ates,c=3DUS??(cn=3DJohn%20Smith)":MAILTO:jsmith@host1.com
+
+   The following is an example of this property used by another calendar
+   user who is acting on behalf of the organizer, with responses
+   intended to be sent back to the organizer, not the other calendar
+   user:
+
+     ORGANIZER;SENT-BY="MAILTO:jane_doe@host.com":
+      MAILTO:jsmith@host1.com
+
+<span class="h5"><h5><a name="section-4.8.4.4">4.8.4.4</a> Recurrence ID</h5></span>
+
+   Property Name: RECURRENCE-ID
+
+   Purpose: This property is used in conjunction with the "UID" and
+   "SEQUENCE" property to identify a specific instance of a recurring
+   "VEVENT", "VTODO" or "VJOURNAL" calendar component. The property
+   value is the effective value of the "DTSTART" property of the
+   recurrence instance.
+
+   Value Type: The default value type for this property is DATE-TIME.
+   The time format can be any of the valid forms defined for a DATE-TIME
+   value type. See DATE-TIME value type definition for specific
+   interpretations of the various forms. The value type can be set to
+   DATE.
+
+
+
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                   [Page 107]</span>
+</pre><pre class="newpage"><a name="page-108" id="page-108" href="#page-108" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+   Property Parameters: Non-standard property, value data type, time
+   zone identifier and recurrence identifier range parameters can be
+   specified on this property.
+
+   Conformance: This property can be specified in an iCalendar object
+   containing a recurring calendar component.
+
+   Description: The full range of calendar components specified by a
+   recurrence set is referenced by referring to just the "UID" property
+   value corresponding to the calendar component. The "RECURRENCE-ID"
+   property allows the reference to an individual instance within the
+   recurrence set.
+
+   If the value of the "DTSTART" property is a DATE type value, then the
+   value MUST be the calendar date for the recurrence instance.
+
+   The date/time value is set to the time when the original recurrence
+   instance would occur; meaning that if the intent is to change a
+   Friday meeting to Thursday, the date/time is still set to the
+   original Friday meeting.
+
+   The "RECURRENCE-ID" property is used in conjunction with the "UID"
+   and "SEQUENCE" property to identify a particular instance of a
+   recurring event, to-do or journal. For a given pair of "UID" and
+   "SEQUENCE" property values, the "RECURRENCE-ID" value for a
+   recurrence instance is fixed. When the definition of the recurrence
+   set for a calendar component changes, and hence the "SEQUENCE"
+   property value changes, the "RECURRENCE-ID" for a given recurrence
+   instance might also change.The "RANGE" parameter is used to specify
+   the effective range of recurrence instances from the instance
+   specified by the "RECURRENCE-ID" property value. The default value
+   for the range parameter is the single recurrence instance only. The
+   value can also be "THISANDPRIOR" to indicate a range defined by the
+   given recurrence instance and all prior instances or the value can be
+   "THISANDFUTURE" to indicate a range defined by the given recurrence
+   instance and all subsequent instances.
+
+   Format Definition: The property is defined by the following notation:
+
+     recurid    = "RECURRENCE-ID" ridparam ":" ridval CRLF
+
+     ridparam   = *(
+
+                ; the following are optional,
+                ; but MUST NOT occur more than once
+
+                (";" "VALUE" "=" ("DATE-TIME" / "DATE)) /
+                (";" tzidparam) / (";" rangeparam) /
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                   [Page 108]</span>
+</pre><pre class="newpage"><a name="page-109" id="page-109" href="#page-109" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+                ; the following is optional,
+                ; and MAY occur more than once
+
+                (";" xparam)
+
+                )
+
+     ridval     = date-time / date
+     ;Value MUST match value type
+
+   Example: The following are examples of this property:
+
+     RECURRENCE-ID;VALUE=DATE:19960401
+
+     RECURRENCE-ID;RANGE=THISANDFUTURE:19960120T120000Z
+
+<span class="h5"><h5><a name="section-4.8.4.5">4.8.4.5</a> Related To</h5></span>
+
+   Property Name: RELATED-TO
+
+   Purpose: The property is used to represent a relationship or
+   reference between one calendar component and another.
+
+   Value Type: TEXT
+
+   Property Parameters: Non-standard and relationship type property
+   parameters can be specified on this property.
+
+   Conformance: The property can be specified one or more times in the
+   "VEVENT", "VTODO" or "VJOURNAL" calendar components.
+
+   Description: The property value consists of the persistent, globally
+   unique identifier of another calendar component. This value would be
+   represented in a calendar component by the "UID" property.
+
+   By default, the property value points to another calendar component
+   that has a PARENT relationship to the referencing object. The
+   "RELTYPE" property parameter is used to either explicitly state the
+   default PARENT relationship type to the referenced calendar component
+   or to override the default PARENT relationship type and specify
+   either a CHILD or SIBLING relationship. The PARENT relationship
+   indicates that the calendar component is a subordinate of the
+   referenced calendar component. The CHILD relationship indicates that
+   the calendar component is a superior of the referenced calendar
+   component. The SIBLING relationship indicates that the calendar
+   component is a peer of the referenced calendar component.
+
+
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                   [Page 109]</span>
+</pre><pre class="newpage"><a name="page-110" id="page-110" href="#page-110" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+   Changes to a calendar component referenced by this property can have
+   an implicit impact on the related calendar component. For example, if
+   a group event changes its start or end date or time, then the
+   related, dependent events will need to have their start and end dates
+   changed in a corresponding way. Similarly, if a PARENT calendar
+   component is canceled or deleted, then there is an implied impact to
+   the related CHILD calendar components. This property is intended only
+   to provide information on the relationship of calendar components. It
+   is up to the target calendar system to maintain any property
+   implications of this relationship.
+
+   Format Definition: The property is defined by the following notation:
+
+     related    = "RELATED-TO" [<a href="#ref-relparam">relparam</a>] ":" text CRLF
+
+     relparam   = *(
+
+                ; the following is optional,
+                ; but MUST NOT occur more than once
+
+                (";" reltypeparam) /
+
+                ; the following is optional,
+                ; and MAY occur more than once
+
+                (";" xparm)
+
+                )
+
+   The following is an example of this property:
+
+     RELATED-TO:&lt;jsmith.part7.19960817T083000.xyzMail@host3.com&gt;
+
+     RELATED-TO:&lt;19960401-080045-4000F192713-0052@host1.com&gt;
+
+<span class="h5"><h5><a name="section-4.8.4.6">4.8.4.6</a> Uniform Resource Locator</h5></span>
+
+   Property Name: URL
+
+   Purpose: This property defines a Uniform Resource Locator (URL)
+   associated with the iCalendar object.
+
+   Value Type: URI
+
+   Property Parameters: Non-standard property parameters can be
+   specified on this property.
+
+
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                   [Page 110]</span>
+</pre><pre class="newpage"><a name="page-111" id="page-111" href="#page-111" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+   Conformance: This property can be specified once in the "VEVENT",
+   "VTODO", "VJOURNAL" or "VFREEBUSY" calendar components.
+
+   Description: This property may be used in a calendar component to
+   convey a location where a more dynamic rendition of the calendar
+   information associated with the calendar component can be found. This
+   memo does not attempt to standardize the form of the URI, nor the
+   format of the resource pointed to by the property value. If the URL
+   property and Content-Location MIME header are both specified, they
+   MUST point to the same resource.
+
+   Format Definition: The property is defined by the following notation:
+
+     url        = "URL" urlparam ":" uri CRLF
+
+     urlparam   = *(";" xparam)
+
+   Example: The following is an example of this property:
+
+     URL:http://abc.com/pub/calendars/jsmith/mytime.ics
+
+<span class="h5"><h5><a name="section-4.8.4.7">4.8.4.7</a> Unique Identifier</h5></span>
+
+   Property Name: UID
+
+   Purpose: This property defines the persistent, globally unique
+   identifier for the calendar component.
+
+   Value Type: TEXT
+
+   Property Parameters: Non-standard property parameters can be
+   specified on this property.
+
+   Conformance: The property MUST be specified in the "VEVENT", "VTODO",
+   "VJOURNAL" or "VFREEBUSY" calendar components.
+
+   Description: The UID itself MUST be a globally unique identifier. The
+   generator of the identifier MUST guarantee that the identifier is
+   unique. There are several algorithms that can be used to accomplish
+   this. The identifier is RECOMMENDED to be the identical syntax to the
+   [<a href="http://tools.ietf.org/html/rfc822" title="&quot;Standard for the Format of ARPA Internet Text Messages&quot;">RFC 822</a>] addr-spec. A good method to assure uniqueness is to put the
+   domain name or a domain literal IP address of the host on which the
+   identifier was created on the right hand side of the "@", and on the
+   left hand side, put a combination of the current calendar date and
+   time of day (i.e., formatted in as a DATE-TIME value) along with some
+   other currently unique (perhaps sequential) identifier available on
+   the system (for example, a process id number). Using a date/time
+   value on the left hand side and a domain name or domain literal on
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                   [Page 111]</span>
+</pre><pre class="newpage"><a name="page-112" id="page-112" href="#page-112" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+   the right hand side makes it possible to guarantee uniqueness since
+   no two hosts should be using the same domain name or IP address at
+   the same time. Though other algorithms will work, it is RECOMMENDED
+   that the right hand side contain some domain identifier (either of
+   the host itself or otherwise) such that the generator of the message
+   identifier can guarantee the uniqueness of the left hand side within
+   the scope of that domain.
+
+   This is the method for correlating scheduling messages with the
+   referenced "VEVENT", "VTODO", or "VJOURNAL" calendar component.
+
+   The full range of calendar components specified by a recurrence set
+   is referenced by referring to just the "UID" property value
+   corresponding to the calendar component. The "RECURRENCE-ID" property
+   allows the reference to an individual instance within the recurrence
+   set.
+
+   This property is an important method for group scheduling
+   applications to match requests with later replies, modifications or
+   deletion requests. Calendaring and scheduling applications MUST
+   generate this property in "VEVENT", "VTODO" and "VJOURNAL" calendar
+   components to assure interoperability with other group scheduling
+   applications. This identifier is created by the calendar system that
+   generates an iCalendar object.
+
+   Implementations MUST be able to receive and persist values of at
+   least 255 characters for this property.
+
+   Format Definition: The property is defined by the following notation:
+
+     uid        = "UID" uidparam ":" text CRLF
+
+     uidparam   = *(";" xparam)
+
+   Example: The following is an example of this property:
+
+     UID:19960401T080045Z-4000F192713-0052@host1.com
+
+<span class="h4"><h4><a name="section-4.8.5">4.8.5</a> Recurrence Component Properties</h4></span>
+
+   The following properties specify recurrence information in calendar
+   components.
+
+<span class="h5"><h5><a name="section-4.8.5.1">4.8.5.1</a> Exception Date/Times</h5></span>
+
+   Property Name: EXDATE
+
+
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                   [Page 112]</span>
+</pre><pre class="newpage"><a name="page-113" id="page-113" href="#page-113" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+   Purpose: This property defines the list of date/time exceptions for a
+   recurring calendar component.
+
+   Value Type: The default value type for this property is DATE-TIME.
+   The value type can be set to DATE.
+
+   Property Parameters: Non-standard, value data type and time zone
+   identifier property parameters can be specified on this property.
+
+   Conformance: This property can be specified in an iCalendar object
+   that includes a recurring calendar component.
+
+   Description: The exception dates, if specified, are used in computing
+   the recurrence set. The recurrence set is the complete set of
+   recurrence instances for a calendar component. The recurrence set is
+   generated by considering the initial "DTSTART" property along with
+   the "RRULE", "RDATE", "EXDATE" and "EXRULE" properties contained
+   within the iCalendar object. The "DTSTART" property defines the first
+   instance in the recurrence set. Multiple instances of the "RRULE" and
+   "EXRULE" properties can also be specified to define more
+   sophisticated recurrence sets. The final recurrence set is generated
+   by gathering all of the start date-times generated by any of the
+   specified "RRULE" and "RDATE" properties, and then excluding any
+   start date and times which fall within the union of start date and
+   times generated by any specified "EXRULE" and "EXDATE" properties.
+   This implies that start date and times within exclusion related
+   properties (i.e., "EXDATE" and "EXRULE") take precedence over those
+   specified by inclusion properties (i.e., "RDATE" and "RRULE"). Where
+   duplicate instances are generated by the "RRULE" and "RDATE"
+   properties, only one recurrence is considered. Duplicate instances
+   are ignored.
+
+   The "EXDATE" property can be used to exclude the value specified in
+   "DTSTART". However, in such cases the original "DTSTART" date MUST
+   still be maintained by the calendaring and scheduling system because
+   the original "DTSTART" value has inherent usage dependencies by other
+   properties such as the "RECURRENCE-ID".
+
+   Format Definition: The property is defined by the following notation:
+
+     exdate     = "EXDATE" exdtparam ":" exdtval *("," exdtval) CRLF
+
+     exdtparam  = *(
+
+                ; the following are optional,
+                ; but MUST NOT occur more than once
+
+                (";" "VALUE" "=" ("DATE-TIME" / "DATE")) /
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                   [Page 113]</span>
+</pre><pre class="newpage"><a name="page-114" id="page-114" href="#page-114" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+                (";" tzidparam) /
+
+                ; the following is optional,
+                ; and MAY occur more than once
+
+                (";" xparam)
+
+                )
+
+     exdtval    = date-time / date
+     ;Value MUST match value type
+
+   Example: The following is an example of this property:
+
+     EXDATE:19960402T010000Z,19960403T010000Z,19960404T010000Z
+
+<span class="h5"><h5><a name="section-4.8.5.2">4.8.5.2</a> Exception Rule</h5></span>
+
+   Property Name: EXRULE
+
+   Purpose: This property defines a rule or repeating pattern for an
+   exception to a recurrence set.
+
+   Value Type: RECUR
+
+   Property Parameters: Non-standard property parameters can be
+   specified on this property.
+
+   Conformance: This property can be specified in "VEVENT", "VTODO" or
+   "VJOURNAL" calendar components.
+
+   Description: The exception rule, if specified, is used in computing
+   the recurrence set. The recurrence set is the complete set of
+   recurrence instances for a calendar component. The recurrence set is
+   generated by considering the initial "DTSTART" property along with
+   the "RRULE", "RDATE", "EXDATE" and "EXRULE" properties contained
+   within the iCalendar object. The "DTSTART" defines the first instance
+   in the recurrence set. Multiple instances of the "RRULE" and "EXRULE"
+   properties can also be specified to define more sophisticated
+   recurrence sets. The final recurrence set is generated by gathering
+   all of the start date-times generated by any of the specified "RRULE"
+   and "RDATE" properties, and excluding any start date and times which
+   fall within the union of start date and times generated by any
+   specified "EXRULE" and "EXDATE" properties. This implies that start
+   date and times within exclusion related properties (i.e., "EXDATE"
+   and "EXRULE") take precedence over those specified by inclusion
+
+
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                   [Page 114]</span>
+</pre><pre class="newpage"><a name="page-115" id="page-115" href="#page-115" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+   properties (i.e., "RDATE" and "RRULE"). Where duplicate instances are
+   generated by the "RRULE" and "RDATE" properties, only one recurrence
+   is considered. Duplicate instances are ignored.
+
+   The "EXRULE" property can be used to exclude the value specified in
+   "DTSTART". However, in such cases the original "DTSTART" date MUST
+   still be maintained by the calendaring and scheduling system because
+   the original "DTSTART" value has inherent usage dependencies by other
+   properties such as the "RECURRENCE-ID".
+
+   Format Definition: The property is defined by the following notation:
+
+     exrule     = "EXRULE" exrparam ":" recur CRLF
+
+     exrparam   = *(";" xparam)
+
+   Example: The following are examples of this property. Except every
+   other week, on Tuesday and Thursday for 4 occurrences:
+
+     EXRULE:FREQ=WEEKLY;COUNT=4;INTERVAL=2;BYDAY=TU,TH
+
+   Except daily for 10 occurrences:
+
+     EXRULE:FREQ=DAILY;COUNT=10
+
+   Except yearly in June and July for 8 occurrences:
+
+     EXRULE:FREQ=YEARLY;COUNT=8;BYMONTH=6,7
+
+<span class="h5"><h5><a name="section-4.8.5.3">4.8.5.3</a> Recurrence Date/Times</h5></span>
+
+   Property Name: RDATE
+
+   Purpose: This property defines the list of date/times for a
+   recurrence set.
+
+   Value Type: The default value type for this property is DATE-TIME.
+   The value type can be set to DATE or PERIOD.
+
+   Property Parameters: Non-standard, value data type and time zone
+   identifier property parameters can be specified on this property.
+
+   Conformance: The property can be specified in "VEVENT", "VTODO",
+   "VJOURNAL" or "VTIMEZONE" calendar components.
+
+
+
+
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                   [Page 115]</span>
+</pre><pre class="newpage"><a name="page-116" id="page-116" href="#page-116" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+   Description: This property can appear along with the "RRULE" property
+   to define an aggregate set of repeating occurrences. When they both
+   appear in an iCalendar object, the recurring events are defined by
+   the union of occurrences defined by both the "RDATE" and "RRULE".
+
+   The recurrence dates, if specified, are used in computing the
+   recurrence set. The recurrence set is the complete set of recurrence
+   instances for a calendar component. The recurrence set is generated
+   by considering the initial "DTSTART" property along with the "RRULE",
+   "RDATE", "EXDATE" and "EXRULE" properties contained within the
+   iCalendar object. The "DTSTART" property defines the first instance
+   in the recurrence set. Multiple instances of the "RRULE" and "EXRULE"
+   properties can also be specified to define more sophisticated
+   recurrence sets. The final recurrence set is generated by gathering
+   all of the start date/times generated by any of the specified "RRULE"
+   and "RDATE" properties, and excluding any start date/times which fall
+   within the union of start date/times generated by any specified
+   "EXRULE" and "EXDATE" properties. This implies that start date/times
+   within exclusion related properties (i.e., "EXDATE" and "EXRULE")
+   take precedence over those specified by inclusion properties (i.e.,
+   "RDATE" and "RRULE"). Where duplicate instances are generated by the
+   "RRULE" and "RDATE" properties, only one recurrence is considered.
+   Duplicate instances are ignored.
+
+   Format Definition: The property is defined by the following notation:
+
+     rdate      = "RDATE" rdtparam ":" rdtval *("," rdtval) CRLF
+
+     rdtparam   = *(
+
+                ; the following are optional,
+                ; but MUST NOT occur more than once
+
+                (";" "VALUE" "=" ("DATE-TIME" / "DATE" / "PERIOD")) /
+                (";" tzidparam) /
+
+                ; the following is optional,
+                ; and MAY occur more than once
+
+                (";" xparam)
+
+                )
+
+     rdtval     = date-time / date / period
+     ;Value MUST match value type
+
+   Example: The following are examples of this property:
+
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                   [Page 116]</span>
+</pre><pre class="newpage"><a name="page-117" id="page-117" href="#page-117" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+     RDATE:19970714T123000Z
+
+     RDATE;TZID=US-EASTERN:19970714T083000
+
+     RDATE;VALUE=PERIOD:19960403T020000Z/19960403T040000Z,
+      19960404T010000Z/PT3H
+
+     RDATE;VALUE=DATE:19970101,19970120,19970217,19970421
+      19970526,19970704,19970901,19971014,19971128,19971129,19971225
+
+<span class="h5"><h5><a name="section-4.8.5.4">4.8.5.4</a> Recurrence Rule</h5></span>
+
+   Property Name: RRULE
+
+   Purpose: This property defines a rule or repeating pattern for
+   recurring events, to-dos, or time zone definitions.
+
+   Value Type: RECUR
+
+   Property Parameters: Non-standard property parameters can be
+   specified on this property.
+
+   Conformance: This property can be specified one or more times in
+   recurring "VEVENT", "VTODO" and "VJOURNAL" calendar components. It
+   can also be specified once in each STANDARD or DAYLIGHT sub-component
+   of the "VTIMEZONE" calendar component.
+
+   Description: The recurrence rule, if specified, is used in computing
+   the recurrence set. The recurrence set is the complete set of
+   recurrence instances for a calendar component. The recurrence set is
+   generated by considering the initial "DTSTART" property along with
+   the "RRULE", "RDATE", "EXDATE" and "EXRULE" properties contained
+   within the iCalendar object. The "DTSTART" property defines the first
+   instance in the recurrence set. Multiple instances of the "RRULE" and
+   "EXRULE" properties can also be specified to define more
+   sophisticated recurrence sets. The final recurrence set is generated
+   by gathering all of the start date/times generated by any of the
+   specified "RRULE" and "RDATE" properties, and excluding any start
+   date/times which fall within the union of start date/times generated
+   by any specified "EXRULE" and "EXDATE" properties. This implies that
+   start date/times within exclusion related properties (i.e., "EXDATE"
+   and "EXRULE") take precedence over those specified by inclusion
+   properties (i.e., "RDATE" and "RRULE"). Where duplicate instances are
+   generated by the "RRULE" and "RDATE" properties, only one recurrence
+   is considered. Duplicate instances are ignored.
+
+
+
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                   [Page 117]</span>
+</pre><pre class="newpage"><a name="page-118" id="page-118" href="#page-118" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+   The "DTSTART" and "DTEND" property pair or "DTSTART" and "DURATION"
+   property pair, specified within the iCalendar object defines the
+   first instance of the recurrence. When used with a recurrence rule,
+   the "DTSTART" and "DTEND" properties MUST be specified in local time
+   and the appropriate set of "VTIMEZONE" calendar components MUST be
+   included. For detail on the usage of the "VTIMEZONE" calendar
+   component, see the "VTIMEZONE" calendar component definition.
+
+   Any duration associated with the iCalendar object applies to all
+   members of the generated recurrence set. Any modified duration for
+   specific recurrences MUST be explicitly specified using the "RDATE"
+   property.
+
+   Format Definition: This property is defined by the following
+   notation:
+
+     rrule      = "RRULE" rrulparam ":" recur CRLF
+
+     rrulparam  = *(";" xparam)
+
+   Example: All examples assume the Eastern United States time zone.
+
+   Daily for 10 occurrences:
+
+     DTSTART;TZID=US-Eastern:19970902T090000
+     RRULE:FREQ=DAILY;COUNT=10
+
+     ==&gt; (1997 9:00 AM EDT)September 2-11
+
+   Daily until December 24, 1997:
+
+     DTSTART;TZID=US-Eastern:19970902T090000
+     RRULE:FREQ=DAILY;UNTIL=19971224T000000Z
+
+     ==&gt; (1997 9:00 AM EDT)September 2-30;October 1-25
+         (1997 9:00 AM EST)October 26-31;November 1-30;December 1-23
+
+   Every other day - forever:
+
+     DTSTART;TZID=US-Eastern:19970902T090000
+     RRULE:FREQ=DAILY;INTERVAL=2
+     ==&gt; (1997 9:00 AM EDT)September2,4,6,8...24,26,28,30;
+          October 2,4,6...20,22,24
+         (1997 9:00 AM EST)October 26,28,30;November 1,3,5,7...25,27,29;
+          Dec 1,3,...
+
+   Every 10 days, 5 occurrences:
+
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                   [Page 118]</span>
+</pre><pre class="newpage"><a name="page-119" id="page-119" href="#page-119" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+     DTSTART;TZID=US-Eastern:19970902T090000
+     RRULE:FREQ=DAILY;INTERVAL=10;COUNT=5
+
+     ==&gt; (1997 9:00 AM EDT)September 2,12,22;October 2,12
+
+   Everyday in January, for 3 years:
+
+     DTSTART;TZID=US-Eastern:19980101T090000
+     RRULE:FREQ=YEARLY;UNTIL=20000131T090000Z;
+      BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA
+     or
+     RRULE:FREQ=DAILY;UNTIL=20000131T090000Z;BYMONTH=1
+
+     ==&gt; (1998 9:00 AM EDT)January 1-31
+         (1999 9:00 AM EDT)January 1-31
+         (2000 9:00 AM EDT)January 1-31
+
+   Weekly for 10 occurrences
+
+     DTSTART;TZID=US-Eastern:19970902T090000
+     RRULE:FREQ=WEEKLY;COUNT=10
+
+     ==&gt; (1997 9:00 AM EDT)September 2,9,16,23,30;October 7,14,21
+         (1997 9:00 AM EST)October 28;November 4
+
+   Weekly until December 24, 1997
+
+     DTSTART;TZID=US-Eastern:19970902T090000
+     RRULE:FREQ=WEEKLY;UNTIL=19971224T000000Z
+
+     ==&gt; (1997 9:00 AM EDT)September 2,9,16,23,30;October 7,14,21
+         (1997 9:00 AM EST)October 28;November 4,11,18,25;
+                           December 2,9,16,23
+   Every other week - forever:
+
+     DTSTART;TZID=US-Eastern:19970902T090000
+     RRULE:FREQ=WEEKLY;INTERVAL=2;WKST=SU
+
+     ==&gt; (1997 9:00 AM EDT)September 2,16,30;October 14
+         (1997 9:00 AM EST)October 28;November 11,25;December 9,23
+         (1998 9:00 AM EST)January 6,20;February
+     ...
+
+   Weekly on Tuesday and Thursday for 5 weeks:
+
+    DTSTART;TZID=US-Eastern:19970902T090000
+    RRULE:FREQ=WEEKLY;UNTIL=19971007T000000Z;WKST=SU;BYDAY=TU,TH
+    or
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                   [Page 119]</span>
+</pre><pre class="newpage"><a name="page-120" id="page-120" href="#page-120" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+    RRULE:FREQ=WEEKLY;COUNT=10;WKST=SU;BYDAY=TU,TH
+
+    ==&gt; (1997 9:00 AM EDT)September 2,4,9,11,16,18,23,25,30;October 2
+
+   Every other week on Monday, Wednesday and Friday until December 24,
+   1997, but starting on Tuesday, September 2, 1997:
+
+     DTSTART;TZID=US-Eastern:19970902T090000
+     RRULE:FREQ=WEEKLY;INTERVAL=2;UNTIL=19971224T000000Z;WKST=SU;
+      BYDAY=MO,WE,FR
+     ==&gt; (1997 9:00 AM EDT)September 2,3,5,15,17,19,29;October
+     1,3,13,15,17
+         (1997 9:00 AM EST)October 27,29,31;November 10,12,14,24,26,28;
+                           December 8,10,12,22
+
+   Every other week on Tuesday and Thursday, for 8 occurrences:
+
+     DTSTART;TZID=US-Eastern:19970902T090000
+     RRULE:FREQ=WEEKLY;INTERVAL=2;COUNT=8;WKST=SU;BYDAY=TU,TH
+
+     ==&gt; (1997 9:00 AM EDT)September 2,4,16,18,30;October 2,14,16
+
+   Monthly on the 1st Friday for ten occurrences:
+
+     DTSTART;TZID=US-Eastern:19970905T090000
+     RRULE:FREQ=MONTHLY;COUNT=10;BYDAY=1FR
+
+     ==&gt; (1997 9:00 AM EDT)September 5;October 3
+         (1997 9:00 AM EST)November 7;Dec 5
+         (1998 9:00 AM EST)January 2;February 6;March 6;April 3
+         (1998 9:00 AM EDT)May 1;June 5
+
+   Monthly on the 1st Friday until December 24, 1997:
+
+     DTSTART;TZID=US-Eastern:19970905T090000
+     RRULE:FREQ=MONTHLY;UNTIL=19971224T000000Z;BYDAY=1FR
+
+     ==&gt; (1997 9:00 AM EDT)September 5;October 3
+         (1997 9:00 AM EST)November 7;December 5
+
+   Every other month on the 1st and last Sunday of the month for 10
+   occurrences:
+
+     DTSTART;TZID=US-Eastern:19970907T090000
+     RRULE:FREQ=MONTHLY;INTERVAL=2;COUNT=10;BYDAY=1SU,-1SU
+
+     ==&gt; (1997 9:00 AM EDT)September 7,28
+         (1997 9:00 AM EST)November 2,30
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                   [Page 120]</span>
+</pre><pre class="newpage"><a name="page-121" id="page-121" href="#page-121" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+         (1998 9:00 AM EST)January 4,25;March 1,29
+         (1998 9:00 AM EDT)May 3,31
+
+   Monthly on the second to last Monday of the month for 6 months:
+
+     DTSTART;TZID=US-Eastern:19970922T090000
+     RRULE:FREQ=MONTHLY;COUNT=6;BYDAY=-2MO
+
+     ==&gt; (1997 9:00 AM EDT)September 22;October 20
+         (1997 9:00 AM EST)November 17;December 22
+         (1998 9:00 AM EST)January 19;February 16
+
+   Monthly on the third to the last day of the month, forever:
+
+     DTSTART;TZID=US-Eastern:19970928T090000
+     RRULE:FREQ=MONTHLY;BYMONTHDAY=-3
+
+     ==&gt; (1997 9:00 AM EDT)September 28
+         (1997 9:00 AM EST)October 29;November 28;December 29
+         (1998 9:00 AM EST)January 29;February 26
+     ...
+
+   Monthly on the 2nd and 15th of the month for 10 occurrences:
+
+     DTSTART;TZID=US-Eastern:19970902T090000
+     RRULE:FREQ=MONTHLY;COUNT=10;BYMONTHDAY=2,15
+
+     ==&gt; (1997 9:00 AM EDT)September 2,15;October 2,15
+         (1997 9:00 AM EST)November 2,15;December 2,15
+         (1998 9:00 AM EST)January 2,15
+
+   Monthly on the first and last day of the month for 10 occurrences:
+
+     DTSTART;TZID=US-Eastern:19970930T090000
+     RRULE:FREQ=MONTHLY;COUNT=10;BYMONTHDAY=1,-1
+
+     ==&gt; (1997 9:00 AM EDT)September 30;October 1
+         (1997 9:00 AM EST)October 31;November 1,30;December 1,31
+         (1998 9:00 AM EST)January 1,31;February 1
+
+   Every 18 months on the 10th thru 15th of the month for 10
+   occurrences:
+
+     DTSTART;TZID=US-Eastern:19970910T090000
+     RRULE:FREQ=MONTHLY;INTERVAL=18;COUNT=10;BYMONTHDAY=10,11,12,13,14,
+      15
+
+     ==&gt; (1997 9:00 AM EDT)September 10,11,12,13,14,15
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                   [Page 121]</span>
+</pre><pre class="newpage"><a name="page-122" id="page-122" href="#page-122" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+         (1999 9:00 AM EST)March 10,11,12,13
+
+   Every Tuesday, every other month:
+
+     DTSTART;TZID=US-Eastern:19970902T090000
+     RRULE:FREQ=MONTHLY;INTERVAL=2;BYDAY=TU
+
+     ==&gt; (1997 9:00 AM EDT)September 2,9,16,23,30
+         (1997 9:00 AM EST)November 4,11,18,25
+         (1998 9:00 AM EST)January 6,13,20,27;March 3,10,17,24,31
+     ...
+
+   Yearly in June and July for 10 occurrences:
+
+     DTSTART;TZID=US-Eastern:19970610T090000
+     RRULE:FREQ=YEARLY;COUNT=10;BYMONTH=6,7
+     ==&gt; (1997 9:00 AM EDT)June 10;July 10
+         (1998 9:00 AM EDT)June 10;July 10
+         (1999 9:00 AM EDT)June 10;July 10
+         (2000 9:00 AM EDT)June 10;July 10
+         (2001 9:00 AM EDT)June 10;July 10
+     Note: Since none of the BYDAY, BYMONTHDAY or BYYEARDAY components
+     are specified, the day is gotten from DTSTART
+
+   Every other year on January, February, and March for 10 occurrences:
+
+     DTSTART;TZID=US-Eastern:19970310T090000
+     RRULE:FREQ=YEARLY;INTERVAL=2;COUNT=10;BYMONTH=1,2,3
+
+     ==&gt; (1997 9:00 AM EST)March 10
+         (1999 9:00 AM EST)January 10;February 10;March 10
+         (2001 9:00 AM EST)January 10;February 10;March 10
+         (2003 9:00 AM EST)January 10;February 10;March 10
+
+   Every 3rd year on the 1st, 100th and 200th day for 10 occurrences:
+
+     DTSTART;TZID=US-Eastern:19970101T090000
+     RRULE:FREQ=YEARLY;INTERVAL=3;COUNT=10;BYYEARDAY=1,100,200
+
+     ==&gt; (1997 9:00 AM EST)January 1
+         (1997 9:00 AM EDT)April 10;July 19
+         (2000 9:00 AM EST)January 1
+         (2000 9:00 AM EDT)April 9;July 18
+         (2003 9:00 AM EST)January 1
+         (2003 9:00 AM EDT)April 10;July 19
+         (2006 9:00 AM EST)January 1
+
+   Every 20th Monday of the year, forever:
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                   [Page 122]</span>
+</pre><pre class="newpage"><a name="page-123" id="page-123" href="#page-123" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+     DTSTART;TZID=US-Eastern:19970519T090000
+     RRULE:FREQ=YEARLY;BYDAY=20MO
+
+     ==&gt; (1997 9:00 AM EDT)May 19
+         (1998 9:00 AM EDT)May 18
+         (1999 9:00 AM EDT)May 17
+     ...
+
+   Monday of week number 20 (where the default start of the week is
+   Monday), forever:
+
+     DTSTART;TZID=US-Eastern:19970512T090000
+     RRULE:FREQ=YEARLY;BYWEEKNO=20;BYDAY=MO
+
+     ==&gt; (1997 9:00 AM EDT)May 12
+         (1998 9:00 AM EDT)May 11
+         (1999 9:00 AM EDT)May 17
+     ...
+
+   Every Thursday in March, forever:
+
+     DTSTART;TZID=US-Eastern:19970313T090000
+     RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=TH
+
+     ==&gt; (1997 9:00 AM EST)March 13,20,27
+         (1998 9:00 AM EST)March 5,12,19,26
+         (1999 9:00 AM EST)March 4,11,18,25
+     ...
+
+   Every Thursday, but only during June, July, and August, forever:
+
+     DTSTART;TZID=US-Eastern:19970605T090000
+     RRULE:FREQ=YEARLY;BYDAY=TH;BYMONTH=6,7,8
+
+     ==&gt; (1997 9:00 AM EDT)June 5,12,19,26;July 3,10,17,24,31;
+                       August 7,14,21,28
+         (1998 9:00 AM EDT)June 4,11,18,25;July 2,9,16,23,30;
+                       August 6,13,20,27
+         (1999 9:00 AM EDT)June 3,10,17,24;July 1,8,15,22,29;
+                       August 5,12,19,26
+     ...
+
+   Every Friday the 13th, forever:
+
+     DTSTART;TZID=US-Eastern:19970902T090000
+     EXDATE;TZID=US-Eastern:19970902T090000
+     RRULE:FREQ=MONTHLY;BYDAY=FR;BYMONTHDAY=13
+
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                   [Page 123]</span>
+</pre><pre class="newpage"><a name="page-124" id="page-124" href="#page-124" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+     ==&gt; (1998 9:00 AM EST)February 13;March 13;November 13
+         (1999 9:00 AM EDT)August 13
+         (2000 9:00 AM EDT)October 13
+     ...
+
+   The first Saturday that follows the first Sunday of the month,
+    forever:
+
+     DTSTART;TZID=US-Eastern:19970913T090000
+     RRULE:FREQ=MONTHLY;BYDAY=SA;BYMONTHDAY=7,8,9,10,11,12,13
+
+     ==&gt; (1997 9:00 AM EDT)September 13;October 11
+         (1997 9:00 AM EST)November 8;December 13
+         (1998 9:00 AM EST)January 10;February 7;March 7
+         (1998 9:00 AM EDT)April 11;May 9;June 13...
+     ...
+
+   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;BYMONTH=11;BYDAY=TU;BYMONTHDAY=2,3,4,
+      5,6,7,8
+
+     ==&gt; (1996 9:00 AM EST)November 5
+         (2000 9:00 AM EST)November 7
+         (2004 9:00 AM EST)November 2
+     ...
+
+   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;BYDAY=TU,WE,TH;BYSETPOS=3
+
+     ==&gt; (1997 9:00 AM EDT)September 4;October 7
+         (1997 9:00 AM EST)November 6
+
+   The 2nd to last weekday of the month:
+
+     DTSTART;TZID=US-Eastern:19970929T090000
+     RRULE:FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=-2
+
+     ==&gt; (1997 9:00 AM EDT)September 29
+         (1997 9:00 AM EST)October 30;November 27;December 30
+         (1998 9:00 AM EST)January 29;February 26;March 30
+     ...
+
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                   [Page 124]</span>
+</pre><pre class="newpage"><a name="page-125" id="page-125" href="#page-125" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+   Every 3 hours from 9:00 AM to 5:00 PM on a specific day:
+
+     DTSTART;TZID=US-Eastern:19970902T090000
+     RRULE:FREQ=HOURLY;INTERVAL=3;UNTIL=19970902T170000Z
+
+     ==&gt; (September 2, 1997 EDT)09:00,12:00,15:00
+
+   Every 15 minutes for 6 occurrences:
+
+     DTSTART;TZID=US-Eastern:19970902T090000
+     RRULE:FREQ=MINUTELY;INTERVAL=15;COUNT=6
+
+     ==&gt; (September 2, 1997 EDT)09:00,09:15,09:30,09:45,10:00,10:15
+
+   Every hour and a half for 4 occurrences:
+
+     DTSTART;TZID=US-Eastern:19970902T090000
+     RRULE:FREQ=MINUTELY;INTERVAL=90;COUNT=4
+
+     ==&gt; (September 2, 1997 EDT)09:00,10:30;12:00;13:30
+
+   Every 20 minutes from 9:00 AM to 4:40 PM every day:
+
+     DTSTART;TZID=US-Eastern:19970902T090000
+     RRULE:FREQ=DAILY;BYHOUR=9,10,11,12,13,14,15,16;BYMINUTE=0,20,40
+     or
+     RRULE:FREQ=MINUTELY;INTERVAL=20;BYHOUR=9,10,11,12,13,14,15,16
+
+     ==&gt; (September 2, 1997 EDT)9:00,9:20,9:40,10:00,10:20,
+                                ... 16:00,16:20,16:40
+         (September 3, 1997 EDT)9:00,9:20,9:40,10:00,10:20,
+                               ...16:00,16:20,16:40
+     ...
+
+   An example where the days generated makes a difference because of
+   WKST:
+
+     DTSTART;TZID=US-Eastern:19970805T090000
+     RRULE:FREQ=WEEKLY;INTERVAL=2;COUNT=4;BYDAY=TU,SU;WKST=MO
+
+     ==&gt; (1997 EDT)Aug 5,10,19,24
+
+     changing only WKST from MO to SU, yields different results...
+
+     DTSTART;TZID=US-Eastern:19970805T090000
+     RRULE:FREQ=WEEKLY;INTERVAL=2;COUNT=4;BYDAY=TU,SU;WKST=SU
+     ==&gt; (1997 EDT)August 5,17,19,31
+
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                   [Page 125]</span>
+</pre><pre class="newpage"><a name="page-126" id="page-126" href="#page-126" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+<span class="h4"><h4><a name="section-4.8.6">4.8.6</a> Alarm Component Properties</h4></span>
+
+   The following properties specify alarm information in calendar
+   components.
+
+<span class="h5"><h5><a name="section-4.8.6.1">4.8.6.1</a> Action</h5></span>
+
+   Property Name: ACTION
+
+   Purpose: This property defines the action to be invoked when an alarm
+   is triggered.
+
+   Value Type: TEXT
+
+   Property Parameters: Non-standard property parameters can be
+   specified on this property.
+
+   Conformance: This property MUST be specified once in a "VALARM"
+   calendar component.
+
+   Description: Each "VALARM" calendar component has a particular type
+   of action associated with it. This property specifies the type of
+   action
+
+   Format Definition: The property is defined by the following notation:
+
+     action     = "ACTION" actionparam ":" actionvalue CRLF
+
+     actionparam        = *(";" xparam)
+
+     actionvalue        = "AUDIO" / "DISPLAY" / "EMAIL" / "PROCEDURE"
+                        / iana-token / x-name
+
+   Example: The following are examples of this property in a "VALARM"
+   calendar component:
+
+     ACTION:AUDIO
+
+     ACTION:DISPLAY
+
+     ACTION:PROCEDURE
+
+<span class="h5"><h5><a name="section-4.8.6.2">4.8.6.2</a> Repeat Count</h5></span>
+
+   Property Name: REPEAT
+
+   Purpose: This property defines the number of time the alarm should be
+   repeated, after the initial trigger.
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                   [Page 126]</span>
+</pre><pre class="newpage"><a name="page-127" id="page-127" href="#page-127" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+   Value Type: INTEGER
+
+   Property Parameters: Non-standard property parameters can be
+   specified on this property.
+
+   Conformance: This property can be specified in a "VALARM" calendar
+   component.
+
+   Description: If the alarm triggers more than once, then this property
+   MUST be specified along with the "DURATION" property.
+
+   Format Definition: The property is defined by the following notation:
+
+     repeatcnt  = "REPEAT" repparam ":" integer CRLF
+     ;Default is "0", zero.
+
+     repparam   = *(";" xparam)
+
+   Example: The following is an example of this property for an alarm
+   that repeats 4 additional times with a 5 minute delay after the
+   initial triggering of the alarm:
+
+     REPEAT:4
+     DURATION:PT5M
+
+<span class="h5"><h5><a name="section-4.8.6.3">4.8.6.3</a> Trigger</h5></span>
+
+   Property Name: TRIGGER
+
+   Purpose: This property specifies when an alarm will trigger.
+
+   Value Type: The default value type is DURATION. The value type can be
+   set to a DATE-TIME value type, in which case the value MUST specify a
+   UTC formatted DATE-TIME value.
+
+   Property Parameters: Non-standard, value data type, time zone
+   identifier or trigger relationship property parameters can be
+   specified on this property. The trigger relationship property
+   parameter MUST only be specified when the value type is DURATION.
+
+   Conformance: This property MUST be specified in the "VALARM" calendar
+   component.
+
+   Description: Within the "VALARM" calendar component, this property
+   defines when the alarm will trigger. The default value type is
+   DURATION, specifying a relative time for the trigger of the alarm.
+   The default duration is relative to the start of an event or to-do
+   that the alarm is associated with. The duration can be explicitly set
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                   [Page 127]</span>
+</pre><pre class="newpage"><a name="page-128" id="page-128" href="#page-128" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+   to trigger from either the end or the start of the associated event
+   or to-do with the "RELATED" parameter. A value of START will set the
+   alarm to trigger off the start of the associated event or to-do. A
+   value of END will set the alarm to trigger off the end of the
+   associated event or to-do.
+
+   Either a positive or negative duration may be specified for the
+   "TRIGGER" property. An alarm with a positive duration is triggered
+   after the associated start or end of the event or to-do. An alarm
+   with a negative duration is triggered before the associated start or
+   end of the event or to-do.
+
+   The "RELATED" property parameter is not valid if the value type of
+   the property is set to DATE-TIME (i.e., for an absolute date and time
+   alarm trigger). If a value type of DATE-TIME is specified, then the
+   property value MUST be specified in the UTC time format. If an
+   absolute trigger is specified on an alarm for a recurring event or
+   to-do, then the alarm will only trigger for the specified absolute
+   date/time, along with any specified repeating instances.
+
+   If the trigger is set relative to START, then the "DTSTART" property
+   MUST be present in the associated "VEVENT" or "VTODO" calendar
+   component. If an alarm is specified for an event with the trigger set
+   relative to the END, then the "DTEND" property or the "DSTART" and
+   "DURATION' properties MUST be present in the associated "VEVENT"
+   calendar component. If the alarm is specified for a to-do with a
+   trigger set relative to the END, then either the "DUE" property or
+   the "DSTART" and "DURATION' properties MUST be present in the
+   associated "VTODO" calendar component.
+
+   Alarms specified in an event or to-do which is defined in terms of a
+   DATE value type will be triggered relative to 00:00:00 UTC on the
+   specified date. For example, if "DTSTART:19980205, then the duration
+   trigger will be relative to19980205T000000Z.
+
+   Format Definition: The property is defined by the following notation:
+
+     trigger    = "TRIGGER" (trigrel / trigabs)
+
+     trigrel    = *(
+
+                ; the following are optional,
+                ; but MUST NOT occur more than once
+
+                  (";" "VALUE" "=" "DURATION") /
+                  (";" trigrelparam) /
+
+                ; the following is optional,
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                   [Page 128]</span>
+</pre><pre class="newpage"><a name="page-129" id="page-129" href="#page-129" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+                ; and MAY occur more than once
+
+                  (";" xparam)
+                  ) ":"  dur-value
+
+     trigabs    = 1*(
+
+                ; the following is REQUIRED,
+                ; but MUST NOT occur more than once
+
+                  (";" "VALUE" "=" "DATE-TIME") /
+
+                ; the following is optional,
+                ; and MAY occur more than once
+
+                  (";" xparam)
+
+                  ) ":" date-time
+
+   Example: A trigger set 15 minutes prior to the start of the event or
+   to-do.
+
+     TRIGGER:-P15M
+
+   A trigger set 5 minutes after the end of the event or to-do.
+
+     TRIGGER;RELATED=END:P5M
+
+   A trigger set to an absolute date/time.
+
+     TRIGGER;VALUE=DATE-TIME:19980101T050000Z
+
+<span class="h4"><h4><a name="section-4.8.7">4.8.7</a> Change Management Component Properties</h4></span>
+
+   The following properties specify change management information in
+   calendar components.
+
+<span class="h5"><h5><a name="section-4.8.7.1">4.8.7.1</a> Date/Time Created</h5></span>
+
+   Property Name: CREATED
+
+   Purpose: This property specifies the date and time that the calendar
+   information was created by the calendar user agent in the calendar
+   store.
+
+        Note: This is analogous to the creation date and time for a file
+        in the file system.
+
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                   [Page 129]</span>
+</pre><pre class="newpage"><a name="page-130" id="page-130" href="#page-130" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+   Value Type: DATE-TIME
+
+   Property Parameters: Non-standard property parameters can be
+   specified on this property.
+
+   Conformance: The property can be specified once in "VEVENT", "VTODO"
+   or "VJOURNAL" calendar components.
+
+   Description: The date and time is a UTC value.
+
+   Format Definition: The property is defined by the following notation:
+
+     created    = "CREATED" creaparam ":" date-time CRLF
+
+     creaparam  = *(";" xparam)
+
+   Example: The following is an example of this property:
+
+     CREATED:19960329T133000Z
+
+<span class="h5"><h5><a name="section-4.8.7.2">4.8.7.2</a> Date/Time Stamp</h5></span>
+
+   Property Name: DTSTAMP
+
+   Purpose: The property indicates the date/time that the instance of
+   the iCalendar object was created.
+
+   Value Type: DATE-TIME
+
+   Property Parameters: Non-standard property parameters can be
+   specified on this property.
+
+   Conformance: This property MUST be included in the "VEVENT", "VTODO",
+   "VJOURNAL" or "VFREEBUSY" calendar components.
+
+   Description: The value MUST be specified in the UTC time format.
+
+   This property is also useful to protocols such as [<a href="#ref-IMIP" title="&quot;iCalendar Message-based Interoperability Protocol (IMIP)&quot;">IMIP</a>] that have
+   inherent latency issues with the delivery of content. This property
+   will assist in the proper sequencing of messages containing iCalendar
+   objects.
+
+   This property is different than the "CREATED" and "LAST-MODIFIED"
+   properties. These two properties are used to specify when the
+   particular calendar data in the calendar store was created and last
+   modified. This is different than when the iCalendar object
+   representation of the calendar service information was created or
+   last modified.
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                   [Page 130]</span>
+</pre><pre class="newpage"><a name="page-131" id="page-131" href="#page-131" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+   Format Definition: The property is defined by the following notation:
+
+     dtstamp    = "DTSTAMP" stmparam ":" date-time CRLF
+
+     stmparam   = *(";" xparam)
+
+   Example:
+
+     DTSTAMP:19971210T080000Z
+
+<span class="h5"><h5><a name="section-4.8.7.3">4.8.7.3</a> Last Modified</h5></span>
+
+   Property Name: LAST-MODIFIED
+
+   Purpose: The property specifies the date and time that the
+   information associated with the calendar component was last revised
+   in the calendar store.
+
+        Note: This is analogous to the modification date and time for a
+        file in the file system.
+
+   Value Type: DATE-TIME
+
+   Property Parameters: Non-standard property parameters can be
+   specified on this property.
+
+   Conformance: This property can be specified in the "EVENT", "VTODO",
+   "VJOURNAL" or "VTIMEZONE" calendar components.
+
+   Description: The property value MUST be specified in the UTC time
+   format.
+
+   Format Definition: The property is defined by the following notation:
+
+     last-mod   = "LAST-MODIFIED" lstparam ":" date-time CRLF
+
+     lstparam   = *(";" xparam)
+
+   Example: The following is are examples of this property:
+
+     LAST-MODIFIED:19960817T133000Z
+
+<span class="h5"><h5><a name="section-4.8.7.4">4.8.7.4</a> Sequence Number</h5></span>
+
+   Property Name: SEQUENCE
+
+   Purpose: This property defines the revision sequence number of the
+   calendar component within a sequence of revisions.
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                   [Page 131]</span>
+</pre><pre class="newpage"><a name="page-132" id="page-132" href="#page-132" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+   Value Type: integer
+
+   Property Parameters: Non-standard property parameters can be
+   specified on this property.
+
+   Conformance: The property can be specified in "VEVENT", "VTODO" or
+   "VJOURNAL" calendar component.
+
+   Description: When a calendar component is created, its sequence
+   number is zero (US-ASCII decimal 48). It is monotonically incremented
+   by the "Organizer's" CUA each time the "Organizer" makes a
+   significant revision to the calendar component. When the "Organizer"
+   makes changes to one of the following properties, the sequence number
+   MUST be incremented:
+
+     .  "DTSTART"
+
+     .  "DTEND"
+
+     .  "DUE"
+
+     .  "RDATE"
+
+     .  "RRULE"
+
+     .  "EXDATE"
+
+     .  "EXRULE"
+
+     .  "STATUS"
+
+   In addition, changes made by the "Organizer" to other properties can
+   also force the sequence number to be incremented. The "Organizer" CUA
+   MUST increment the sequence number when ever it makes changes to
+   properties in the calendar component that the "Organizer" deems will
+   jeopardize the validity of the participation status of the
+   "Attendees". For example, changing the location of a meeting from one
+   locale to another distant locale could effectively impact the
+   participation status of the "Attendees".
+
+   The "Organizer" includes this property in an iCalendar object that it
+   sends to an "Attendee" to specify the current version of the calendar
+   component.
+
+   The "Attendee" includes this property in an iCalendar object that it
+   sends to the "Organizer" to specify the version of the calendar
+   component that the "Attendee" is referring to.
+
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                   [Page 132]</span>
+</pre><pre class="newpage"><a name="page-133" id="page-133" href="#page-133" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+   A change to the sequence number is not the mechanism that an
+   "Organizer" uses to request a response from the "Attendees". The
+   "RSVP" parameter on the "ATTENDEE" property is used by the
+   "Organizer" to indicate that a response from the "Attendees" is
+   requested.
+
+   Format Definition: This property is defined by the following
+   notation:
+
+     seq = "SEQUENCE" seqparam ":" integer CRLF
+     ; Default is "0"
+
+     seqparam   = *(";" xparam)
+
+   Example: The following is an example of this property for a calendar
+   component that was just created by the "Organizer".
+
+     SEQUENCE:0
+
+   The following is an example of this property for a calendar component
+   that has been revised two different times by the "Organizer".
+
+     SEQUENCE:2
+
+<span class="h4"><h4><a name="section-4.8.8">4.8.8</a> Miscellaneous Component Properties</h4></span>
+
+   The following properties specify information about a number of
+   miscellaneous features of calendar components.
+
+<span class="h5"><h5><a name="section-4.8.8.1">4.8.8.1</a> Non-standard Properties</h5></span>
+
+   Property Name: Any property name with a "X-" prefix
+
+   Purpose: This class of property provides a framework for defining
+   non-standard properties.
+
+   Value Type: TEXT
+
+   Property Parameters: Non-standard and language property parameters
+   can be specified on this property.
+
+   Conformance: This property can be specified in any calendar
+   component.
+
+   Description: The MIME Calendaring and Scheduling Content Type
+   provides a "standard mechanism for doing non-standard things". This
+   extension support is provided for implementers to "push the envelope"
+   on the existing version of the memo. Extension properties are
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                   [Page 133]</span>
+</pre><pre class="newpage"><a name="page-134" id="page-134" href="#page-134" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+   specified by property and/or property parameter names that have the
+   prefix text of "X-" (the two character sequence: LATIN CAPITAL LETTER
+   X character followed by the HYPEN-MINUS character). It is recommended
+   that vendors concatenate onto this sentinel another short prefix text
+   to identify the vendor. This will facilitate readability of the
+   extensions and minimize possible collision of names between different
+   vendors. User agents that support this content type are expected to
+   be able to parse the extension properties and property parameters but
+   can ignore them.
+
+   At present, there is no registration authority for names of extension
+   properties and property parameters. The data type for this property
+   is TEXT. Optionally, the data type can be any of the other valid data
+   types.
+
+   Format Definition: The property is defined by the following notation:
+
+     x-prop     = x-name *(";" xparam) [";" languageparam] ":" text CRLF
+        ; Lines longer than 75 octets should be folded
+
+   Example: The following might be the ABC vendor's extension for an
+   audio-clip form of subject property:
+
+     X-ABC-MMSUBJ;X-ABC-MMSUBJTYPE=wave:http://load.noise.org/mysubj.wav
+
+<span class="h5"><h5><a name="section-4.8.8.2">4.8.8.2</a> Request Status</h5></span>
+
+   Property Name: REQUEST-STATUS
+
+   Purpose: This property defines the status code returned for a
+   scheduling request.
+
+   Value Type: TEXT
+
+   Property Parameters: Non-standard and language property parameters
+   can be specified on this property.
+
+   Conformance: The property can be specified in "VEVENT", "VTODO",
+   "VJOURNAL" or "VFREEBUSY" calendar component.
+
+   Description: This property is used to return status code information
+   related to the processing of an associated iCalendar object. The data
+   type for this property is TEXT.
+
+   The value consists of a short return status component, a longer
+   return status description component, and optionally a status-specific
+   data component. The components of the value are separated by the
+   SEMICOLON character (US-ASCII decimal 59).
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                   [Page 134]</span>
+</pre><pre class="newpage"><a name="page-135" id="page-135" href="#page-135" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+   The short return status is a PERIOD character (US-ASCII decimal 46)
+   separated 3-tuple of integers. For example, "3.1.1". The successive
+   levels of integers provide for a successive level of status code
+   granularity.
+
+   The following are initial classes for the return status code.
+   Individual iCalendar object methods will define specific return
+   status codes for these classes. In addition, other classes for the
+   return status code may be defined using the registration process
+   defined later in this memo.
+
+     |==============+===============================================|
+     | Short Return | Longer Return Status Description              |
+     | Status Code  |                                               |
+     |==============+===============================================|
+     |    1.xx      | Preliminary success. This class of status     |
+     |              | of status code indicates that the request has |
+     |              | request has been initially processed but that |
+     |              | completion is pending.                        |
+     |==============+===============================================|
+     |    2.xx      | Successful. This class of status code         |
+     |              | indicates that the request was completed      |
+     |              | successfuly. However, the exact status code   |
+     |              | can indicate that a fallback has been taken.  |
+     |==============+===============================================|
+     |    3.xx      | Client Error. This class of status code       |
+     |              | indicates that the request was not successful.|
+     |              | The error is the result of either a syntax or |
+     |              | a semantic error in the client formatted      |
+     |              | request. Request should not be retried until  |
+     |              | the condition in the request is corrected.    |
+     |==============+===============================================|
+     |    4.xx      | Scheduling Error. This class of status code   |
+     |              | indicates that the request was not successful.|
+     |              | Some sort of error occurred within the        |
+     |              | calendaring and scheduling service, not       |
+     |              | directly related to the request itself.       |
+     |==============+===============================================|
+
+   Format Definition: The property is defined by the following notation:
+
+     rstatus    = "REQUEST-STATUS" rstatparam ":"
+                  statcode ";" statdesc [";" extdata]
+
+     rstatparam = *(
+
+                ; the following is optional,
+                ; but MUST NOT occur more than once
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                   [Page 135]</span>
+</pre><pre class="newpage"><a name="page-136" id="page-136" href="#page-136" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+                (";" languageparm) /
+
+                ; the following is optional,
+                ; and MAY occur more than once
+
+                (";" xparam)
+
+                )
+
+     statcode   = 1*DIGIT *("." 1*DIGIT)
+     ;Hierarchical, numeric return status code
+
+     statdesc   = text
+     ;Textual status description
+
+     extdata    = text
+     ;Textual exception data. For example, the offending property
+     ;name and value or complete property line.
+
+   Example: The following are some possible examples of this property.
+   The COMMA and SEMICOLON separator characters in the property value
+   are BACKSLASH character escaped because they appear in a  text value.
+
+     REQUEST-STATUS:2.0;Success
+
+     REQUEST-STATUS:3.1;Invalid property value;DTSTART:96-Apr-01
+
+     REQUEST-STATUS:2.8; Success\, repeating event ignored. Scheduled
+      as a single event.;RRULE:FREQ=WEEKLY\;INTERVAL=2
+
+     REQUEST-STATUS:4.1;Event conflict. Date/time is busy.
+
+     REQUEST-STATUS:3.7;Invalid calendar user;ATTENDEE:
+      MAILTO:jsmith@host.com
+
+<span class="h2"><h2><a name="section-5">5</a> iCalendar Object Examples</h2></span>
+
+   The following examples are provided as an informational source of
+   illustrative iCalendar objects consistent with this content type.
+
+   The following example specifies a three-day conference that begins at
+   8:00 AM EDT, September 18, 1996 and end at 6:00 PM EDT, September 20,
+   1996.
+
+     BEGIN:VCALENDAR PRODID:-//xyz Corp//NONSGML PDA Calendar Verson
+     1.0//EN VERSION:2.0 BEGIN:VEVENT DTSTAMP:19960704T120000Z
+     UID:uid1@host.com ORGANIZER:MAILTO:jsmith@host.com
+     DTSTART:19960918T143000Z DTEND:19960920T220000Z STATUS:CONFIRMED
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                   [Page 136]</span>
+</pre><pre class="newpage"><a name="page-137" id="page-137" href="#page-137" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+     CATEGORIES:CONFERENCE SUMMARY:Networld+Interop Conference
+     DESCRIPTION:Networld+Interop Conference
+       and Exhibit\nAtlanta World Congress Center\n
+      Atlanta, Georgia END:VEVENT END:VCALENDAR
+
+   The following example specifies a group scheduled meeting that begin
+   at 8:30 AM EST on March 12, 1998 and end at 9:30 AM EST on March 12,
+   1998. The "Organizer" has scheduled the meeting with one or more
+   calendar users in a group. A time zone specification for Eastern
+   United States has been specified.
+
+     BEGIN:VCALENDAR
+     PRODID:-//RDU Software//NONSGML HandCal//EN
+     VERSION:2.0
+     BEGIN:VTIMEZONE
+     TZID:US-Eastern
+     BEGIN:STANDARD
+     DTSTART:19981025T020000
+     RDATE:19981025T020000
+     TZOFFSETFROM:-0400
+     TZOFFSETTO:-0500
+     TZNAME:EST
+     END:STANDARD
+     BEGIN:DAYLIGHT
+     DTSTART:19990404T020000
+     RDATE:19990404T020000
+     TZOFFSETFROM:-0500
+     TZOFFSETTO:-0400
+     TZNAME:EDT
+     END:DAYLIGHT
+     END:VTIMEZONE
+     BEGIN:VEVENT
+     DTSTAMP:19980309T231000Z
+     UID:guid-1.host1.com
+     ORGANIZER;ROLE=CHAIR:MAILTO:mrbig@host.com
+     ATTENDEE;RSVP=TRUE;ROLE=REQ-PARTICIPANT;CUTYPE=GROUP:
+      MAILTO:employee-A@host.com
+     DESCRIPTION:Project XYZ Review Meeting
+     CATEGORIES:MEETING
+     CLASS:PUBLIC
+     CREATED:19980309T130000Z
+     SUMMARY:XYZ Project Review
+     DTSTART;TZID=US-Eastern:19980312T083000
+     DTEND;TZID=US-Eastern:19980312T093000
+     LOCATION:1CP Conference Room 4350
+     END:VEVENT
+     END:VCALENDAR
+
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                   [Page 137]</span>
+</pre><pre class="newpage"><a name="page-138" id="page-138" href="#page-138" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+   The following is an example of an iCalendar object passed in a MIME
+   message with a single body part consisting of a "text/calendar"
+   Content Type.
+
+     TO:jsmith@host1.com
+     FROM:jdoe@host1.com
+     MIME-VERSION:1.0
+     MESSAGE-ID:&lt;id3@host1.com&gt;
+     CONTENT-TYPE:text/calendar
+
+     BEGIN:VCALENDAR
+     METHOD:xyz
+     VERSION:2.0
+     PRODID:-//ABC Corporation//NONSGML My Product//EN
+     BEGIN:VEVENT
+     DTSTAMP:19970324T1200Z
+     SEQUENCE:0
+     UID:uid3@host1.com
+     ORGANIZER:MAILTO:jdoe@host1.com
+     ATTENDEE;RSVP=TRUE:MAILTO:jsmith@host1.com
+     DTSTART:19970324T123000Z
+     DTEND:19970324T210000Z
+     CATEGORIES:MEETING,PROJECT
+     CLASS:PUBLIC
+     SUMMARY:Calendaring Interoperability Planning Meeting
+     DESCRIPTION:Discuss how we can test c&amp;s interoperability\n
+      using iCalendar and other IETF standards.
+     LOCATION:LDB Lobby
+     ATTACH;FMTTYPE=application/postscript:ftp://xyzCorp.com/pub/
+      conf/bkgrnd.ps
+     END:VEVENT
+     END:VCALENDAR
+
+   The following is an example of a to-do due on April 15, 1998. An
+   audio alarm has been specified to remind the calendar user at noon,
+   the day before the to-do is expected to be completed and repeat
+   hourly, four additional times. The to-do definition has been modified
+   twice since it was initially created.
+
+     BEGIN:VCALENDAR
+     VERSION:2.0
+     PRODID:-//ABC Corporation//NONSGML My Product//EN
+     BEGIN:VTODO
+     DTSTAMP:19980130T134500Z
+     SEQUENCE:2
+     UID:uid4@host1.com
+     ORGANIZER:MAILTO:unclesam@us.gov
+     ATTENDEE;PARTSTAT=ACCEPTED:MAILTO:jqpublic@host.com
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                   [Page 138]</span>
+</pre><pre class="newpage"><a name="page-139" id="page-139" href="#page-139" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+     DUE:19980415T235959
+     STATUS:NEEDS-ACTION
+     SUMMARY:Submit Income Taxes
+     BEGIN:VALARM
+     ACTION:AUDIO
+     TRIGGER:19980403T120000
+     ATTACH;FMTTYPE=audio/basic:http://host.com/pub/audio-
+      files/ssbanner.aud
+     REPEAT:4
+     DURATION:PT1H
+     END:VALARM
+     END:VTODO
+     END:VCALENDAR
+
+   The following is an example of a journal entry.
+
+     BEGIN:VCALENDAR
+     VERSION:2.0
+     PRODID:-//ABC Corporation//NONSGML My Product//EN
+     BEGIN:VJOURNAL
+     DTSTAMP:19970324T120000Z
+     UID:uid5@host1.com
+     ORGANIZER:MAILTO:jsmith@host.com
+     STATUS:DRAFT
+     CLASS:PUBLIC
+     CATEGORY:Project Report, XYZ, Weekly Meeting
+     DESCRIPTION:Project xyz Review Meeting Minutes\n
+      Agenda\n1. Review of project version 1.0 requirements.\n2.
+     Definition
+      of project processes.\n3. Review of project schedule.\n
+      Participants: John Smith, Jane Doe, Jim Dandy\n-It was
+       decided that the requirements need to be signed off by
+       product marketing.\n-Project processes were accepted.\n
+      -Project schedule needs to account for scheduled holidays
+       and employee vacation time. Check with HR for specific
+       dates.\n-New schedule will be distributed by Friday.\n-
+      Next weeks meeting is cancelled. No meeting until 3/23.
+     END:VJOURNAL
+     END:VCALENDAR
+
+   The following is an example of published busy time information. The
+   iCalendar object might be placed in the network resource
+   www.host.com/calendar/busytime/jsmith.ifb.
+
+     BEGIN:VCALENDAR
+     VERSION:2.0
+     PRODID:-//RDU Software//NONSGML HandCal//EN
+     BEGIN:VFREEBUSY
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                   [Page 139]</span>
+</pre><pre class="newpage"><a name="page-140" id="page-140" href="#page-140" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+     ORGANIZER:MAILTO:jsmith@host.com
+     DTSTART:19980313T141711Z
+     DTEND:19980410T141711Z
+     FREEBUSY:19980314T233000Z/19980315T003000Z
+     FREEBUSY:19980316T153000Z/19980316T163000Z
+     FREEBUSY:19980318T030000Z/19980318T040000Z
+     URL:http://www.host.com/calendar/busytime/jsmith.ifb
+     END:VFREEBUSY
+     END:VCALENDAR
+
+<span class="h2"><h2><a name="section-6">6</a> Recommended Practices</h2></span>
+
+   These recommended practices should be followed in order to assure
+   consistent handling of the following cases for an iCalendar object.
+
+   1.  Content lines longer than 75 octets SHOULD be folded.
+
+   2.  A calendar entry with a "DTSTART" property but no "DTEND"
+       property does not take up any time. It is intended to represent
+       an event that is associated with a given calendar date and time
+       of day, such as an anniversary. Since the event does not take up
+       any time, it MUST NOT be used to record busy time no matter what
+       the value for the "TRANSP" property.
+
+   3.  When the "DTSTART" and "DTEND", for "VEVENT", "VJOURNAL" and
+       "VFREEBUSY" calendar components, and "DTSTART" and "DUE", for
+       "VTODO" calendar components, have the same value data type (e.g.,
+       DATE-TIME), they SHOULD specify values in the same time format
+       (e.g., UTC time format).
+
+   4.  When the combination of the "RRULE" and "RDATE" properties on an
+       iCalendar object produces multiple instances having the same
+       start date/time, they should be collapsed to, and considered as,
+       a single instance.
+
+   5.  When a calendar user receives multiple requests for the same
+       calendar component (e.g., REQUEST for a "VEVENT" calendar
+       component) as a result of being on multiple mailing lists
+       specified by "ATTENDEE" properties in the request, they SHOULD
+       respond to only one of the requests. The calendar user SHOULD
+       also specify (using the "MEMBER" parameter of the "ATTENDEE"
+       property) which mailing list they are a member of.
+
+   6.  An implementation can truncate a "SUMMARY" property value to 255
+       characters.
+
+
+
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                   [Page 140]</span>
+</pre><pre class="newpage"><a name="page-141" id="page-141" href="#page-141" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+   7.  If seconds of the minute are not supported by an implementation,
+       then a value of "00" SHOULD be specified for the seconds
+       component in a time value.
+
+   8.  If the value type parameter (VALUE=) contains an unknown value
+       type, it SHOULD be treated as TEXT.
+
+   9.  TZURL values SHOULD NOT be specified as a FILE URI type. This URI
+       form can be useful within an organization, but is problematic in
+       the Internet.
+
+   10.  Some possible English values for CATEGORIES property include
+        "ANNIVERSARY", "APPOINTMENT", "BUSINESS", "EDUCATION",
+        "HOLIDAY", "MEETING", "MISCELLANEOUS", "NON-WORKING HOURS", "NOT
+        IN OFFICE", "PERSONAL", "PHONE CALL", "SICK DAY", "SPECIAL
+        OCCASION", "TRAVEL", "VACATION". Categories can be specified in
+        any registered language.
+
+   11.  Some possible English values for RESOURCES property include
+        "CATERING", "CHAIRS", "COMPUTER PROJECTOR", "EASEL", "OVERHEAD
+        PROJECTOR", "SPEAKER PHONE", "TABLE", "TV", "VCR", "VIDEO
+        PHONE", "VEHICLE". Resources can be specified in any registered
+        language.
+
+<span class="h2"><h2><a name="section-7">7</a> Registration of Content Type Elements</h2></span>
+
+   This section provides the process for registration of MIME
+   Calendaring and Scheduling Content Type iCalendar object methods and
+   new or modified properties.
+
+<span class="h3"><h3><a name="section-7.1">7.1</a> Registration of New and Modified iCalendar Object Methods</h3></span>
+
+   New MIME Calendaring and Scheduling Content Type iCalendar object
+   methods are registered by the publication of an IETF Request for
+   Comments (RFC). Changes to an iCalendar object method are registered
+   by the publication of a revision of the RFC defining the method.
+
+<span class="h3"><h3><a name="section-7.2">7.2</a> Registration of New Properties</h3></span>
+
+   This section defines procedures by which new properties or enumerated
+   property values for the MIME Calendaring and Scheduling Content Type
+   can be registered with the IANA. Non-IANA properties can be used by
+   bilateral agreement, provided the associated properties names follow
+   the "X-" convention.
+
+   The procedures defined here are designed to allow public comment and
+   review of new properties, while posing only a small impediment to the
+   definition of new properties.
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                   [Page 141]</span>
+</pre><pre class="newpage"><a name="page-142" id="page-142" href="#page-142" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+   Registration of a new property is accomplished by the following
+   steps.
+
+<span class="h4"><h4><a name="section-7.2.1">7.2.1</a> Define the property</h4></span>
+
+   A property is defined by completing the following template.
+
+     To: ietf-calendar@imc.org
+
+     Subject: Registration of text/calendar MIME property XXX
+
+     Property name:
+
+     Property purpose:
+
+     Property value type(s):
+
+     Property parameter (s):
+
+     Conformance:
+
+     Description:
+
+     Format definition:
+
+     Examples:
+
+   The meaning of each field in the template is as follows.
+
+   Property name: The name of the property, as it will appear in the
+   body of an text/calendar MIME Content-Type "property: value" line to
+   the left of the colon ":".
+
+   Property purpose: The purpose of the property (e.g., to indicate a
+   delegate for the event or to-do, etc.). Give a short but clear
+   description.
+
+   Property value type (s): Any of the valid value types for the
+   property value needs to be specified. The default value type also
+   needs to be specified. If a new value type is specified, it needs to
+   be declared in this section.
+
+   Property parameter (s): Any of the valid property parameters for the
+   property needs to be specified.
+
+   Conformance: The calendar components that the property can appear in
+   needs to be specified.
+
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                   [Page 142]</span>
+</pre><pre class="newpage"><a name="page-143" id="page-143" href="#page-143" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+   Description: Any special notes about the property, how it is to be
+   used, etc.
+
+   Format definition: The ABNF for the property definition needs to be
+   specified.
+
+   Examples: One or more examples of instances of the property needs to
+   be specified.
+
+<span class="h4"><h4><a name="section-7.2.2">7.2.2</a> Post the Property definition</h4></span>
+
+   The property description MUST be posted to the new property
+   discussion list, ietf-calendar@imc.org.
+
+<span class="h4"><h4><a name="section-7.2.3">7.2.3</a>   Allow a comment period</h4></span>
+
+   Discussion on the new property MUST be allowed to take place on the
+   list for a minimum of two weeks. Consensus MUST be reached on the
+   property before proceeding to the next step.
+
+<span class="h4"><h4><a name="section-7.2.4">7.2.4</a> Submit the property for approval</h4></span>
+
+   Once the two-week comment period has elapsed, and the proposer is
+   convinced consensus has been reached on the property, the
+   registration application should be submitted to the Method Reviewer
+   for approval. The Method Reviewer is appointed to the Application
+   Area Directors and can either accept or reject the property
+   registration. An accepted registration should be passed on by the
+   Method Reviewer to the IANA for inclusion in the official IANA method
+   registry. The registration can be rejected for any of the following
+   reasons. 1) Insufficient comment period; 2) Consensus not reached; 3)
+   Technical deficiencies raised on the list or elsewhere have not been
+   addressed. The Method Reviewer's decision to reject a property can be
+   appealed by the proposer to the IESG, or the objections raised can be
+   addressed by the proposer and the property resubmitted.
+
+<span class="h3"><h3><a name="section-7.3">7.3</a> Property Change Control</h3></span>
+
+   Existing properties can be changed using the same process by which
+   they were registered.
+
+        1.           Define the change
+
+        2.           Post the change
+
+        3.           Allow a comment period
+
+        4.           Submit the property for approval
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                   [Page 143]</span>
+</pre><pre class="newpage"><a name="page-144" id="page-144" href="#page-144" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+   Note that the original author or any other interested party can
+   propose a change to an existing property, but that such changes
+   should only be proposed when there are serious omissions or errors in
+   the published memo. The Method Reviewer can object to a change if it
+   is not backward compatible, but is not required to do so.
+
+   Property definitions can never be deleted from the IANA registry, but
+   properties which are no longer believed to be useful can be declared
+   OBSOLETE by a change to their "intended use" field.
+
+<span class="h2"><h2><a name="section-8">8</a> References</h2></span>
+
+   [<a name="ref-IMIP" id="ref-IMIP">IMIP</a>]     Dawson, F., Mansour, S. and S. Silverberg, "iCalendar
+              Message-based Interoperability Protocol (IMIP)", <a href="http://tools.ietf.org/html/rfc2447">RFC 2447</a>,
+              November 1998.
+
+   [<a name="ref-ITIP" id="ref-ITIP">ITIP</a>]     Silverberg, S., Mansour, S., Dawson, F. and R. Hopson,
+              "iCalendar Transport-Independent Interoperability Protocol
+              (iTIP) : Scheduling Events, Busy Time, To-dos and Journal
+              Entries", <a href="http://tools.ietf.org/html/rfc2446">RFC 2446</a>, November 1998.
+
+   [ISO 8601] ISO 8601, "Data elements and interchange formats-
+              Information interchange--Representation of dates and
+              times", International Organization for Standardization,
+              June, 1988.
+
+   [ISO 9070] ISO/IEC 9070, "Information Technology_SGML Support
+              Facilities--Registration Procedures for Public Text Owner
+              Identifiers", Second Edition, International Organization
+              for Standardization, April 1991.
+
+   [<a name="ref-RFC 822" id="ref-RFC 822">RFC 822</a>]  Crocker, D., "Standard for the Format of ARPA Internet
+              Text Messages", STD 11, <a href="http://tools.ietf.org/html/rfc822">RFC 822</a>, August 1982.
+
+   [<a name="ref-RFC 1738" id="ref-RFC 1738">RFC 1738</a>] Berners-Lee, T., Masinter, L. and M. McCahill, "Uniform
+              Resource Locators (URL)", <a href="http://tools.ietf.org/html/rfc1738">RFC 1738</a>, December 1994.
+
+   [<a name="ref-RFC 1766" id="ref-RFC 1766">RFC 1766</a>] Alvestrand, H., "Tags for the Identification of
+              Languages", <a href="http://tools.ietf.org/html/rfc1766">RFC 1766</a>, March 1995.
+
+   [<a name="ref-RFC 2045" id="ref-RFC 2045">RFC 2045</a>] Freed, N. and N. Borenstein, " Multipurpose Internet Mail
+              Extensions (MIME) - Part One: Format of Internet Message
+              Bodies", <a href="http://tools.ietf.org/html/rfc2045">RFC 2045</a>, November 1996.
+
+   [<a name="ref-RFC 2046" id="ref-RFC 2046">RFC 2046</a>] Freed, N. and N. Borenstein, " Multipurpose Internet Mail
+              Extensions (MIME) - Part Two: Media Types", <a href="http://tools.ietf.org/html/rfc2046">RFC 2046</a>,
+              November 1996.
+
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                   [Page 144]</span>
+</pre><pre class="newpage"><a name="page-145" id="page-145" href="#page-145" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+   [<a name="ref-RFC 2048" id="ref-RFC 2048">RFC 2048</a>] Freed, N., Klensin, J. and J. Postel, "Multipurpose
+              Internet Mail Extensions (MIME) - Part Four: Registration
+              Procedures", <a href="http://tools.ietf.org/html/rfc2048">RFC 2048</a>, January 1997.
+
+   [<a name="ref-RFC 2119" id="ref-RFC 2119">RFC 2119</a>] Bradner, S., "Key words for use in RFCs to Indicate
+              Requirement Levels", <a href="http://tools.ietf.org/html/bcp14">BCP 14</a>, <a href="http://tools.ietf.org/html/rfc2119">RFC 2119</a>, March 1997.
+
+   [<a name="ref-RFC 2234" id="ref-RFC 2234">RFC 2234</a>] Crocker, D. and P. Overell, "Augmented BNF for Syntax
+              Specifications: ABNF", <a href="http://tools.ietf.org/html/rfc2234">RFC 2234</a>, November 1997.
+
+   [<a name="ref-RFC 2279" id="ref-RFC 2279">RFC 2279</a>] Yergeau, F., "UTF-8, a transformation format of ISO
+              10646", <a href="http://tools.ietf.org/html/rfc2279">RFC 2279</a>, January 1998.
+
+   [<a name="ref-RFC 2425" id="ref-RFC 2425">RFC 2425</a>] Howes, T., Smith, M. and F. Dawson, "A MIME Content-Type
+              for Directory Information", <a href="http://tools.ietf.org/html/rfc2425">RFC 2425</a>, September 1998.
+
+   [<a name="ref-RFC 2426" id="ref-RFC 2426">RFC 2426</a>] Dawson, F. and T. Howes, "vCard MIME Directory Profile",
+              <a href="http://tools.ietf.org/html/rfc2426">RFC 2426</a>, September 1998.
+
+   [<a name="ref-TZ" id="ref-TZ">TZ</a>]       Olson, A.D., et al, Time zone code and data,
+              <a href="ftp://elsie.nci.nih.gov/pub/">ftp://elsie.nci.nih.gov/pub/</a>, updated periodically.
+
+   [<a name="ref-VCAL" id="ref-VCAL">VCAL</a>]     Internet Mail Consortium, "vCalendar - The Electronic
+              Calendaring and Scheduling Exchange Format",
+              <a href="http://www.imc.org/pdi/vcal-10.txt">http://www.imc.org/pdi/vcal-10.txt</a>, September 18, 1996.
+
+<span class="h2"><h2><a name="section-9">9</a> Acknowledgments</h2></span>
+
+   A hearty thanks to the IETF Calendaring and Scheduling Working Group
+   and also the following individuals who have participated in the
+   drafting, review and discussion of this memo:
+
+   Roland Alden, Harald T. Alvestrand, Eric Berman, Denis Bigorgne, John
+   Binici, Bill Bliss, Philippe Boucher, Steve Carter, Andre
+   Courtemanche, Dave Crocker, David Curley, Alec Dun, John Evans, Ross
+   Finlayson, Randell Flint, Ned Freed, Patrik Faltstrom, Chuck
+   Grandgent, Mark Handley, Steve Hanna, Paul B. Hill, Paul Hoffman,
+   Ross Hopson, Mark Horton, Daryl Huff, Bruce Kahn, C. Harald Koch,
+   Ryan Jansen, Don Lavange, Antoine Leca, Theodore Lorek, Steve
+   Mansour, Skip Montanaro, Keith Moore, Cecil Murray, Chris Newman,
+   John Noerenberg, Ralph Patterson, Pete Resnick, Keith Rhodes, Robert
+   Ripberger, John Rose, Doug Royer, Andras Salamar, Ted Schuh, Vinod
+   Seraphin, Derrick Shadel, Ken Shan, Andrew Shuman, Steve Silverberg,
+   William P. Spencer, John Sun, Mark Towfiq, Yvonne Tso, Robert Visnov,
+   James L. Weiner, Mike Weston, William Wyatt.
+
+
+
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                   [Page 145]</span>
+</pre><pre class="newpage"><a name="page-146" id="page-146" href="#page-146" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+<span class="h2"><h2><a name="section-10">10</a> Authors' and Chairs' Addresses</h2></span>
+
+   The following address information is provided in a MIME-VCARD,
+   Electronic Business Card, format.
+
+   The authors of this memo are:
+
+   BEGIN:VCARD
+   VERSION:3.0
+   N:Dawson;Frank
+   FN:Frank Dawson
+   ORG:Lotus Development Corporation
+   ADR;TYPE=WORK,POSTAL,PARCEL:;;6544 Battleford Drive;
+    Raleigh;NC;27613-3502;USA
+   TEL;TYPE=WORK,MSG:+1-919-676-9515
+   TEL;TYPE=WORK,FAX:+1-919-676-9564
+   EMAIL;TYPE=PREF,INTERNET:Frank_Dawson@Lotus.com
+   EMAIL;TYPE=INTERNET:fdawson@earthlink.net
+   URL:http://home.earthlink.net/~fdawson
+   END:VCARD
+
+   BEGIN:VCARD
+   VERSION:3.0
+   N:Stenerson;Derik
+   FN:Derik Stenerson
+   ORG:Microsoft Corporation
+   ADR;TYPE=WORK,POSTAL,PARCEL:;;One Microsoft Way;
+    Redmond;WA;98052-6399;USA
+   TEL;TYPE=WORK,MSG:+1-425-936-5522
+   TEL;TYPE=WORK,FAX:+1-425-936-7329
+   EMAIL;TYPE=INTERNET:deriks@Microsoft.com
+   END:VCARD
+
+   The iCalendar object is a result of the work of the Internet
+   Engineering Task Force Calendaring and Scheduling Working Group. The
+   chairmen of that working group are:
+
+   BEGIN:VCARD
+   VERSION:3.0
+   N:Ganguly;Anik
+   FN:Anik Ganguly
+   ORG: Open Text Inc.
+   ADR;TYPE=WORK,POSTAL,PARCEL:;Suite 101;38777 West Six Mile Road;
+    Livonia;MI;48152;USA
+   TEL;TYPE=WORK,MSG:+1-734-542-5955
+   EMAIL;TYPE=INTERNET:ganguly@acm.org
+   END:VCARD
+
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                   [Page 146]</span>
+</pre><pre class="newpage"><a name="page-147" id="page-147" href="#page-147" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+   The co-chairman of that working group is:
+
+   BEGIN:VCARD
+   VERSION:3.0
+   N:Moskowitz;Robert
+   FN:Robert Moskowitz
+   EMAIL;TYPE=INTERNET:rgm-ietf@htt-consult.com
+   END:VCARD
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+<span class="grey">Dawson &amp; Stenerson          Standards Track                   [Page 147]</span>
+</pre><pre class="newpage"><a name="page-148" id="page-148" href="#page-148" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>                       iCalendar                   November 1998</span>
+
+
+<span class="h2"><h2><a name="section-11">11</a>.  Full Copyright Statement</h2></span>
+
+   Copyright (C) The Internet Society (1998).  All Rights Reserved.
+
+   This document and translations of it may be copied and furnished to
+   others, and derivative works that comment on or otherwise explain it
+   or assist in its implementation may be prepared, copied, published
+   and distributed, in whole or in part, without restriction of any
+   kind, provided that the above copyright notice and this paragraph are
+   included on all such copies and derivative works.  However, this
+   document itself may not be modified in any way, such as by removing
+   the copyright notice or references to the Internet Society or other
+   Internet organizations, except as needed for the purpose of
+   developing Internet standards in which case the procedures for
+   copyrights defined in the Internet Standards process must be
+   followed, or as required to translate it into languages other than
+   English.
+
+   The limited permissions granted above are perpetual and will not be
+   revoked by the Internet Society or its successors or assigns.
+
+   This document and the information contained herein is provided on an
+   "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
+   TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+   BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+   HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Dawson &amp; Stenerson          Standards Track                   [Page 148]
+</pre><pre class="newpage">
+</pre><br>
+<span class="noprint"><small><small>Html markup produced by rfcmarkup 1.74, available from
+<a href="http://tools.ietf.org/tools/rfcmarkup/">http://tools.ietf.org/tools/rfcmarkup/</a>
+</small></small></span>
+</body></html>
\ No newline at end of file
diff --git a/caldav/rfc4791.html b/caldav/rfc4791.html
new file mode 100644 (file)
index 0000000..eb59ed8
--- /dev/null
@@ -0,0 +1,6116 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xml:lang="en" xmlns="http://www.w3.org/1999/xhtml" lang="en"><head>
+
+
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+    <meta name="robots" content="index,follow">
+    <meta name="creator" content="rfcmarkup version 1.74">
+    <link rel="icon" href="http://tools.ietf.org/images/rfc.png" type="image/png">
+    <link rel="shortcut icon" href="http://tools.ietf.org/images/rfc.png" type="image/png">
+    <title>RFC 4791 - Calendaring Extensions to WebDAV (CalDAV)</title>
+    
+    <style type="text/css">
+       body {
+           margin: 0px 8px;
+            font-size: 1em;
+       }
+        h1, h2, h3, h4, h5, h6, .h1, .h2, .h3, .h4, .h5, .h6 {
+           font-weight: bold;
+            line-height: 0pt;
+            display: inline;
+            white-space: pre;
+            font-family: monospace;
+            font-size: 1em;
+           font-weight: bold;
+        }
+        pre {
+            font-size: 1em;
+            margin-top: 0px;
+            margin-bottom: 0px;
+        }
+       .pre {
+           white-space: pre;
+           font-family: monospace;
+       }
+       .header{
+           font-weight: bold;
+       }
+        .newpage {
+            page-break-before: always;
+        }
+        .invisible {
+            text-decoration: none;
+            color: white;
+        }
+        @media print {
+            body {
+                font-size: 10.5pt;
+            }
+            h1, h2, h3, h4, h5, h6 {
+                font-size: 10.5pt;
+            }
+        
+            a:link, a:visited {
+                color: inherit;
+                text-decoration: none;
+            }
+            .noprint {
+                display: none;
+            }
+        }
+       @media screen {
+           .grey, .grey a:link, .grey a:visited {
+               color: #777;
+           }
+            .docinfo {
+                background-color: #EEE;
+            }
+            .top {
+                border-top: 7px solid #EEE;
+            }
+            .bgwhite  { background-color: white; }
+            .bgred    { background-color: #F44; }
+            .bggrey   { background-color: #666; }
+            .bgbrown  { background-color: #840; }            
+            .bgorange { background-color: #FA0; }
+            .bgyellow { background-color: #EE0; }
+            .bgmagenta{ background-color: #F4F; }
+            .bgblue   { background-color: #66F; }
+            .bgcyan   { background-color: #4DD; }
+            .bggreen  { background-color: #4F4; }
+
+            .legend   { font-size: 90%; }
+            .cplate   { font-size: 70%; border: solid grey 1px; }
+       }
+    </style>
+
+    <script type="text/javascript"><!--
+    function addHeaderTags() {
+       var spans = document.getElementsByTagName("span");
+       for (var i=0; i < spans.length; i++) {
+           var elem = spans[i];
+           if (elem) {
+               var level = elem.getAttribute("class");
+                if (level == "h1" || level == "h2" || level == "h3" || level == "h4" || level == "h5" || level == "h6") {
+                    elem.innerHTML = "<"+level+">"+elem.innerHTML+"</"+level+">";              
+                }
+           }
+       }
+    }
+    var legend_html = "Colour legend:<br />      <table>         <tr><td>Unknown:</td>          <td><span class='cplate bgwhite'>&nbsp;&nbsp;&nbsp;&nbsp;</span></td></tr>         <tr><td>Draft:</td>            <td><span class='cplate bgred'>&nbsp;&nbsp;&nbsp;&nbsp;</span></td></tr>         <tr><td>Informational:</td>    <td><span class='cplate bgorange'>&nbsp;&nbsp;&nbsp;&nbsp;</span></td></tr>         <tr><td>Experimental:</td>     <td><span class='cplate bgyellow'>&nbsp;&nbsp;&nbsp;&nbsp;</span></td></tr>         <tr><td>Best Common Practice:</td><td><span class='cplate bgmagenta'>&nbsp;&nbsp;&nbsp;&nbsp;</span></td></tr>         <tr><td>Proposed Standard:</td><td><span class='cplate bgblue'>&nbsp;&nbsp;&nbsp;&nbsp;</span></td></tr>         <tr><td>Draft Standard:</td>   <td><span class='cplate bgcyan'>&nbsp;&nbsp;&nbsp;&nbsp;</span></td></tr>         <tr><td>Standard:</td>         <td><span class='cplate bggreen'>&nbsp;&nbsp;&nbsp;&nbsp;</span></td></tr>         <tr><td>Historic:</td>         <td><span class='cplate bggrey'>&nbsp;&nbsp;&nbsp;&nbsp;</span></td></tr>         <tr><td>Obsolete:</td>         <td><span class='cplate bgbrown'>&nbsp;&nbsp;&nbsp;&nbsp;</span></td></tr>     </table>";
+    function showElem(id) {
+        var elem = document.getElementById(id);
+        elem.innerHTML = eval(id+"_html");
+        elem.style.visibility='visible';
+    }
+    function hideElem(id) {
+        var elem = document.getElementById(id);
+        elem.style.visibility='hidden';        
+        elem.innerHTML = "";
+    }
+    // -->
+    </script>
+</head><body onload="addHeaderTags()">
+   <div style="height: 13px;">
+      <div onmouseover="this.style.cursor='pointer';" onclick="showElem('legend');" onmouseout="hideElem('legend')" style="height: 6px; position: absolute;" class="pre noprint docinfo bgblue" title="Click for colour legend.">                                                                        </div>
+      <div id="legend" class="docinfo noprint pre legend" style="border: 1px solid rgb(51, 68, 85); padding: 4px 9px 5px 7px; position: absolute; top: 4px; left: 4ex; visibility: hidden; background-color: white;" onmouseover="showElem('legend');" onmouseout="hideElem('legend');">
+      </div>
+   </div>
+<span class="pre noprint docinfo top">[<a href="http://tools.ietf.org/html/" title="Document search and retrieval page">RFCs/IDs</a>] [<a href="http://tools.ietf.org/rfc/rfc4791.txt" title="Plaintext version of this document">Plain</a>] [From <a href="http://tools.ietf.org/html/draft-dusseault-caldav">draft-dusseault-caldav</a>]                        </span><br>
+<span class="pre noprint docinfo">                                                                        </span><br>
+<span class="pre noprint docinfo">                                                       PROPOSED STANDARD</span><br>
+<span class="pre noprint docinfo">                                                                  <a href="http://www.rfc-editor.org/errata_search.php?rfc=4791">Errata</a></span><br>
+<pre>Network Working Group                                           C. Daboo
+Request for Comments: 4791                                         Apple
+Category: Standards Track                                B. Desruisseaux
+                                                                  Oracle
+                                                            L. Dusseault
+                                                             CommerceNet
+                                                              March 2007
+
+
+               <span class="h1"><h1>Calendaring Extensions to WebDAV (CalDAV)</h1></span>
+
+Status of This Memo
+
+   This document specifies an Internet standards track protocol for the
+   Internet community, and requests discussion and suggestions for
+   improvements.  Please refer to the current edition of the "Internet
+   Official Protocol Standards" (STD 1) for the standardization state
+   and status of this protocol.  Distribution of this memo is unlimited.
+
+Copyright Notice
+
+   Copyright (C) The IETF Trust (2007).
+
+Abstract
+
+   This document defines extensions to the Web Distributed Authoring and
+   Versioning (WebDAV) protocol to specify a standard way of accessing,
+   managing, and sharing calendaring and scheduling information based on
+   the iCalendar format.  This document defines the "calendar-access"
+   feature of CalDAV.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                     [Page 1]</span>
+</pre><pre class="newpage"><a name="page-2" id="page-2" href="#page-2" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+Table of Contents
+
+   <a href="#section-1">1</a>.  Introduction . . . . . . . . . . . . . . . . . . . . . . . . .  <a href="#page-5">5</a>
+     <a href="#section-1.1">1.1</a>.  Notational Conventions . . . . . . . . . . . . . . . . . .  <a href="#page-5">5</a>
+     <a href="#section-1.2">1.2</a>.  XML Namespaces and Processing  . . . . . . . . . . . . . .  <a href="#page-5">5</a>
+     <a href="#section-1.3">1.3</a>.  Method Preconditions and Postconditions  . . . . . . . . .  <a href="#page-6">6</a>
+   <a href="#section-2">2</a>.  Requirements Overview  . . . . . . . . . . . . . . . . . . . .  <a href="#page-6">6</a>
+   <a href="#section-3">3</a>.  Calendaring Data Model . . . . . . . . . . . . . . . . . . . .  <a href="#page-7">7</a>
+     <a href="#section-3.1">3.1</a>.  Calendar Server  . . . . . . . . . . . . . . . . . . . . .  <a href="#page-7">7</a>
+     <a href="#section-3.2">3.2</a>.  Recurrence and the Data Model  . . . . . . . . . . . . . .  <a href="#page-8">8</a>
+   <a href="#section-4">4</a>.  Calendar Resources . . . . . . . . . . . . . . . . . . . . . .  <a href="#page-9">9</a>
+     <a href="#section-4.1">4.1</a>.  Calendar Object Resources  . . . . . . . . . . . . . . . .  <a href="#page-9">9</a>
+     <a href="#section-4.2">4.2</a>.  Calendar Collection  . . . . . . . . . . . . . . . . . . . <a href="#page-10">10</a>
+   <a href="#section-5">5</a>.  Calendar Access Feature  . . . . . . . . . . . . . . . . . . . <a href="#page-11">11</a>
+     <a href="#section-5.1">5.1</a>.  Calendar Access Support  . . . . . . . . . . . . . . . . . <a href="#page-11">11</a>
+       5.1.1.  Example: Using OPTIONS for the Discovery of
+               Calendar Access Support  . . . . . . . . . . . . . . . <a href="#page-12">12</a>
+     <a href="#section-5.2">5.2</a>.  Calendar Collection Properties . . . . . . . . . . . . . . <a href="#page-12">12</a>
+       <a href="#section-5.2.1">5.2.1</a>.  CALDAV:calendar-description Property . . . . . . . . . <a href="#page-12">12</a>
+       <a href="#section-5.2.2">5.2.2</a>.  CALDAV:calendar-timezone Property  . . . . . . . . . . <a href="#page-13">13</a>
+       <a href="#section-5.2.3">5.2.3</a>.  CALDAV:supported-calendar-component-set Property . . . <a href="#page-14">14</a>
+       <a href="#section-5.2.4">5.2.4</a>.  CALDAV:supported-calendar-data Property  . . . . . . . <a href="#page-15">15</a>
+       <a href="#section-5.2.5">5.2.5</a>.  CALDAV:max-resource-size Property  . . . . . . . . . . <a href="#page-16">16</a>
+       <a href="#section-5.2.6">5.2.6</a>.  CALDAV:min-date-time Property  . . . . . . . . . . . . <a href="#page-17">17</a>
+       <a href="#section-5.2.7">5.2.7</a>.  CALDAV:max-date-time Property  . . . . . . . . . . . . <a href="#page-18">18</a>
+       <a href="#section-5.2.8">5.2.8</a>.  CALDAV:max-instances Property  . . . . . . . . . . . . <a href="#page-19">19</a>
+       <a href="#section-5.2.9">5.2.9</a>.  CALDAV:max-attendees-per-instance Property . . . . . . <a href="#page-19">19</a>
+       <a href="#section-5.2.10">5.2.10</a>. Additional Precondition for PROPPATCH  . . . . . . . . <a href="#page-20">20</a>
+     <a href="#section-5.3">5.3</a>.  Creating Resources . . . . . . . . . . . . . . . . . . . . <a href="#page-20">20</a>
+       <a href="#section-5.3.1">5.3.1</a>.  MKCALENDAR Method  . . . . . . . . . . . . . . . . . . <a href="#page-20">20</a>
+         <a href="#section-5.3.1.1">5.3.1.1</a>.  Status Codes . . . . . . . . . . . . . . . . . . . <a href="#page-22">22</a>
+         <a href="#section-5.3.1.2">5.3.1.2</a>.  Example: Successful MKCALENDAR Request . . . . . . <a href="#page-23">23</a>
+       <a href="#section-5.3.2">5.3.2</a>.  Creating Calendar Object Resources . . . . . . . . . . <a href="#page-25">25</a>
+         5.3.2.1.  Additional Preconditions for PUT, COPY, and
+                   MOVE . . . . . . . . . . . . . . . . . . . . . . . <a href="#page-26">26</a>
+       5.3.3.  Non-Standard Components, Properties, and Parameters  . 28
+       <a href="#section-5.3.4">5.3.4</a>.  Calendar Object Resource Entity Tag  . . . . . . . . . <a href="#page-28">28</a>
+   <a href="#section-6">6</a>.  Calendaring Access Control . . . . . . . . . . . . . . . . . . <a href="#page-29">29</a>
+     <a href="#section-6.1">6.1</a>.  Calendaring Privilege  . . . . . . . . . . . . . . . . . . <a href="#page-29">29</a>
+       <a href="#section-6.1.1">6.1.1</a>.  CALDAV:read-free-busy Privilege  . . . . . . . . . . . <a href="#page-29">29</a>
+     <a href="#section-6.2">6.2</a>.  Additional Principal Property  . . . . . . . . . . . . . . <a href="#page-30">30</a>
+       <a href="#section-6.2.1">6.2.1</a>.  CALDAV:calendar-home-set Property  . . . . . . . . . . <a href="#page-30">30</a>
+   <a href="#section-7">7</a>.  Calendaring Reports  . . . . . . . . . . . . . . . . . . . . . <a href="#page-31">31</a>
+     <a href="#section-7.1">7.1</a>.  REPORT Method  . . . . . . . . . . . . . . . . . . . . . . <a href="#page-31">31</a>
+     <a href="#section-7.2">7.2</a>.  Ordinary Collections . . . . . . . . . . . . . . . . . . . <a href="#page-31">31</a>
+     <a href="#section-7.3">7.3</a>.  Date and Floating Time . . . . . . . . . . . . . . . . . . <a href="#page-32">32</a>
+     <a href="#section-7.4">7.4</a>.  Time Range Filtering . . . . . . . . . . . . . . . . . . . <a href="#page-32">32</a>
+     <a href="#section-7.5">7.5</a>.  Searching Text: Collations . . . . . . . . . . . . . . . . <a href="#page-33">33</a>
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                     [Page 2]</span>
+</pre><pre class="newpage"><a name="page-3" id="page-3" href="#page-3" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+       <a href="#section-7.5.1">7.5.1</a>.  CALDAV:supported-collation-set Property  . . . . . . . <a href="#page-34">34</a>
+     <a href="#section-7.6">7.6</a>.  Partial Retrieval  . . . . . . . . . . . . . . . . . . . . <a href="#page-34">34</a>
+     <a href="#section-7.7">7.7</a>.  Non-Standard Components, Properties, and Parameters  . . . <a href="#page-35">35</a>
+     <a href="#section-7.8">7.8</a>.  CALDAV:calendar-query REPORT . . . . . . . . . . . . . . . <a href="#page-36">36</a>
+       <a href="#section-7.8.1">7.8.1</a>.  Example: Partial Retrieval of Events by Time Range . . <a href="#page-38">38</a>
+       <a href="#section-7.8.2">7.8.2</a>.  Example: Partial Retrieval of Recurring Events . . . . <a href="#page-42">42</a>
+       <a href="#section-7.8.3">7.8.3</a>.  Example: Expanded Retrieval of Recurring Events  . . . <a href="#page-45">45</a>
+       7.8.4.  Example: Partial Retrieval of Stored Free Busy
+               Components . . . . . . . . . . . . . . . . . . . . . . <a href="#page-48">48</a>
+       <a href="#section-7.8.5">7.8.5</a>.  Example: Retrieval of To-Dos by Alarm Time Range . . . <a href="#page-50">50</a>
+       <a href="#section-7.8.6">7.8.6</a>.  Example: Retrieval of Event by UID . . . . . . . . . . <a href="#page-51">51</a>
+       <a href="#section-7.8.7">7.8.7</a>.  Example: Retrieval of Events by PARTSTAT . . . . . . . <a href="#page-53">53</a>
+       <a href="#section-7.8.8">7.8.8</a>.  Example: Retrieval of Events Only  . . . . . . . . . . <a href="#page-55">55</a>
+       <a href="#section-7.8.9">7.8.9</a>.  Example: Retrieval of All Pending To-Dos . . . . . . . <a href="#page-59">59</a>
+       <a href="#section-7.8.10">7.8.10</a>. Example: Attempt to Query Unsupported Property . . . . <a href="#page-62">62</a>
+     <a href="#section-7.9">7.9</a>.  CALDAV:calendar-multiget REPORT  . . . . . . . . . . . . . <a href="#page-63">63</a>
+       7.9.1.  Example: Successful CALDAV:calendar-multiget REPORT  . 64
+     <a href="#section-7.10">7.10</a>. CALDAV:free-busy-query REPORT  . . . . . . . . . . . . . . <a href="#page-66">66</a>
+       <a href="#section-7.10.1">7.10.1</a>. Example: Successful CALDAV:free-busy-query REPORT  . . <a href="#page-68">68</a>
+   <a href="#section-8">8</a>.  Guidelines . . . . . . . . . . . . . . . . . . . . . . . . . . <a href="#page-69">69</a>
+     <a href="#section-8.1">8.1</a>.  Client-to-Client Interoperability  . . . . . . . . . . . . <a href="#page-69">69</a>
+     <a href="#section-8.2">8.2</a>.  Synchronization Operations . . . . . . . . . . . . . . . . <a href="#page-69">69</a>
+       <a href="#section-8.2.1">8.2.1</a>.  Use of Reports . . . . . . . . . . . . . . . . . . . . <a href="#page-69">69</a>
+         <a href="#section-8.2.1.1">8.2.1.1</a>.  Restrict the Time Range  . . . . . . . . . . . . . <a href="#page-69">69</a>
+         <a href="#section-8.2.1.2">8.2.1.2</a>.  Synchronize by Time Range  . . . . . . . . . . . . <a href="#page-70">70</a>
+         <a href="#section-8.2.1.3">8.2.1.3</a>.  Synchronization Process  . . . . . . . . . . . . . <a href="#page-70">70</a>
+       <a href="#section-8.2.2">8.2.2</a>.  Restrict the Properties Returned . . . . . . . . . . . <a href="#page-72">72</a>
+     <a href="#section-8.3">8.3</a>.  Use of Locking . . . . . . . . . . . . . . . . . . . . . . <a href="#page-72">72</a>
+     <a href="#section-8.4">8.4</a>.  Finding Calendars  . . . . . . . . . . . . . . . . . . . . <a href="#page-72">72</a>
+     <a href="#section-8.5">8.5</a>.  Storing and Using Attachments  . . . . . . . . . . . . . . <a href="#page-74">74</a>
+       <a href="#section-8.5.1">8.5.1</a>.  Inline Attachments . . . . . . . . . . . . . . . . . . <a href="#page-74">74</a>
+       <a href="#section-8.5.2">8.5.2</a>.  External Attachments . . . . . . . . . . . . . . . . . <a href="#page-75">75</a>
+     <a href="#section-8.6">8.6</a>.  Storing and Using Alarms . . . . . . . . . . . . . . . . . <a href="#page-76">76</a>
+   <a href="#section-9">9</a>.  XML Element Definitions  . . . . . . . . . . . . . . . . . . . <a href="#page-77">77</a>
+     <a href="#section-9.1">9.1</a>.  CALDAV:calendar XML Element  . . . . . . . . . . . . . . . <a href="#page-77">77</a>
+     <a href="#section-9.2">9.2</a>.  CALDAV:mkcalendar XML Element  . . . . . . . . . . . . . . <a href="#page-77">77</a>
+     <a href="#section-9.3">9.3</a>.  CALDAV:mkcalendar-response XML Element . . . . . . . . . . <a href="#page-78">78</a>
+     <a href="#section-9.4">9.4</a>.  CALDAV:supported-collation XML Element . . . . . . . . . . <a href="#page-78">78</a>
+     <a href="#section-9.5">9.5</a>.  CALDAV:calendar-query XML Element  . . . . . . . . . . . . <a href="#page-78">78</a>
+     <a href="#section-9.6">9.6</a>.  CALDAV:calendar-data XML Element . . . . . . . . . . . . . <a href="#page-79">79</a>
+       <a href="#section-9.6.1">9.6.1</a>.  CALDAV:comp XML Element  . . . . . . . . . . . . . . . <a href="#page-80">80</a>
+       <a href="#section-9.6.2">9.6.2</a>.  CALDAV:allcomp XML Element . . . . . . . . . . . . . . <a href="#page-81">81</a>
+       <a href="#section-9.6.3">9.6.3</a>.  CALDAV:allprop XML Element . . . . . . . . . . . . . . <a href="#page-81">81</a>
+       <a href="#section-9.6.4">9.6.4</a>.  CALDAV:prop XML Element  . . . . . . . . . . . . . . . <a href="#page-82">82</a>
+       <a href="#section-9.6.5">9.6.5</a>.  CALDAV:expand XML Element  . . . . . . . . . . . . . . <a href="#page-82">82</a>
+       <a href="#section-9.6.6">9.6.6</a>.  CALDAV:limit-recurrence-set XML Element  . . . . . . . <a href="#page-83">83</a>
+       <a href="#section-9.6.7">9.6.7</a>.  CALDAV:limit-freebusy-set XML Element  . . . . . . . . <a href="#page-84">84</a>
+     <a href="#section-9.7">9.7</a>.  CALDAV:filter XML Element  . . . . . . . . . . . . . . . . <a href="#page-85">85</a>
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                     [Page 3]</span>
+</pre><pre class="newpage"><a name="page-4" id="page-4" href="#page-4" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+       <a href="#section-9.7.1">9.7.1</a>.  CALDAV:comp-filter XML Element . . . . . . . . . . . . <a href="#page-85">85</a>
+       <a href="#section-9.7.2">9.7.2</a>.  CALDAV:prop-filter XML Element . . . . . . . . . . . . <a href="#page-86">86</a>
+       <a href="#section-9.7.3">9.7.3</a>.  CALDAV:param-filter XML Element  . . . . . . . . . . . <a href="#page-87">87</a>
+       <a href="#section-9.7.4">9.7.4</a>.  CALDAV:is-not-defined XML Element  . . . . . . . . . . <a href="#page-88">88</a>
+       <a href="#section-9.7.5">9.7.5</a>.  CALDAV:text-match XML Element  . . . . . . . . . . . . <a href="#page-88">88</a>
+     <a href="#section-9.8">9.8</a>.  CALDAV:timezone XML Element  . . . . . . . . . . . . . . . <a href="#page-89">89</a>
+     <a href="#section-9.9">9.9</a>.  CALDAV:time-range XML Element  . . . . . . . . . . . . . . <a href="#page-90">90</a>
+     <a href="#section-9.10">9.10</a>. CALDAV:calendar-multiget XML Element . . . . . . . . . . . <a href="#page-94">94</a>
+     <a href="#section-9.11">9.11</a>. CALDAV:free-busy-query XML Element . . . . . . . . . . . . <a href="#page-95">95</a>
+   <a href="#section-10">10</a>. Internationalization Considerations  . . . . . . . . . . . . . <a href="#page-95">95</a>
+   <a href="#section-11">11</a>. Security Considerations  . . . . . . . . . . . . . . . . . . . <a href="#page-95">95</a>
+   <a href="#section-12">12</a>. IANA Considerations  . . . . . . . . . . . . . . . . . . . . . <a href="#page-96">96</a>
+     <a href="#section-12.1">12.1</a>. Namespace Registration . . . . . . . . . . . . . . . . . . <a href="#page-96">96</a>
+   <a href="#section-13">13</a>. Acknowledgements . . . . . . . . . . . . . . . . . . . . . . . <a href="#page-96">96</a>
+   <a href="#section-14">14</a>. References . . . . . . . . . . . . . . . . . . . . . . . . . . <a href="#page-97">97</a>
+     <a href="#section-14.1">14.1</a>. Normative References . . . . . . . . . . . . . . . . . . . <a href="#page-97">97</a>
+     <a href="#section-14.2">14.2</a>. Informative References . . . . . . . . . . . . . . . . . . <a href="#page-98">98</a>
+   <a href="#appendix-A">Appendix A</a>.  CalDAV Method Privilege Table (Normative) . . . . . . <a href="#page-99">99</a>
+   <a href="#appendix-B">Appendix B</a>.  Calendar Collections Used in the Examples . . . . . . <a href="#page-99">99</a>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                     [Page 4]</span>
+</pre><pre class="newpage"><a name="page-5" id="page-5" href="#page-5" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+<span class="h2"><h2><a name="section-1">1</a>.  Introduction</h2></span>
+
+   The concept of using HTTP [<a href="http://tools.ietf.org/html/rfc2616" title="&quot;Hypertext Transfer Protocol -- HTTP/1.1&quot;">RFC2616</a>] and WebDAV [<a href="http://tools.ietf.org/html/rfc2518" title="&quot;HTTP Extensions for Distributed Authoring -- WEBDAV&quot;">RFC2518</a>] as a basis
+   for a calendar access protocol is by no means a new concept: it was
+   discussed in the IETF CALSCH working group as early as 1997 or 1998.
+   Several companies have implemented calendar access protocols using
+   HTTP to upload and download iCalendar [<a href="http://tools.ietf.org/html/rfc2445" title="&quot;Internet Calendaring and Scheduling Core Object Specification (iCalendar)&quot;">RFC2445</a>] objects, and using
+   WebDAV to get listings of resources.  However, those implementations
+   do not interoperate because there are many small and big decisions to
+   be made in how to model calendaring data as WebDAV resources, as well
+   as how to implement required features that aren't already part of
+   WebDAV.  This document proposes a way to model calendar data in
+   WebDAV, with additional features to make an interoperable calendar
+   access protocol.
+
+<span class="h3"><h3><a name="section-1.1">1.1</a>.  Notational Conventions</h3></span>
+
+   The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+   "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
+   document are to be interpreted as described in [<a href="http://tools.ietf.org/html/rfc2119" title="&quot;Key words for use in RFCs to Indicate Requirement Levels&quot;">RFC2119</a>].
+
+   The term "protected" is used in the Conformance field of property
+   definitions as defined in <a href="http://tools.ietf.org/html/rfc3253#section-1.4.2">Section&nbsp;1.4.2 of [RFC3253]</a>.
+
+   When XML element types in the namespaces "DAV:" and
+   "urn:ietf:params:xml:ns:caldav" are referenced in this document
+   outside of the context of an XML fragment, the string "DAV:" and
+   "CALDAV:" will be prefixed to the element type names, respectively.
+
+<span class="h3"><h3><a name="section-1.2">1.2</a>.  XML Namespaces and Processing</h3></span>
+
+   Definitions of XML elements in this document use XML element type
+   declarations (as found in XML Document Type Declarations), described
+   in <a href="#section-3.2">Section 3.2</a> of [<a href="#ref-W3C.REC-xml-20060816" title="&quot;Extensible Markup Language (XML) 1.0 (Fourth Edition)&quot;">W3C.REC-xml-20060816</a>].
+
+   The namespace "urn:ietf:params:xml:ns:caldav" is reserved for the XML
+   elements defined in this specification, its revisions, and related
+   CalDAV specifications.  XML elements defined by individual
+   implementations MUST NOT use the "urn:ietf:params:xml:ns:caldav"
+   namespace, and instead should use a namespace that they control.
+
+   The XML declarations used in this document do not include namespace
+   information.  Thus, implementers must not use these declarations as
+   the only way to create valid CalDAV properties or to validate CalDAV
+   XML element types.  Some of the declarations refer to XML elements
+   defined by WebDAV [<a href="http://tools.ietf.org/html/rfc2518" title="&quot;HTTP Extensions for Distributed Authoring -- WEBDAV&quot;">RFC2518</a>], which use the "DAV:" namespace.
+   Wherever such XML elements appear, they are explicitly prefixed with
+   "DAV:" to avoid confusion.
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                     [Page 5]</span>
+</pre><pre class="newpage"><a name="page-6" id="page-6" href="#page-6" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+   Also note that some CalDAV XML element names are identical to WebDAV
+   XML element names, though their namespace differs.  Care must be
+   taken not to confuse the two sets of names.
+
+   Processing of XML by CalDAV clients and servers MUST follow the rules
+   described in [<a href="http://tools.ietf.org/html/rfc2518" title="&quot;HTTP Extensions for Distributed Authoring -- WEBDAV&quot;">RFC2518</a>]; in particular, <a href="#section-14">Section 14</a>, and Appendix 3 of
+   that specification.
+
+<span class="h3"><h3><a name="section-1.3">1.3</a>.  Method Preconditions and Postconditions</h3></span>
+
+   A "precondition" of a method describes the state of the server that
+   must be true for that method to be performed.  A "postcondition" of a
+   method describes the state of the server that must be true after that
+   method has been completed.  If a method precondition or postcondition
+   for a request is not satisfied, the response status of the request
+   MUST either be 403 (Forbidden), if the request should not be repeated
+   because it will always fail, or 409 (Conflict), if it is expected
+   that the user might be able to resolve the conflict and resubmit the
+   request.
+
+   In order to allow better client handling of 403 and 409 responses, a
+   distinct XML element type is associated with each method precondition
+   and postcondition of a request.  When a particular precondition is
+   not satisfied or a particular postcondition cannot be achieved, the
+   appropriate XML element MUST be returned as the child of a top-level
+   DAV:error element in the response body, unless otherwise negotiated
+   by the request.
+
+<span class="h2"><h2><a name="section-2">2</a>.  Requirements Overview</h2></span>
+
+   This section lists what functionality is required of a CalDAV server.
+   To advertise support for CalDAV, a server:
+
+   o  MUST support iCalendar [<a href="http://tools.ietf.org/html/rfc2445" title="&quot;Internet Calendaring and Scheduling Core Object Specification (iCalendar)&quot;">RFC2445</a>] as a media type for the calendar
+      object resource format;
+
+   o  MUST support WebDAV Class 1 [<a href="http://tools.ietf.org/html/rfc2518" title="&quot;HTTP Extensions for Distributed Authoring -- WEBDAV&quot;">RFC2518</a>] (note that [<a href="#ref-rfc2518bis" title="&quot;HTTP Extensions for Distributed Authoring - WebDAV&quot;">rfc2518bis</a>]
+      describes clarifications to [<a href="http://tools.ietf.org/html/rfc2518" title="&quot;HTTP Extensions for Distributed Authoring -- WEBDAV&quot;">RFC2518</a>] that aid interoperability);
+
+   o  MUST support WebDAV ACL [<a href="http://tools.ietf.org/html/rfc3744" title="&quot;Web Distributed Authoring and Versioning (WebDAV) Access Control Protocol&quot;">RFC3744</a>] with the additional privilege
+      defined in <a href="#section-6.1">Section 6.1</a> of this document;
+
+   o  MUST support transport over TLS [<a href="http://tools.ietf.org/html/rfc2246" title="&quot;The TLS Protocol Version 1.0&quot;">RFC2246</a>] as defined in [<a href="http://tools.ietf.org/html/rfc2818" title="&quot;HTTP Over TLS&quot;">RFC2818</a>]
+      (note that [<a href="http://tools.ietf.org/html/rfc2246" title="&quot;The TLS Protocol Version 1.0&quot;">RFC2246</a>] has been obsoleted by [<a href="http://tools.ietf.org/html/rfc4346" title="&quot;The Transport Layer Security (TLS) Protocol Version 1.1&quot;">RFC4346</a>]);
+
+   o  MUST support ETags [<a href="http://tools.ietf.org/html/rfc2616" title="&quot;Hypertext Transfer Protocol -- HTTP/1.1&quot;">RFC2616</a>] with additional requirements
+      specified in <a href="#section-5.3.4">Section 5.3.4</a> of this document;
+
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                     [Page 6]</span>
+</pre><pre class="newpage"><a name="page-7" id="page-7" href="#page-7" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+   o  MUST support all calendaring reports defined in <a href="#section-7">Section 7</a> of this
+      document; and
+
+   o  MUST advertise support on all calendar collections and calendar
+      object resources for the calendaring reports in the DAV:supported-
+      report-set property, as defined in Versioning Extensions to WebDAV
+      [<a href="http://tools.ietf.org/html/rfc3253" title="&quot;Versioning Extensions to WebDAV (Web Distributed Authoring and Versioning)&quot;">RFC3253</a>].
+
+   In addition, a server:
+
+   o  SHOULD support the MKCALENDAR method defined in <a href="#section-5.3.1">Section 5.3.1</a> of
+      this document.
+
+<span class="h2"><h2><a name="section-3">3</a>.  Calendaring Data Model</h2></span>
+
+   One of the features that has made WebDAV a successful protocol is its
+   firm data model.  This makes it a useful framework for other
+   applications such as calendaring.  This specification follows the
+   same pattern by developing all features based on a well-described
+   data model.
+
+   As a brief overview, a CalDAV calendar is modeled as a WebDAV
+   collection with a defined structure; each calendar collection
+   contains a number of resources representing calendar objects as its
+   direct child resource.  Each resource representing a calendar object
+   (event, to-do, journal entry, or other calendar components) is called
+   a "calendar object resource".  Each calendar object resource and each
+   calendar collection can be individually locked and have individual
+   WebDAV properties.  Requirements derived from this model are provided
+   in <a href="#section-4.1">Section 4.1</a> and <a href="#section-4.2">Section 4.2</a>.
+
+<span class="h3"><h3><a name="section-3.1">3.1</a>.  Calendar Server</h3></span>
+
+   A CalDAV server is a calendaring-aware engine combined with a WebDAV
+   repository.  A WebDAV repository is a set of WebDAV collections,
+   containing other WebDAV resources, within a unified URL namespace.
+   For example, the repository "http://www.example.com/webdav/" may
+   contain WebDAV collections and resources, all of which have URLs
+   beginning with "http://www.example.com/webdav/".  Note that the root
+   URL, "http://www.example.com/", may not itself be a WebDAV repository
+   (for example, if the WebDAV support is implemented through a servlet
+   or other Web server extension).
+
+   A WebDAV repository MAY include calendar data in some parts of its
+   URL namespace, and non-calendaring data in other parts.
+
+   A WebDAV repository can advertise itself as a CalDAV server if it
+   supports the functionality defined in this specification at any point
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                     [Page 7]</span>
+</pre><pre class="newpage"><a name="page-8" id="page-8" href="#page-8" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+   within the root of the repository.  That might mean that calendaring
+   data is spread throughout the repository and mixed with non-calendar
+   data in nearby collections (e.g., calendar data may be found in
+   /home/lisa/calendars/ as well as in /home/bernard/calendars/, and
+   non-calendar data in /home/lisa/contacts/).  Or, it might mean that
+   calendar data can be found only in certain sections of the repository
+   (e.g., /calendar/).  Calendaring features are only required in the
+   repository sections that are or contain calendar object resources.
+   Therefore, a repository confining calendar data to the /calendar/
+   collection would only need to support the CalDAV required features
+   within that collection.
+
+   The CalDAV server or repository is the canonical location for
+   calendar data and state information.  Clients may submit requests to
+   change data or download data.  Clients may store calendar objects
+   offline and attempt to synchronize at a later time.  However, clients
+   MUST be prepared for calendar data on the server to change between
+   the time of last synchronization and when attempting an update, as
+   calendar collections may be shared and accessible via multiple
+   clients.  Entity tags and other features make this possible.
+
+<span class="h3"><h3><a name="section-3.2">3.2</a>.  Recurrence and the Data Model</h3></span>
+
+   Recurrence is an important part of the data model because it governs
+   how many resources are expected to exist.  This specification models
+   a recurring calendar component and its recurrence exceptions as a
+   single resource.  In this model, recurrence rules, recurrence dates,
+   exception rules, and exception dates are all part of the data in a
+   single calendar object resource.  This model avoids problems of
+   limiting how many recurrence instances to store in the repository,
+   how to keep recurrence instances in sync with the recurring calendar
+   component, and how to link recurrence exceptions with the recurring
+   calendar component.  It also results in less data to synchronize
+   between client and server, and makes it easier to make changes to all
+   recurrence instances or to a recurrence rule.  It makes it easier to
+   create a recurring calendar component and to delete all recurrence
+   instances.
+
+   Clients are not forced to retrieve information about all recurrence
+   instances of a recurring component.  The CALDAV:calendar-query and
+   CALDAV:calendar-multiget reports defined in this document allow
+   clients to retrieve only recurrence instances that overlap a given
+   time range.
+
+
+
+
+
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                     [Page 8]</span>
+</pre><pre class="newpage"><a name="page-9" id="page-9" href="#page-9" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+<span class="h2"><h2><a name="section-4">4</a>.  Calendar Resources</h2></span>
+
+<span class="h3"><h3><a name="section-4.1">4.1</a>.  Calendar Object Resources</h3></span>
+
+   Calendar object resources contained in calendar collections MUST NOT
+   contain more than one type of calendar component (e.g., VEVENT,
+   VTODO, VJOURNAL, VFREEBUSY, etc.) with the exception of VTIMEZONE
+   components, which MUST be specified for each unique TZID parameter
+   value specified in the iCalendar object.  For instance, a calendar
+   object resource can contain one VEVENT component and one VTIMEZONE
+   component, but it cannot contain one VEVENT component and one VTODO
+   component.  Instead, the VEVENT and VTODO components would have to be
+   stored in separate calendar object resources in the same collection.
+
+   Calendar object resources contained in calendar collections MUST NOT
+   specify the iCalendar METHOD property.
+
+   The UID property value of the calendar components contained in a
+   calendar object resource MUST be unique in the scope of the calendar
+   collection in which they are stored.
+
+   Calendar components in a calendar collection that have different UID
+   property values MUST be stored in separate calendar object resources.
+
+   Calendar components with the same UID property value, in a given
+   calendar collection, MUST be contained in the same calendar object
+   resource.  This ensures that all components in a recurrence "set" are
+   contained in the same calendar object resource.  It is possible for a
+   calendar object resource to just contain components that represent
+   "overridden" instances (ones that modify the behavior of a regular
+   instance, and thus include a RECURRENCE-ID property) without also
+   including the "master" recurring component (the one that defines the
+   recurrence "set" and does not contain any RECURRENCE-ID property).
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                     [Page 9]</span>
+</pre><pre class="newpage"><a name="page-10" id="page-10" href="#page-10" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+   For example, given the following iCalendar object:
+
+   BEGIN:VCALENDAR
+   PRODID:-//Example Corp.//CalDAV Client//EN
+   VERSION:2.0
+   BEGIN:VEVENT
+   UID:1@example.com
+   SUMMARY:One-off Meeting
+   DTSTAMP:20041210T183904Z
+   DTSTART:20041207T120000Z
+   DTEND:20041207T130000Z
+   END:VEVENT
+   BEGIN:VEVENT
+   UID:2@example.com
+   SUMMARY:Weekly Meeting
+   DTSTAMP:20041210T183838Z
+   DTSTART:20041206T120000Z
+   DTEND:20041206T130000Z
+   RRULE:FREQ=WEEKLY
+   END:VEVENT
+   BEGIN:VEVENT
+   UID:2@example.com
+   SUMMARY:Weekly Meeting
+   RECURRENCE-ID:20041213T120000Z
+   DTSTAMP:20041210T183838Z
+   DTSTART:20041213T130000Z
+   DTEND:20041213T140000Z
+   END:VEVENT
+   END:VCALENDAR
+
+   The VEVENT component with the UID value "1@example.com" would be
+   stored in its own calendar object resource.  The two VEVENT
+   components with the UID value "2@example.com", which represent a
+   recurring event where one recurrence instance has been overridden,
+   would be stored in the same calendar object resource.
+
+<span class="h3"><h3><a name="section-4.2">4.2</a>.  Calendar Collection</h3></span>
+
+   A calendar collection contains calendar object resources that
+   represent calendar components within a calendar.  A calendar
+   collection is manifested to clients as a WebDAV resource collection
+   identified by a URL.  A calendar collection MUST report the DAV:
+   collection and CALDAV:calendar XML elements in the value of the DAV:
+   resourcetype property.  The element type declaration for CALDAV:
+   calendar is:
+
+       &lt;!ELEMENT calendar EMPTY&gt;
+
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                    [Page 10]</span>
+</pre><pre class="newpage"><a name="page-11" id="page-11" href="#page-11" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+   A calendar collection can be created through provisioning (i.e.,
+   automatically created when a user's account is provisioned), or it
+   can be created with the MKCALENDAR method (see <a href="#section-5.3.1">Section 5.3.1</a>).  This
+   method can be useful for a user to create additional calendars (e.g.,
+   soccer schedule) or for users to share a calendar (e.g., team events
+   or conference rooms).  However, note that this document doesn't
+   define the purpose of extra calendar collections.  Users must rely on
+   non-standard cues to find out what a calendar collection is for, or
+   use the CALDAV:calendar-description property defined in <a href="#section-5.2.1">Section 5.2.1</a>
+   to provide such a cue.
+
+   The following restrictions are applied to the resources within a
+   calendar collection:
+
+   a.  Calendar collections MUST only contain calendar object resources
+       and collections that are not calendar collections, i.e., the only
+       "top-level" non-collection resources allowed in a calendar
+       collection are calendar object resources.  This ensures that
+       calendar clients do not have to deal with non-calendar data in a
+       calendar collection, though they do have to distinguish between
+       calendar object resources and collections when using standard
+       WebDAV techniques to examine the contents of a collection.
+
+   b.  Collections contained in calendar collections MUST NOT contain
+       calendar collections at any depth, i.e., "nesting" of calendar
+       collections within other calendar collections at any depth is not
+       allowed.  This specification does not define how collections
+       contained in a calendar collection are used or how they relate to
+       any calendar object resources contained in the calendar
+       collection.
+
+   Multiple calendar collections MAY be children of the same collection.
+
+<span class="h2"><h2><a name="section-5">5</a>.  Calendar Access Feature</h2></span>
+
+<span class="h3"><h3><a name="section-5.1">5.1</a>.  Calendar Access Support</h3></span>
+
+   A server supporting the features described in this document MUST
+   include "calendar-access" as a field in the DAV response header from
+   an OPTIONS request on any resource that supports any calendar
+   properties, reports, method, or privilege.  A value of "calendar-
+   access" in the DAV response header MUST indicate that the server
+   supports all MUST level requirements specified in this document.
+
+
+
+
+
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                    [Page 11]</span>
+</pre><pre class="newpage"><a name="page-12" id="page-12" href="#page-12" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+<span class="h4"><h4><a name="section-5.1.1">5.1.1</a>.  Example: Using OPTIONS for the Discovery of Calendar Access</h4></span>
+        Support
+
+   &gt;&gt; Request &lt;&lt;
+
+   OPTIONS /home/bernard/calendars/ HTTP/1.1
+   Host: cal.example.com
+
+   &gt;&gt; Response &lt;&lt;
+
+   HTTP/1.1 200 OK
+   Allow: OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE, COPY, MOVE
+   Allow: PROPFIND, PROPPATCH, LOCK, UNLOCK, REPORT, ACL
+   DAV: 1, 2, access-control, calendar-access
+   Date: Sat, 11 Nov 2006 09:32:12 GMT
+   Content-Length: 0
+
+   In this example, the OPTIONS method returns the value "calendar-
+   access" in the DAV response header to indicate that the collection
+   "/home/bernard/calendars/" supports the properties, reports, method,
+   or privilege defined in this specification.
+
+<span class="h3"><h3><a name="section-5.2">5.2</a>.  Calendar Collection Properties</h3></span>
+
+   This section defines properties for calendar collections.
+
+<span class="h4"><h4><a name="section-5.2.1">5.2.1</a>.  CALDAV:calendar-description Property</h4></span>
+
+   Name:  calendar-description
+
+   Namespace:  urn:ietf:params:xml:ns:caldav
+
+   Purpose:  Provides a human-readable description of the calendar
+      collection.
+
+   Conformance:  This property MAY be defined on any calendar
+      collection.  If defined, it MAY be protected and SHOULD NOT be
+      returned by a PROPFIND DAV:allprop request (as defined in Section
+      12.14.1 of [<a href="http://tools.ietf.org/html/rfc2518" title="&quot;HTTP Extensions for Distributed Authoring -- WEBDAV&quot;">RFC2518</a>]).  An xml:lang attribute indicating the human
+      language of the description SHOULD be set for this property by
+      clients or through server provisioning.  Servers MUST return any
+      xml:lang attribute if set for the property.
+
+   Description:  If present, the property contains a description of the
+      calendar collection that is suitable for presentation to a user.
+      If not present, the client should assume no description for the
+      calendar collection.
+
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                    [Page 12]</span>
+</pre><pre class="newpage"><a name="page-13" id="page-13" href="#page-13" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+   Definition:
+
+         &lt;!ELEMENT calendar-description (#PCDATA)&gt;
+         PCDATA value: string
+
+   Example:
+
+         &lt;C:calendar-description xml:lang="fr-CA"
+            xmlns:C="urn:ietf:params:xml:ns:caldav"
+         &gt;Calendrier de Mathilde Desruisseaux&lt;/C:calendar-description&gt;
+
+<span class="h4"><h4><a name="section-5.2.2">5.2.2</a>.  CALDAV:calendar-timezone Property</h4></span>
+
+   Name:  calendar-timezone
+
+   Namespace:  urn:ietf:params:xml:ns:caldav
+
+   Purpose:  Specifies a time zone on a calendar collection.
+
+   Conformance:  This property SHOULD be defined on all calendar
+      collections.  If defined, it SHOULD NOT be returned by a PROPFIND
+      DAV:allprop request (as defined in <a href="http://tools.ietf.org/html/rfc2518#section-12.14.1">Section&nbsp;12.14.1 of [RFC2518]</a>).
+
+   Description:  The CALDAV:calendar-timezone property is used to
+      specify the time zone the server should rely on to resolve "date"
+      values and "date with local time" values (i.e., floating time) to
+      "date with UTC time" values.  The server will require this
+      information to determine if a calendar component scheduled with
+      "date" values or "date with local time" values overlaps a CALDAV:
+      time-range specified in a CALDAV:calendar-query REPORT.  The
+      server will also require this information to compute the proper
+      FREEBUSY time period as "date with UTC time" in the VFREEBUSY
+      component returned in a response to a CALDAV:free-busy-query
+      REPORT request that takes into account calendar components
+      scheduled with "date" values or "date with local time" values.  In
+      the absence of this property, the server MAY rely on the time zone
+      of their choice.
+
+   Note:  The iCalendar data embedded within the CALDAV:calendar-
+      timezone XML element MUST follow the standard XML character data
+      encoding rules, including use of &amp;lt;, &amp;gt;, &amp;amp; etc. entity
+      encoding or the use of a &lt;![CDATA[ ... ]]&gt; construct.  In the
+      later case, the iCalendar data cannot contain the character
+      sequence "]]&gt;", which is the end delimiter for the CDATA section.
+
+
+
+
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                    [Page 13]</span>
+</pre><pre class="newpage"><a name="page-14" id="page-14" href="#page-14" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+   Definition:
+
+         &lt;!ELEMENT calendar-timezone (#PCDATA)&gt;
+         PCDATA value: an iCalendar object with exactly one VTIMEZONE
+               component.
+
+   Example:
+
+   &lt;C:calendar-timezone
+       xmlns:C="urn:ietf:params:xml:ns:caldav"&gt;BEGIN:VCALENDAR
+   PRODID:-//Example Corp.//CalDAV Client//EN
+   VERSION:2.0
+   BEGIN:VTIMEZONE
+   TZID:US-Eastern
+   LAST-MODIFIED:19870101T000000Z
+   BEGIN:STANDARD
+   DTSTART:19671029T020000
+   RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
+   TZOFFSETFROM:-0400
+   TZOFFSETTO:-0500
+   TZNAME:Eastern Standard Time (US &amp;amp; Canada)
+   END:STANDARD
+   BEGIN:DAYLIGHT
+   DTSTART:19870405T020000
+   RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4
+   TZOFFSETFROM:-0500
+   TZOFFSETTO:-0400
+   TZNAME:Eastern Daylight Time (US &amp;amp; Canada)
+   END:DAYLIGHT
+   END:VTIMEZONE
+   END:VCALENDAR
+   &lt;/C:calendar-timezone&gt;
+
+<span class="h4"><h4><a name="section-5.2.3">5.2.3</a>.  CALDAV:supported-calendar-component-set Property</h4></span>
+
+   Name:  supported-calendar-component-set
+
+   Namespace:  urn:ietf:params:xml:ns:caldav
+
+   Purpose:  Specifies the calendar component types (e.g., VEVENT,
+      VTODO, etc.) that calendar object resources can contain in the
+      calendar collection.
+
+   Conformance:  This property MAY be defined on any calendar
+      collection.  If defined, it MUST be protected and SHOULD NOT be
+      returned by a PROPFIND DAV:allprop request (as defined in Section
+      12.14.1 of [<a href="http://tools.ietf.org/html/rfc2518" title="&quot;HTTP Extensions for Distributed Authoring -- WEBDAV&quot;">RFC2518</a>]).
+
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                    [Page 14]</span>
+</pre><pre class="newpage"><a name="page-15" id="page-15" href="#page-15" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+   Description:  The CALDAV:supported-calendar-component-set property is
+      used to specify restrictions on the calendar component types that
+      calendar object resources may contain in a calendar collection.
+      Any attempt by the client to store calendar object resources with
+      component types not listed in this property, if it exists, MUST
+      result in an error, with the CALDAV:supported-calendar-component
+      precondition (<a href="#section-5.3.2.1">Section 5.3.2.1</a>) being violated.  Since this
+      property is protected, it cannot be changed by clients using a
+      PROPPATCH request.  However, clients can initialize the value of
+      this property when creating a new calendar collection with
+      MKCALENDAR.  The empty-element tag &lt;C:comp name="VTIMEZONE"/&gt; MUST
+      only be specified if support for calendar object resources that
+      only contain VTIMEZONE components is provided or desired.  Support
+      for VTIMEZONE components in calendar object resources that contain
+      VEVENT or VTODO components is always assumed.  In the absence of
+      this property, the server MUST accept all component types, and the
+      client can assume that all component types are accepted.
+
+   Definition:
+
+         &lt;!ELEMENT supported-calendar-component-set (comp+)&gt;
+
+   Example:
+
+         &lt;C:supported-calendar-component-set
+             xmlns:C="urn:ietf:params:xml:ns:caldav"&gt;
+           &lt;C:comp name="VEVENT"/&gt;
+           &lt;C:comp name="VTODO"/&gt;
+         &lt;/C:supported-calendar-component-set&gt;
+
+<span class="h4"><h4><a name="section-5.2.4">5.2.4</a>.  CALDAV:supported-calendar-data Property</h4></span>
+
+   Name:  supported-calendar-data
+
+   Namespace:  urn:ietf:params:xml:ns:caldav
+
+   Purpose:  Specifies what media types are allowed for calendar object
+      resources in a calendar collection.
+
+   Conformance:  This property MAY be defined on any calendar
+      collection.  If defined, it MUST be protected and SHOULD NOT be
+      returned by a PROPFIND DAV:allprop request (as defined in Section
+      12.14.1 of [<a href="http://tools.ietf.org/html/rfc2518" title="&quot;HTTP Extensions for Distributed Authoring -- WEBDAV&quot;">RFC2518</a>]).
+
+   Description:  The CALDAV:supported-calendar-data property is used to
+      specify the media type supported for the calendar object resources
+      contained in a given calendar collection (e.g., iCalendar version
+      2.0).  Any attempt by the client to store calendar object
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                    [Page 15]</span>
+</pre><pre class="newpage"><a name="page-16" id="page-16" href="#page-16" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+      resources with a media type not listed in this property MUST
+      result in an error, with the CALDAV:supported-calendar-data
+      precondition (<a href="#section-5.3.2.1">Section 5.3.2.1</a>) being violated.  In the absence of
+      this property, the server MUST only accept data with the media
+      type "text/calendar" and iCalendar version 2.0, and clients can
+      assume that the server will only accept this data.
+
+   Definition:
+
+         &lt;!ELEMENT supported-calendar-data (calendar-data+)&gt;
+
+   Example:
+
+         &lt;C:supported-calendar-data
+            xmlns:C="urn:ietf:params:xml:ns:caldav"&gt;
+           &lt;C:calendar-data content-type="text/calendar" version="2.0"/&gt;
+         &lt;/C:supported-calendar-data&gt;
+
+<span class="h4"><h4><a name="section-5.2.5">5.2.5</a>.  CALDAV:max-resource-size Property</h4></span>
+
+   Name:  max-resource-size
+
+   Namespace:  urn:ietf:params:xml:ns:caldav
+
+   Purpose:  Provides a numeric value indicating the maximum size of a
+      resource in octets that the server is willing to accept when a
+      calendar object resource is stored in a calendar collection.
+
+   Conformance:  This property MAY be defined on any calendar
+      collection.  If defined, it MUST be protected and SHOULD NOT be
+      returned by a PROPFIND DAV:allprop request (as defined in Section
+      12.14.1 of [<a href="http://tools.ietf.org/html/rfc2518" title="&quot;HTTP Extensions for Distributed Authoring -- WEBDAV&quot;">RFC2518</a>]).
+
+   Description:  The CALDAV:max-resource-size is used to specify a
+      numeric value that represents the maximum size in octets that the
+      server is willing to accept when a calendar object resource is
+      stored in a calendar collection.  Any attempt to store a calendar
+      object resource exceeding this size MUST result in an error, with
+      the CALDAV:max-resource-size precondition (<a href="#section-5.3.2.1">Section 5.3.2.1</a>) being
+      violated.  In the absence of this property, the client can assume
+      that the server will allow storing a resource of any reasonable
+      size.
+
+   Definition:
+
+         &lt;!ELEMENT max-resource-size (#PCDATA)&gt;
+         PCDATA value: a numeric value (positive integer)
+
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                    [Page 16]</span>
+</pre><pre class="newpage"><a name="page-17" id="page-17" href="#page-17" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+   Example:
+
+         &lt;C:max-resource-size xmlns:C="urn:ietf:params:xml:ns:caldav"
+         &gt;102400&lt;/C:max-resource-size&gt;
+
+<span class="h4"><h4><a name="section-5.2.6">5.2.6</a>.  CALDAV:min-date-time Property</h4></span>
+
+   Name:  min-date-time
+
+   Namespace:  urn:ietf:params:xml:ns:caldav
+
+   Purpose:  Provides a DATE-TIME value indicating the earliest date and
+      time (in UTC) that the server is willing to accept for any DATE or
+      DATE-TIME value in a calendar object resource stored in a calendar
+      collection.
+
+   Conformance:  This property MAY be defined on any calendar
+      collection.  If defined, it MUST be protected and SHOULD NOT be
+      returned by a PROPFIND DAV:allprop request (as defined in Section
+      12.14.1 of [<a href="http://tools.ietf.org/html/rfc2518" title="&quot;HTTP Extensions for Distributed Authoring -- WEBDAV&quot;">RFC2518</a>]).
+
+   Description:  The CALDAV:min-date-time is used to specify an
+      iCalendar DATE-TIME value in UTC that indicates the earliest
+      inclusive date that the server is willing to accept for any
+      explicit DATE or DATE-TIME value in a calendar object resource
+      stored in a calendar collection.  Any attempt to store a calendar
+      object resource using a DATE or DATE-TIME value earlier than this
+      value MUST result in an error, with the CALDAV:min-date-time
+      precondition (<a href="#section-5.3.2.1">Section 5.3.2.1</a>) being violated.  Note that servers
+      MUST accept recurring components that specify instances beyond
+      this limit, provided none of those instances have been overridden.
+      In that case, the server MAY simply ignore those instances outside
+      of the acceptable range when processing reports on the calendar
+      object resource.  In the absence of this property, the client can
+      assume any valid iCalendar date may be used at least up to the
+      CALDAV:max-date-time value, if that is defined.
+
+   Definition:
+
+         &lt;!ELEMENT min-date-time (#PCDATA)&gt;
+         PCDATA value: an iCalendar format DATE-TIME value in UTC
+
+   Example:
+
+         &lt;C:min-date-time xmlns:C="urn:ietf:params:xml:ns:caldav"
+         &gt;19000101T000000Z&lt;/C:min-date-time&gt;
+
+
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                    [Page 17]</span>
+</pre><pre class="newpage"><a name="page-18" id="page-18" href="#page-18" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+<span class="h4"><h4><a name="section-5.2.7">5.2.7</a>.  CALDAV:max-date-time Property</h4></span>
+
+   Name:  max-date-time
+
+   Namespace:  urn:ietf:params:xml:ns:caldav
+
+   Purpose:  Provides a DATE-TIME value indicating the latest date and
+      time (in UTC) that the server is willing to accept for any DATE or
+      DATE-TIME value in a calendar object resource stored in a calendar
+      collection.
+
+   Conformance:  This property MAY be defined on any calendar
+      collection.  If defined, it MUST be protected and SHOULD NOT be
+      returned by a PROPFIND DAV:allprop request (as defined in Section
+      12.14.1 of [<a href="http://tools.ietf.org/html/rfc2518" title="&quot;HTTP Extensions for Distributed Authoring -- WEBDAV&quot;">RFC2518</a>]).
+
+   Description:  The CALDAV:max-date-time is used to specify an
+      iCalendar DATE-TIME value in UTC that indicates the inclusive
+      latest date that the server is willing to accept for any date or
+      time value in a calendar object resource stored in a calendar
+      collection.  Any attempt to store a calendar object resource using
+      a DATE or DATE-TIME value later than this value MUST result in an
+      error, with the CALDAV:max-date-time precondition
+      (<a href="#section-5.3.2.1">Section 5.3.2.1</a>) being violated.  Note that servers MUST accept
+      recurring components that specify instances beyond this limit,
+      provided none of those instances have been overridden.  In that
+      case, the server MAY simply ignore those instances outside of the
+      acceptable range when processing reports on the calendar object
+      resource.  In the absence of this property, the client can assume
+      any valid iCalendar date may be used at least down to the CALDAV:
+      min-date-time value, if that is defined.
+
+   Definition:
+
+         &lt;!ELEMENT max-date-time (#PCDATA)&gt;
+         PCDATA value: an iCalendar format DATE-TIME value in UTC
+
+   Example:
+
+         &lt;C:max-date-time xmlns:C="urn:ietf:params:xml:ns:caldav"
+         &gt;20491231T235959Z&lt;/C:max-date-time&gt;
+
+
+
+
+
+
+
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                    [Page 18]</span>
+</pre><pre class="newpage"><a name="page-19" id="page-19" href="#page-19" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+<span class="h4"><h4><a name="section-5.2.8">5.2.8</a>.  CALDAV:max-instances Property</h4></span>
+
+   Name:  max-instances
+
+   Namespace:  urn:ietf:params:xml:ns:caldav
+
+   Purpose:  Provides a numeric value indicating the maximum number of
+      recurrence instances that a calendar object resource stored in a
+      calendar collection can generate.
+
+   Conformance:  This property MAY be defined on any calendar
+      collection.  If defined, it MUST be protected and SHOULD NOT be
+      returned by a PROPFIND DAV:allprop request (as defined in Section
+      12.14.1 of [<a href="http://tools.ietf.org/html/rfc2518" title="&quot;HTTP Extensions for Distributed Authoring -- WEBDAV&quot;">RFC2518</a>]).
+
+   Description:  The CALDAV:max-instances is used to specify a numeric
+      value that indicates the maximum number of recurrence instances
+      that a calendar object resource stored in a calendar collection
+      can generate.  Any attempt to store a calendar object resource
+      with a recurrence pattern that generates more instances than this
+      value MUST result in an error, with the CALDAV:max-instances
+      precondition (<a href="#section-5.3.2.1">Section 5.3.2.1</a>) being violated.  In the absence of
+      this property, the client can assume that the server has no limits
+      on the number of recurrence instances it can handle or expand.
+
+   Definition:
+
+         &lt;!ELEMENT max-instances (#PCDATA)&gt;
+         PCDATA value: a numeric value (integer greater than zero)
+
+   Example:
+
+         &lt;C:max-instances xmlns:C="urn:ietf:params:xml:ns:caldav"
+         &gt;100&lt;/C:max-instances&gt;
+
+<span class="h4"><h4><a name="section-5.2.9">5.2.9</a>.  CALDAV:max-attendees-per-instance Property</h4></span>
+
+   Name:  max-attendees-per-instance
+
+   Namespace:  urn:ietf:params:xml:ns:caldav
+
+   Purpose:  Provides a numeric value indicating the maximum number of
+      ATTENDEE properties in any instance of a calendar object resource
+      stored in a calendar collection.
+
+   Conformance:  This property MAY be defined on any calendar
+      collection.  If defined, it MUST be protected and SHOULD NOT be
+
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                    [Page 19]</span>
+</pre><pre class="newpage"><a name="page-20" id="page-20" href="#page-20" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+      returned by a PROPFIND DAV:allprop request (as defined in Section
+      12.14.1 of [<a href="http://tools.ietf.org/html/rfc2518" title="&quot;HTTP Extensions for Distributed Authoring -- WEBDAV&quot;">RFC2518</a>]).
+
+   Description:  The CALDAV:max-attendees-per-instance is used to
+      specify a numeric value that indicates the maximum number of
+      iCalendar ATTENDEE properties on any one instance of a calendar
+      object resource stored in a calendar collection.  Any attempt to
+      store a calendar object resource with more ATTENDEE properties per
+      instance than this value MUST result in an error, with the CALDAV:
+      max-attendees-per-instance precondition (<a href="#section-5.3.2.1">Section 5.3.2.1</a>) being
+      violated.  In the absence of this property, the client can assume
+      that the server can handle any number of ATTENDEE properties in a
+      calendar component.
+
+   Definition:
+
+         &lt;!ELEMENT max-attendees-per-instance (#PCDATA)&gt;
+         PCDATA value: a numeric value (integer greater than zero)
+
+   Example:
+
+         &lt;C:max-attendees-per-instance
+              xmlns:C="urn:ietf:params:xml:ns:caldav"
+         &gt;25&lt;/C:max-attendees-per-instance&gt;
+
+<span class="h4"><h4><a name="section-5.2.10">5.2.10</a>.  Additional Precondition for PROPPATCH</h4></span>
+
+   This specification requires an additional Precondition for the
+   PROPPATCH method.  The precondition is:
+
+      (CALDAV:valid-calendar-data): The time zone specified in CALDAV:
+      calendar-timezone property MUST be a valid iCalendar object
+      containing a single valid VTIMEZONE component.
+
+<span class="h3"><h3><a name="section-5.3">5.3</a>.  Creating Resources</h3></span>
+
+   Calendar collections and calendar object resources may be created by
+   either a CalDAV client or by the CalDAV server.  This specification
+   defines restrictions and a data model that both clients and servers
+   MUST adhere to when manipulating such calendar data.
+
+<span class="h4"><h4><a name="section-5.3.1">5.3.1</a>.  MKCALENDAR Method</h4></span>
+
+   An HTTP request using the MKCALENDAR method creates a new calendar
+   collection resource.  A server MAY restrict calendar collection
+   creation to particular collections.
+
+
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                    [Page 20]</span>
+</pre><pre class="newpage"><a name="page-21" id="page-21" href="#page-21" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+   Support for MKCALENDAR on the server is only RECOMMENDED and not
+   REQUIRED because some calendar stores only support one calendar per
+   user (or principal), and those are typically pre-created for each
+   account.  However, servers and clients are strongly encouraged to
+   support MKCALENDAR whenever possible to allow users to create
+   multiple calendar collections to help organize their data better.
+
+   Clients SHOULD use the DAV:displayname property for a human-readable
+   name of the calendar.  Clients can either specify the value of the
+   DAV:displayname property in the request body of the MKCALENDAR
+   request, or alternatively issue a PROPPATCH request to change the
+   DAV:displayname property to the appropriate value immediately after
+   issuing the MKCALENDAR request.  Clients SHOULD NOT set the DAV:
+   displayname property to be the same as any other calendar collection
+   at the same URI "level".  When displaying calendar collections to
+   users, clients SHOULD check the DAV:displayname property and use that
+   value as the name of the calendar.  In the event that the DAV:
+   displayname property is empty, the client MAY use the last part of
+   the calendar collection URI as the name; however, that path segment
+   may be "opaque" and not represent any meaningful human-readable text.
+
+   If a MKCALENDAR request fails, the server state preceding the request
+   MUST be restored.
+
+   Marshalling:
+      If a request body is included, it MUST be a CALDAV:mkcalendar XML
+      element.  Instruction processing MUST occur in the order
+      instructions are received (i.e., from top to bottom).
+      Instructions MUST either all be executed or none executed.  Thus,
+      if any error occurs during processing, all executed instructions
+      MUST be undone and a proper error result returned.  Instruction
+      processing details can be found in the definition of the DAV:set
+      instruction in <a href="http://tools.ietf.org/html/rfc2518#section-12.13.2">Section&nbsp;12.13.2 of [RFC2518]</a>.
+
+         &lt;!ELEMENT mkcalendar (DAV:set)&gt;
+
+      If a response body for a successful request is included, it MUST
+      be a CALDAV:mkcalendar-response XML element.
+
+         &lt;!ELEMENT mkcalendar-response ANY&gt;
+
+      The response MUST include a Cache-Control:no-cache header.
+
+   Preconditions:
+
+      (DAV:resource-must-be-null): A resource MUST NOT exist at the
+      Request-URI;
+
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                    [Page 21]</span>
+</pre><pre class="newpage"><a name="page-22" id="page-22" href="#page-22" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+      (CALDAV:calendar-collection-location-ok): The Request-URI MUST
+      identify a location where a calendar collection can be created;
+
+      (CALDAV:valid-calendar-data): The time zone specified in the
+      CALDAV:calendar-timezone property MUST be a valid iCalendar object
+      containing a single valid VTIMEZONE component;
+
+      (DAV:needs-privilege): The DAV:bind privilege MUST be granted to
+      the current user on the parent collection of the Request-URI.
+
+   Postconditions:
+
+      (CALDAV:initialize-calendar-collection): A new calendar collection
+      exists at the Request-URI.  The DAV:resourcetype of the calendar
+      collection MUST contain both DAV:collection and CALDAV:calendar
+      XML elements.
+
+<span class="h5"><h5><a name="section-5.3.1.1">5.3.1.1</a>.  Status Codes</h5></span>
+
+   The following are examples of response codes one would expect to get
+   in a response to a MKCALENDAR request.  Note that this list is by no
+   means exhaustive.
+
+      201 (Created) - The calendar collection resource was created in
+      its entirety;
+
+      207 (Multi-Status) - The calendar collection resource was not
+      created since one or more DAV:set instructions specified in the
+      request body could not be processed successfully.  The following
+      are examples of response codes one would expect to be used in a
+      207 (Multi-Status) response in this situation:
+
+         403 (Forbidden) - The client, for reasons the server chooses
+         not to specify, cannot alter one of the properties;
+
+         409 (Conflict) - The client has provided a value whose
+         semantics are not appropriate for the property.  This includes
+         trying to set read-only properties;
+
+         424 (Failed Dependency) - The DAV:set instruction on the
+         specified resource would have succeeded if it were not for the
+         failure of another DAV:set instruction specified in the request
+         body;
+
+         423 (Locked) - The specified resource is locked and the client
+         either is not a lock owner or the lock type requires a lock
+         token to be submitted and the client did not submit it; and
+
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                    [Page 22]</span>
+</pre><pre class="newpage"><a name="page-23" id="page-23" href="#page-23" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+         507 (Insufficient Storage) - The server did not have sufficient
+         space to record the property;
+
+      403 (Forbidden) - This indicates at least one of two conditions:
+      1) the server does not allow the creation of calendar collections
+      at the given location in its namespace, or 2) the parent
+      collection of the Request-URI exists but cannot accept members;
+
+      409 (Conflict) - A collection cannot be made at the Request-URI
+      until one or more intermediate collections have been created;
+
+      415 (Unsupported Media Type) - The server does not support the
+      request type of the body; and
+
+      507 (Insufficient Storage) - The resource does not have sufficient
+      space to record the state of the resource after the execution of
+      this method.
+
+<span class="h5"><h5><a name="section-5.3.1.2">5.3.1.2</a>.  Example: Successful MKCALENDAR Request</h5></span>
+
+   This example creates a calendar collection called /home/lisa/
+   calendars/events/ on the server cal.example.com with specific values
+   for the properties DAV:displayname, CALDAV:calendar-description,
+   CALDAV:supported-calendar-component-set, and CALDAV:calendar-
+   timezone.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                    [Page 23]</span>
+</pre><pre class="newpage"><a name="page-24" id="page-24" href="#page-24" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+   &gt;&gt; Request &lt;&lt;
+
+   MKCALENDAR /home/lisa/calendars/events/ HTTP/1.1
+   Host: cal.example.com
+   Content-Type: application/xml; charset="utf-8"
+   Content-Length: xxxx
+
+   &lt;?xml version="1.0" encoding="utf-8" ?&gt;
+   &lt;C:mkcalendar xmlns:D="DAV:"
+                 xmlns:C="urn:ietf:params:xml:ns:caldav"&gt;
+     &lt;D:set&gt;
+       &lt;D:prop&gt;
+         &lt;D:displayname&gt;Lisa's Events&lt;/D:displayname&gt;
+         &lt;C:calendar-description xml:lang="en"
+   &gt;Calendar restricted to events.&lt;/C:calendar-description&gt;
+         &lt;C:supported-calendar-component-set&gt;
+           &lt;C:comp name="VEVENT"/&gt;
+         &lt;/C:supported-calendar-component-set&gt;
+         &lt;C:calendar-timezone&gt;&lt;![CDATA[BEGIN:VCALENDAR
+   PRODID:-//Example Corp.//CalDAV Client//EN
+   VERSION:2.0
+   BEGIN:VTIMEZONE
+   TZID:US-Eastern
+   LAST-MODIFIED:19870101T000000Z
+   BEGIN:STANDARD
+   DTSTART:19671029T020000
+   RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
+   TZOFFSETFROM:-0400
+   TZOFFSETTO:-0500
+   TZNAME:Eastern Standard Time (US &amp; Canada)
+   END:STANDARD
+   BEGIN:DAYLIGHT
+   DTSTART:19870405T020000
+   RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4
+   TZOFFSETFROM:-0500
+   TZOFFSETTO:-0400
+   TZNAME:Eastern Daylight Time (US &amp; Canada)
+   END:DAYLIGHT
+   END:VTIMEZONE
+   END:VCALENDAR
+   ]]&gt;&lt;/C:calendar-timezone&gt;
+       &lt;/D:prop&gt;
+     &lt;/D:set&gt;
+   &lt;/C:mkcalendar&gt;
+
+
+
+
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                    [Page 24]</span>
+</pre><pre class="newpage"><a name="page-25" id="page-25" href="#page-25" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+   &gt;&gt; Response &lt;&lt;
+
+   HTTP/1.1 201 Created
+   Cache-Control: no-cache
+   Date: Sat, 11 Nov 2006 09:32:12 GMT
+   Content-Length: 0
+
+<span class="h4"><h4><a name="section-5.3.2">5.3.2</a>.  Creating Calendar Object Resources</h4></span>
+
+   Clients populate calendar collections with calendar object resources.
+   The URL for each calendar object resource is entirely arbitrary and
+   does not need to bear a specific relationship to the calendar object
+   resource's iCalendar properties or other metadata.  New calendar
+   object resources MUST be created with a PUT request targeted at an
+   unmapped URI.  A PUT request targeted at a mapped URI updates an
+   existing calendar object resource.
+
+   When servers create new resources, it's not hard for the server to
+   choose an unmapped URI.  It's slightly tougher for clients, because a
+   client might not want to examine all resources in the collection and
+   might not want to lock the entire collection to ensure that a new
+   resource isn't created with a name collision.  However, there is an
+   HTTP feature to mitigate this.  If the client intends to create a new
+   non-collection resource, such as a new VEVENT, the client SHOULD use
+   the HTTP request header "If-None-Match: *" on the PUT request.  The
+   Request-URI on the PUT request MUST include the target collection,
+   where the resource is to be created, plus the name of the resource in
+   the last path segment.  The "If-None-Match: *" request header ensures
+   that the client will not inadvertently overwrite an existing resource
+   if the last path segment turned out to already be used.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                    [Page 25]</span>
+</pre><pre class="newpage"><a name="page-26" id="page-26" href="#page-26" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+   &gt;&gt; Request &lt;&lt;
+
+   PUT /home/lisa/calendars/events/qwue23489.ics HTTP/1.1
+   If-None-Match: *
+   Host: cal.example.com
+   Content-Type: text/calendar
+   Content-Length: xxxx
+
+   BEGIN:VCALENDAR
+   VERSION:2.0
+   PRODID:-//Example Corp.//CalDAV Client//EN
+   BEGIN:VEVENT
+   UID:20010712T182145Z-123401@example.com
+   DTSTAMP:20060712T182145Z
+   DTSTART:20060714T170000Z
+   DTEND:20060715T040000Z
+   SUMMARY:Bastille Day Party
+   END:VEVENT
+   END:VCALENDAR
+
+   &gt;&gt; Response &lt;&lt;
+
+   HTTP/1.1 201 Created
+   Content-Length: 0
+   Date: Sat, 11 Nov 2006 09:32:12 GMT
+   ETag: "123456789-000-111"
+
+   The request to change an existing event is the same, but with a
+   specific ETag in the "If-Match" header, rather than the "If-None-
+   Match" header.
+
+   As indicated in <a href="http://tools.ietf.org/html/rfc2445#section-3.10">Section&nbsp;3.10 of [RFC2445]</a>, the URL of calendar object
+   resources containing (an arbitrary set of) calendaring and scheduling
+   information may be suffixed by ".ics", and the URL of calendar object
+   resources containing free or busy time information may be suffixed by
+   ".ifb".
+
+<span class="h5"><h5><a name="section-5.3.2.1">5.3.2.1</a>.  Additional Preconditions for PUT, COPY, and MOVE</h5></span>
+
+   This specification creates additional Preconditions for PUT, COPY,
+   and MOVE methods.  These preconditions apply when a PUT operation of
+   a calendar object resource into a calendar collection occurs, or when
+   a COPY or MOVE operation of a calendar object resource into a
+   calendar collection occurs, or when a COPY or MOVE operation occurs
+   on a calendar collection.
+
+
+
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                    [Page 26]</span>
+</pre><pre class="newpage"><a name="page-27" id="page-27" href="#page-27" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+   The new preconditions are:
+
+      (CALDAV:supported-calendar-data): The resource submitted in the
+      PUT request, or targeted by a COPY or MOVE request, MUST be a
+      supported media type (i.e., iCalendar) for calendar object
+      resources;
+
+      (CALDAV:valid-calendar-data): The resource submitted in the PUT
+      request, or targeted by a COPY or MOVE request, MUST be valid data
+      for the media type being specified (i.e., MUST contain valid
+      iCalendar data);
+
+      (CALDAV:valid-calendar-object-resource): The resource submitted in
+      the PUT request, or targeted by a COPY or MOVE request, MUST obey
+      all restrictions specified in <a href="#section-4.1">Section 4.1</a> (e.g., calendar object
+      resources MUST NOT contain more than one type of calendar
+      component, calendar object resources MUST NOT specify the
+      iCalendar METHOD property, etc.);
+
+      (CALDAV:supported-calendar-component): The resource submitted in
+      the PUT request, or targeted by a COPY or MOVE request, MUST
+      contain a type of calendar component that is supported in the
+      targeted calendar collection;
+
+      (CALDAV:no-uid-conflict): The resource submitted in the PUT
+      request, or targeted by a COPY or MOVE request, MUST NOT specify
+      an iCalendar UID property value already in use in the targeted
+      calendar collection or overwrite an existing calendar object
+      resource with one that has a different UID property value.
+      Servers SHOULD report the URL of the resource that is already
+      making use of the same UID property value in the DAV:href element;
+
+                &lt;!ELEMENT no-uid-conflict (DAV:href)&gt;
+
+      (CALDAV:calendar-collection-location-ok): In a COPY or MOVE
+      request, when the Request-URI is a calendar collection, the
+      Destination-URI MUST identify a location where a calendar
+      collection can be created;
+
+      (CALDAV:max-resource-size): The resource submitted in the PUT
+      request, or targeted by a COPY or MOVE request, MUST have an octet
+      size less than or equal to the value of the CALDAV:max-resource-
+      size property value (<a href="#section-5.2.5">Section 5.2.5</a>) on the calendar collection
+      where the resource will be stored;
+
+      (CALDAV:min-date-time): The resource submitted in the PUT request,
+      or targeted by a COPY or MOVE request, MUST have all of its
+      iCalendar DATE or DATE-TIME property values (for each recurring
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                    [Page 27]</span>
+</pre><pre class="newpage"><a name="page-28" id="page-28" href="#page-28" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+      instance) greater than or equal to the value of the CALDAV:min-
+      date-time property value (<a href="#section-5.2.6">Section 5.2.6</a>) on the calendar
+      collection where the resource will be stored;
+
+      (CALDAV:max-date-time): The resource submitted in the PUT request,
+      or targeted by a COPY or MOVE request, MUST have all of its
+      iCalendar DATE or DATE-TIME property values (for each recurring
+      instance) less than the value of the CALDAV:max-date-time property
+      value (<a href="#section-5.2.7">Section 5.2.7</a>) on the calendar collection where the
+      resource will be stored;
+
+      (CALDAV:max-instances): The resource submitted in the PUT request,
+      or targeted by a COPY or MOVE request, MUST generate a number of
+      recurring instances less than or equal to the value of the CALDAV:
+      max-instances property value (<a href="#section-5.2.8">Section 5.2.8</a>) on the calendar
+      collection where the resource will be stored;
+
+      (CALDAV:max-attendees-per-instance): The resource submitted in the
+      PUT request, or targeted by a COPY or MOVE request, MUST have a
+      number of ATTENDEE properties on any one instance less than or
+      equal to the value of the CALDAV:max-attendees-per-instance
+      property value (<a href="#section-5.2.9">Section 5.2.9</a>) on the calendar collection where
+      the resource will be stored;
+
+<span class="h4"><h4><a name="section-5.3.3">5.3.3</a>.  Non-Standard Components, Properties, and Parameters</h4></span>
+
+   iCalendar provides a "standard mechanism for doing non-standard
+   things".  This extension support allows implementers to make use of
+   non-standard components, properties, and parameters whose names are
+   prefixed with the text "X-".
+
+   Servers MUST support the use of non-standard components, properties,
+   and parameters in calendar object resources stored via the PUT
+   method.
+
+   Servers may need to enforce rules for their own "private" components,
+   properties, or parameters, so servers MAY reject any attempt by the
+   client to change those or use values for those outside of any
+   restrictions the server may have.  Servers SHOULD ensure that any
+   "private" components, properties, or parameters it uses follow the
+   convention of including a vendor id in the "X-" name, as described in
+   <a href="http://tools.ietf.org/html/rfc2445#section-4.2">Section&nbsp;4.2 of [RFC2445]</a>, e.g., "X-ABC-PRIVATE".
+
+<span class="h4"><h4><a name="section-5.3.4">5.3.4</a>.  Calendar Object Resource Entity Tag</h4></span>
+
+   The DAV:getetag property MUST be defined and set to a strong entity
+   tag on all calendar object resources.
+
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                    [Page 28]</span>
+</pre><pre class="newpage"><a name="page-29" id="page-29" href="#page-29" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+   A response to a GET request targeted at a calendar object resource
+   MUST contain an ETag response header field indicating the current
+   value of the strong entity tag of the calendar object resource.
+
+   Servers SHOULD return a strong entity tag (ETag header) in a PUT
+   response when the stored calendar object resource is equivalent by
+   octet equality to the calendar object resource submitted in the body
+   of the PUT request.  This allows clients to reliably use the returned
+   strong entity tag for data synchronization purposes.  For instance,
+   the client can do a PROPFIND request on the stored calendar object
+   resource and have the DAV:getetag property returned, and compare that
+   value with the strong entity tag it received on the PUT response, and
+   know that if they are equal, then the calendar object resource on the
+   server has not been changed.
+
+   In the case where the data stored by a server as a result of a PUT
+   request is not equivalent by octet equality to the submitted calendar
+   object resource, the behavior of the ETag response header is not
+   specified here, with the exception that a strong entity tag MUST NOT
+   be returned in the response.  As a result, clients may need to
+   retrieve the modified calendar object resource (and ETag) as a basis
+   for further changes, rather than use the calendar object resource it
+   had sent with the PUT request.
+
+<span class="h2"><h2><a name="section-6">6</a>.  Calendaring Access Control</h2></span>
+
+<span class="h3"><h3><a name="section-6.1">6.1</a>.  Calendaring Privilege</h3></span>
+
+   CalDAV servers MUST support and adhere to the requirements of WebDAV
+   ACL [<a href="http://tools.ietf.org/html/rfc3744" title="&quot;Web Distributed Authoring and Versioning (WebDAV) Access Control Protocol&quot;">RFC3744</a>].  WebDAV ACL provides a framework for an extensible set
+   of privileges that can be applied to WebDAV collections and ordinary
+   resources.  CalDAV servers MUST also support the calendaring
+   privilege defined in this section.
+
+<span class="h4"><h4><a name="section-6.1.1">6.1.1</a>.  CALDAV:read-free-busy Privilege</h4></span>
+
+   Calendar users often wish to allow other users to see their busy time
+   information, without viewing the other details of the calendar
+   components (e.g., location, summary, attendees).  This allows a
+   significant amount of privacy while still allowing other users to
+   schedule meetings at times when the user is likely to be free.
+
+   The CALDAV:read-free-busy privilege controls which calendar
+   collections, regular collections, and calendar object resources are
+   examined when a CALDAV:free-busy-query REPORT request is processed
+   (see <a href="#section-7.10">Section 7.10</a>).  This privilege can be granted on calendar
+   collections, regular collections, or calendar object resources.
+
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                    [Page 29]</span>
+</pre><pre class="newpage"><a name="page-30" id="page-30" href="#page-30" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+   Servers MUST support this privilege on all calendar collections,
+   regular collections, and calendar object resources.
+
+
+           &lt;!ELEMENT read-free-busy EMPTY&gt;
+
+   The CALDAV:read-free-busy privilege MUST be aggregated in the DAV:
+   read privilege.  Servers MUST allow the CALDAV:read-free-busy to be
+   granted without the DAV:read privilege being granted.
+
+   Clients should note that when only the CALDAV:read-free-busy
+   privilege has been granted on a resource, access to GET, HEAD,
+   OPTIONS, and PROPFIND on the resource is not implied (those
+   operations are governed by the DAV:read privilege).
+
+<span class="h3"><h3><a name="section-6.2">6.2</a>.  Additional Principal Property</h3></span>
+
+   This section defines an additional property for WebDAV principal
+   resources, as defined in [<a href="http://tools.ietf.org/html/rfc3744" title="&quot;Web Distributed Authoring and Versioning (WebDAV) Access Control Protocol&quot;">RFC3744</a>].
+
+<span class="h4"><h4><a name="section-6.2.1">6.2.1</a>.  CALDAV:calendar-home-set Property</h4></span>
+
+   Name:  calendar-home-set
+
+   Namespace:  urn:ietf:params:xml:ns:caldav
+
+   Purpose:  Identifies the URL of any WebDAV collections that contain
+      calendar collections owned by the associated principal resource.
+
+   Conformance:  This property SHOULD be defined on a principal
+      resource.  If defined, it MAY be protected and SHOULD NOT be
+      returned by a PROPFIND DAV:allprop request (as defined in Section
+      12.14.1 of [<a href="http://tools.ietf.org/html/rfc2518" title="&quot;HTTP Extensions for Distributed Authoring -- WEBDAV&quot;">RFC2518</a>]).
+
+   Description:  The CALDAV:calendar-home-set property is meant to allow
+      users to easily find the calendar collections owned by the
+      principal.  Typically, users will group all the calendar
+      collections that they own under a common collection.  This
+      property specifies the URL of collections that are either calendar
+      collections or ordinary collections that have child or descendant
+      calendar collections owned by the principal.
+
+   Definition:
+
+         &lt;!ELEMENT calendar-home-set (DAV:href*)&gt;
+
+
+
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                    [Page 30]</span>
+</pre><pre class="newpage"><a name="page-31" id="page-31" href="#page-31" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+   Example:
+
+       &lt;C:calendar-home-set xmlns:D="DAV:"
+                            xmlns:C="urn:ietf:params:xml:ns:caldav"&gt;
+         &lt;D:href&gt;http://cal.example.com/home/bernard/calendars/&lt;/D:href&gt;
+       &lt;/C:calendar-home-set&gt;
+
+<span class="h2"><h2><a name="section-7">7</a>.  Calendaring Reports</h2></span>
+
+   This section defines the reports that CalDAV servers MUST support on
+   calendar collections and calendar object resources.
+
+   CalDAV servers MUST advertise support for these reports on all
+   calendar collections and calendar object resources with the DAV:
+   supported-report-set property, defined in <a href="http://tools.ietf.org/html/rfc3253#section-3.1.5">Section&nbsp;3.1.5 of [RFC3253]</a>.
+   CalDAV servers MAY also advertise support for these reports on
+   ordinary collections.
+
+   Some of these reports allow calendar data (from possibly multiple
+   resources) to be returned.
+
+<span class="h3"><h3><a name="section-7.1">7.1</a>.  REPORT Method</h3></span>
+
+   The REPORT method (defined in <a href="http://tools.ietf.org/html/rfc3253#section-3.6">Section&nbsp;3.6 of [RFC3253]</a>) provides an
+   extensible mechanism for obtaining information about one or more
+   resources.  Unlike the PROPFIND method, which returns the value of
+   one or more named properties, the REPORT method can involve more
+   complex processing.  REPORT is valuable in cases where the server has
+   access to all of the information needed to perform the complex
+   request (such as a query), and where it would require multiple
+   requests for the client to retrieve the information needed to perform
+   the same request.
+
+   CalDAV servers MUST support the DAV:expand-property REPORT defined in
+   <a href="http://tools.ietf.org/html/rfc3253#section-3.8">Section&nbsp;3.8 of [RFC3253]</a>.
+
+<span class="h3"><h3><a name="section-7.2">7.2</a>.  Ordinary Collections</h3></span>
+
+   Servers MAY support the reports defined in this document on ordinary
+   collections (collections that are not calendar collections), in
+   addition to calendar collections or calendar object resources.  In
+   computing responses to the reports on ordinary collections, servers
+   MUST only consider calendar object resources contained in calendar
+   collections that are targeted by the REPORT request, based on the
+   value of the Depth request header.
+
+
+
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                    [Page 31]</span>
+</pre><pre class="newpage"><a name="page-32" id="page-32" href="#page-32" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+<span class="h3"><h3><a name="section-7.3">7.3</a>.  Date and Floating Time</h3></span>
+
+   iCalendar provides a way to specify DATE and DATE-TIME values that
+   are not bound to any time zone in particular, hereafter called
+   "floating date" and "floating time", respectively.  These values are
+   used to represent the same day, hour, minute, and second value,
+   regardless of which time zone is being observed.  For instance, the
+   DATE value "20051111", represents November 11, 2005 in no specific
+   time zone, while the DATE-TIME value "20051111T111100" represents
+   November 11, 2005, at 11:11 A.M. in no specific time zone.
+
+   CalDAV servers may need to convert "floating date" and "floating
+   time" values in date with UTC time values in the processing of
+   calendaring REPORT requests.
+
+   For the CALDAV:calendar-query REPORT, CalDAV servers MUST rely on the
+   value of the CALDAV:timezone XML element, if specified as part of the
+   request body, to perform the proper conversion of "floating date" and
+   "floating time" values to date with UTC time values.  If the CALDAV:
+   timezone XML element is not specified in the request body, CalDAV
+   servers MUST rely on the value of the CALDAV:calendar-timezone
+   property, if defined, or else the CalDAV servers MAY rely on the time
+   zone of their choice.
+
+   For the CALDAV:free-busy-query REPORT, CalDAV servers MUST rely on
+   the value of the CALDAV:calendar-timezone property, if defined, to
+   compute the proper FREEBUSY time period value as date with UTC time
+   for calendar components scheduled with "floating date" or "floating
+   time".  If the CALDAV:calendar-timezone property is not defined,
+   CalDAV servers MAY rely on the time zone of their choice.
+
+<span class="h3"><h3><a name="section-7.4">7.4</a>.  Time Range Filtering</h3></span>
+
+   Some of the reports defined in this section can include a time range
+   filter that is used to restrict the set of calendar object resources
+   returned to just those that overlap the specified time range.  The
+   time range filter can be applied to a calendar component as a whole,
+   or to specific calendar component properties with DATE or DATE-TIME
+   value types.
+
+   To determine whether a calendar object resource matches the time
+   range filter element, the start and end times for the targeted
+   component or property are determined and then compared to the
+   requested time range.  If there is an overlap with the requested time
+   range, then the calendar object resource matches the filter element.
+   The rules defined in [<a href="http://tools.ietf.org/html/rfc2445" title="&quot;Internet Calendaring and Scheduling Core Object Specification (iCalendar)&quot;">RFC2445</a>] for determining the actual start and
+   end times of calendar components MUST be used, and these are fully
+   enumerated in <a href="#section-9.9">Section 9.9</a> of this document.
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                    [Page 32]</span>
+</pre><pre class="newpage"><a name="page-33" id="page-33" href="#page-33" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+   When such time range filtering is used, special consideration must be
+   given to recurring calendar components, such as VEVENT and VTODO.
+   The server MUST expand recurring components to determine whether any
+   recurrence instances overlap the specified time range.  If one or
+   more recurrence instances overlap the time range, then the calendar
+   object resource matches the filter element.
+
+<span class="h3"><h3><a name="section-7.5">7.5</a>.  Searching Text: Collations</h3></span>
+
+   Some of the reports defined in this section do text matches of
+   character strings provided by the client and are compared to stored
+   calendar data.  Since iCalendar data is, by default, encoded in the
+   UTF-8 charset and may include characters outside the US-ASCII charset
+   range in some property and parameter values, there is a need to
+   ensure that text matching follows well-defined rules.
+
+   To deal with this, this specification makes use of the IANA Collation
+   Registry defined in [<a href="http://tools.ietf.org/html/rfc4790" title="&quot;Internet Application Protocol Collation Registry&quot;">RFC4790</a>] to specify collations that may be used
+   to carry out the text comparison operations with a well-defined rule.
+
+   The comparisons used in CalDAV are all "substring" matches, as per
+   <a href="http://tools.ietf.org/html/rfc4790#section-4.2">[RFC4790], Section&nbsp;4.2</a>.  Collations supported by the server MUST
+   support "substring" match operations.
+
+   CalDAV servers are REQUIRED to support the "i;ascii-casemap" and
+   "i;octet" collations, as described in [<a href="http://tools.ietf.org/html/rfc4790" title="&quot;Internet Application Protocol Collation Registry&quot;">RFC4790</a>], and MAY support
+   other collations.
+
+   Servers MUST advertise the set of collations that they support via
+   the CALDAV:supported-collation-set property defined on any resource
+   that supports reports that use collations.
+
+   Clients MUST only use collations from the list advertised by the
+   server.
+
+   In the absence of a collation explicitly specified by the client, or
+   if the client specifies the "default" collation identifier (as
+   defined in <a href="http://tools.ietf.org/html/rfc4790#section-3.1">[RFC4790], Section&nbsp;3.1</a>), the server MUST default to using
+   "i;ascii-casemap" as the collation.
+
+   Wildcards (as defined in <a href="http://tools.ietf.org/html/rfc4790#section-3.2">[RFC4790], Section&nbsp;3.2</a>) MUST NOT be used in
+   the collation identifier.
+
+   If the client chooses a collation not supported by the server, the
+   server MUST respond with a CALDAV:supported-collation precondition
+   error response.
+
+
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                    [Page 33]</span>
+</pre><pre class="newpage"><a name="page-34" id="page-34" href="#page-34" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+<span class="h4"><h4><a name="section-7.5.1">7.5.1</a>.  CALDAV:supported-collation-set Property</h4></span>
+
+   Name:  supported-collation-set
+
+   Namespace:  urn:ietf:params:xml:ns:caldav
+
+   Purpose:  Identifies the set of collations supported by the server
+      for text matching operations.
+
+   Conformance:  This property MUST be defined on any resource that
+      supports a report that does text matching.  If defined, it MUST be
+      protected and SHOULD NOT be returned by a PROPFIND DAV:allprop
+      request (as defined in <a href="http://tools.ietf.org/html/rfc2518#section-12.14.1">Section&nbsp;12.14.1 of [RFC2518]</a>).
+
+   Description:  The CALDAV:supported-collation-set property contains
+      zero or more CALDAV:supported-collation elements, which specify
+      the collection identifiers of the collations supported by the
+      server.
+
+   Definition:
+
+         &lt;!ELEMENT supported-collation-set (supported-collation*)&gt;
+
+         &lt;!ELEMENT supported-collation (#PCDATA)&gt;
+
+   Example:
+
+       &lt;C:supported-collation-set
+           xmlns:C="urn:ietf:params:xml:ns:caldav"&gt;
+         &lt;C:supported-collation&gt;i;ascii-casemap&lt;/C:supported-collation&gt;
+         &lt;C:supported-collation&gt;i;octet&lt;/C:supported-collation&gt;
+       &lt;/C:supported-collation-set&gt;
+
+<span class="h3"><h3><a name="section-7.6">7.6</a>.  Partial Retrieval</h3></span>
+
+   Some calendaring reports defined in this document allow partial
+   retrieval of calendar object resources.  A CalDAV client can specify
+   what information to return in the body of a calendaring REPORT
+   request.
+
+   A CalDAV client can request particular WebDAV property values, all
+   WebDAV property values, or a list of the names of the resource's
+   WebDAV properties.  A CalDAV client can also request calendar data to
+   be returned and specify whether all calendar components and
+   properties should be returned, or only particular ones.  See CALDAV:
+   calendar-data in <a href="#section-9.6">Section 9.6</a>.
+
+
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                    [Page 34]</span>
+</pre><pre class="newpage"><a name="page-35" id="page-35" href="#page-35" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+   By default, the returned calendar data will include the component
+   that defines the recurrence set, referred to as the "master
+   component", as well as the components that define exceptions to the
+   recurrence set, referred to as the "overridden components".
+
+   A CalDAV client that is only interested in the recurrence instances
+   that overlap a specified time range can request to receive only the
+   "master component", along with the "overridden components" that
+   impact the specified time range, and thus, limit the data returned by
+   the server (see CALDAV:limit-recurrence-set in <a href="#section-9.6.6">Section 9.6.6</a>).  An
+   overridden component impacts a time range if its current start and
+   end times overlap the time range, or if the original start and end
+   times -- the ones that would have been used if the instance were not
+   overridden -- overlap the time range, or if it affects other
+   instances that overlap the time range.
+
+   A CalDAV client with no support for recurrence properties (i.e.,
+   EXDATE, EXRULE, RDATE, and RRULE) and possibly VTIMEZONE components,
+   or a client unwilling to perform recurrence expansion because of
+   limited processing capability, can request to receive only the
+   recurrence instances that overlap a specified time range as separate
+   calendar components that each define exactly one recurrence instance
+   (see CALDAV:expand in <a href="#section-9.6.5">Section 9.6.5</a>.)
+
+   Finally, in the case of VFREEBUSY components, a CalDAV client can
+   request to receive only the FREEBUSY property values that overlap a
+   specified time range (see CALDAV:limit-freebusy-set in
+   <a href="#section-9.6.7">Section 9.6.7</a>.)
+
+<span class="h3"><h3><a name="section-7.7">7.7</a>.  Non-Standard Components, Properties, and Parameters</h3></span>
+
+   Servers MUST support the use of non-standard component, property, or
+   parameter names in the CALDAV:calendar-data XML element in
+   calendaring REPORT requests to allow clients to request that non-
+   standard components, properties, and parameters be returned in the
+   calendar data provided in the response.
+
+   Servers MAY support the use of non-standard component, property, or
+   parameter names in the CALDAV:comp-filter, CALDAV:prop-filter, and
+   CALDAV:param-filter XML elements specified in the CALDAV:filter XML
+   element of calendaring REPORT requests.
+
+   Servers MUST fail with the CALDAV:supported-filter precondition if a
+   calendaring REPORT request uses a CALDAV:comp-filter, CALDAV:prop-
+   filter, or CALDAV:param-filter XML element that makes reference to a
+   non-standard component, property, or parameter name on which the
+   server does not support queries.
+
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                    [Page 35]</span>
+</pre><pre class="newpage"><a name="page-36" id="page-36" href="#page-36" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+<span class="h3"><h3><a name="section-7.8">7.8</a>.  CALDAV:calendar-query REPORT</h3></span>
+
+   The CALDAV:calendar-query REPORT performs a search for all calendar
+   object resources that match a specified filter.  The response of this
+   report will contain all the WebDAV properties and calendar object
+   resource data specified in the request.  In the case of the CALDAV:
+   calendar-data XML element, one can explicitly specify the calendar
+   components and properties that should be returned in the calendar
+   object resource data that matches the filter.
+
+   The format of this report is modeled on the PROPFIND method.  The
+   request and response bodies of the CALDAV:calendar-query REPORT use
+   XML elements that are also used by PROPFIND.  In particular, the
+   request can include XML elements to request WebDAV properties to be
+   returned.  When that occurs, the response should follow the same
+   behavior as PROPFIND with respect to the DAV:multistatus response
+   elements used to return specific property results.  For instance, a
+   request to retrieve the value of a property that does not exist is an
+   error and MUST be noted with a response XML element that contains a
+   404 (Not Found) status value.
+
+   Support for the CALDAV:calendar-query REPORT is REQUIRED.
+
+   Marshalling:
+
+      The request body MUST be a CALDAV:calendar-query XML element, as
+      defined in <a href="#section-9.5">Section 9.5</a>.
+
+      The request MAY include a Depth header.  If no Depth header is
+      included, Depth:0 is assumed.
+
+      The response body for a successful request MUST be a DAV:
+      multistatus XML element (i.e., the response uses the same format
+      as the response for PROPFIND).  In the case where there are no
+      response elements, the returned DAV:multistatus XML element is
+      empty.
+
+      The response body for a successful CALDAV:calendar-query REPORT
+      request MUST contain a DAV:response element for each iCalendar
+      object that matched the search filter.  Calendar data is being
+      returned in the CALDAV:calendar-data XML element inside the DAV:
+      propstat XML element.
+
+   Preconditions:
+
+      (CALDAV:supported-calendar-data): The attributes "content-type"
+      and "version" of the CALDAV:calendar-data XML element (see
+
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                    [Page 36]</span>
+</pre><pre class="newpage"><a name="page-37" id="page-37" href="#page-37" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+      <a href="#section-9.6">Section 9.6</a>) specify a media type supported by the server for
+      calendar object resources.
+
+      (CALDAV:valid-filter): The CALDAV:filter XML element (see
+      <a href="#section-9.7">Section 9.7</a>) specified in the REPORT request MUST be valid.  For
+      instance, a CALDAV:filter cannot nest a &lt;C:comp name="VEVENT"&gt;
+      element in a &lt;C:comp name="VTODO"&gt; element, and a CALDAV:filter
+      cannot nest a &lt;C:time-range start="..." end="..."&gt; element in a
+      &lt;C:prop name="SUMMARY"&gt; element.
+
+      (CALDAV:supported-filter): The CALDAV:comp-filter (see
+      <a href="#section-9.7.1">Section 9.7.1</a>), CALDAV:prop-filter (see <a href="#section-9.7.2">Section 9.7.2</a>), and
+      CALDAV:param-filter (see <a href="#section-9.7.3">Section 9.7.3</a>) XML elements used in the
+      CALDAV:filter XML element (see <a href="#section-9.7">Section 9.7</a>) in the REPORT request
+      only make reference to components, properties, and parameters for
+      which queries are supported by the server, i.e., if the CALDAV:
+      filter element attempts to reference an unsupported component,
+      property, or parameter, this precondition is violated.  Servers
+      SHOULD report the CALDAV:comp-filter, CALDAV:prop-filter, or
+      CALDAV:param-filter for which it does not provide support.
+
+            &lt;!ELEMENT supported-filter (comp-filter*,
+                                        prop-filter*,
+                                        param-filter*)&gt;
+
+      (CALDAV:valid-calendar-data): The time zone specified in the
+      REPORT request MUST be a valid iCalendar object containing a
+      single valid VTIMEZONE component.
+
+      (CALDAV:min-date-time): Any XML element specifying a range of time
+      MUST have its start or end DATE or DATE-TIME values greater than
+      or equal to the value of the CALDAV:min-date-time property value
+      (<a href="#section-5.2.6">Section 5.2.6</a>) on the calendar collections being targeted by the
+      REPORT request;
+
+      (CALDAV:max-date-time): Any XML element specifying a range of time
+      MUST have its start or end DATE or DATE-TIME values less than or
+      equal to the value of the CALDAV:max-date-time property value
+      (<a href="#section-5.2.7">Section 5.2.7</a>) on the calendar collections being targeted by the
+      REPORT request;
+
+      (CALDAV:supported-collation): Any XML attribute specifying a
+      collation MUST specify a collation supported by the server as
+      described in <a href="#section-7.5">Section 7.5</a>.
+
+
+
+
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                    [Page 37]</span>
+</pre><pre class="newpage"><a name="page-38" id="page-38" href="#page-38" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+   Postconditions:
+
+      (DAV:number-of-matches-within-limits): The number of matching
+      calendar object resources must fall within server-specific,
+      predefined limits.  For example, this condition might be triggered
+      if a search specification would cause the return of an extremely
+      large number of responses.
+
+<span class="h4"><h4><a name="section-7.8.1">7.8.1</a>.  Example: Partial Retrieval of Events by Time Range</h4></span>
+
+   In this example, the client requests the server to return specific
+   components and properties of the VEVENT components that overlap the
+   time range from January 4, 2006, at 00:00:00 A.M. UTC to January 5,
+   2006, at 00:00:00 A.M. UTC.  In addition, the DAV:getetag property is
+   also requested and returned as part of the response.  Note that the
+   first calendar object returned is a recurring event whose first
+   instance lies outside the requested time range, but whose third
+   instance does overlap the time range.  Note that due to the CALDAV:
+   calendar-data element restrictions, the DTSTAMP property in VEVENT
+   components has not been returned, and the only property returned in
+   the VCALENDAR object is VERSION.
+
+   See <a href="#appendix-B">Appendix B</a> for the calendar data being targeted by this example.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                    [Page 38]</span>
+</pre><pre class="newpage"><a name="page-39" id="page-39" href="#page-39" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+   &gt;&gt; Request &lt;&lt;
+
+   REPORT /bernard/work/ HTTP/1.1
+   Host: cal.example.com
+   Depth: 1
+   Content-Type: application/xml; charset="utf-8"
+   Content-Length: xxxx
+
+   &lt;?xml version="1.0" encoding="utf-8" ?&gt;
+   &lt;C:calendar-query xmlns:D="DAV:"
+                 xmlns:C="urn:ietf:params:xml:ns:caldav"&gt;
+     &lt;D:prop&gt;
+       &lt;D:getetag/&gt;
+       &lt;C:calendar-data&gt;
+         &lt;C:comp name="VCALENDAR"&gt;
+           &lt;C:prop name="VERSION"/&gt;
+           &lt;C:comp name="VEVENT"&gt;
+             &lt;C:prop name="SUMMARY"/&gt;
+             &lt;C:prop name="UID"/&gt;
+             &lt;C:prop name="DTSTART"/&gt;
+             &lt;C:prop name="DTEND"/&gt;
+             &lt;C:prop name="DURATION"/&gt;
+             &lt;C:prop name="RRULE"/&gt;
+             &lt;C:prop name="RDATE"/&gt;
+             &lt;C:prop name="EXRULE"/&gt;
+             &lt;C:prop name="EXDATE"/&gt;
+             &lt;C:prop name="RECURRENCE-ID"/&gt;
+           &lt;/C:comp&gt;
+           &lt;C:comp name="VTIMEZONE"/&gt;
+         &lt;/C:comp&gt;
+       &lt;/C:calendar-data&gt;
+     &lt;/D:prop&gt;
+     &lt;C:filter&gt;
+       &lt;C:comp-filter name="VCALENDAR"&gt;
+         &lt;C:comp-filter name="VEVENT"&gt;
+           &lt;C:time-range start="20060104T000000Z"
+                         end="20060105T000000Z"/&gt;
+         &lt;/C:comp-filter&gt;
+       &lt;/C:comp-filter&gt;
+     &lt;/C:filter&gt;
+   &lt;/C:calendar-query&gt;
+
+   &gt;&gt; Response &lt;&lt;
+
+   HTTP/1.1 207 Multi-Status
+   Date: Sat, 11 Nov 2006 09:32:12 GMT
+   Content-Type: application/xml; charset="utf-8"
+   Content-Length: xxxx
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                    [Page 39]</span>
+</pre><pre class="newpage"><a name="page-40" id="page-40" href="#page-40" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+   &lt;?xml version="1.0" encoding="utf-8" ?&gt;
+   &lt;D:multistatus xmlns:D="DAV:"
+              xmlns:C="urn:ietf:params:xml:ns:caldav"&gt;
+     &lt;D:response&gt;
+       &lt;D:href&gt;http://cal.example.com/bernard/work/abcd2.ics&lt;/D:href&gt;
+       &lt;D:propstat&gt;
+         &lt;D:prop&gt;
+           &lt;D:getetag&gt;"fffff-abcd2"&lt;/D:getetag&gt;
+           &lt;C:calendar-data&gt;BEGIN:VCALENDAR
+   VERSION:2.0
+   BEGIN:VTIMEZONE
+   LAST-MODIFIED:20040110T032845Z
+   TZID:US/Eastern
+   BEGIN:DAYLIGHT
+   DTSTART:20000404T020000
+   RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4
+   TZNAME:EDT
+   TZOFFSETFROM:-0500
+   TZOFFSETTO:-0400
+   END:DAYLIGHT
+   BEGIN:STANDARD
+   DTSTART:20001026T020000
+   RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
+   TZNAME:EST
+   TZOFFSETFROM:-0400
+   TZOFFSETTO:-0500
+   END:STANDARD
+   END:VTIMEZONE
+   BEGIN:VEVENT
+   DTSTART;TZID=US/Eastern:20060102T120000
+   DURATION:PT1H
+   RRULE:FREQ=DAILY;COUNT=5
+   SUMMARY:Event #2
+   UID:00959BC664CA650E933C892C@example.com
+   END:VEVENT
+   BEGIN:VEVENT
+   DTSTART;TZID=US/Eastern:20060104T140000
+   DURATION:PT1H
+   RECURRENCE-ID;TZID=US/Eastern:20060104T120000
+   SUMMARY:Event #2 bis
+   UID:00959BC664CA650E933C892C@example.com
+   END:VEVENT
+   BEGIN:VEVENT
+   DTSTART;TZID=US/Eastern:20060106T140000
+   DURATION:PT1H
+   RECURRENCE-ID;TZID=US/Eastern:20060106T120000
+   SUMMARY:Event #2 bis bis
+   UID:00959BC664CA650E933C892C@example.com
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                    [Page 40]</span>
+</pre><pre class="newpage"><a name="page-41" id="page-41" href="#page-41" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+   END:VEVENT
+   END:VCALENDAR
+   &lt;/C:calendar-data&gt;
+         &lt;/D:prop&gt;
+         &lt;D:status&gt;HTTP/1.1 200 OK&lt;/D:status&gt;
+       &lt;/D:propstat&gt;
+     &lt;/D:response&gt;
+     &lt;D:response&gt;
+       &lt;D:href&gt;http://cal.example.com/bernard/work/abcd3.ics&lt;/D:href&gt;
+       &lt;D:propstat&gt;
+         &lt;D:prop&gt;
+           &lt;D:getetag&gt;"fffff-abcd3"&lt;/D:getetag&gt;
+           &lt;C:calendar-data&gt;BEGIN:VCALENDAR
+   VERSION:2.0
+   PRODID:-//Example Corp.//CalDAV Client//EN
+   BEGIN:VTIMEZONE
+   LAST-MODIFIED:20040110T032845Z
+   TZID:US/Eastern
+   BEGIN:DAYLIGHT
+   DTSTART:20000404T020000
+   RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4
+   TZNAME:EDT
+   TZOFFSETFROM:-0500
+   TZOFFSETTO:-0400
+   END:DAYLIGHT
+   BEGIN:STANDARD
+   DTSTART:20001026T020000
+   RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
+   TZNAME:EST
+   TZOFFSETFROM:-0400
+   TZOFFSETTO:-0500
+   END:STANDARD
+   END:VTIMEZONE
+   BEGIN:VEVENT
+   DTSTART;TZID=US/Eastern:20060104T100000
+   DURATION:PT1H
+   SUMMARY:Event #3
+   UID:DC6C50A017428C5216A2F1CD@example.com
+   END:VEVENT
+   END:VCALENDAR
+   &lt;/C:calendar-data&gt;
+         &lt;/D:prop&gt;
+         &lt;D:status&gt;HTTP/1.1 200 OK&lt;/D:status&gt;
+       &lt;/D:propstat&gt;
+     &lt;/D:response&gt;
+   &lt;/D:multistatus&gt;
+
+
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                    [Page 41]</span>
+</pre><pre class="newpage"><a name="page-42" id="page-42" href="#page-42" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+<span class="h4"><h4><a name="section-7.8.2">7.8.2</a>.  Example: Partial Retrieval of Recurring Events</h4></span>
+
+   In this example, the client requests the server to return VEVENT
+   components that overlap the time range from January 3, 2006, at 00:
+   00:00 A.M. UTC to January 5, 2006, at 00:00:00 A.M. UTC.  Use of the
+   CALDAV:limit-recurrence-set element causes the server to only return
+   overridden recurrence components that overlap the time range
+   specified in that element or that affect other instances that overlap
+   the time range (e.g., in the case of a THISANDFUTURE behavior).  In
+   this example, the first overridden component in the matching resource
+   is returned, but the second one is not.
+
+   See <a href="#appendix-B">Appendix B</a> for the calendar data being targeted by this example.
+
+   &gt;&gt; Request &lt;&lt;
+
+   REPORT /bernard/work/ HTTP/1.1
+   Host: cal.example.com
+   Depth: 1
+   Content-Type: application/xml; charset="utf-8"
+   Content-Length: xxxx
+
+   &lt;?xml version="1.0" encoding="utf-8" ?&gt;
+   &lt;C:calendar-query xmlns:D="DAV:"
+                     xmlns:C="urn:ietf:params:xml:ns:caldav"&gt;
+     &lt;D:prop&gt;
+       &lt;C:calendar-data&gt;
+         &lt;C:limit-recurrence-set start="20060103T000000Z"
+                                 end="20060105T000000Z"/&gt;
+       &lt;/C:calendar-data&gt;
+     &lt;/D:prop&gt;
+     &lt;C:filter&gt;
+       &lt;C:comp-filter name="VCALENDAR"&gt;
+         &lt;C:comp-filter name="VEVENT"&gt;
+           &lt;C:time-range start="20060103T000000Z"
+                         end="20060105T000000Z"/&gt;
+         &lt;/C:comp-filter&gt;
+       &lt;/C:comp-filter&gt;
+     &lt;/C:filter&gt;
+   &lt;/C:calendar-query&gt;
+
+   &gt;&gt; Response &lt;&lt;
+
+   HTTP/1.1 207 Multi-Status
+   Date: Sat, 11 Nov 2006 09:32:12 GMT
+   Content-Type: application/xml; charset="utf-8"
+   Content-Length: xxxx
+
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                    [Page 42]</span>
+</pre><pre class="newpage"><a name="page-43" id="page-43" href="#page-43" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+   &lt;?xml version="1.0" encoding="utf-8" ?&gt;
+   &lt;D:multistatus xmlns:D="DAV:"
+              xmlns:C="urn:ietf:params:xml:ns:caldav"&gt;
+     &lt;D:response&gt;
+       &lt;D:href&gt;http://cal.example.com/bernard/work/abcd2.ics&lt;/D:href&gt;
+       &lt;D:propstat&gt;
+         &lt;D:prop&gt;
+           &lt;D:getetag&gt;"fffff-abcd2"&lt;/D:getetag&gt;
+           &lt;C:calendar-data&gt;BEGIN:VCALENDAR
+   VERSION:2.0
+   PRODID:-//Example Corp.//CalDAV Client//EN
+   BEGIN:VTIMEZONE
+   LAST-MODIFIED:20040110T032845Z
+   TZID:US/Eastern
+   BEGIN:DAYLIGHT
+   DTSTART:20000404T020000
+   RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4
+   TZNAME:EDT
+   TZOFFSETFROM:-0500
+   TZOFFSETTO:-0400
+   END:DAYLIGHT
+   BEGIN:STANDARD
+   DTSTART:20001026T020000
+   RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
+   TZNAME:EST
+   TZOFFSETFROM:-0400
+   TZOFFSETTO:-0500
+   END:STANDARD
+   END:VTIMEZONE
+   BEGIN:VEVENT
+   DTSTAMP:20060206T001121Z
+   DTSTART;TZID=US/Eastern:20060102T120000
+   DURATION:PT1H
+   RRULE:FREQ=DAILY;COUNT=5
+   SUMMARY:Event #2
+   UID:00959BC664CA650E933C892C@example.com
+   END:VEVENT
+   BEGIN:VEVENT
+   DTSTAMP:20060206T001121Z
+   DTSTART;TZID=US/Eastern:20060104T140000
+   DURATION:PT1H
+   RECURRENCE-ID;TZID=US/Eastern:20060104T120000
+   SUMMARY:Event #2 bis
+   UID:00959BC664CA650E933C892C@example.com
+   END:VEVENT
+   END:VCALENDAR
+   &lt;/C:calendar-data&gt;
+         &lt;/D:prop&gt;
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                    [Page 43]</span>
+</pre><pre class="newpage"><a name="page-44" id="page-44" href="#page-44" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+         &lt;D:status&gt;HTTP/1.1 200 OK&lt;/D:status&gt;
+       &lt;/D:propstat&gt;
+     &lt;/D:response&gt;
+     &lt;D:response&gt;
+       &lt;D:href&gt;http://cal.example.com/bernard/work/abcd3.ics&lt;/D:href&gt;
+       &lt;D:propstat&gt;
+         &lt;D:prop&gt;
+           &lt;D:getetag&gt;"fffff-abcd3"&lt;/D:getetag&gt;
+           &lt;C:calendar-data&gt;BEGIN:VCALENDAR
+   VERSION:2.0
+   PRODID:-//Example Corp.//CalDAV Client//EN
+   BEGIN:VTIMEZONE
+   LAST-MODIFIED:20040110T032845Z
+   TZID:US/Eastern
+   BEGIN:DAYLIGHT
+   DTSTART:20000404T020000
+   RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4
+   TZNAME:EDT
+   TZOFFSETFROM:-0500
+   TZOFFSETTO:-0400
+   END:DAYLIGHT
+   BEGIN:STANDARD
+   DTSTART:20001026T020000
+   RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
+   TZNAME:EST
+   TZOFFSETFROM:-0400
+   TZOFFSETTO:-0500
+   END:STANDARD
+   END:VTIMEZONE
+   BEGIN:VEVENT
+   ATTENDEE;PARTSTAT=ACCEPTED;ROLE=CHAIR:mailto:cyrus@example.com
+   ATTENDEE;PARTSTAT=NEEDS-ACTION:mailto:lisa@example.com
+   DTSTAMP:20060206T001220Z
+   DTSTART;TZID=US/Eastern:20060104T100000
+   DURATION:PT1H
+   LAST-MODIFIED:20060206T001330Z
+   ORGANIZER:mailto:cyrus@example.com
+   SEQUENCE:1
+   STATUS:TENTATIVE
+   SUMMARY:Event #3
+   UID:DC6C50A017428C5216A2F1CD@example.com
+   X-ABC-GUID:E1CX5Dr-0007ym-Hz@example.com
+   END:VEVENT
+   END:VCALENDAR
+   &lt;/C:calendar-data&gt;
+         &lt;/D:prop&gt;
+         &lt;D:status&gt;HTTP/1.1 200 OK&lt;/D:status&gt;
+       &lt;/D:propstat&gt;
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                    [Page 44]</span>
+</pre><pre class="newpage"><a name="page-45" id="page-45" href="#page-45" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+     &lt;/D:response&gt;
+   &lt;/D:multistatus&gt;
+
+<span class="h4"><h4><a name="section-7.8.3">7.8.3</a>.  Example: Expanded Retrieval of Recurring Events</h4></span>
+
+   In this example, the client requests the server to return VEVENT
+   components that overlap the time range from January 2, 2006, at 00:
+   00:00 A.M. UTC to January 5, 2006, at 00:00:00 A.M. UTC and to return
+   recurring calendar components expanded into individual recurrence
+   instance calendar components.  Use of the CALDAV:expand element
+   causes the server to only return overridden recurrence instances that
+   overlap the time range specified in that element.
+
+   See <a href="#appendix-B">Appendix B</a> for the calendar data being targeted by this example.
+
+   &gt;&gt; Request &lt;&lt;
+
+   REPORT /bernard/work/ HTTP/1.1
+   Host: cal.example.com
+   Depth: 1
+   Content-Type: application/xml; charset="utf-8"
+   Content-Length: xxxx
+
+   &lt;?xml version="1.0" encoding="utf-8" ?&gt;
+   &lt;C:calendar-query xmlns:D="DAV:"
+                     xmlns:C="urn:ietf:params:xml:ns:caldav"&gt;
+     &lt;D:prop&gt;
+       &lt;C:calendar-data&gt;
+         &lt;C:expand start="20060103T000000Z"
+                   end="20060105T000000Z"/&gt;
+       &lt;/C:calendar-data&gt;
+     &lt;/D:prop&gt;
+     &lt;C:filter&gt;
+       &lt;C:comp-filter name="VCALENDAR"&gt;
+         &lt;C:comp-filter name="VEVENT"&gt;
+           &lt;C:time-range start="20060103T000000Z"
+                         end="20060105T000000Z"/&gt;
+         &lt;/C:comp-filter&gt;
+       &lt;/C:comp-filter&gt;
+     &lt;/C:filter&gt;
+   &lt;/C:calendar-query&gt;
+
+   &gt;&gt; Response &lt;&lt;
+
+   HTTP/1.1 207 Multi-Status
+   Date: Sat, 11 Nov 2006 09:32:12 GMT
+   Content-Type: application/xml; charset="utf-8"
+   Content-Length: xxxx
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                    [Page 45]</span>
+</pre><pre class="newpage"><a name="page-46" id="page-46" href="#page-46" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+   &lt;?xml version="1.0" encoding="utf-8" ?&gt;
+   &lt;D:multistatus xmlns:D="DAV:"
+              xmlns:C="urn:ietf:params:xml:ns:caldav"&gt;
+     &lt;D:response&gt;
+       &lt;D:href&gt;http://cal.example.com/bernard/work/abcd2.ics&lt;/D:href&gt;
+       &lt;D:propstat&gt;
+         &lt;D:prop&gt;
+           &lt;D:getetag&gt;"fffff-abcd2"&lt;/D:getetag&gt;
+           &lt;C:calendar-data&gt;BEGIN:VCALENDAR
+   VERSION:2.0
+   PRODID:-//Example Corp.//CalDAV Client//EN
+   BEGIN:VEVENT
+   DTSTAMP:20060206T001121Z
+   DTSTART:20060103T170000
+   DURATION:PT1H
+   RECURRENCE-ID:20060103T170000
+   SUMMARY:Event #2
+   UID:00959BC664CA650E933C892C@example.com
+   END:VEVENT
+   BEGIN:VEVENT
+   DTSTAMP:20060206T001121Z
+   DTSTART:20060104T190000
+   DURATION:PT1H
+   RECURRENCE-ID:20060104T170000
+   SUMMARY:Event #2 bis
+   UID:00959BC664CA650E933C892C@example.com
+   END:VEVENT
+   END:VCALENDAR
+   &lt;/C:calendar-data&gt;
+         &lt;/D:prop&gt;
+         &lt;D:status&gt;HTTP/1.1 200 OK&lt;/D:status&gt;
+       &lt;/D:propstat&gt;
+     &lt;/D:response&gt;
+     &lt;D:response&gt;
+       &lt;D:href&gt;http://cal.example.com/bernard/work/abcd3.ics&lt;/D:href&gt;
+       &lt;D:propstat&gt;
+         &lt;D:prop&gt;
+           &lt;D:getetag&gt;"fffff-abcd3"&lt;/D:getetag&gt;
+           &lt;C:calendar-data&gt;BEGIN:VCALENDAR
+   VERSION:2.0
+   PRODID:-//Example Corp.//CalDAV Client//EN
+   BEGIN:VEVENT
+   ATTENDEE;PARTSTAT=ACCEPTED;ROLE=CHAIR:mailto:cyrus@example.com
+   ATTENDEE;PARTSTAT=NEEDS-ACTION:mailto:lisa@example.com
+   DTSTAMP:20060206T001220Z
+   DTSTART:20060104T150000
+   DURATION:PT1H
+   LAST-MODIFIED:20060206T001330Z
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                    [Page 46]</span>
+</pre><pre class="newpage"><a name="page-47" id="page-47" href="#page-47" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+   ORGANIZER:mailto:cyrus@example.com
+   SEQUENCE:1
+   STATUS:TENTATIVE
+   SUMMARY:Event #3
+   UID:DC6C50A017428C5216A2F1CD@example.com
+   X-ABC-GUID:E1CX5Dr-0007ym-Hz@example.com
+   END:VEVENT
+   END:VCALENDAR
+   &lt;/C:calendar-data&gt;
+         &lt;/D:prop&gt;
+         &lt;D:status&gt;HTTP/1.1 200 OK&lt;/D:status&gt;
+       &lt;/D:propstat&gt;
+     &lt;/D:response&gt;
+   &lt;/D:multistatus&gt;
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                    [Page 47]</span>
+</pre><pre class="newpage"><a name="page-48" id="page-48" href="#page-48" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+<span class="h4"><h4><a name="section-7.8.4">7.8.4</a>.  Example: Partial Retrieval of Stored Free Busy Components</h4></span>
+
+   In this example, the client requests the server to return the
+   VFREEBUSY components that have free busy information that overlap the
+   time range from January 2, 2006, at 00:00:00 A.M. UTC (inclusively)
+   to January 3, 2006, at 00:00:00 A.M. UTC (exclusively).  Use of the
+   CALDAV:limit-freebusy-set element causes the server to only return
+   the FREEBUSY property values that overlap the time range specified in
+   that element.  Note that this is not an example of discovering when
+   the calendar owner is busy.
+
+   See <a href="#appendix-B">Appendix B</a> for the calendar data being targeted by this example.
+
+   &gt;&gt; Request &lt;&lt;
+
+   REPORT /bernard/work/ HTTP/1.1
+   Host: cal.example.com
+   Depth: 1
+   Content-Type: application/xml; charset="utf-8"
+   Content-Length: xxxx
+
+   &lt;?xml version="1.0" encoding="utf-8" ?&gt;
+   &lt;C:calendar-query xmlns:D="DAV:"
+                 xmlns:C="urn:ietf:params:xml:ns:caldav"&gt;
+     &lt;D:prop&gt;
+       &lt;C:calendar-data&gt;
+         &lt;C:limit-freebusy-set start="20060102T000000Z"
+                                 end="20060103T000000Z"/&gt;
+       &lt;/C:calendar-data&gt;
+     &lt;/D:prop&gt;
+     &lt;C:filter&gt;
+       &lt;C:comp-filter name="VCALENDAR"&gt;
+         &lt;C:comp-filter name="VFREEBUSY"&gt;
+           &lt;C:time-range start="20060102T000000Z"
+                           end="20060103T000000Z"/&gt;
+         &lt;/C:comp-filter&gt;
+       &lt;/C:comp-filter&gt;
+     &lt;/C:filter&gt;
+   &lt;/C:calendar-query&gt;
+
+
+
+
+
+
+
+
+
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                    [Page 48]</span>
+</pre><pre class="newpage"><a name="page-49" id="page-49" href="#page-49" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+   &gt;&gt; Response &lt;&lt;
+
+   HTTP/1.1 207 Multi-Status
+   Date: Sat, 11 Nov 2006 09:32:12 GMT
+   Content-Type: application/xml; charset="utf-8"
+   Content-Length: xxxx
+
+   &lt;?xml version="1.0" encoding="utf-8" ?&gt;
+   &lt;D:multistatus xmlns:D="DAV:"
+                  xmlns:C="urn:ietf:params:xml:ns:caldav"&gt;
+     &lt;D:response&gt;
+       &lt;D:href&gt;http://cal.example.com/bernard/work/abcd8.ics&lt;/D:href&gt;
+       &lt;D:propstat&gt;
+         &lt;D:prop&gt;
+           &lt;D:getetag&gt;"fffff-abcd8"&lt;/D:getetag&gt;
+           &lt;C:calendar-data&gt;BEGIN:VCALENDAR
+   VERSION:2.0
+   PRODID:-//Example Corp.//CalDAV Client//EN
+   BEGIN:VFREEBUSY
+   ORGANIZER;CN="Bernard Desruisseaux":mailto:bernard@example.com
+   UID:76ef34-54a3d2@example.com
+   DTSTAMP:20050530T123421Z
+   DTSTART:20060101T100000Z
+   DTEND:20060108T100000Z
+   FREEBUSY;FBTYPE=BUSY-TENTATIVE:20060102T100000Z/20060102T120000Z
+   END:VFREEBUSY
+   END:VCALENDAR
+   &lt;/C:calendar-data&gt;
+         &lt;/D:prop&gt;
+         &lt;D:status&gt;HTTP/1.1 200 OK&lt;/D:status&gt;
+       &lt;/D:propstat&gt;
+     &lt;/D:response&gt;
+   &lt;/D:multistatus&gt;
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                    [Page 49]</span>
+</pre><pre class="newpage"><a name="page-50" id="page-50" href="#page-50" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+<span class="h4"><h4><a name="section-7.8.5">7.8.5</a>.  Example: Retrieval of To-Dos by Alarm Time Range</h4></span>
+
+   In this example, the client requests the server to return the VTODO
+   components that have an alarm trigger scheduled in the specified time
+   range.
+
+   See <a href="#appendix-B">Appendix B</a> for the calendar data being targeted by this example.
+
+   &gt;&gt; Request &lt;&lt;
+
+   REPORT /bernard/work/ HTTP/1.1
+   Host: cal.example.com
+   Depth: 1
+   Content-Type: application/xml; charset="utf-8"
+   Content-Length: xxxx
+
+   &lt;?xml version="1.0" encoding="utf-8" ?&gt;
+   &lt;C:calendar-query xmlns:C="urn:ietf:params:xml:ns:caldav"&gt;
+     &lt;D:prop xmlns:D="DAV:"&gt;
+       &lt;D:getetag/&gt;
+       &lt;C:calendar-data/&gt;
+     &lt;/D:prop&gt;
+     &lt;C:filter&gt;
+       &lt;C:comp-filter name="VCALENDAR"&gt;
+         &lt;C:comp-filter name="VTODO"&gt;
+           &lt;C:comp-filter name="VALARM"&gt;
+             &lt;C:time-range start="20060106T100000Z"
+                             end="20060107T100000Z"/&gt;
+           &lt;/C:comp-filter&gt;
+         &lt;/C:comp-filter&gt;
+       &lt;/C:comp-filter&gt;
+     &lt;/C:filter&gt;
+   &lt;/C:calendar-query&gt;
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                    [Page 50]</span>
+</pre><pre class="newpage"><a name="page-51" id="page-51" href="#page-51" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+   &gt;&gt; Response &lt;&lt;
+
+   HTTP/1.1 207 Multi-Status
+   Date: Sat, 11 Nov 2006 09:32:12 GMT
+   Content-Type: application/xml; charset="utf-8"
+   Content-Length: xxxx
+
+   &lt;?xml version="1.0" encoding="utf-8" ?&gt;
+   &lt;D:multistatus xmlns:D="DAV:"
+                  xmlns:C="urn:ietf:params:xml:ns:caldav"&gt;
+     &lt;D:response&gt;
+       &lt;D:href&gt;http://cal.example.com/bernard/work/abcd4.ics&lt;/D:href&gt;
+       &lt;D:propstat&gt;
+         &lt;D:prop&gt;
+           &lt;D:getetag&gt;"fffff-abcd4"&lt;/D:getetag&gt;
+           &lt;C:calendar-data&gt;BEGIN:VCALENDAR
+   VERSION:2.0
+   PRODID:-//Example Corp.//CalDAV Client//EN
+   BEGIN:VTODO
+   DTSTAMP:20060205T235300Z
+   DUE;TZID=US/Eastern:20060106T120000
+   LAST-MODIFIED:20060205T235308Z
+   SEQUENCE:1
+   STATUS:NEEDS-ACTION
+   SUMMARY:Task #2
+   UID:E10BA47467C5C69BB74E8720@example.com
+   BEGIN:VALARM
+   ACTION:AUDIO
+   TRIGGER;RELATED=START:-PT10M
+   END:VALARM
+   END:VTODO
+   END:VCALENDAR
+   &lt;/C:calendar-data&gt;
+         &lt;/D:prop&gt;
+         &lt;D:status&gt;HTTP/1.1 200 OK&lt;/D:status&gt;
+       &lt;/D:propstat&gt;
+     &lt;/D:response&gt;
+   &lt;/D:multistatus&gt;
+
+<span class="h4"><h4><a name="section-7.8.6">7.8.6</a>.  Example: Retrieval of Event by UID</h4></span>
+
+   In this example, the client requests the server to return the VEVENT
+   component that has the UID property set to
+   "DC6C50A017428C5216A2F1CD@example.com".
+
+   See <a href="#appendix-B">Appendix B</a> for the calendar data being targeted by this example.
+
+
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                    [Page 51]</span>
+</pre><pre class="newpage"><a name="page-52" id="page-52" href="#page-52" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+   &gt;&gt; Request &lt;&lt;
+
+   REPORT /bernard/work/ HTTP/1.1
+   Host: cal.example.com
+   Depth: 1
+   Content-Type: application/xml; charset="utf-8"
+   Content-Length: xxxx
+
+   &lt;?xml version="1.0" encoding="utf-8" ?&gt;
+   &lt;C:calendar-query xmlns:C="urn:ietf:params:xml:ns:caldav"&gt;
+     &lt;D:prop xmlns:D="DAV:"&gt;
+       &lt;D:getetag/&gt;
+       &lt;C:calendar-data/&gt;
+     &lt;/D:prop&gt;
+     &lt;C:filter&gt;
+       &lt;C:comp-filter name="VCALENDAR"&gt;
+         &lt;C:comp-filter name="VEVENT"&gt;
+           &lt;C:prop-filter name="UID"&gt;
+             &lt;C:text-match collation="i;octet"
+             &gt;DC6C50A017428C5216A2F1CD@example.com&lt;/C:text-match&gt;
+           &lt;/C:prop-filter&gt;
+         &lt;/C:comp-filter&gt;
+       &lt;/C:comp-filter&gt;
+     &lt;/C:filter&gt;
+   &lt;/C:calendar-query&gt;
+
+   &gt;&gt; Response &lt;&lt;
+
+   HTTP/1.1 207 Multi-Status
+   Date: Sat, 11 Nov 2006 09:32:12 GMT
+   Content-Type: application/xml; charset="utf-8"
+   Content-Length: xxxx
+
+   &lt;?xml version="1.0" encoding="utf-8" ?&gt;
+   &lt;D:multistatus xmlns:D="DAV:"
+                  xmlns:C="urn:ietf:params:xml:ns:caldav"&gt;
+     &lt;D:response&gt;
+       &lt;D:href&gt;http://cal.example.com/bernard/work/abcd3.ics&lt;/D:href&gt;
+       &lt;D:propstat&gt;
+         &lt;D:prop&gt;
+           &lt;D:getetag&gt;"fffff-abcd3"&lt;/D:getetag&gt;
+           &lt;C:calendar-data&gt;BEGIN:VCALENDAR
+   VERSION:2.0
+   PRODID:-//Example Corp.//CalDAV Client//EN
+   BEGIN:VTIMEZONE
+   LAST-MODIFIED:20040110T032845Z
+   TZID:US/Eastern
+   BEGIN:DAYLIGHT
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                    [Page 52]</span>
+</pre><pre class="newpage"><a name="page-53" id="page-53" href="#page-53" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+   DTSTART:20000404T020000
+   RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4
+   TZNAME:EDT
+   TZOFFSETFROM:-0500
+   TZOFFSETTO:-0400
+   END:DAYLIGHT
+   BEGIN:STANDARD
+   DTSTART:20001026T020000
+   RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
+   TZNAME:EST
+   TZOFFSETFROM:-0400
+   TZOFFSETTO:-0500
+   END:STANDARD
+   END:VTIMEZONE
+   BEGIN:VEVENT
+   ATTENDEE;PARTSTAT=ACCEPTED;ROLE=CHAIR:mailto:cyrus@example.com
+   ATTENDEE;PARTSTAT=NEEDS-ACTION:mailto:lisa@example.com
+   DTSTAMP:20060206T001220Z
+   DTSTART;TZID=US/Eastern:20060104T100000
+   DURATION:PT1H
+   LAST-MODIFIED:20060206T001330Z
+   ORGANIZER:mailto:cyrus@example.com
+   SEQUENCE:1
+   STATUS:TENTATIVE
+   SUMMARY:Event #3
+   UID:DC6C50A017428C5216A2F1CD@example.com
+   X-ABC-GUID:E1CX5Dr-0007ym-Hz@example.com
+   END:VEVENT
+   END:VCALENDAR
+   &lt;/C:calendar-data&gt;
+         &lt;/D:prop&gt;
+         &lt;D:status&gt;HTTP/1.1 200 OK&lt;/D:status&gt;
+       &lt;/D:propstat&gt;
+     &lt;/D:response&gt;
+   &lt;/D:multistatus&gt;
+
+<span class="h4"><h4><a name="section-7.8.7">7.8.7</a>.  Example: Retrieval of Events by PARTSTAT</h4></span>
+
+   In this example, the client requests the server to return the VEVENT
+   components that have the ATTENDEE property with the value
+   "mailto:lisa@example.com" and for which the PARTSTAT parameter is set
+   to NEEDS-ACTION.
+
+   See <a href="#appendix-B">Appendix B</a> for the calendar data being targeted by this example.
+
+
+
+
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                    [Page 53]</span>
+</pre><pre class="newpage"><a name="page-54" id="page-54" href="#page-54" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+   &gt;&gt; Request &lt;&lt;
+
+   REPORT /bernard/work/ HTTP/1.1
+   Host: cal.example.com
+   Depth: 1
+   Content-Type: application/xml; charset="utf-8"
+   Content-Length: xxxx
+
+   &lt;?xml version="1.0" encoding="utf-8" ?&gt;
+   &lt;C:calendar-query xmlns:C="urn:ietf:params:xml:ns:caldav"&gt;
+     &lt;D:prop xmlns:D="DAV:"&gt;
+       &lt;D:getetag/&gt;
+       &lt;C:calendar-data/&gt;
+     &lt;/D:prop&gt;
+     &lt;C:filter&gt;
+       &lt;C:comp-filter name="VCALENDAR"&gt;
+         &lt;C:comp-filter name="VEVENT"&gt;
+           &lt;C:prop-filter name="ATTENDEE"&gt;
+             &lt;C:text-match collation="i;ascii-casemap"
+              &gt;mailto:lisa@example.com&lt;/C:text-match&gt;
+             &lt;C:param-filter name="PARTSTAT"&gt;
+               &lt;C:text-match collation="i;ascii-casemap"
+                &gt;NEEDS-ACTION&lt;/C:text-match&gt;
+             &lt;/C:param-filter&gt;
+           &lt;/C:prop-filter&gt;
+         &lt;/C:comp-filter&gt;
+       &lt;/C:comp-filter&gt;
+     &lt;/C:filter&gt;
+   &lt;/C:calendar-query&gt;
+
+   &gt;&gt; Response &lt;&lt;
+
+   HTTP/1.1 207 Multi-Status
+   Date: Sat, 11 Nov 2006 09:32:12 GMT
+   Content-Type: application/xml; charset="utf-8"
+   Content-Length: xxxx
+
+   &lt;?xml version="1.0" encoding="utf-8" ?&gt;
+   &lt;D:multistatus xmlns:D="DAV:"
+                  xmlns:C="urn:ietf:params:xml:ns:caldav"&gt;
+     &lt;D:response&gt;
+       &lt;D:href&gt;http://cal.example.com/bernard/work/abcd3.ics&lt;/D:href&gt;
+       &lt;D:propstat&gt;
+         &lt;D:prop&gt;
+           &lt;D:getetag&gt;"fffff-abcd3"&lt;/D:getetag&gt;
+           &lt;C:calendar-data&gt;BEGIN:VCALENDAR
+   VERSION:2.0
+   PRODID:-//Example Corp.//CalDAV Client//EN
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                    [Page 54]</span>
+</pre><pre class="newpage"><a name="page-55" id="page-55" href="#page-55" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+   BEGIN:VTIMEZONE
+   LAST-MODIFIED:20040110T032845Z
+   TZID:US/Eastern
+   BEGIN:DAYLIGHT
+   DTSTART:20000404T020000
+   RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4
+   TZNAME:EDT
+   TZOFFSETFROM:-0500
+   TZOFFSETTO:-0400
+   END:DAYLIGHT
+   BEGIN:STANDARD
+   DTSTART:20001026T020000
+   RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
+   TZNAME:EST
+   TZOFFSETFROM:-0400
+   TZOFFSETTO:-0500
+   END:STANDARD
+   END:VTIMEZONE
+   BEGIN:VEVENT
+   ATTENDEE;PARTSTAT=ACCEPTED;ROLE=CHAIR:mailto:cyrus@example.com
+   ATTENDEE;PARTSTAT=NEEDS-ACTION:mailto:lisa@example.com
+   DTSTAMP:20060206T001220Z
+   DTSTART;TZID=US/Eastern:20060104T100000
+   DURATION:PT1H
+   LAST-MODIFIED:20060206T001330Z
+   ORGANIZER:mailto:cyrus@example.com
+   SEQUENCE:1
+   STATUS:TENTATIVE
+   SUMMARY:Event #3
+   UID:DC6C50A017428C5216A2F1CD@example.com
+   X-ABC-GUID:E1CX5Dr-0007ym-Hz@example.com
+   END:VEVENT
+   END:VCALENDAR
+   &lt;/C:calendar-data&gt;
+         &lt;/D:prop&gt;
+         &lt;D:status&gt;HTTP/1.1 200 OK&lt;/D:status&gt;
+       &lt;/D:propstat&gt;
+     &lt;/D:response&gt;
+   &lt;/D:multistatus&gt;
+
+<span class="h4"><h4><a name="section-7.8.8">7.8.8</a>.  Example: Retrieval of Events Only</h4></span>
+
+   In this example, the client requests the server to return all VEVENT
+   components.
+
+   See <a href="#appendix-B">Appendix B</a> for the calendar data being targeted by this example.
+
+
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                    [Page 55]</span>
+</pre><pre class="newpage"><a name="page-56" id="page-56" href="#page-56" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+   &gt;&gt; Request &lt;&lt;
+
+   REPORT /bernard/work/ HTTP/1.1
+   Host: cal.example.com
+   Depth: 1
+   Content-Type: application/xml; charset="utf-8"
+   Content-Length: xxxx
+
+   &lt;?xml version="1.0" encoding="utf-8" ?&gt;
+   &lt;C:calendar-query xmlns:C="urn:ietf:params:xml:ns:caldav"&gt;
+     &lt;D:prop xmlns:D="DAV:"&gt;
+       &lt;D:getetag/&gt;
+       &lt;C:calendar-data/&gt;
+     &lt;/D:prop&gt;
+     &lt;C:filter&gt;
+       &lt;C:comp-filter name="VCALENDAR"&gt;
+         &lt;C:comp-filter name="VEVENT"/&gt;
+       &lt;/C:comp-filter&gt;
+     &lt;/C:filter&gt;
+   &lt;/C:calendar-query&gt;
+
+   &gt;&gt; Response &lt;&lt;
+
+   HTTP/1.1 207 Multi-Status
+   Date: Sat, 11 Nov 2006 09:32:12 GMT
+   Content-Type: application/xml; charset="utf-8"
+   Content-Length: xxxx
+
+   &lt;?xml version="1.0" encoding="utf-8" ?&gt;
+   &lt;D:multistatus xmlns:D="DAV:"
+                  xmlns:C="urn:ietf:params:xml:ns:caldav"&gt;
+     &lt;D:response&gt;
+       &lt;D:href&gt;http://cal.example.com/bernard/work/abcd1.ics&lt;/D:href&gt;
+       &lt;D:propstat&gt;
+         &lt;D:prop&gt;
+           &lt;D:getetag&gt;"fffff-abcd1"&lt;/D:getetag&gt;
+           &lt;C:calendar-data&gt;BEGIN:VCALENDAR
+   VERSION:2.0
+   PRODID:-//Example Corp.//CalDAV Client//EN
+   BEGIN:VTIMEZONE
+   LAST-MODIFIED:20040110T032845Z
+   TZID:US/Eastern
+   BEGIN:DAYLIGHT
+   DTSTART:20000404T020000
+   RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4
+   TZNAME:EDT
+   TZOFFSETFROM:-0500
+   TZOFFSETTO:-0400
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                    [Page 56]</span>
+</pre><pre class="newpage"><a name="page-57" id="page-57" href="#page-57" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+   END:DAYLIGHT
+   BEGIN:STANDARD
+   DTSTART:20001026T020000
+   RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
+   TZNAME:EST
+   TZOFFSETFROM:-0400
+   TZOFFSETTO:-0500
+   END:STANDARD
+   END:VTIMEZONE
+   BEGIN:VEVENT
+   DTSTAMP:20060206T001102Z
+   DTSTART;TZID=US/Eastern:20060102T100000
+   DURATION:PT1H
+   SUMMARY:Event #1
+   Description:Go Steelers!
+   UID:74855313FA803DA593CD579A@example.com
+   END:VEVENT
+   END:VCALENDAR
+   &lt;/C:calendar-data&gt;
+         &lt;/D:prop&gt;
+         &lt;D:status&gt;HTTP/1.1 200 OK&lt;/D:status&gt;
+       &lt;/D:propstat&gt;
+     &lt;/D:response&gt;
+     &lt;D:response&gt;
+       &lt;D:href&gt;http://cal.example.com/bernard/work/abcd2.ics&lt;/D:href&gt;
+       &lt;D:propstat&gt;
+         &lt;D:prop&gt;
+           &lt;D:getetag&gt;"fffff-abcd2"&lt;/D:getetag&gt;
+           &lt;C:calendar-data&gt;BEGIN:VCALENDAR
+   VERSION:2.0
+   PRODID:-//Example Corp.//CalDAV Client//EN
+   BEGIN:VTIMEZONE
+   LAST-MODIFIED:20040110T032845Z
+   TZID:US/Eastern
+   BEGIN:DAYLIGHT
+   DTSTART:20000404T020000
+   RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4
+   TZNAME:EDT
+   TZOFFSETFROM:-0500
+   TZOFFSETTO:-0400
+   END:DAYLIGHT
+   BEGIN:STANDARD
+   DTSTART:20001026T020000
+   RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
+   TZNAME:EST
+   TZOFFSETFROM:-0400
+   TZOFFSETTO:-0500
+   END:STANDARD
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                    [Page 57]</span>
+</pre><pre class="newpage"><a name="page-58" id="page-58" href="#page-58" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+   END:VTIMEZONE
+   BEGIN:VEVENT
+   DTSTAMP:20060206T001121Z
+   DTSTART;TZID=US/Eastern:20060102T120000
+   DURATION:PT1H
+   RRULE:FREQ=DAILY;COUNT=5
+   SUMMARY:Event #2
+   UID:00959BC664CA650E933C892C@example.com
+   END:VEVENT
+   BEGIN:VEVENT
+   DTSTAMP:20060206T001121Z
+   DTSTART;TZID=US/Eastern:20060104T140000
+   DURATION:PT1H
+   RECURRENCE-ID;TZID=US/Eastern:20060104T120000
+   SUMMARY:Event #2 bis
+   UID:00959BC664CA650E933C892C@example.com
+   END:VEVENT
+   BEGIN:VEVENT
+   DTSTAMP:20060206T001121Z
+   DTSTART;TZID=US/Eastern:20060106T140000
+   DURATION:PT1H
+   RECURRENCE-ID;TZID=US/Eastern:20060106T120000
+   SUMMARY:Event #2 bis bis
+   UID:00959BC664CA650E933C892C@example.com
+   END:VEVENT
+   END:VCALENDAR
+   &lt;/C:calendar-data&gt;
+         &lt;/D:prop&gt;
+         &lt;D:status&gt;HTTP/1.1 200 OK&lt;/D:status&gt;
+       &lt;/D:propstat&gt;
+     &lt;/D:response&gt;
+     &lt;D:response&gt;
+       &lt;D:href&gt;http://cal.example.com/bernard/work/abcd3.ics&lt;/D:href&gt;
+       &lt;D:propstat&gt;
+         &lt;D:prop&gt;
+           &lt;D:getetag&gt;"fffff-abcd3"&lt;/D:getetag&gt;
+           &lt;C:calendar-data&gt;BEGIN:VCALENDAR
+   VERSION:2.0
+   PRODID:-//Example Corp.//CalDAV Client//EN
+   BEGIN:VTIMEZONE
+   LAST-MODIFIED:20040110T032845Z
+   TZID:US/Eastern
+   BEGIN:DAYLIGHT
+   DTSTART:20000404T020000
+   RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4
+   TZNAME:EDT
+   TZOFFSETFROM:-0500
+   TZOFFSETTO:-0400
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                    [Page 58]</span>
+</pre><pre class="newpage"><a name="page-59" id="page-59" href="#page-59" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+   END:DAYLIGHT
+   BEGIN:STANDARD
+   DTSTART:20001026T020000
+   RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
+   TZNAME:EST
+   TZOFFSETFROM:-0400
+   TZOFFSETTO:-0500
+   END:STANDARD
+   END:VTIMEZONE
+   BEGIN:VEVENT
+   ATTENDEE;PARTSTAT=ACCEPTED;ROLE=CHAIR:mailto:cyrus@example.com
+   ATTENDEE;PARTSTAT=NEEDS-ACTION:mailto:lisa@example.com
+   DTSTAMP:20060206T001220Z
+   DTSTART;TZID=US/Eastern:20060104T100000
+   DURATION:PT1H
+   LAST-MODIFIED:20060206T001330Z
+   ORGANIZER:mailto:cyrus@example.com
+   SEQUENCE:1
+   STATUS:TENTATIVE
+   SUMMARY:Event #3
+   UID:DC6C50A017428C5216A2F1CD@example.com
+   X-ABC-GUID:E1CX5Dr-0007ym-Hz@example.com
+   END:VEVENT
+   END:VCALENDAR
+   &lt;/C:calendar-data&gt;
+         &lt;/D:prop&gt;
+         &lt;D:status&gt;HTTP/1.1 200 OK&lt;/D:status&gt;
+       &lt;/D:propstat&gt;
+     &lt;/D:response&gt;
+   &lt;/D:multistatus&gt;
+
+<span class="h4"><h4><a name="section-7.8.9">7.8.9</a>.  Example: Retrieval of All Pending To-Dos</h4></span>
+
+   In this example, the client requests the server to return all VTODO
+   components that do not include a COMPLETED property and do not have a
+   STATUS property value matching CANCELLED, i.e., VTODOs that still
+   need to be worked on.
+
+   See <a href="#appendix-B">Appendix B</a> for the calendar data being targeted by this example.
+
+
+
+
+
+
+
+
+
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                    [Page 59]</span>
+</pre><pre class="newpage"><a name="page-60" id="page-60" href="#page-60" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+   &gt;&gt; Request &lt;&lt;
+
+   REPORT /bernard/work/ HTTP/1.1
+   Host: cal.example.com
+   Depth: 1
+   Content-Type: application/xml; charset="utf-8"
+   Content-Length: xxxx
+
+   &lt;?xml version="1.0" encoding="utf-8" ?&gt;
+   &lt;C:calendar-query xmlns:C="urn:ietf:params:xml:ns:caldav"&gt;
+     &lt;D:prop xmlns:D="DAV:"&gt;
+       &lt;D:getetag/&gt;
+       &lt;C:calendar-data/&gt;
+     &lt;/D:prop&gt;
+     &lt;C:filter&gt;
+       &lt;C:comp-filter name="VCALENDAR"&gt;
+         &lt;C:comp-filter name="VTODO"&gt;
+           &lt;C:prop-filter name="COMPLETED"&gt;
+             &lt;C:is-not-defined/&gt;
+           &lt;/C:prop-filter&gt;
+           &lt;C:prop-filter name="STATUS"&gt;
+             &lt;C:text-match
+                negate-condition="yes"&gt;CANCELLED&lt;/C:text-match&gt;
+           &lt;/C:prop-filter&gt;
+         &lt;/C:comp-filter&gt;
+       &lt;/C:comp-filter&gt;
+     &lt;/C:filter&gt;
+   &lt;/C:calendar-query&gt;
+
+   &gt;&gt; Response &lt;&lt;
+
+   HTTP/1.1 207 Multi-Status
+   Date: Sat, 11 Nov 2006 09:32:12 GMT
+   Content-Type: application/xml; charset="utf-8"
+   Content-Length: xxxx
+
+   &lt;?xml version="1.0" encoding="utf-8" ?&gt;
+   &lt;D:multistatus xmlns:D="DAV:"
+                  xmlns:C="urn:ietf:params:xml:ns:caldav"&gt;
+     &lt;D:response&gt;
+       &lt;D:href&gt;http://cal.example.com/bernard/work/abcd4.ics&lt;/D:href&gt;
+       &lt;D:propstat&gt;
+         &lt;D:prop&gt;
+           &lt;D:getetag&gt;"fffff-abcd4"&lt;/D:getetag&gt;
+           &lt;C:calendar-data&gt;BEGIN:VCALENDAR
+   VERSION:2.0
+   PRODID:-//Example Corp.//CalDAV Client//EN
+   BEGIN:VTODO
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                    [Page 60]</span>
+</pre><pre class="newpage"><a name="page-61" id="page-61" href="#page-61" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+   DTSTAMP:20060205T235335Z
+   DUE;VALUE=DATE:20060104
+   STATUS:NEEDS-ACTION
+   SUMMARY:Task #1
+   UID:DDDEEB7915FA61233B861457@example.com
+   BEGIN:VALARM
+   ACTION:AUDIO
+   TRIGGER;RELATED=START:-PT10M
+   END:VALARM
+   END:VTODO
+   END:VCALENDAR
+   &lt;/C:calendar-data&gt;
+         &lt;/D:prop&gt;
+         &lt;D:status&gt;HTTP/1.1 200 OK&lt;/D:status&gt;
+       &lt;/D:propstat&gt;
+     &lt;/D:response&gt;
+
+     &lt;D:response&gt;
+       &lt;D:href&gt;http://cal.example.com/bernard/work/abcd5.ics&lt;/D:href&gt;
+       &lt;D:propstat&gt;
+         &lt;D:prop&gt;
+           &lt;D:getetag&gt;"fffff-abcd5"&lt;/D:getetag&gt;
+           &lt;C:calendar-data&gt;BEGIN:VCALENDAR
+   VERSION:2.0
+   PRODID:-//Example Corp.//CalDAV Client//EN
+   BEGIN:VTODO
+   DTSTAMP:20060205T235300Z
+   DUE;VALUE=DATE:20060106
+   LAST-MODIFIED:20060205T235308Z
+   SEQUENCE:1
+   STATUS:NEEDS-ACTION
+   SUMMARY:Task #2
+   UID:E10BA47467C5C69BB74E8720@example.com
+   BEGIN:VALARM
+   ACTION:AUDIO
+   TRIGGER;RELATED=START:-PT10M
+   END:VALARM
+   END:VTODO
+   END:VCALENDAR
+   &lt;/C:calendar-data&gt;
+         &lt;/D:prop&gt;
+         &lt;D:status&gt;HTTP/1.1 200 OK&lt;/D:status&gt;
+       &lt;/D:propstat&gt;
+     &lt;/D:response&gt;
+   &lt;/D:multistatus&gt;
+
+
+
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                    [Page 61]</span>
+</pre><pre class="newpage"><a name="page-62" id="page-62" href="#page-62" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+<span class="h4"><h4><a name="section-7.8.10">7.8.10</a>.  Example: Attempt to Query Unsupported Property</h4></span>
+
+   In this example, the client requests the server to return all VEVENT
+   components that include an X-ABC-GUID property with a value matching
+   "ABC".  However, the server does not support querying that non-
+   standard property, and instead returns an error response.
+
+   See <a href="#appendix-B">Appendix B</a> for the calendar data being targeted by this example.
+
+   &gt;&gt; Request &lt;&lt;
+
+   REPORT /bernard/work/ HTTP/1.1
+   Host: cal.example.com
+   Depth: 1
+   Content-Type: application/xml; charset="utf-8"
+   Content-Length: xxxx
+
+   &lt;?xml version="1.0" encoding="utf-8" ?&gt;
+   &lt;C:calendar-query xmlns:C="urn:ietf:params:xml:ns:caldav"&gt;
+     &lt;D:prop xmlns:D="DAV:"&gt;
+       &lt;D:getetag/&gt;
+       &lt;C:calendar-data/&gt;
+     &lt;/D:prop&gt;
+     &lt;C:filter&gt;
+       &lt;C:comp-filter name="VCALENDAR"&gt;
+         &lt;C:comp-filter name="VEVENT"&gt;
+           &lt;C:prop-filter name="X-ABC-GUID"&gt;
+             &lt;C:text-match&gt;ABC&lt;/C:text-match&gt;
+           &lt;/C:prop-filter&gt;
+         &lt;/C:comp-filter&gt;
+       &lt;/C:comp-filter&gt;
+     &lt;/C:filter&gt;
+   &lt;/C:calendar-query&gt;
+
+   &gt;&gt; Response &lt;&lt;
+
+   HTTP/1.1 403 Forbidden
+   Date: Sat, 11 Nov 2005 09:32:12 GMT
+   Content-Type: application/xml; charset="utf-8"
+   Content-Length: xxxx
+
+   &lt;?xml version="1.0" encoding="utf-8" ?&gt;
+   &lt;D:error&gt;
+     &lt;C:supported-filter&gt;
+       &lt;C:prop-filter name="X-ABC-GUID"/&gt;
+     &lt;/C:supported-filter&gt;
+   &lt;/D:error&gt;
+
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                    [Page 62]</span>
+</pre><pre class="newpage"><a name="page-63" id="page-63" href="#page-63" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+<span class="h3"><h3><a name="section-7.9">7.9</a>.  CALDAV:calendar-multiget REPORT</h3></span>
+
+   The CALDAV:calendar-multiget REPORT is used to retrieve specific
+   calendar object resources from within a collection, if the Request-
+   URI is a collection, or to retrieve a specific calendar object
+   resource, if the Request-URI is a calendar object resource.  This
+   report is similar to the CALDAV:calendar-query REPORT (see
+   <a href="#section-7.8">Section 7.8</a>), except that it takes a list of DAV:href elements,
+   instead of a CALDAV:filter element, to determine which calendar
+   object resources to return.
+
+   Support for the CALDAV:calendar-multiget REPORT is REQUIRED.
+
+   Marshalling:
+
+      The request body MUST be a CALDAV:calendar-multiget XML element
+      (see <a href="#section-9.10">Section 9.10</a>).  If the Request-URI is a collection resource,
+      then the DAV:href elements MUST refer to calendar object resources
+      within that collection, and they MAY refer to calendar object
+      resources at any depth within the collection.  As a result, the
+      "Depth" header MUST be ignored by the server and SHOULD NOT be
+      sent by the client.  If the Request-URI refers to a non-collection
+      resource, then there MUST be a single DAV:href element that is
+      equivalent to the Request-URI.
+
+      The response body for a successful request MUST be a DAV:
+      multistatus XML element.
+
+      The response body for a successful CALDAV:calendar-multiget REPORT
+      request MUST contain a DAV:response element for each calendar
+      object resource referenced by the provided set of DAV:href
+      elements.  Calendar data is being returned in the CALDAV:calendar-
+      data element inside the DAV:prop element.
+
+      In the case of an error accessing any of the provided DAV:href
+      resources, the server MUST return the appropriate error status
+      code in the DAV:status element of the corresponding DAV:response
+      element.
+
+   Preconditions:
+
+      (CALDAV:supported-calendar-data): The attributes "content-type"
+      and "version" of the CALDAV:calendar-data XML elements (see
+      <a href="#section-9.6">Section 9.6</a>) specify a media type supported by the server for
+      calendar object resources.
+
+      (CALDAV:min-date-time): Any XML element specifying a range of time
+      MUST have its start or end DATE or DATE-TIME values greater than
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                    [Page 63]</span>
+</pre><pre class="newpage"><a name="page-64" id="page-64" href="#page-64" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+      or equal to the value of the CALDAV:min-date-time property value
+      (<a href="#section-5.2.6">Section 5.2.6</a>) on the calendar collections being targeted by the
+      REPORT request;
+
+      (CALDAV:max-date-time): Any XML element specifying a range of time
+      MUST have its start or end DATE or DATE-TIME values less than or
+      equal to the value of the CALDAV:max-date-time property value
+      (<a href="#section-5.2.7">Section 5.2.7</a>) on the calendar collections being targeted by the
+      REPORT request;
+
+   Postconditions:
+
+      None.
+
+<span class="h4"><h4><a name="section-7.9.1">7.9.1</a>.  Example: Successful CALDAV:calendar-multiget REPORT</h4></span>
+
+   In this example, the client requests the server to return specific
+   properties of the VEVENT components referenced by specific URIs.  In
+   addition, the DAV:getetag property is also requested and returned as
+   part of the response.  Note that in this example, the resource at
+   http://cal.example.com/bernard/work/mtg1.ics does not exist,
+   resulting in an error status response.
+
+   See <a href="#appendix-B">Appendix B</a> for the calendar data being targeted by this example.
+
+   &gt;&gt; Request &lt;&lt;
+
+   REPORT /bernard/work/ HTTP/1.1
+   Host: cal.example.com
+   Content-Type: application/xml; charset="utf-8"
+   Content-Length: xxxx
+
+   &lt;?xml version="1.0" encoding="utf-8" ?&gt;
+   &lt;C:calendar-multiget xmlns:D="DAV:"
+                    xmlns:C="urn:ietf:params:xml:ns:caldav"&gt;
+     &lt;D:prop&gt;
+       &lt;D:getetag/&gt;
+       &lt;C:calendar-data/&gt;
+     &lt;/D:prop&gt;
+     &lt;D:href&gt;/bernard/work/abcd1.ics&lt;/D:href&gt;
+     &lt;D:href&gt;/bernard/work/mtg1.ics&lt;/D:href&gt;
+   &lt;/C:calendar-multiget&gt;
+
+   &gt;&gt; Response &lt;&lt;
+
+   HTTP/1.1 207 Multi-Status
+   Date: Sat, 11 Nov 2006 09:32:12 GMT
+   Content-Type: application/xml; charset="utf-8"
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                    [Page 64]</span>
+</pre><pre class="newpage"><a name="page-65" id="page-65" href="#page-65" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+   Content-Length: xxxx
+
+   &lt;?xml version="1.0" encoding="utf-8" ?&gt;
+   &lt;D:multistatus xmlns:D="DAV:"
+                  xmlns:C="urn:ietf:params:xml:ns:caldav"&gt;
+     &lt;D:response&gt;
+       &lt;D:href&gt;http://cal.example.com/bernard/work/abcd1.ics&lt;/D:href&gt;
+       &lt;D:propstat&gt;
+         &lt;D:prop&gt;
+           &lt;D:getetag&gt;"fffff-abcd1"&lt;/D:getetag&gt;
+           &lt;C:calendar-data&gt;BEGIN:VCALENDAR
+   VERSION:2.0
+   PRODID:-//Example Corp.//CalDAV Client//EN
+   BEGIN:VTIMEZONE
+   LAST-MODIFIED:20040110T032845Z
+   TZID:US/Eastern
+   BEGIN:DAYLIGHT
+   DTSTART:20000404T020000
+   RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4
+   TZNAME:EDT
+   TZOFFSETFROM:-0500
+   TZOFFSETTO:-0400
+   END:DAYLIGHT
+   BEGIN:STANDARD
+   DTSTART:20001026T020000
+   RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
+   TZNAME:EST
+   TZOFFSETFROM:-0400
+   TZOFFSETTO:-0500
+   END:STANDARD
+   END:VTIMEZONE
+   BEGIN:VEVENT
+   DTSTAMP:20060206T001102Z
+   DTSTART;TZID=US/Eastern:20060102T100000
+   DURATION:PT1H
+   SUMMARY:Event #1
+   Description:Go Steelers!
+   UID:74855313FA803DA593CD579A@example.com
+   END:VEVENT
+   END:VCALENDAR
+   &lt;/C:calendar-data&gt;
+         &lt;/D:prop&gt;
+         &lt;D:status&gt;HTTP/1.1 200 OK&lt;/D:status&gt;
+       &lt;/D:propstat&gt;
+     &lt;/D:response&gt;
+     &lt;D:response&gt;
+       &lt;D:href&gt;http://cal.example.com/bernard/work/mtg1.ics&lt;/D:href&gt;
+       &lt;D:status&gt;HTTP/1.1 404 Not Found&lt;/D:status&gt;
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                    [Page 65]</span>
+</pre><pre class="newpage"><a name="page-66" id="page-66" href="#page-66" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+     &lt;/D:response&gt;
+   &lt;/D:multistatus&gt;
+
+<span class="h3"><h3><a name="section-7.10">7.10</a>.  CALDAV:free-busy-query REPORT</h3></span>
+
+   The CALDAV:free-busy-query REPORT generates a VFREEBUSY component
+   containing free busy information for all the calendar object
+   resources targeted by the request and that have the CALDAV:read-free-
+   busy or DAV:read privilege granted to the current user.
+
+   Only VEVENT components without a TRANSP property or with the TRANSP
+   property set to OPAQUE, and VFREEBUSY components SHOULD be considered
+   in generating the free busy time information.
+
+   In the case of VEVENT components, the free or busy time type (FBTYPE)
+   of the FREEBUSY properties in the returned VFREEBUSY component SHOULD
+   be derived from the value of the TRANSP and STATUS properties, as
+   outlined in the table below:
+
+         +---------------------------++------------------+
+         |          VEVENT           ||    VFREEBUSY     |
+         +-------------+-------------++------------------+
+         | TRANSP      | STATUS      || FBTYPE           |
+         +=============+=============++==================+
+         |             | CONFIRMED   || BUSY             |
+         |             | (default)   ||                  |
+         | OPAQUE      +-------------++------------------+
+         | (default)   | CANCELLED   || FREE             |
+         |             +-------------++------------------+
+         |             | TENTATIVE   || BUSY-TENTATIVE   |
+         |             +-------------++------------------+
+         |             | x-name      || BUSY or          |
+         |             |             || x-name           |
+         +-------------+-------------++------------------+
+         |             | CONFIRMED   ||                  |
+         | TRANSPARENT | CANCELLED   || FREE             |
+         |             | TENTATIVE   ||                  |
+         |             | x-name      ||                  |
+         +-------------+-------------++------------------+
+
+   Duplicate busy time periods with the same FBTYPE parameter value
+   SHOULD NOT be specified in the returned VFREEBUSY component.  Servers
+   SHOULD coalesce consecutive or overlapping busy time periods of the
+   same type.  Busy time periods with different FBTYPE parameter values
+   MAY overlap.
+
+   Support for the CALDAV:free-busy-query REPORT is REQUIRED.
+
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                    [Page 66]</span>
+</pre><pre class="newpage"><a name="page-67" id="page-67" href="#page-67" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+   Marshalling:
+
+      The request body MUST be a CALDAV:free-busy-query XML element (see
+      <a href="#section-9.11">Section 9.11</a>), which MUST contain exactly one CALDAV:time-range
+      XML element, as defined in <a href="#section-9.9">Section 9.9</a>.
+
+      The request MAY include a Depth header.  If no Depth header is
+      included, Depth:0 is assumed.
+
+      The response body for a successful request MUST be an iCalendar
+      object that contains exactly one VFREEBUSY component that
+      describes the busy time intervals for the calendar object
+      resources containing VEVENT, or VFREEBUSY components that satisfy
+      the Depth value and for which the current user is at least granted
+      the CALDAV:read-free-busy privilege.  If no calendar object
+      resources are found to satisfy these conditions, a VFREEBUSY
+      component with no FREEBUSY property MUST be returned.  This report
+      only returns busy time information.  Free time information can be
+      inferred from the returned busy time information.
+
+      If the current user is not granted the CALDAV:read-free-busy or
+      DAV:read privileges on the Request-URI, the CALDAV:free-busy-query
+      REPORT request MUST fail and return a 404 (Not Found) status
+      value.  This restriction will prevent users from discovering URLs
+      of resources for which they are only granted the CALDAV:read-free-
+      busy privilege.
+
+      The CALDAV:free-busy-query REPORT request can only be run against
+      a collection (either a regular collection or a calendar
+      collection).  An attempt to run the report on a calendar object
+      resource MUST fail and return a 403 (Forbidden) status value.
+
+   Preconditions:
+
+      None.
+
+   Postconditions:
+
+      (DAV:number-of-matches-within-limits): The number of matching
+      calendar object resources must fall within server-specific,
+      predefined limits.  For example, this postcondition might fail if
+      the specified CALDAV:time-range would cause an extremely large
+      number of calendar object resources to be considered in computing
+      the response.
+
+
+
+
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                    [Page 67]</span>
+</pre><pre class="newpage"><a name="page-68" id="page-68" href="#page-68" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+<span class="h4"><h4><a name="section-7.10.1">7.10.1</a>.  Example: Successful CALDAV:free-busy-query REPORT</h4></span>
+
+   In this example, the client requests the server to return free busy
+   information on the calendar collection /bernard/work/, between 9:00
+   A.M. and 5:00 P.M. EST (2:00 P.M. and 10:00 P.M. UTC) on the January
+   4, 2006.  The server responds, indicating two busy time intervals of
+   one hour, one of which is tentative.
+
+   See <a href="#appendix-B">Appendix B</a> for the calendar data being targeted by this example.
+
+   &gt;&gt; Request &lt;&lt;
+
+   REPORT /bernard/work/ HTTP/1.1
+   Host: cal.example.com
+   Depth: 1
+   Content-Type: application/xml; charset="utf-8"
+   Content-Length: xxxx
+
+   &lt;?xml version="1.0" encoding="utf-8" ?&gt;
+   &lt;C:free-busy-query xmlns:C="urn:ietf:params:xml:ns:caldav"&gt;
+     &lt;C:time-range start="20060104T140000Z"
+                     end="20060105T220000Z"/&gt;
+   &lt;/C:free-busy-query&gt;
+
+   &gt;&gt; Response &lt;&lt;
+
+   HTTP/1.1 200 OK
+   Date: Sat, 11 Nov 2006 09:32:12 GMT
+   Content-Type: text/calendar
+   Content-Length: xxxx
+
+   BEGIN:VCALENDAR
+   VERSION:2.0
+   PRODID:-//Example Corp.//CalDAV Server//EN
+   BEGIN:VFREEBUSY
+   DTSTAMP:20050125T090000Z
+   DTSTART:20060104T140000Z
+   DTEND:20060105T220000Z
+   FREEBUSY;FBTYPE=BUSY-TENTATIVE:20060104T150000Z/PT1H
+   FREEBUSY:20060104T190000Z/PT1H
+   END:VFREEBUSY
+   END:VCALENDAR
+
+
+
+
+
+
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                    [Page 68]</span>
+</pre><pre class="newpage"><a name="page-69" id="page-69" href="#page-69" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+<span class="h2"><h2><a name="section-8">8</a>.  Guidelines</h2></span>
+
+<span class="h3"><h3><a name="section-8.1">8.1</a>.  Client-to-Client Interoperability</h3></span>
+
+   There are a number of actions clients can take that will be legal
+   (the server will not return errors), but that can degrade
+   interoperability with other client implementations accessing the same
+   data.  For example, a recurrence rule could be replaced with a set of
+   recurrence dates, a single recurring event could be replaced with a
+   set of independent resources to represent each recurrence, or the
+   start/end time values can be translated from the original time zone
+   to another time zone.  Although this advice amounts to iCalendar
+   interoperability best practices and is not limited only to CalDAV
+   usage, interoperability problems are likely to be more evident in
+   CalDAV use cases.
+
+<span class="h3"><h3><a name="section-8.2">8.2</a>.  Synchronization Operations</h3></span>
+
+   WebDAV already provides functionality required to synchronize a
+   collection or set of collections, to make changes offline, and
+   provides a simple way to resolve conflicts when reconnected.  ETags
+   are the key to making this work, but these are not required of all
+   WebDAV servers.  Since offline functionality is more important to
+   calendar applications than to some other WebDAV applications, CalDAV
+   servers MUST support ETags, as specified in <a href="#section-5.3.4">Section 5.3.4</a>.
+
+<span class="h4"><h4><a name="section-8.2.1">8.2.1</a>.  Use of Reports</h4></span>
+
+<span class="h5"><h5><a name="section-8.2.1.1">8.2.1.1</a>.  Restrict the Time Range</h5></span>
+
+   The reports provided in CalDAV can be used by clients to optimize
+   their performance in terms of network bandwidth usage and resource
+   consumption on the local client machine.  Both are certainly major
+   considerations for mobile or handheld devices with limited capacity,
+   but they are also relevant to desktop client applications in cases
+   where the calendar collections contain large amounts of data.
+
+   Typically, clients present calendar data to users in views that span
+   a finite time interval, so whenever possible, clients should only
+   retrieve calendar components from the server using CALDAV:calendar-
+   query REPORT, combined with a CALDAV:time-range element, to limit the
+   set of returned components to just those needed to populate the
+   current view.
+
+
+
+
+
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                    [Page 69]</span>
+</pre><pre class="newpage"><a name="page-70" id="page-70" href="#page-70" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+<span class="h5"><h5><a name="section-8.2.1.2">8.2.1.2</a>.  Synchronize by Time Range</h5></span>
+
+   Typically in a calendar, historical data (events, to-dos, etc. that
+   have completed prior to the current date) do not change, though they
+   may be deleted.  As a result, a client can speed up the
+   synchronization process by only considering data for the present time
+   and the future up to a reasonable limit (e.g., one week, one month).
+   If the user then tries to examine a portion of the calendar outside
+   the range that has been synchronized, the client can perform another
+   synchronization operation on the new time interval being examined.
+   This "just-in-time" synchronization can minimize bandwidth for common
+   user interaction behaviors.
+
+<span class="h5"><h5><a name="section-8.2.1.3">8.2.1.3</a>.  Synchronization Process</h5></span>
+
+   If a client wants to support calendar data synchronization, as
+   opposed to downloading calendar data each time it is needed, the
+   client needs to cache the calendar object resource's URI and ETag,
+   along with the actual calendar data.  While the URI remains static
+   for the lifetime of the calendar object resource, the ETag will
+   change with each successive change to the calendar object resource.
+   Thus, to synchronize a local data cache with the server, the client
+   can first fetch the URI/ETag pairs for the time interval being
+   considered, and compare those results with the cached data.  Any
+   cached component whose ETag differs from that on the server needs to
+   be refreshed.
+
+   In order to properly detect the changes between the server and client
+   data, the client will need to keep a record of which calendar object
+   resources have been created, changed, or deleted since the last
+   synchronization operation so that it can reconcile those changes with
+   the data on the server.
+
+   Here's an example of how to do that:
+
+   The client issues a CALDAV:calendar-query REPORT request for a
+   specific time range and asks for only the DAV:getetag property to be
+   returned:
+
+
+
+
+
+
+
+
+
+
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                    [Page 70]</span>
+</pre><pre class="newpage"><a name="page-71" id="page-71" href="#page-71" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+   REPORT /bernard/work/ HTTP/1.1
+   Host: cal.example.com
+   Depth: 1
+   Content-Type: application/xml; charset="utf-8"
+   Content-Length: xxxx
+
+   &lt;?xml version="1.0" encoding="utf-8" ?&gt;
+   &lt;C:calendar-query xmlns:D="DAV:"
+                     xmlns:C="urn:ietf:params:xml:ns:caldav"&gt;
+     &lt;D:prop&gt;
+       &lt;D:getetag/&gt;
+     &lt;/D:prop&gt;
+     &lt;C:filter&gt;
+       &lt;C:comp-filter name="VCALENDAR"&gt;
+         &lt;C:comp-filter name="VEVENT"&gt;
+           &lt;C:time-range start="20040902T000000Z"
+                           end="20040903T000000Z"/&gt;
+         &lt;/C:comp-filter&gt;
+       &lt;/C:comp-filter&gt;
+     &lt;/C:filter&gt;
+   &lt;/C:calendar-query&gt;
+
+   The client then uses the results to determine which calendar object
+   resources have changed, been created, or deleted on the server, and
+   how those relate to locally cached calendar object resources that may
+   have changed, been created, or deleted.  If the client determines
+   that there are calendar object resources on the server that need to
+   be fetched, the client issues a CALDAV:calendar-multiget REPORT
+   request to fetch its calendar data:
+
+   REPORT /bernard/work/ HTTP/1.1
+   Host: cal.example.com
+   Content-Type: application/xml; charset="utf-8"
+   Content-Length: xxxx
+
+   &lt;?xml version="1.0" encoding="utf-8" ?&gt;
+   &lt;C:calendar-multiget xmlns:D="DAV:"
+                        xmlns:C="urn:ietf:params:xml:ns:caldav"&gt;
+     &lt;D:prop&gt;
+       &lt;D:getetag/&gt;
+       &lt;C:calendar-data/&gt;
+     &lt;/D:prop&gt;
+     &lt;D:href&gt;/bernard/work/abcd1.ics&lt;/D:href&gt;
+     &lt;D:href&gt;/bernard/work/mtg1.ics&lt;/D:href&gt;
+   &lt;/C:calendar-multiget&gt;
+
+
+
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                    [Page 71]</span>
+</pre><pre class="newpage"><a name="page-72" id="page-72" href="#page-72" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+<span class="h4"><h4><a name="section-8.2.2">8.2.2</a>.  Restrict the Properties Returned</h4></span>
+
+   A client may not need all the calendar properties of a calendar
+   object resource when presenting information to the user.  Since some
+   calendar property values can be large (e.g., ATTACH or ATTENDEE), a
+   client can choose to restrict the calendar properties to be returned
+   in a calendaring REPORT request to those it knows it will use.
+
+   However, if a client needs to make a change to a calendar object
+   resource, it can only change the entire calendar object resource via
+   a PUT request.  There is currently no way to incrementally make a
+   change to a set of calendar properties of a calendar object resource.
+   As a result, the client will have to get the entire calendar object
+   resource that is being changed.
+
+<span class="h3"><h3><a name="section-8.3">8.3</a>.  Use of Locking</h3></span>
+
+   WebDAV locks can be used to prevent two clients that are modifying
+   the same resource from either overwriting each others' changes
+   (though that problem can also be solved by using ETags) or wasting
+   time making changes that will conflict with another set of changes.
+   In a multi-user calendar system, an interactive calendar client could
+   lock an event while the user is editing the event, and unlock the
+   event when the user finishes or cancels.  Locks can also be used to
+   prevent changes while data is being reorganized.  For example, a
+   calendar client might lock two calendar collections prior to moving a
+   bunch of calendar resources from one to another.
+
+   Clients are responsible for requesting a lock timeout period that is
+   appropriate to the use case.  When the user explicitly decides to
+   reserve a resource and prevent other changes, a long timeout might be
+   appropriate, but in cases where the client automatically decides to
+   lock the resource, the timeout should be short (and the client can
+   always refresh the lock should it need to).  A short lock timeout
+   means that if the client is unable to remove the lock, the other
+   calendar users aren't prevented from making changes.
+
+<span class="h3"><h3><a name="section-8.4">8.4</a>.  Finding Calendars</h3></span>
+
+   Much of the time, a calendar client (or agent) will discover a new
+   calendar's location by being provided directly with the URL.  For
+   example, a user will type his or her own calendar location into
+   client configuration information or copy and paste a URL from email
+   into the calendar application.  The client need only confirm that the
+   URL points to a resource that is a calendar collection.  The client
+   may also be able to browse WebDAV collections to find calendar
+   collections.
+
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                    [Page 72]</span>
+</pre><pre class="newpage"><a name="page-73" id="page-73" href="#page-73" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+   The choice of HTTP URLs means that calendar object resources are
+   backward compatible with existing software, but does have the
+   disadvantage that existing software does not usually know to look at
+   the OPTIONS response to that URL to determine what can be done with
+   it.  This is somewhat of a barrier for WebDAV usage as well as with
+   CalDAV usage.  This specification does not offer a way through this
+   other than making the information available in the OPTIONS response
+   should this be requested.
+
+   For calendar sharing and scheduling use cases, one might wish to find
+   the calendar belonging to another user.  If the other user has a
+   calendar in the same repository, that calendar can be found by using
+   the principal namespace required by WebDAV ACL support.  For other
+   cases, the authors have no universal solution, but implementers can
+   consider whether to use vCard [<a href="http://tools.ietf.org/html/rfc2426" title="&quot;vCard MIME Directory Profile&quot;">RFC2426</a>] or LDAP [<a href="http://tools.ietf.org/html/rfc4511" title="&quot;Lightweight Directory Access Protocol (LDAP): The Protocol&quot;">RFC4511</a>] standards
+   together with calendar attributes [<a href="http://tools.ietf.org/html/rfc2739" title="&quot;Calendar Attributes for vCard and LDAP&quot;">RFC2739</a>].
+
+   Because CalDAV requires servers to support WebDAV ACL [<a href="http://tools.ietf.org/html/rfc3744" title="&quot;Web Distributed Authoring and Versioning (WebDAV) Access Control Protocol&quot;">RFC3744</a>],
+   including principal namespaces, and with the addition of the CALDAV:
+   calendar-home-set property, there are a couple options for CalDAV
+   clients to find one's own calendar or another user's calendar.
+
+   In this case, a DAV:principal-match REPORT is used to find a named
+   property (the CALDAV:calendar-home-set) on the Principal-URL of the
+   current user.  Using this, a WebDAV client can learn "who am I" and
+   "where are my calendars".  The REPORT request body looks like this:
+
+   &lt;?xml version="1.0" encoding="utf-8" ?&gt;
+   &lt;D:principal-match xmlns:D="DAV:"&gt;
+     &lt;D:self/&gt;
+     &lt;D:prop&gt;
+       &lt;C:calendar-home-set
+          xmlns:C="urn:ietf:params:xml:ns:caldav"/&gt;
+     &lt;/D:prop&gt;
+   &lt;/D:principal-match&gt;
+
+   To find other users' calendars, the DAV:principal-property-search
+   REPORT can be used to filter on some properties and return others.
+   To search for a calendar owned by a user named "Laurie", the REPORT
+   request body would look like this:
+
+
+
+
+
+
+
+
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                    [Page 73]</span>
+</pre><pre class="newpage"><a name="page-74" id="page-74" href="#page-74" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+   &lt;?xml version="1.0" encoding="utf-8" ?&gt;
+   &lt;D:principal-property-search xmlns:D="DAV:"&gt;
+     &lt;D:property-search&gt;
+       &lt;D:prop&gt;
+         &lt;D:displayname/&gt;
+       &lt;/D:prop&gt;
+       &lt;D:match&gt;Laurie&lt;/D:match&gt;
+     &lt;/D:property-search&gt;
+     &lt;D:prop&gt;
+       &lt;C:calendar-home-set
+          xmlns:C="urn:ietf:params:xml:ns:caldav"/&gt;
+       &lt;D:displayname/&gt;
+     &lt;/D:prop&gt;
+   &lt;/D:principal-property-search&gt;
+
+   The server performs a case-sensitive or caseless search for a
+   matching string subset of "Laurie" within the DAV:displayname
+   property.  Thus, the server might return "Laurie Dusseault", "Laurier
+   Desruisseaux", or "Wilfrid Laurier" as matching DAV:displayname
+   values, and return the calendars for each of these.
+
+<span class="h3"><h3><a name="section-8.5">8.5</a>.  Storing and Using Attachments</h3></span>
+
+   CalDAV clients MAY create attachments in calendar components either
+   as inline or external.  This section contains some guidelines for
+   creating and managing attachments.
+
+<span class="h4"><h4><a name="section-8.5.1">8.5.1</a>.  Inline Attachments</h4></span>
+
+   CalDAV clients MUST support inline attachments as specified in
+   iCalendar [<a href="http://tools.ietf.org/html/rfc2445" title="&quot;Internet Calendaring and Scheduling Core Object Specification (iCalendar)&quot;">RFC2445</a>].  CalDAV servers MUST support inline attachments,
+   so clients can rely on being able to create attachments this way.  On
+   the other hand, inline attachments have some drawbacks:
+
+   o  Servers MAY impose limitations on the size of calendar object
+      resources (i.e., refusing PUT requests of very large iCalendar
+      objects).  Servers that impose such limitations MUST use the
+      CALDAV:max-resource-size property on a calendar collection to
+      inform the client as to what the limitation is (see
+      <a href="#section-5.2.5">Section 5.2.5</a>).
+
+   o  Servers MAY impose storage quota limitations on calendar
+      collections (See [<a href="http://tools.ietf.org/html/rfc4331" title="&quot;Quota and Size Properties for Distributed Authoring and Versioning (DAV) Collections&quot;">RFC4331</a>]).
+
+   o  Any change to a calendar object resource containing an inline
+      attachment requires the entire inline attachment to be re-
+      uploaded.
+
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                    [Page 74]</span>
+</pre><pre class="newpage"><a name="page-75" id="page-75" href="#page-75" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+   o  Clients synchronizing a changed calendar object resource have to
+      download the entire calendar object resource, even if the
+      attachment is unchanged.
+
+<span class="h4"><h4><a name="section-8.5.2">8.5.2</a>.  External Attachments</h4></span>
+
+   CalDAV clients SHOULD support downloading of external attachments
+   referenced by arbitrary URI schemes, by either processing them
+   directly, or by passing the attachment URI to a suitable "helper
+   application" for processing, if such an application exists.  CalDAV
+   clients MUST support downloading of external attachments referenced
+   by the "http" or "https" URI schemes.  An external attachment could
+   be:
+
+   o  In a collection in the calendar collection containing the calendar
+      object resource;
+
+   o  Somewhere else in the same repository that hosts the calendar
+      collection; or
+
+   o  On an HTTP or FTP server elsewhere.
+
+   CalDAV servers MAY provide support for child collections in calendar
+   collections.  CalDAV servers MAY allow the MKCOL method to create
+   child collections in calendar collections.  Child collections of
+   calendar collections MAY contain any type of resource except calendar
+   collections that they MUST NOT contain.  Some CalDAV servers won't
+   allow child collections in calendar collections, and it may be
+   possible on such a server to discover other locations where
+   attachments can be stored.
+
+   Clients are entirely responsible for maintaining reference
+   consistency with calendar components that link to external
+   attachments.  A client deleting a calendar component with an external
+   attachment might therefore also delete the attachment if that's
+   appropriate; however, appropriateness can be very hard to determine.
+   A new component might easily reference some pre-existing Web resource
+   that is intended to have independent existence from the calendar
+   component (the "attachment" could be a major proposal to be discussed
+   in a meeting, for instance).  Best practices will probably emerge and
+   should probably be documented, but for now, clients should be wary of
+   engaging in aggressive "cleanup" of external attachments.  A client
+   could involve the user in making decisions about removing
+   unreferenced documents, or a client could be conservative in only
+   deleting attachments it had created.
+
+   Also, clients are responsible for consistency of permissions when
+   using external attachments.  One reason for servers to support the
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                    [Page 75]</span>
+</pre><pre class="newpage"><a name="page-76" id="page-76" href="#page-76" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+   storage of attachments within child collections of calendar
+   collections is that ACL inheritance might make it easier to grant the
+   same permissions to attachments that are granted on the calendar
+   collection.  Otherwise, it can be very difficult to keep permissions
+   synchronized.  With attachments stored on separate repositories, it
+   can be impossible to keep permissions consistent -- the two
+   repositories may not support the same permissions or have the same
+   set of principals.  Some systems have used tickets or other anonymous
+   access control mechanisms to provide partially satisfactory solutions
+   to these kinds of problems.
+
+<span class="h3"><h3><a name="section-8.6">8.6</a>.  Storing and Using Alarms</h3></span>
+
+   Note that all CalDAV calendar collections (including those the user
+   might treat as public or group calendars) can contain alarm
+   information on events and to-dos.  Users can synchronize a calendar
+   between multiple devices and decide to have alarms execute on a
+   different device than the device that created the alarm.  Not all
+   alarm action types are completely interoperable (e.g., those that
+   name a sound file to play).
+
+      When the action is AUDIO and the client is configured to execute
+      the alarm, the client SHOULD play the suggested sound if it's
+      available or play another sound, but SHOULD NOT rewrite the alarm
+      just to replace the suggested sound with a sound that's locally
+      available.
+
+      When the action is DISPLAY and the client is configured to execute
+      the alarm, the client SHOULD execute a display alarm by displaying
+      according to the suggested description or some reasonable
+      replacement, but SHOULD NOT rewrite the alarm for its own
+      convenience.
+
+      When the action is EMAIL and the client is incapable of sending
+      email, it SHOULD ignore the alarm, but it MUST continue to
+      synchronize the alarm itself.
+
+      This specification makes no recommendations about executing alarms
+      of type PROCEDURE, except to note that clients are advised to take
+      care to avoid creating security holes by executing these.
+
+   Non-interoperable alarm information (e.g., should somebody define a
+   color to be used in a display alarm) should be put in non-standard
+   properties inside the VALARM component in order to keep the basic
+   alarm usable on all devices.
+
+   Clients that allow changes to calendar object resources MUST
+   synchronize the alarm data that already exists in the resources.
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                    [Page 76]</span>
+</pre><pre class="newpage"><a name="page-77" id="page-77" href="#page-77" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+   Clients MAY execute alarms that are downloaded in this fashion,
+   possibly based on user preference.  If a client is only doing read
+   operations on a calendar and there is no risk of losing alarm
+   information, then the client MAY discard alarm information.
+
+   This specification makes no attempt to provide multi-user alarms on
+   group calendars or to find out for whom an alarm is intended.
+   Addressing those issues might require extensions to iCalendar; for
+   example, to store alarms per-user, or to indicate for which user a
+   VALARM was intended.  In the meantime, clients might maximize
+   interoperability by generally not uploading alarm information to
+   public, group, or resource calendars.
+
+<span class="h2"><h2><a name="section-9">9</a>.  XML Element Definitions</h2></span>
+
+<span class="h3"><h3><a name="section-9.1">9.1</a>.  CALDAV:calendar XML Element</h3></span>
+
+   Name:  calendar
+
+   Namespace:  urn:ietf:params:xml:ns:caldav
+
+   Purpose:  Specifies the resource type of a calendar collection.
+
+   Description:  See <a href="#section-4.2">Section 4.2</a>.
+
+   Definition:
+
+         &lt;!ELEMENT calendar EMPTY&gt;
+
+<span class="h3"><h3><a name="section-9.2">9.2</a>.  CALDAV:mkcalendar XML Element</h3></span>
+
+   Name:  mkcalendar
+
+   Namespace:  urn:ietf:params:xml:ns:caldav
+
+   Purpose:  Specifies a request that includes the WebDAV property
+      values to be set for a calendar collection resource when it is
+      created.
+
+   Description:  See <a href="#section-5.3.1">Section 5.3.1</a>.
+
+   Definition:
+
+         &lt;!ELEMENT mkcalendar (DAV:set)&gt;
+
+
+
+
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                    [Page 77]</span>
+</pre><pre class="newpage"><a name="page-78" id="page-78" href="#page-78" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+<span class="h3"><h3><a name="section-9.3">9.3</a>.  CALDAV:mkcalendar-response XML Element</h3></span>
+
+   Name:  mkcalendar-response
+
+   Namespace:  urn:ietf:params:xml:ns:caldav
+
+   Purpose:  Specifies a response body for a successful MKCALENDAR
+      request.
+
+   Description:  See <a href="#section-5.3.1">Section 5.3.1</a>.
+
+   Definition:
+
+         &lt;!ELEMENT mkcalendar-response ANY&gt;
+
+<span class="h3"><h3><a name="section-9.4">9.4</a>.  CALDAV:supported-collation XML Element</h3></span>
+
+   Name:  supported-collation
+
+   Namespace:  urn:ietf:params:xml:ns:caldav
+
+   Purpose:  Identifies a single collation via its collation identifier,
+      as defined by [<a href="http://tools.ietf.org/html/rfc4790" title="&quot;Internet Application Protocol Collation Registry&quot;">RFC4790</a>].
+
+   Description:  The CALDAV:supported-collation contains the text of a
+      collation identifier, as described in <a href="#section-7.5.1">Section 7.5.1</a>.
+
+   Definition:
+
+         &lt;!ELEMENT supported-collation (#PCDATA)&gt;
+         PCDATA value: collation identifier
+
+<span class="h3"><h3><a name="section-9.5">9.5</a>.  CALDAV:calendar-query XML Element</h3></span>
+
+   Name:  calendar-query
+
+   Namespace:  urn:ietf:params:xml:ns:caldav
+
+   Purpose:  Defines a report for querying calendar object resources.
+
+   Description:  See <a href="#section-7.8">Section 7.8</a>.
+
+   Definition:
+
+         &lt;!ELEMENT calendar-query ((DAV:allprop |
+                                    DAV:propname |
+                                    DAV:prop)?, filter, timezone?)&gt;
+
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                    [Page 78]</span>
+</pre><pre class="newpage"><a name="page-79" id="page-79" href="#page-79" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+<span class="h3"><h3><a name="section-9.6">9.6</a>.  CALDAV:calendar-data XML Element</h3></span>
+
+   Name:  calendar-data
+
+   Namespace:  urn:ietf:params:xml:ns:caldav
+
+   Purpose:  Specified one of the following:
+
+      1.  A supported media type for calendar object resources when
+          nested in the CALDAV:supported-calendar-data property;
+
+      2.  The parts of a calendar object resource should be returned by
+          a calendaring report;
+
+      3.  The content of a calendar object resource in a response to a
+          calendaring report.
+
+   Description:  When nested in the CALDAV:supported-calendar-data
+      property, the CALDAV:calendar-data XML element specifies a media
+      type supported by the CalDAV server for calendar object resources.
+
+      When used in a calendaring REPORT request, the CALDAV:calendar-
+      data XML element specifies which parts of calendar object
+      resources need to be returned in the response.  If the CALDAV:
+      calendar-data XML element doesn't contain any CALDAV:comp element,
+      calendar object resources will be returned in their entirety.
+
+      Finally, when used in a calendaring REPORT response, the CALDAV:
+      calendar-data XML element specifies the content of a calendar
+      object resource.  Given that XML parsers normalize the two-
+      character sequence CRLF (US-ASCII decimal 13 and US-ASCII decimal
+      10) to a single LF character (US-ASCII decimal 10), the CR
+      character (US-ASCII decimal 13) MAY be omitted in calendar object
+      resources specified in the CALDAV:calendar-data XML element.
+      Furthermore, calendar object resources specified in the CALDAV:
+      calendar-data XML element MAY be invalid per their media type
+      specification if the CALDAV:calendar-data XML element part of the
+      calendaring REPORT request did not specify required properties
+      (e.g., UID, DTSTAMP, etc.), or specified a CALDAV:prop XML element
+      with the "novalue" attribute set to "yes".
+
+   Note:  The CALDAV:calendar-data XML element is specified in requests
+      and responses inside the DAV:prop XML element as if it were a
+      WebDAV property.  However, the CALDAV:calendar-data XML element is
+      not a WebDAV property and, as such, is not returned in PROPFIND
+      responses, nor used in PROPPATCH requests.
+
+
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                    [Page 79]</span>
+</pre><pre class="newpage"><a name="page-80" id="page-80" href="#page-80" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+   Note:  The iCalendar data embedded within the CALDAV:calendar-data
+      XML element MUST follow the standard XML character data encoding
+      rules, including use of &amp;lt;, &amp;gt;, &amp;amp; etc. entity encoding or
+      the use of a &lt;![CDATA[ ... ]]&gt; construct.  In the later case, the
+      iCalendar data cannot contain the character sequence "]]&gt;", which
+      is the end delimiter for the CDATA section.
+
+   Definition:
+
+         &lt;!ELEMENT calendar-data EMPTY&gt;
+
+         when nested in the CALDAV:supported-calendar-data property
+         to specify a supported media type for calendar object
+         resources;
+
+         &lt;!ELEMENT calendar-data (comp?,
+                                  (expand | limit-recurrence-set)?,
+                                  limit-freebusy-set?)&gt;
+
+         when nested in the DAV:prop XML element in a calendaring
+         REPORT request to specify which parts of calendar object
+         resources should be returned in the response;
+
+         &lt;!ELEMENT calendar-data (#PCDATA)&gt;
+         PCDATA value: iCalendar object
+
+         when nested in the DAV:prop XML element in a calendaring
+         REPORT response to specify the content of a returned
+         calendar object resource.
+
+         &lt;!ATTLIST calendar-data content-type CDATA "text/calendar"
+                                 version CDATA "2.0"&gt;
+         content-type value: a MIME media type
+         version value: a version string
+
+         attributes can be used on all three variants of the
+         CALDAV:calendar-data XML element.
+
+<span class="h4"><h4><a name="section-9.6.1">9.6.1</a>.  CALDAV:comp XML Element</h4></span>
+
+   Name:  comp
+
+   Namespace:  urn:ietf:params:xml:ns:caldav
+
+   Purpose:  Defines which component types to return.
+
+
+
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                    [Page 80]</span>
+</pre><pre class="newpage"><a name="page-81" id="page-81" href="#page-81" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+   Description:  The name value is a calendar component name (e.g.,
+      VEVENT).
+
+   Definition:
+
+         &lt;!ELEMENT comp ((allprop | prop*), (allcomp | comp*))&gt;
+
+         &lt;!ATTLIST comp name CDATA #REQUIRED&gt;
+         name value: a calendar component name
+
+   Note:  The CALDAV:prop and CALDAV:allprop elements have the same name
+      as the DAV:prop and DAV:allprop elements defined in [<a href="http://tools.ietf.org/html/rfc2518" title="&quot;HTTP Extensions for Distributed Authoring -- WEBDAV&quot;">RFC2518</a>].
+      However, the CALDAV:prop and CALDAV:allprop elements are defined
+      in the "urn:ietf:params:xml:ns:caldav" namespace instead of the
+      "DAV:" namespace.
+
+<span class="h4"><h4><a name="section-9.6.2">9.6.2</a>.  CALDAV:allcomp XML Element</h4></span>
+
+   Name:  allcomp
+
+   Namespace:  urn:ietf:params:xml:ns:caldav
+
+   Purpose:  Specifies that all components shall be returned.
+
+   Description:  The CALDAV:allcomp XML element can be used when the
+      client wants all types of components returned by a calendaring
+      REPORT request.
+
+   Definition:
+
+         &lt;!ELEMENT allcomp EMPTY&gt;
+
+<span class="h4"><h4><a name="section-9.6.3">9.6.3</a>.  CALDAV:allprop XML Element</h4></span>
+
+   Name:  allprop
+
+   Namespace:  urn:ietf:params:xml:ns:caldav
+
+   Purpose:  Specifies that all properties shall be returned.
+
+   Description:  The CALDAV:allprop XML element can be used when the
+      client wants all properties of components returned by a
+      calendaring REPORT request.
+
+   Definition:
+
+         &lt;!ELEMENT allprop EMPTY&gt;
+
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                    [Page 81]</span>
+</pre><pre class="newpage"><a name="page-82" id="page-82" href="#page-82" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+   Note:  The CALDAV:allprop element has the same name as the DAV:
+      allprop element defined in [<a href="http://tools.ietf.org/html/rfc2518" title="&quot;HTTP Extensions for Distributed Authoring -- WEBDAV&quot;">RFC2518</a>].  However, the CALDAV:allprop
+      element is defined in the "urn:ietf:params:xml:ns:caldav"
+      namespace instead of the "DAV:" namespace.
+
+<span class="h4"><h4><a name="section-9.6.4">9.6.4</a>.  CALDAV:prop XML Element</h4></span>
+
+   Name:  prop
+
+   Namespace:  urn:ietf:params:xml:ns:caldav
+
+   Purpose:  Defines which properties to return in the response.
+
+   Description:  The "name" attribute specifies the name of the calendar
+      property to return (e.g., ATTENDEE).  The "novalue" attribute can
+      be used by clients to request that the actual value of the
+      property not be returned (if the "novalue" attribute is set to
+      "yes").  In that case, the server will return just the iCalendar
+      property name and any iCalendar parameters and a trailing ":"
+      without the subsequent value data.
+
+   Definition:
+
+         &lt;!ELEMENT prop EMPTY&gt;
+
+         &lt;!ATTLIST prop name CDATA #REQUIRED
+                        novalue (yes | no) "no"&gt;
+         name value: a calendar property name
+         novalue value: "yes" or "no"
+
+   Note:  The CALDAV:prop element has the same name as the DAV:prop
+      element defined in [<a href="http://tools.ietf.org/html/rfc2518" title="&quot;HTTP Extensions for Distributed Authoring -- WEBDAV&quot;">RFC2518</a>].  However, the CALDAV:prop element is
+      defined in the "urn:ietf:params:xml:ns:caldav" namespace instead
+      of the "DAV:" namespace.
+
+<span class="h4"><h4><a name="section-9.6.5">9.6.5</a>.  CALDAV:expand XML Element</h4></span>
+
+   Name:  expand
+
+   Namespace:  urn:ietf:params:xml:ns:caldav
+
+   Purpose:  Forces the server to expand recurring components into
+      individual recurrence instances.
+
+   Description:  The CALDAV:expand XML element specifies that for a
+      given calendaring REPORT request, the server MUST expand the
+      recurrence set into calendar components that define exactly one
+
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                    [Page 82]</span>
+</pre><pre class="newpage"><a name="page-83" id="page-83" href="#page-83" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+      recurrence instance, and MUST return only those whose scheduled
+      time intersect a specified time range.
+
+      The "start" attribute specifies the inclusive start of the time
+      range, and the "end" attribute specifies the non-inclusive end of
+      the time range.  Both attributes are specified as date with UTC
+      time value.  The value of the "end" attribute MUST be greater than
+      the value of the "start" attribute.
+
+      The server MUST use the same logic as defined for CALDAV:time-
+      range to determine if a recurrence instance intersects the
+      specified time range.
+
+      Recurring components, other than the initial instance, MUST
+      include a RECURRENCE-ID property indicating which instance they
+      refer to.
+
+      The returned calendar components MUST NOT use recurrence
+      properties (i.e., EXDATE, EXRULE, RDATE, and RRULE) and MUST NOT
+      have reference to or include VTIMEZONE components.  Date and local
+      time with reference to time zone information MUST be converted
+      into date with UTC time.
+
+   Definition:
+
+         &lt;!ELEMENT expand EMPTY&gt;
+
+         &lt;!ATTLIST expand start CDATA #REQUIRED
+                          end   CDATA #REQUIRED&gt;
+         start value: an iCalendar "date with UTC time"
+         end value: an iCalendar "date with UTC time"
+
+<span class="h4"><h4><a name="section-9.6.6">9.6.6</a>.  CALDAV:limit-recurrence-set XML Element</h4></span>
+
+   Name:  limit-recurrence-set
+
+   Namespace:  urn:ietf:params:xml:ns:caldav
+
+   Purpose:  Specifies a time range to limit the set of "overridden
+      components" returned by the server.
+
+   Description:  The CALDAV:limit-recurrence-set XML element specifies
+      that for a given calendaring REPORT request, the server MUST
+      return, in addition to the "master component", only the
+      "overridden components" that impact a specified time range.  An
+      overridden component impacts a time range if its current start and
+      end times overlap the time range, or if the original start and end
+
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                    [Page 83]</span>
+</pre><pre class="newpage"><a name="page-84" id="page-84" href="#page-84" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+      times -- the ones that would have been used if the instance were
+      not overridden -- overlap the time range.
+
+      The "start" attribute specifies the inclusive start of the time
+      range, and the "end" attribute specifies the non-inclusive end of
+      the time range.  Both attributes are specified as date with UTC
+      time value.  The value of the "end" attribute MUST be greater than
+      the value of the "start" attribute.
+
+      The server MUST use the same logic as defined for CALDAV:time-
+      range to determine if the current or original scheduled time of an
+      "overridden" recurrence instance intersects the specified time
+      range.
+
+      Overridden components that have a RANGE parameter on their
+      RECURRENCE-ID property may specify one or more instances in the
+      recurrence set, and some of those instances may fall within the
+      specified time range or may have originally fallen within the
+      specified time range prior to being overridden.  If that is the
+      case, the overridden component MUST be included in the results, as
+      it has a direct impact on the interpretation of instances within
+      the specified time range.
+
+   Definition:
+
+         &lt;!ELEMENT limit-recurrence-set EMPTY&gt;
+
+         &lt;!ATTLIST limit-recurrence-set start CDATA #REQUIRED
+                                        end   CDATA #REQUIRED&gt;
+         start value: an iCalendar "date with UTC time"
+         end value: an iCalendar "date with UTC time"
+
+<span class="h4"><h4><a name="section-9.6.7">9.6.7</a>.  CALDAV:limit-freebusy-set XML Element</h4></span>
+
+   Name:  limit-freebusy-set
+
+   Namespace:  urn:ietf:params:xml:ns:caldav
+
+   Purpose:  Specifies a time range to limit the set of FREEBUSY values
+      returned by the server.
+
+   Description:  The CALDAV:limit-freebusy-set XML element specifies
+      that for a given calendaring REPORT request, the server MUST only
+      return the FREEBUSY property values of a VFREEBUSY component that
+      intersects a specified time range.
+
+      The "start" attribute specifies the inclusive start of the time
+      range, and the "end" attribute specifies the non-inclusive end of
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                    [Page 84]</span>
+</pre><pre class="newpage"><a name="page-85" id="page-85" href="#page-85" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+      the time range.  Both attributes are specified as "date with UTC
+      time" value.  The value of the "end" attribute MUST be greater
+      than the value of the "start" attribute.
+
+      The server MUST use the same logic as defined for CALDAV:time-
+      range to determine if a FREEBUSY property value intersects the
+      specified time range.
+
+   Definition:
+
+         &lt;!ELEMENT limit-freebusy-set EMPTY&gt;
+
+         &lt;!ATTLIST limit-freebusy-set start CDATA #REQUIRED
+                                      end   CDATA #REQUIRED&gt;
+         start value: an iCalendar "date with UTC time"
+         end value: an iCalendar "date with UTC time"
+
+<span class="h3"><h3><a name="section-9.7">9.7</a>.  CALDAV:filter XML Element</h3></span>
+
+   Name:  filter
+
+   Namespace:  urn:ietf:params:xml:ns:caldav
+
+   Purpose:  Specifies a filter to limit the set of calendar components
+      returned by the server.
+
+   Description:  The CALDAV:filter XML element specifies the search
+      filter used to limit the calendar components returned by a
+      calendaring REPORT request.
+
+   Definition:
+
+         &lt;!ELEMENT filter (comp-filter)&gt;
+
+<span class="h4"><h4><a name="section-9.7.1">9.7.1</a>.  CALDAV:comp-filter XML Element</h4></span>
+
+   Name:  comp-filter
+
+   Namespace:  urn:ietf:params:xml:ns:caldav
+
+   Purpose:  Specifies search criteria on calendar components.
+
+   Description:  The CALDAV:comp-filter XML element specifies a query
+      targeted at the calendar object (i.e., VCALENDAR) or at a specific
+      calendar component type (e.g., VEVENT).  The scope of the
+      CALDAV:comp-filter XML element is the calendar object when used as
+      a child of the CALDAV:filter XML element.  The scope of the
+      CALDAV:comp-filter XML element is the enclosing calendar component
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                    [Page 85]</span>
+</pre><pre class="newpage"><a name="page-86" id="page-86" href="#page-86" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+      when used as a child of another CALDAV:comp-filter XML element.  A
+      CALDAV:comp-filter is said to match if:
+
+      *  The CALDAV:comp-filter XML element is empty and the calendar
+         object or calendar component type specified by the "name"
+         attribute exists in the current scope;
+
+      or:
+
+      *  The CALDAV:comp-filter XML element contains a CALDAV:is-not-
+         defined XML element and the calendar object or calendar
+         component type specified by the "name" attribute does not exist
+         in the current scope;
+
+      or:
+
+      *  The CALDAV:comp-filter XML element contains a CALDAV:time-range
+         XML element and at least one recurrence instance in the
+         targeted calendar component is scheduled to overlap the
+         specified time range, and all specified CALDAV:prop-filter and
+         CALDAV:comp-filter child XML elements also match the targeted
+         calendar component;
+
+      or:
+
+      *  The CALDAV:comp-filter XML element only contains CALDAV:prop-
+         filter and CALDAV:comp-filter child XML elements that all match
+         the targeted calendar component.
+
+   Definition:
+
+         &lt;!ELEMENT comp-filter (is-not-defined | (time-range?,
+                                prop-filter*, comp-filter*))&gt;
+
+         &lt;!ATTLIST comp-filter name CDATA #REQUIRED&gt;
+         name value: a calendar object or calendar component
+                     type (e.g., VEVENT)
+
+<span class="h4"><h4><a name="section-9.7.2">9.7.2</a>.  CALDAV:prop-filter XML Element</h4></span>
+
+   Name:  prop-filter
+
+   Namespace:  urn:ietf:params:xml:ns:caldav
+
+   Purpose:  Specifies search criteria on calendar properties.
+
+   Description:  The CALDAV:prop-filter XML element specifies a query
+      targeted at a specific calendar property (e.g., CATEGORIES) in the
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                    [Page 86]</span>
+</pre><pre class="newpage"><a name="page-87" id="page-87" href="#page-87" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+      scope of the enclosing calendar component.  A calendar property is
+      said to match a CALDAV:prop-filter if:
+
+      *  The CALDAV:prop-filter XML element is empty and a property of
+         the type specified by the "name" attribute exists in the
+         enclosing calendar component;
+
+      or:
+
+      *  The CALDAV:prop-filter XML element contains a CALDAV:is-not-
+         defined XML element and no property of the type specified by
+         the "name" attribute exists in the enclosing calendar
+         component;
+
+      or:
+
+      *  The CALDAV:prop-filter XML element contains a CALDAV:time-range
+         XML element and the property value overlaps the specified time
+         range, and all specified CALDAV:param-filter child XML elements
+         also match the targeted property;
+
+      or:
+
+      *  The CALDAV:prop-filter XML element contains a CALDAV:text-match
+         XML element and the property value matches it, and all
+         specified CALDAV:param-filter child XML elements also match the
+         targeted property;
+
+   Definition:
+
+         &lt;!ELEMENT prop-filter (is-not-defined |
+                                ((time-range | text-match)?,
+                                 param-filter*))&gt;
+
+         &lt;!ATTLIST prop-filter name CDATA #REQUIRED&gt;
+         name value: a calendar property name (e.g., ATTENDEE)
+
+<span class="h4"><h4><a name="section-9.7.3">9.7.3</a>.  CALDAV:param-filter XML Element</h4></span>
+
+   Name:  param-filter
+
+   Namespace:  urn:ietf:params:xml:ns:caldav
+
+   Purpose:  Limits the search to specific parameter values.
+
+   Description:  The CALDAV:param-filter XML element specifies a query
+      targeted at a specific calendar property parameter (e.g.,
+      PARTSTAT) in the scope of the calendar property on which it is
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                    [Page 87]</span>
+</pre><pre class="newpage"><a name="page-88" id="page-88" href="#page-88" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+      defined.  A calendar property parameter is said to match a CALDAV:
+      param-filter if:
+
+      *  The CALDAV:param-filter XML element is empty and a parameter of
+         the type specified by the "name" attribute exists on the
+         calendar property being examined;
+
+      or:
+
+      *  The CALDAV:param-filter XML element contains a CALDAV:is-not-
+         defined XML element and no parameter of the type specified by
+         the "name" attribute exists on the calendar property being
+         examined;
+
+   Definition:
+
+         &lt;!ELEMENT param-filter (is-not-defined | text-match?)&gt;
+
+         &lt;!ATTLIST param-filter name CDATA #REQUIRED&gt;
+         name value: a property parameter name (e.g., PARTSTAT)
+
+<span class="h4"><h4><a name="section-9.7.4">9.7.4</a>.  CALDAV:is-not-defined XML Element</h4></span>
+
+   Name:  is-not-defined
+
+   Namespace:  urn:ietf:params:xml:ns:caldav
+
+   Purpose:  Specifies that a match should occur if the enclosing
+      component, property, or parameter does not exist.
+
+   Description:  The CALDAV:is-not-defined XML element specifies that a
+      match occurs if the enclosing component, property, or parameter
+      value specified in a calendaring REPORT request does not exist in
+      the calendar data being tested.
+
+   Definition:
+
+         &lt;!ELEMENT is-not-defined EMPTY&gt;
+
+<span class="h4"><h4><a name="section-9.7.5">9.7.5</a>.  CALDAV:text-match XML Element</h4></span>
+
+   Name:  text-match
+
+   Namespace:  urn:ietf:params:xml:ns:caldav
+
+   Purpose:  Specifies a substring match on a property or parameter
+      value.
+
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                    [Page 88]</span>
+</pre><pre class="newpage"><a name="page-89" id="page-89" href="#page-89" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+   Description:  The CALDAV:text-match XML element specifies text used
+      for a substring match against the property or parameter value
+      specified in a calendaring REPORT request.
+
+      The "collation" attribute is used to select the collation that the
+      server MUST use for character string matching.  In the absence of
+      this attribute, the server MUST use the "i;ascii-casemap"
+      collation.
+
+      The "negate-condition" attribute is used to indicate that this
+      test returns a match if the text matches when the attribute value
+      is set to "no", or return a match if the text does not match, if
+      the attribute value is set to "yes".  For example, this can be
+      used to match components with a STATUS property not set to
+      CANCELLED.
+
+   Definition:
+
+         &lt;!ELEMENT text-match (#PCDATA)&gt;
+         PCDATA value: string
+
+         &lt;!ATTLIST text-match collation        CDATA "i;ascii-casemap"
+                              negate-condition (yes | no) "no"&gt;
+
+<span class="h3"><h3><a name="section-9.8">9.8</a>.  CALDAV:timezone XML Element</h3></span>
+
+   Name:  timezone
+
+   Namespace:  urn:ietf:params:xml:ns:caldav
+
+   Purpose:  Specifies the time zone component to use when determining
+      the results of a report.
+
+   Description:  The CALDAV:timezone XML element specifies that for a
+      given calendaring REPORT request, the server MUST rely on the
+      specified VTIMEZONE component instead of the CALDAV:calendar-
+      timezone property of the calendar collection, in which the
+      calendar object resource is contained to resolve "date" values and
+      "date with local time" values (i.e., floating time) to "date with
+      UTC time" values.  The server will require this information to
+      determine if a calendar component scheduled with "date" values or
+      "date with local time" values intersects a CALDAV:time-range
+      specified in a CALDAV:calendar-query REPORT.
+
+   Note:  The iCalendar data embedded within the CALDAV:timezone XML
+      element MUST follow the standard XML character data encoding
+      rules, including use of &amp;lt;, &amp;gt;, &amp;amp; etc. entity encoding or
+      the use of a &lt;![CDATA[ ... ]]&gt; construct.  In the later case, the
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                    [Page 89]</span>
+</pre><pre class="newpage"><a name="page-90" id="page-90" href="#page-90" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+      iCalendar data cannot contain the character sequence "]]&gt;", which
+      is the end delimiter for the CDATA section.
+
+   Definition:
+
+         &lt;!ELEMENT timezone (#PCDATA)&gt;
+         PCDATA value: an iCalendar object with exactly one VTIMEZONE
+
+<span class="h3"><h3><a name="section-9.9">9.9</a>.  CALDAV:time-range XML Element</h3></span>
+
+   Name:  time-range
+
+   Namespace:  urn:ietf:params:xml:ns:caldav
+
+   Purpose:  Specifies a time range to limit the set of calendar
+      components returned by the server.
+
+   Description:  The CALDAV:time-range XML element specifies that for a
+      given calendaring REPORT request, the server MUST only return the
+      calendar object resources that, depending on the context, have a
+      component or property whose value intersects a specified time
+      range.
+
+      The "start" attribute specifies the inclusive start of the time
+      range, and the "end" attribute specifies the non-inclusive end of
+      the time range.  Both attributes MUST be specified as "date with
+      UTC time" value.  Time ranges open at one end can be specified by
+      including only one attribute; however, at least one attribute MUST
+      always be present in the CALDAV:time-range element.  If either the
+      "start" or "end" attribute is not specified in the CALDAV:time-
+      range XML element, assume "-infinity" and "+infinity" as their
+      value, respectively.  If both "start" and "end" are present, the
+      value of the "end" attribute MUST be greater than the value of the
+      "start" attribute.
+
+      Time range tests MUST consider every recurrence instance when
+      testing the time range condition; if any one instance matches,
+      then the test returns true.  Testing recurrence instances requires
+      the server to infer an effective value for DTSTART, DTEND,
+      DURATION, and DUE properties for an instance based on the
+      recurrence patterns and any overrides.
+
+      A VEVENT component overlaps a given time range if the condition
+      for the corresponding component state specified in the table below
+      is satisfied.  Note that, as specified in [<a href="http://tools.ietf.org/html/rfc2445" title="&quot;Internet Calendaring and Scheduling Core Object Specification (iCalendar)&quot;">RFC2445</a>], the DTSTART
+      property is REQUIRED in the VEVENT component.  The conditions
+      depend on the presence of the DTEND and DURATION properties in the
+      VEVENT component.  Furthermore, the value of the DTEND property
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                    [Page 90]</span>
+</pre><pre class="newpage"><a name="page-91" id="page-91" href="#page-91" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+      MUST be later in time than the value of the DTSTART property.  The
+      duration of a VEVENT component with no DTEND and DURATION
+      properties is 1 day (+P1D) when the DTSTART is a DATE value, and 0
+      seconds when the DTSTART is a DATE-TIME value.
+
+      +---------------------------------------------------------------+
+      | VEVENT has the DTEND property?                                |
+      |   +-----------------------------------------------------------+
+      |   | VEVENT has the DURATION property?                         |
+      |   |   +-------------------------------------------------------+
+      |   |   | DURATION property value is greater than 0 seconds?    |
+      |   |   |   +---------------------------------------------------+
+      |   |   |   | DTSTART property is a DATE-TIME value?            |
+      |   |   |   |   +-----------------------------------------------+
+      |   |   |   |   | Condition to evaluate                         |
+      +---+---+---+---+-----------------------------------------------+
+      | Y | N | N | * | (start &lt;  DTEND AND end &gt; DTSTART)            |
+      +---+---+---+---+-----------------------------------------------+
+      | N | Y | Y | * | (start &lt;  DTSTART+DURATION AND end &gt; DTSTART) |
+      |   |   +---+---+-----------------------------------------------+
+      |   |   | N | * | (start &lt;= DTSTART AND end &gt; DTSTART)          |
+      +---+---+---+---+-----------------------------------------------+
+      | N | N | N | Y | (start &lt;= DTSTART AND end &gt; DTSTART)          |
+      +---+---+---+---+-----------------------------------------------+
+      | N | N | N | N | (start &lt;  DTSTART+P1D AND end &gt; DTSTART)      |
+      +---+---+---+---+-----------------------------------------------+
+
+      A VTODO component is said to overlap a given time range if the
+      condition for the corresponding component state specified in the
+      table below is satisfied.  The conditions depend on the presence
+      of the DTSTART, DURATION, DUE, COMPLETED, and CREATED properties
+      in the VTODO component.  Note that, as specified in [<a href="http://tools.ietf.org/html/rfc2445" title="&quot;Internet Calendaring and Scheduling Core Object Specification (iCalendar)&quot;">RFC2445</a>], the
+      DUE value MUST be a DATE-TIME value equal to or after the DTSTART
+      value if specified.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                    [Page 91]</span>
+</pre><pre class="newpage"><a name="page-92" id="page-92" href="#page-92" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+   +-------------------------------------------------------------------+
+   | VTODO has the DTSTART property?                                   |
+   |   +---------------------------------------------------------------+
+   |   |   VTODO has the DURATION property?                            |
+   |   |   +-----------------------------------------------------------+
+   |   |   | VTODO has the DUE property?                               |
+   |   |   |   +-------------------------------------------------------+
+   |   |   |   | VTODO has the COMPLETED property?                     |
+   |   |   |   |   +---------------------------------------------------+
+   |   |   |   |   | VTODO has the CREATED property?                   |
+   |   |   |   |   |   +-----------------------------------------------+
+   |   |   |   |   |   | Condition to evaluate                         |
+   +---+---+---+---+---+-----------------------------------------------+
+   | Y | Y | N | * | * | (start  &lt;= DTSTART+DURATION)  AND             |
+   |   |   |   |   |   | ((end   &gt;  DTSTART)  OR                       |
+   |   |   |   |   |   |  (end   &gt;= DTSTART+DURATION))                 |
+   +---+---+---+---+---+-----------------------------------------------+
+   | Y | N | Y | * | * | ((start &lt;  DUE)      OR  (start &lt;= DTSTART))  |
+   |   |   |   |   |   | AND                                           |
+   |   |   |   |   |   | ((end   &gt;  DTSTART)  OR  (end   &gt;= DUE))      |
+   +---+---+---+---+---+-----------------------------------------------+
+   | Y | N | N | * | * | (start  &lt;= DTSTART)  AND (end &gt;  DTSTART)     |
+   +---+---+---+---+---+-----------------------------------------------+
+   | N | N | Y | * | * | (start  &lt;  DUE)      AND (end &gt;= DUE)         |
+   +---+---+---+---+---+-----------------------------------------------+
+   | N | N | N | Y | Y | ((start &lt;= CREATED)  OR  (start &lt;= COMPLETED))|
+   |   |   |   |   |   | AND                                           |
+   |   |   |   |   |   | ((end   &gt;= CREATED)  OR  (end   &gt;= COMPLETED))|
+   +---+---+---+---+---+-----------------------------------------------+
+   | N | N | N | Y | N | (start  &lt;= COMPLETED) AND (end  &gt;= COMPLETED) |
+   +---+---+---+---+---+-----------------------------------------------+
+   | N | N | N | N | Y | (end    &gt;  CREATED)                           |
+   +---+---+---+---+---+-----------------------------------------------+
+   | N | N | N | N | N | TRUE                                          |
+   +---+---+---+---+---+-----------------------------------------------+
+
+      A VJOURNAL component overlaps a given time range if the condition
+      for the corresponding component state specified in the table below
+      is satisfied.  The conditions depend on the presence of the
+      DTSTART property in the VJOURNAL component and on whether the
+      DTSTART is a DATE-TIME or DATE value.  The effective "duration" of
+      a VJOURNAL component is 1 day (+P1D) when the DTSTART is a DATE
+      value, and 0 seconds when the DTSTART is a DATE-TIME value.
+
+
+
+
+
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                    [Page 92]</span>
+</pre><pre class="newpage"><a name="page-93" id="page-93" href="#page-93" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+      +----------------------------------------------------+
+      | VJOURNAL has the DTSTART property?                 |
+      |   +------------------------------------------------+
+      |   | DTSTART property is a DATE-TIME value?         |
+      |   |   +--------------------------------------------+
+      |   |   | Condition to evaluate                      |
+      +---+---+--------------------------------------------+
+      | Y | Y | (start &lt;= DTSTART)     AND (end &gt; DTSTART) |
+      +---+---+--------------------------------------------+
+      | Y | N | (start &lt;  DTSTART+P1D) AND (end &gt; DTSTART) |
+      +---+---+--------------------------------------------+
+      | N | * | FALSE                                      |
+      +---+---+--------------------------------------------+
+
+      A VFREEBUSY component overlaps a given time range if the condition
+      for the corresponding component state specified in the table below
+      is satisfied.  The conditions depend on the presence in the
+      VFREEBUSY component of the DTSTART and DTEND properties, and any
+      FREEBUSY properties in the absence of DTSTART and DTEND.  Any
+      DURATION property is ignored, as it has a special meaning when
+      used in a VFREEBUSY component.
+
+      When only FREEBUSY properties are used, each period in each
+      FREEBUSY property is compared against the time range, irrespective
+      of the type of free busy information (free, busy, busy-tentative,
+      busy-unavailable) represented by the property.
+
+
+      +------------------------------------------------------+
+      | VFREEBUSY has both the DTSTART and DTEND properties? |
+      |   +--------------------------------------------------+
+      |   | VFREEBUSY has the FREEBUSY property?             |
+      |   |   +----------------------------------------------+
+      |   |   | Condition to evaluate                        |
+      +---+---+----------------------------------------------+
+      | Y | * | (start &lt;= DTEND) AND (end &gt; DTSTART)         |
+      +---+---+----------------------------------------------+
+      | N | Y | (start &lt;  freebusy-period-end) AND           |
+      |   |   | (end   &gt;  freebusy-period-start)             |
+      +---+---+----------------------------------------------+
+      | N | N | FALSE                                        |
+      +---+---+----------------------------------------------+
+
+      A VALARM component is said to overlap a given time range if the
+      following condition holds:
+
+         (start &lt;= trigger-time) AND (end &gt; trigger-time)
+
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                    [Page 93]</span>
+</pre><pre class="newpage"><a name="page-94" id="page-94" href="#page-94" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+   A VALARM component can be defined such that it triggers repeatedly.
+   Such a VALARM component is said to overlap a given time range if at
+   least one of its triggers overlaps the time range.
+
+      The calendar properties COMPLETED, CREATED, DTEND, DTSTAMP,
+      DTSTART, DUE, and LAST-MODIFIED overlap a given time range if the
+      following condition holds:
+
+          (start &lt;= date-time) AND (end &gt; date-time)
+
+   Note that if DTEND is not present in a VEVENT, but DURATION is, then
+   the test should instead operate on the 'effective' DTEND, i.e.,
+   DTSTART+DURATION.  Similarly, if DUE is not present in a VTODO, but
+   DTSTART and DURATION are, then the test should instead operate on the
+   'effective' DUE, i.e., DTSTART+DURATION.
+
+      The semantic of CALDAV:time-range is not defined for any other
+      calendar components and properties.
+
+   Definition:
+
+         &lt;!ELEMENT time-range EMPTY&gt;
+
+         &lt;!ATTLIST time-range start CDATA #IMPLIED
+                              end   CDATA #IMPLIED&gt;
+         start value: an iCalendar "date with UTC time"
+         end value: an iCalendar "date with UTC time"
+
+<span class="h3"><h3><a name="section-9.10">9.10</a>.  CALDAV:calendar-multiget XML Element</h3></span>
+
+   Name:  calendar-multiget
+
+   Namespace:  urn:ietf:params:xml:ns:caldav
+
+   Purpose:  CalDAV report used to retrieve specific calendar object
+      resources.
+
+   Description:  See <a href="#section-7.9">Section 7.9</a>.
+
+   Definition:
+
+         &lt;!ELEMENT calendar-multiget ((DAV:allprop |
+                                      DAV:propname |
+                                      DAV:prop)?, DAV:href+)&gt;
+
+
+
+
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                    [Page 94]</span>
+</pre><pre class="newpage"><a name="page-95" id="page-95" href="#page-95" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+<span class="h3"><h3><a name="section-9.11">9.11</a>.  CALDAV:free-busy-query XML Element</h3></span>
+
+   Name:  free-busy-query
+
+   Namespace:  urn:ietf:params:xml:ns:caldav
+
+   Purpose:  CalDAV report used to generate a VFREEBUSY to determine
+      busy time over a specific time range.
+
+   Description:  See <a href="#section-7.10">Section 7.10</a>.
+
+   Definition:
+
+         &lt;!ELEMENT free-busy-query (time-range)&gt;
+
+<span class="h2"><h2><a name="section-10">10</a>.  Internationalization Considerations</h2></span>
+
+   CalDAV allows internationalized strings to be stored and retrieved
+   for the description of calendar collections (see <a href="#section-5.2.1">Section 5.2.1</a>).
+
+   The CALDAV:calendar-query REPORT (<a href="#section-7.8">Section 7.8</a>) includes a text
+   searching option controlled by the CALDAV:text-match element, and
+   details of character handling are covered in the description of that
+   element (see <a href="#section-9.7.5">Section 9.7.5</a>).
+
+<span class="h2"><h2><a name="section-11">11</a>.  Security Considerations</h2></span>
+
+   HTTP protocol transactions are sent in the clear over the network
+   unless protection from snooping is negotiated.  This can be
+   accomplished by use of TLS, as defined in [<a href="http://tools.ietf.org/html/rfc2818" title="&quot;HTTP Over TLS&quot;">RFC2818</a>].  In particular,
+   HTTP Basic authentication MUST NOT be used unless TLS is in effect.
+
+   Servers MUST take adequate precautions to ensure that malicious
+   clients cannot consume excessive server resources (CPU, memory, disk,
+   etc.) through carefully crafted reports.  For example, a client could
+   upload an event with a recurrence rule that specifies a recurring
+   event occurring every second for the next 100 years, which would
+   result in approximately 3 x 10^9 instances!  A report that asks for
+   recurrences to be expanded over that range would likely constitute a
+   denial-of-service attack on the server.
+
+   When creating new resources (including calendar collections), clients
+   MUST ensure that the resource name (the last path segment of the
+   resource URI) assigned to the new resource does not expose any data
+   from within the iCalendar resource itself or information about the
+   nature of a calendar collection.  This is required to ensure that the
+   presence of a specific iCalendar component or nature of components in
+   a collection cannot be inferred based on the name of a resource.
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                    [Page 95]</span>
+</pre><pre class="newpage"><a name="page-96" id="page-96" href="#page-96" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+   When rolling up free-busy information, more information about a
+   user's events is exposed if busy periods overlap or are adjacent
+   (this tells the client requesting the free-busy information that the
+   calendar owner has at least two events, rather than knowing only that
+   the calendar owner has one or more events during the busy period).
+   Thus, a conservative approach to calendar data privacy would have
+   servers always coalesce such busy periods when they are the same
+   type.
+
+   Procedure alarms are a known security risk for either clients or
+   servers to handle, particularly when the alarm was created by another
+   agent.  Clients and servers are not required to execute such
+   procedure alarms.
+
+   Security considerations described in iCalendar [<a href="http://tools.ietf.org/html/rfc2445" title="&quot;Internet Calendaring and Scheduling Core Object Specification (iCalendar)&quot;">RFC2445</a>] and iTIP
+   [<a href="http://tools.ietf.org/html/rfc2446" title="&quot;iCalendar Transport-Independent Interoperability Protocol (iTIP) Scheduling Events, BusyTime, To-dos and Journal Entries&quot;">RFC2446</a>] are also applicable to CalDAV.
+
+   Beyond these, CalDAV does not raise any security considerations that
+   are not present in HTTP [<a href="http://tools.ietf.org/html/rfc2616" title="&quot;Hypertext Transfer Protocol -- HTTP/1.1&quot;">RFC2616</a>] and WebDAV [<a href="http://tools.ietf.org/html/rfc2518" title="&quot;HTTP Extensions for Distributed Authoring -- WEBDAV&quot;">RFC2518</a>], [<a href="http://tools.ietf.org/html/rfc3253" title="&quot;Versioning Extensions to WebDAV (Web Distributed Authoring and Versioning)&quot;">RFC3253</a>],
+   [<a href="http://tools.ietf.org/html/rfc3744" title="&quot;Web Distributed Authoring and Versioning (WebDAV) Access Control Protocol&quot;">RFC3744</a>].
+
+<span class="h2"><h2><a name="section-12">12</a>.  IANA Considerations</h2></span>
+
+   This document uses one new URN to identify a new XML namespace.  The
+   URN conforms to a registry mechanism described in [<a href="http://tools.ietf.org/html/rfc3688" title="&quot;The IETF XML Registry&quot;">RFC3688</a>].
+
+<span class="h3"><h3><a name="section-12.1">12.1</a>.  Namespace Registration</h3></span>
+
+   Registration request for the CalDAV namespace:
+
+   URI: urn:ietf:params:xml:ns:caldav
+
+   Registrant Contact: See the "Authors' Addresses" section of this
+   document.
+
+   XML: None.  Namespace URIs do not represent an XML specification.
+
+<span class="h2"><h2><a name="section-13">13</a>.  Acknowledgements</h2></span>
+
+   The authors would like to thank the following individuals for
+   contributing their ideas and support for writing this specification:
+   Michael Arick, Mario Bonin, Chris Bryant, Scott Carr, Andre
+   Courtemanche, Mike Douglass, Ted Hardie, Marten den Haring, Jeffrey
+   Harris, Sam Hartman, Helge Hess, Jeff McCullough, Alexey Melnikov,
+   Dan Mosedale, Brian Moseley, Francois Perrault, Kervin L. Pierre,
+   Julian F. Reschke, Wilfredo Sanchez Vega, Mike Shaver, Jari
+   Urpalainen, Simon Vaillancourt, and Jim Whitehead.
+
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                    [Page 96]</span>
+</pre><pre class="newpage"><a name="page-97" id="page-97" href="#page-97" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+   The authors would also like to thank the Calendaring and Scheduling
+   Consortium for advice with this specification, and for organizing
+   interoperability testing events to help refine it.
+
+<span class="h2"><h2><a name="section-14">14</a>.  References</h2></span>
+
+<span class="h3"><h3><a name="section-14.1">14.1</a>.  Normative References</h3></span>
+
+   [<a name="ref-RFC2119" id="ref-RFC2119">RFC2119</a>]               Bradner, S., "Key words for use in RFCs to
+                           Indicate Requirement Levels", <a href="http://tools.ietf.org/html/bcp14">BCP 14</a>,
+                           <a href="http://tools.ietf.org/html/rfc2119">RFC 2119</a>, March 1997.
+
+   [<a name="ref-RFC2246" id="ref-RFC2246">RFC2246</a>]               Dierks, T. and C. Allen, "The TLS Protocol
+                           Version 1.0", <a href="http://tools.ietf.org/html/rfc2246">RFC 2246</a>, January 1999.
+
+   [<a name="ref-RFC2445" id="ref-RFC2445">RFC2445</a>]               Dawson, F. and Stenerson, D., "Internet
+                           Calendaring and Scheduling Core Object
+                           Specification (iCalendar)", <a href="http://tools.ietf.org/html/rfc2445">RFC 2445</a>,
+                           November 1998.
+
+   [<a name="ref-RFC2446" id="ref-RFC2446">RFC2446</a>]               Silverberg, S., Mansour, S., Dawson, F., and
+                           R. Hopson, "iCalendar Transport-Independent
+                           Interoperability Protocol (iTIP) Scheduling
+                           Events, BusyTime, To-dos and Journal
+                           Entries", <a href="http://tools.ietf.org/html/rfc2446">RFC 2446</a>, November 1998.
+
+   [<a name="ref-RFC2518" id="ref-RFC2518">RFC2518</a>]               Goland, Y., Whitehead, E., Faizi, A., Carter,
+                           S., and D. Jensen, "HTTP Extensions for
+                           Distributed Authoring -- WEBDAV", <a href="http://tools.ietf.org/html/rfc2518">RFC 2518</a>,
+                           February 1999.
+
+   [<a name="ref-RFC2616" id="ref-RFC2616">RFC2616</a>]               Fielding, R., Gettys, J., Mogul, J., Frystyk,
+                           H., Masinter, L., Leach, P., and T. Berners-
+                           Lee, "Hypertext Transfer Protocol --
+                           HTTP/1.1", <a href="http://tools.ietf.org/html/rfc2616">RFC 2616</a>, June 1999.
+
+   [<a name="ref-RFC2818" id="ref-RFC2818">RFC2818</a>]               Rescorla, E., "HTTP Over TLS", <a href="http://tools.ietf.org/html/rfc2818">RFC 2818</a>,
+                           May 2000.
+
+   [<a name="ref-RFC3253" id="ref-RFC3253">RFC3253</a>]               Clemm, G., Amsden, J., Ellison, T., Kaler,
+                           C., and J. Whitehead, "Versioning Extensions
+                           to WebDAV (Web Distributed Authoring and
+                           Versioning)", <a href="http://tools.ietf.org/html/rfc3253">RFC 3253</a>, March 2002.
+
+   [<a name="ref-RFC3688" id="ref-RFC3688">RFC3688</a>]               Mealling, M., "The IETF XML Registry",
+                           <a href="http://tools.ietf.org/html/bcp81">BCP 81</a>, <a href="http://tools.ietf.org/html/rfc3688">RFC 3688</a>, January 2004.
+
+
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                    [Page 97]</span>
+</pre><pre class="newpage"><a name="page-98" id="page-98" href="#page-98" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+   [<a name="ref-RFC3744" id="ref-RFC3744">RFC3744</a>]               Clemm, G., Reschke, J., Sedlar, E., and J.
+                           Whitehead, "Web Distributed Authoring and
+                           Versioning (WebDAV) Access Control Protocol",
+                           <a href="http://tools.ietf.org/html/rfc3744">RFC 3744</a>, May 2004.
+
+   [<a name="ref-RFC4346" id="ref-RFC4346">RFC4346</a>]               Dierks, T. and E. Rescorla, "The Transport
+                           Layer Security (TLS) Protocol Version 1.1",
+                           <a href="http://tools.ietf.org/html/rfc4346">RFC 4346</a>, April 2006.
+
+   [<a name="ref-RFC4790" id="ref-RFC4790">RFC4790</a>]               Newman, C., Duerst, M., and A. Gulbrandsen,
+                           "Internet Application Protocol Collation
+                           Registry", <a href="http://tools.ietf.org/html/rfc4790">RFC 4790</a>, March 2007.
+
+   [<a name="ref-W3C.REC-xml-20060816" id="ref-W3C.REC-xml-20060816">W3C.REC-xml-20060816</a>]  Paoli, J., Maler, E., Yergeau, F., Sperberg-
+                           McQueen, C., and T. Bray, "Extensible Markup
+                           Language (XML) 1.0 (Fourth Edition)", World
+                           Wide Web Consortium Recommendation REC-xml-
+                           20060816, August 2006,
+                           &lt;<a href="http://www.w3.org/TR/2006/REC-xml-20060816">http://www.w3.org/TR/2006/REC-xml-20060816</a>&gt;.
+
+<span class="h3"><h3><a name="section-14.2">14.2</a>.  Informative References</h3></span>
+
+   [<a name="ref-RFC2426" id="ref-RFC2426">RFC2426</a>]               Dawson, F. and T. Howes, "vCard MIME
+                           Directory Profile", <a href="http://tools.ietf.org/html/rfc2426">RFC 2426</a>, September 1998.
+
+   [<a name="ref-RFC2739" id="ref-RFC2739">RFC2739</a>]               Small, T., Hennessy, D., and F. Dawson,
+                           "Calendar Attributes for vCard and LDAP",
+                           <a href="http://tools.ietf.org/html/rfc2739">RFC 2739</a>, January 2000.
+
+   [<a name="ref-RFC4331" id="ref-RFC4331">RFC4331</a>]               Korver, B. and L. Dusseault, "Quota and Size
+                           Properties for Distributed Authoring and
+                           Versioning (DAV) Collections", <a href="http://tools.ietf.org/html/rfc4331">RFC 4331</a>,
+                           February 2006.
+
+   [<a name="ref-RFC4511" id="ref-RFC4511">RFC4511</a>]               Sermersheim, J., "Lightweight Directory
+                           Access Protocol (LDAP): The Protocol",
+                           <a href="http://tools.ietf.org/html/rfc4511">RFC 4511</a>, June 2006.
+
+   [<a name="ref-rfc2518bis" id="ref-rfc2518bis">rfc2518bis</a>]            Dusseault, L., <a style="text-decoration: none;" href="http://www.google.com/search?sitesearch=tools.ietf.org%2Fhtml%2F&amp;q=inurl:draft-+%22HTTP+Extensions+for%0A+++++++++++++++++++++++++++Distributed+Authoring+-+WebDAV%22">"HTTP Extensions for
+                           Distributed Authoring - WebDAV"</a>, Work
+                           in Progress, December 2006.
+
+
+
+
+
+
+
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                    [Page 98]</span>
+</pre><pre class="newpage"><a name="page-99" id="page-99" href="#page-99" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+<span class="h2"><h2><a name="appendix-A">Appendix A</a>.  CalDAV Method Privilege Table (Normative)</h2></span>
+
+   The following table extends the WebDAV Method Privilege Table
+   specified in <a href="#appendix-B">Appendix B</a> of [<a href="http://tools.ietf.org/html/rfc3744" title="&quot;Web Distributed Authoring and Versioning (WebDAV) Access Control Protocol&quot;">RFC3744</a>].
+
+   +------------+------------------------------------------------------+
+   | METHOD     | PRIVILEGES                                           |
+   +------------+------------------------------------------------------+
+   | MKCALENDAR | DAV:bind                                             |
+   | REPORT     | DAV:read or CALDAV:read-free-busy (on all referenced |
+   |            | resources)                                           |
+   +------------+------------------------------------------------------+
+
+<span class="h2"><h2><a name="appendix-B">Appendix B</a>.  Calendar Collections Used in the Examples</h2></span>
+
+   This appendix shows the calendar object resources contained in the
+   calendar collection queried in the examples throughout this document.
+
+   The content of the calendar collection is being shown as if it were
+   returned by a CALDAV:calendar-query REPORT request designed to return
+   all the calendar data in the collection:
+
+   &gt;&gt; Request &lt;&lt;
+
+   REPORT /bernard/work/ HTTP/1.1
+   Host: cal.example.com
+   Depth: 1
+   Content-Type: application/xml; charset="utf-8"
+   Content-Length: xxxx
+
+   &lt;?xml version="1.0" encoding="utf-8" ?&gt;
+   &lt;C:calendar-query xmlns:D="DAV:"
+                    xmlns:C="urn:ietf:params:xml:ns:caldav"&gt;
+    &lt;D:prop&gt;
+      &lt;D:getetag/&gt;
+      &lt;C:calendar-data/&gt;
+    &lt;/D:prop&gt;
+    &lt;C:filter&gt;
+      &lt;C:comp-filter name="VCALENDAR"/&gt;
+    &lt;/C:filter&gt;
+   &lt;/C:calendar-query&gt;
+
+   &gt;&gt; Response &lt;&lt;
+
+   HTTP/1.1 207 Multi-Status
+   Content-Type: application/xml; charset="utf-8"
+   Content-Length: xxxx
+
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                    [Page 99]</span>
+</pre><pre class="newpage"><a name="page-100" id="page-100" href="#page-100" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+   &lt;?xml version="1.0" encoding="utf-8" ?&gt;
+   &lt;D:multistatus xmlns:D="DAV:"
+                 xmlns:C="urn:ietf:params:xml:ns:caldav"&gt;
+
+     &lt;D:response&gt;
+       &lt;D:href&gt;http://cal.example.com/bernard/work/abcd1.ics&lt;/D:href&gt;
+       &lt;D:propstat&gt;
+         &lt;D:prop&gt;
+           &lt;D:getetag&gt;"fffff-abcd1"&lt;/D:getetag&gt;
+           &lt;C:calendar-data&gt;BEGIN:VCALENDAR
+   VERSION:2.0
+   PRODID:-//Example Corp.//CalDAV Client//EN
+   BEGIN:VTIMEZONE
+   LAST-MODIFIED:20040110T032845Z
+   TZID:US/Eastern
+   BEGIN:DAYLIGHT
+   DTSTART:20000404T020000
+   RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4
+   TZNAME:EDT
+   TZOFFSETFROM:-0500
+   TZOFFSETTO:-0400
+   END:DAYLIGHT
+   BEGIN:STANDARD
+   DTSTART:20001026T020000
+   RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
+   TZNAME:EST
+   TZOFFSETFROM:-0400
+   TZOFFSETTO:-0500
+   END:STANDARD
+   END:VTIMEZONE
+   BEGIN:VEVENT
+   DTSTAMP:20060206T001102Z
+   DTSTART;TZID=US/Eastern:20060102T100000
+   DURATION:PT1H
+   SUMMARY:Event #1
+   Description:Go Steelers!
+   UID:74855313FA803DA593CD579A@example.com
+   END:VEVENT
+   END:VCALENDAR
+   &lt;/C:calendar-data&gt;
+         &lt;/D:prop&gt;
+         &lt;D:status&gt;HTTP/1.1 200 OK&lt;/D:status&gt;
+       &lt;/D:propstat&gt;
+     &lt;/D:response&gt;
+
+     &lt;D:response&gt;
+       &lt;D:href&gt;http://cal.example.com/bernard/work/abcd2.ics&lt;/D:href&gt;
+       &lt;D:propstat&gt;
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                   [Page 100]</span>
+</pre><pre class="newpage"><a name="page-101" id="page-101" href="#page-101" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+         &lt;D:prop&gt;
+           &lt;D:getetag&gt;"fffff-abcd2"&lt;/D:getetag&gt;
+           &lt;C:calendar-data&gt;BEGIN:VCALENDAR
+   VERSION:2.0
+   PRODID:-//Example Corp.//CalDAV Client//EN
+   BEGIN:VTIMEZONE
+   LAST-MODIFIED:20040110T032845Z
+   TZID:US/Eastern
+   BEGIN:DAYLIGHT
+   DTSTART:20000404T020000
+   RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4
+   TZNAME:EDT
+   TZOFFSETFROM:-0500
+   TZOFFSETTO:-0400
+   END:DAYLIGHT
+   BEGIN:STANDARD
+   DTSTART:20001026T020000
+   RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
+   TZNAME:EST
+   TZOFFSETFROM:-0400
+   TZOFFSETTO:-0500
+   END:STANDARD
+   END:VTIMEZONE
+   BEGIN:VEVENT
+   DTSTAMP:20060206T001121Z
+   DTSTART;TZID=US/Eastern:20060102T120000
+   DURATION:PT1H
+   RRULE:FREQ=DAILY;COUNT=5
+   SUMMARY:Event #2
+   UID:00959BC664CA650E933C892C@example.com
+   END:VEVENT
+   BEGIN:VEVENT
+   DTSTAMP:20060206T001121Z
+   DTSTART;TZID=US/Eastern:20060104T140000
+   DURATION:PT1H
+   RECURRENCE-ID;TZID=US/Eastern:20060104T120000
+   SUMMARY:Event #2 bis
+   UID:00959BC664CA650E933C892C@example.com
+   END:VEVENT
+   END:VCALENDAR
+   &lt;/C:calendar-data&gt;
+         &lt;/D:prop&gt;
+         &lt;D:status&gt;HTTP/1.1 200 OK&lt;/D:status&gt;
+       &lt;/D:propstat&gt;
+     &lt;/D:response&gt;
+
+     &lt;D:response&gt;
+       &lt;D:href&gt;http://cal.example.com/bernard/work/abcd3.ics&lt;/D:href&gt;
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                   [Page 101]</span>
+</pre><pre class="newpage"><a name="page-102" id="page-102" href="#page-102" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+       &lt;D:propstat&gt;
+         &lt;D:prop&gt;
+           &lt;D:getetag&gt;"fffff-abcd3"&lt;/D:getetag&gt;
+           &lt;C:calendar-data&gt;BEGIN:VCALENDAR
+   VERSION:2.0
+   PRODID:-//Example Corp.//CalDAV Client//EN
+   BEGIN:VTIMEZONE
+   LAST-MODIFIED:20040110T032845Z
+   TZID:US/Eastern
+   BEGIN:DAYLIGHT
+   DTSTART:20000404T020000
+   RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4
+   TZNAME:EDT
+   TZOFFSETFROM:-0500
+   TZOFFSETTO:-0400
+   END:DAYLIGHT
+   BEGIN:STANDARD
+   DTSTART:20001026T020000
+   RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
+   TZNAME:EST
+   TZOFFSETFROM:-0400
+   TZOFFSETTO:-0500
+   END:STANDARD
+   END:VTIMEZONE
+   BEGIN:VEVENT
+   ATTENDEE;PARTSTAT=ACCEPTED;ROLE=CHAIR:mailto:cyrus@example.com
+   ATTENDEE;PARTSTAT=NEEDS-ACTION:mailto:lisa@example.com
+   DTSTAMP:20060206T001220Z
+   DTSTART;TZID=US/Eastern:20060104T100000
+   DURATION:PT1H
+   LAST-MODIFIED:20060206T001330Z
+   ORGANIZER:mailto:cyrus@example.com
+   SEQUENCE:1
+   STATUS:TENTATIVE
+   SUMMARY:Event #3
+   UID:DC6C50A017428C5216A2F1CD@example.com
+   END:VEVENT
+   END:VCALENDAR
+   &lt;/C:calendar-data&gt;
+         &lt;/D:prop&gt;
+         &lt;D:status&gt;HTTP/1.1 200 OK&lt;/D:status&gt;
+       &lt;/D:propstat&gt;
+     &lt;/D:response&gt;
+
+     &lt;D:response&gt;
+       &lt;D:href&gt;http://cal.example.com/bernard/work/abcd4.ics&lt;/D:href&gt;
+       &lt;D:propstat&gt;
+         &lt;D:prop&gt;
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                   [Page 102]</span>
+</pre><pre class="newpage"><a name="page-103" id="page-103" href="#page-103" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+           &lt;D:getetag&gt;"fffff-abcd4"&lt;/D:getetag&gt;
+           &lt;C:calendar-data&gt;BEGIN:VCALENDAR
+   VERSION:2.0
+   PRODID:-//Example Corp.//CalDAV Client//EN
+   BEGIN:VTODO
+   DTSTAMP:20060205T235335Z
+   DUE;VALUE=DATE:20060104
+   STATUS:NEEDS-ACTION
+   SUMMARY:Task #1
+   UID:DDDEEB7915FA61233B861457@example.com
+   BEGIN:VALARM
+   ACTION:AUDIO
+   TRIGGER;RELATED=START:-PT10M
+   END:VALARM
+   END:VTODO
+   END:VCALENDAR
+   &lt;/C:calendar-data&gt;
+         &lt;/D:prop&gt;
+         &lt;D:status&gt;HTTP/1.1 200 OK&lt;/D:status&gt;
+       &lt;/D:propstat&gt;
+     &lt;/D:response&gt;
+
+     &lt;D:response&gt;
+       &lt;D:href&gt;http://cal.example.com/bernard/work/abcd5.ics&lt;/D:href&gt;
+       &lt;D:propstat&gt;
+         &lt;D:prop&gt;
+           &lt;D:getetag&gt;"fffff-abcd5"&lt;/D:getetag&gt;
+           &lt;C:calendar-data&gt;BEGIN:VCALENDAR
+   VERSION:2.0
+   PRODID:-//Example Corp.//CalDAV Client//EN
+   BEGIN:VTODO
+   DTSTAMP:20060205T235300Z
+   DUE;VALUE=DATE:20060106
+   LAST-MODIFIED:20060205T235308Z
+   SEQUENCE:1
+   STATUS:NEEDS-ACTION
+   SUMMARY:Task #2
+   UID:E10BA47467C5C69BB74E8720@example.com
+   BEGIN:VALARM
+   ACTION:AUDIO
+   TRIGGER;RELATED=START:-PT10M
+   END:VALARM
+   END:VTODO
+   END:VCALENDAR
+   &lt;/C:calendar-data&gt;
+         &lt;/D:prop&gt;
+         &lt;D:status&gt;HTTP/1.1 200 OK&lt;/D:status&gt;
+       &lt;/D:propstat&gt;
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                   [Page 103]</span>
+</pre><pre class="newpage"><a name="page-104" id="page-104" href="#page-104" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+     &lt;/D:response&gt;
+
+     &lt;D:response&gt;
+       &lt;D:href&gt;http://cal.example.com/bernard/work/abcd6.ics&lt;/D:href&gt;
+       &lt;D:propstat&gt;
+         &lt;D:prop&gt;
+           &lt;D:getetag&gt;"fffff-abcd6"&lt;/D:getetag&gt;
+           &lt;C:calendar-data&gt;BEGIN:VCALENDAR
+   VERSION:2.0
+   PRODID:-//Example Corp.//CalDAV Client//EN
+   BEGIN:VTODO
+   COMPLETED:20051223T122322Z
+   DTSTAMP:20060205T235400Z
+   DUE;VALUE=DATE:20051225
+   LAST-MODIFIED:20060205T235308Z
+   SEQUENCE:1
+   STATUS:COMPLETED
+   SUMMARY:Task #3
+   UID:E10BA47467C5C69BB74E8722@example.com
+   END:VTODO
+   END:VCALENDAR
+   &lt;/C:calendar-data&gt;
+         &lt;/D:prop&gt;
+         &lt;D:status&gt;HTTP/1.1 200 OK&lt;/D:status&gt;
+       &lt;/D:propstat&gt;
+     &lt;/D:response&gt;
+
+     &lt;D:response&gt;
+       &lt;D:href&gt;http://cal.example.com/bernard/work/abcd7.ics&lt;/D:href&gt;
+       &lt;D:propstat&gt;
+         &lt;D:prop&gt;
+           &lt;D:getetag&gt;"fffff-abcd7"&lt;/D:getetag&gt;
+           &lt;C:calendar-data&gt;BEGIN:VCALENDAR
+   VERSION:2.0
+   PRODID:-//Example Corp.//CalDAV Client//EN
+   BEGIN:VTODO
+   DTSTAMP:20060205T235600Z
+   DUE;VALUE=DATE:20060101
+   LAST-MODIFIED:20060205T235308Z
+   SEQUENCE:1
+   STATUS:CANCELLED
+   SUMMARY:Task #4
+   UID:E10BA47467C5C69BB74E8725@example.com
+   END:VTODO
+   END:VCALENDAR
+   &lt;/C:calendar-data&gt;
+         &lt;/D:prop&gt;
+         &lt;D:status&gt;HTTP/1.1 200 OK&lt;/D:status&gt;
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                   [Page 104]</span>
+</pre><pre class="newpage"><a name="page-105" id="page-105" href="#page-105" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+       &lt;/D:propstat&gt;
+     &lt;/D:response&gt;
+
+     &lt;D:response&gt;
+       &lt;D:href&gt;http://cal.example.com/bernard/work/abcd8.ics&lt;/D:href&gt;
+       &lt;D:propstat&gt;
+         &lt;D:prop&gt;
+           &lt;D:getetag&gt;"fffff-abcd8"&lt;/D:getetag&gt;
+           &lt;C:calendar-data&gt;BEGIN:VCALENDAR
+   VERSION:2.0
+   PRODID:-//Example Corp.//CalDAV Client//EN
+   BEGIN:VFREEBUSY
+   ORGANIZER;CN="Bernard Desruisseaux":mailto:bernard@example.com
+   UID:76ef34-54a3d2@example.com
+   DTSTAMP:20050530T123421Z
+   DTSTART:20060101T000000Z
+   DTEND:20060108T000000Z
+   FREEBUSY:20050531T230000Z/20050601T010000Z
+   FREEBUSY;FBTYPE=BUSY-TENTATIVE:20060102T100000Z/20060102T120000Z
+   FREEBUSY:20060103T100000Z/20060103T120000Z
+   FREEBUSY:20060104T100000Z/20060104T120000Z
+   FREEBUSY;FBTYPE=BUSY-UNAVAILABLE:20060105T100000Z/20060105T120000Z
+   FREEBUSY:20060106T100000Z/20060106T120000Z
+   END:VFREEBUSY
+   END:VCALENDAR
+   &lt;/C:calendar-data&gt;
+         &lt;/D:prop&gt;
+         &lt;D:status&gt;HTTP/1.1 200 OK&lt;/D:status&gt;
+       &lt;/D:propstat&gt;
+     &lt;/D:response&gt;
+
+   &lt;/D:multistatus&gt;
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                   [Page 105]</span>
+</pre><pre class="newpage"><a name="page-106" id="page-106" href="#page-106" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+Authors' Addresses
+
+   Cyrus Daboo
+   Apple Inc.
+   1 Infinite Loop
+   Cupertino, CA  95014
+   USA
+
+   EMail: cyrus@daboo.name
+   URI:   <a href="http://www.apple.com/">http://www.apple.com/</a>
+
+
+   Bernard Desruisseaux
+   Oracle Corporation
+   600 Blvd. de Maisonneuve West
+   Suite 1900
+   Montreal, QC  H3A 3J2
+   CANADA
+
+   EMail: bernard.desruisseaux@oracle.com
+   URI:   <a href="http://www.oracle.com/">http://www.oracle.com/</a>
+
+
+   Lisa Dusseault
+   CommerceNet
+   169 University Ave.
+   Palo Alto, CA  94301
+   USA
+
+   EMail: ldusseault@commerce.net
+   URI:   <a href="http://commerce.net/">http://commerce.net/</a>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+<span class="grey">Daboo, et al.               Standards Track                   [Page 106]</span>
+</pre><pre class="newpage"><a name="page-107" id="page-107" href="#page-107" class="invisible"> </a>
+<span class="grey"><a href="http://tools.ietf.org/html/rfc4791">RFC 4791</a>                         CalDAV                       March 2007</span>
+
+
+Full Copyright Statement
+
+   Copyright (C) The IETF Trust (2007).
+
+   This document is subject to the rights, licenses and restrictions
+   contained in <a href="http://tools.ietf.org/html/bcp78">BCP 78</a>, and except as set forth therein, the authors
+   retain all their rights.
+
+   This document and the information contained herein are provided on an
+   "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS
+   OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY, THE IETF TRUST AND
+   THE INTERNET ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS
+   OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF
+   THE INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED
+   WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Intellectual Property
+
+   The IETF takes no position regarding the validity or scope of any
+   Intellectual Property Rights or other rights that might be claimed to
+   pertain to the implementation or use of the technology described in
+   this document or the extent to which any license under such rights
+   might or might not be available; nor does it represent that it has
+   made any independent effort to identify any such rights.  Information
+   on the procedures with respect to rights in RFC documents can be
+   found in <a href="http://tools.ietf.org/html/bcp78">BCP 78</a> and <a href="http://tools.ietf.org/html/bcp79">BCP 79</a>.
+
+   Copies of IPR disclosures made to the IETF Secretariat and any
+   assurances of licenses to be made available, or the result of an
+   attempt made to obtain a general license or permission for the use of
+   such proprietary rights by implementers or users of this
+   specification can be obtained from the IETF on-line IPR repository at
+   <a href="http://www.ietf.org/ipr">http://www.ietf.org/ipr</a>.
+
+   The IETF invites any interested party to bring to its attention any
+   copyrights, patents or patent applications, or other proprietary
+   rights that may cover technology that may be required to implement
+   this standard.  Please address the information to the IETF at
+   ietf-ipr@ietf.org.
+
+Acknowledgement
+
+   Funding for the RFC Editor function is currently provided by the
+   Internet Society.
+
+
+
+
+
+
+
+Daboo, et al.               Standards Track                   [Page 107]
+</pre><pre class="newpage">
+</pre><br>
+<span class="noprint"><small><small>Html markup produced by rfcmarkup 1.74, available from
+<a href="http://tools.ietf.org/tools/rfcmarkup/">http://tools.ietf.org/tools/rfcmarkup/</a>
+</small></small></span>
+</body></html>
\ No newline at end of file
diff --git a/caldav/rruleparser.class.php b/caldav/rruleparser.class.php
new file mode 100644 (file)
index 0000000..1eadf49
--- /dev/null
@@ -0,0 +1,468 @@
+<?php
+/* $Id$ */
+
+    require_once 'caldavresource.class.php';
+
+    define ('FREQUENCE', 'HOURLY,DAILY,WEEKLY,MONTHLY,YEARLY');
+
+    /**
+     * Unsupported: SECONDLY, MINUTELY, YEARLY,
+     * BYSECOND, BYMINUTE, BYSETPOS, WKST
+     * Also preceeding where preceeding is allowed is not
+     * supported either.
+     */
+    class RRuleParser {
+
+        private $rules;
+        private $start;
+        private $end;
+        private $range_start;
+        private $range_end;
+        private $freq;
+
+        function __construct($rrule = NULL, $start = NULL, $end = NULL) {
+            $this->freq = preg_split("/[\s,]+/", FREQUENCE);
+            if ($rrule)
+                $this->setRule($rrule, $start, $end);
+        }
+
+        function setRule($rrule, $start, $end) {
+            if (!($start && $end))
+                throw new Exception("Missing values for DTSTART and/or DTEND");
+            //print "$start:$end<br/>";
+            //print var_export($rrule, TRUE) . "<br/>";
+            $this->start = CaldavRessource::iCal2Timestamp($start);
+            $this->end = CaldavRessource::iCal2Timestamp($end);
+            //print CaldavRessource::timestamp2ICal($this->start, TRUE).":".CaldavRessource::timestamp2ICal($this->end, TRUE)."<br/>";
+            $rules = explode(';', $rrule);
+            //print_r($rules);
+            if (count($rules) < 2) {
+                foreach ($rules as $rule) {
+                    $pair = explode('=', $rule);
+                    if (count($pair) < 2 || !in_array($pair[1], $this->freq)) {
+                        $this->rules = array();
+                        throw new Exception("$rrule: Invalid RRULE");
+                    }
+                    $this->rules[strtolower($pair[0])] = explode(',', $pair[1]);
+                }
+            }
+            else {
+                foreach ($rules as $rule) {
+                    $pair = explode('=', $rule);
+                    $this->rules[strtolower($pair[0])] = explode(',', $pair[1]);
+                }
+            }
+            if ($this->rules['until'][0] && $this->rules['count'][0]) {
+                $this->rules = array();
+                throw new Exception("COUNT and UNTIL cannot be present at the same time");
+            }
+            if (!in_array($this->rules['freq'][0], $this->freq)) {
+                trigger_error(
+                    "[{$this->rules['freq'][0]}] Unsupported FREQ",
+                    E_USER_NOTICE);
+                $this->rules = array();
+            }
+        }
+
+        private function getEnd($freq) {
+            $count = $this->getCount();
+            $until = $this->getUntil();
+            //print "$until<br/>";
+            if ($count) {
+                $int = $this->getInterval();
+                $count = ($int) ? $int * $count : $count;
+                switch ($freq) {
+                    case 'HOURLY': $str = "+$count hour"; break;
+                    case 'DAILY': $str = "+$count day"; break;
+                    case 'WEEKLY': $str = "+$count week"; break;
+                    case 'MONTHLY': $str = "+$count month"; break;
+                    case 'YEARLY': $str = "+$count year"; break;
+                }
+                $end = strtotime($str, $this->start);
+                return ($this->range_end && $this->range_end < $end) ?
+                    $this->range_end : $end;
+            }
+            else if ($until) {
+                /*
+                 * UNTIL has first occurrence at end time and
+                 * last ocurrence ending at start time
+                 */
+                //print "$until:".$this->start."<br/>";
+                $u_s = explode("T", CaldavRessource::timestamp2ICal($this->end));
+                $time_s = (int) substr($u_s[1], 0, 2);
+                $u_e = explode("T", $until);
+                $time_e = (int) substr($u_e[1], 0, 2);
+                $until = ($time_s != $time_e) ? "{$u_e[0]}T{$u_s[1]}" : $until;
+                //print "$time_s:$time_e:$until<br/>";
+                $end = CaldavRessource::iCal2Timestamp($until);
+                //print CaldavRessource::timestamp2ICal($end)."<br/>";
+                return ($this->range_end && $this->range_end < $end) ?
+                    $this->range_end : $end;
+            }
+            else
+                return $this->range_end;
+        }
+
+        private function except($ts) {
+            $byhour = $this->getByHour();
+            if ($byhour) {
+                $res = TRUE;
+                $match = gmdate('G', $ts);
+                foreach ($byhour as $hour) {
+                    if ($match == $hour)
+                        $res = FALSE;
+                    if (! $res)
+                        return FALSE;
+                }
+                return TRUE;
+            }
+            $byday = $this->getByDay();
+            if ($byday) {
+                $res = TRUE;
+                $match = substr(strtolower(gmdate('D', $ts)), 0, 2);
+                foreach ($byday as $day) {
+                    //print "$match:$day\n";
+                    if ($match == strtolower($day))
+                        $res = FALSE;
+                    if (! $res)
+                        return FALSE;
+                }
+                return TRUE;
+            }
+            $bymonth = $this->getByMonth();
+            if ($bymonth) {
+                $res = TRUE;
+                $match = gmdate('n', $ts);
+                foreach ($bymonth as $month) {
+                    if ($match == $month)
+                        $res = FALSE;
+                    if (! $res)
+                        return FALSE;
+                }
+                return TRUE;
+            }
+            $bymonthday = $this->getByMonthDay();
+            if ($bymonthday) {
+                $res = TRUE;
+                $match = gmdate('j', $ts);
+                foreach ($bymonthday as $monthday) {
+                    if ($match + 1 == $monthday)
+                        $res = FALSE;
+                    if (! $res)
+                        return FALSE;
+                }
+                return TRUE;
+            }
+            $byweekno = $this->getByWeekNo();
+            if ($byweekno) {
+                $res = TRUE;
+                // Missing to handle Anglo week numbers
+                // (week start on Sunday)
+                $match = gmdate('W', $ts);
+                foreach ($byweekno as $weekno) {
+                    if ($match == $weekno)
+                        $res = FALSE;
+                    if (! $res)
+                        return FALSE;
+                }
+                return TRUE;
+            }
+            $byyearday = $this->getByYearDay();
+            if ($byyearday) {
+                $res = TRUE;
+                $match = gmdate('z', $ts);
+                foreach ($byyearday as $yearday) {
+                    if ($match == $yearday)
+                        $res = FALSE;
+                    if (! $res)
+                        return FALSE;
+                }
+                return TRUE;
+            }
+            return FALSE;
+        }
+
+        private function hourly() {
+            $res = array();
+            $end = $this->getEnd('HOURLY');
+            if (! $end) {
+                /**
+                 * we will maximum handle one month at a time unless
+                 * a specific end date specifies otherwise
+                 */
+                $end = strtotime('+1 month', $this->start);
+            }
+            $int = ($this->getInterval()) ? $this->getInterval() : 1;
+            $c = $this->start;
+            for (; $c < $end; $c = strtotime("+$int hour", $c)) {
+                if (! $this->except($c))
+                    array_push($res, CaldavRessource::timestamp2ICal($c));
+            }
+            return $res;
+        }
+
+        private function daily() {
+            $res = array();
+            $end = $this->getEnd('DAILY');
+            if (! $end) {
+                /**
+                 * we will maximum handle one month at a time unless
+                 * a specific end date specifies otherwise
+                 */
+                $end = strtotime('+1 month', $this->start);
+            }
+            $int = ($this->getInterval()) ? $this->getInterval() : 1;
+            $c = $this->start;
+            for (; $c < $end; $c = strtotime("+$int day", $c)) {
+                if (! $this->except($c))
+                    array_push($res, CaldavRessource::timestamp2ICal($c));
+            }
+            return $res;
+        }
+
+        private function weekly() {
+            $res = array();
+            $end = $this->getEnd('WEEKLY');
+            //print "start: ".CaldavRessource::timestamp2ICal($this->start)." end: ".CaldavRessource::timestamp2ICal($end)."\n";
+            if (! $end) {
+                /**
+                 * we will maximum handle 12 weeks at a time unless
+                 * a specific end date specifies otherwise
+                 */
+                $end = strtotime('+12 week', $this->start);
+            }
+            $int = ($this->getInterval()) ? $this->getInterval() : 1;
+            $c = $this->start;
+            for (; $c < $end; $c = strtotime("+$int week", $c)) {
+            //print CaldavRessource::timestamp2ICal($c)."<br/>";
+                if (! $this->except($c))
+                    array_push($res, CaldavRessource::timestamp2ICal($c));
+            }
+            //print_r($res);
+            return $res;
+        }
+
+        private function monthly() {
+            $res = array();
+            $end = $this->getEnd('MONTHLY');
+            if (! $end) {
+                /**
+                 * we will maximum handle 12 months at a time unless
+                 * a specific end date specifies otherwise
+                 */
+                $end = strtotime('+12 month', $this->start);
+            }
+            $int = ($this->getInterval()) ? $this->getInterval() : 1;
+            $c = $this->start;
+            for (; $c < $end; $c = strtotime("+$int month", $c)) {
+                if (! $this->except($c))
+                    array_push($res, CaldavRessource::timestamp2ICal($c));
+            }
+            return $res;
+        }
+
+        private function yearly() {
+            $res = array();
+            $end = $this->getEnd('YEARLY');
+            if (! $end) {
+                /**
+                 * we will maximum handle 12 years at a time unless
+                 * a specific end date specifies otherwise
+                 */
+                $end = strtotime('+12 year', $this->start);
+            }
+            $int = ($this->getInterval()) ? $this->getInterval() : 1;
+            $c = $this->start;
+            for (; $c < $end; $c = strtotime("+$int year", $c)) {
+                if (! $this->except($c))
+                    array_push($res, CaldavRessource::timestamp2ICal($c));
+            }
+            return $res;
+        }
+
+        private function limitRange($dates) {
+            $res = array();
+            if (!$this->range_start && !$this->range_end) {
+                $res = $dates;
+            }
+            else if ($this->range_start && !$this->range_end) {
+                $start = CaldavRessource::timestamp2ICal($this->range_start);
+                foreach ($dates as $date) {
+                    if (CaldavRessource::datecmp($start, $date) < 0)
+                        array_push($res, $date);
+                }
+            }
+            else {
+                $start = CaldavRessource::timestamp2ICal($this->range_start);
+                $end = CaldavRessource::timestamp2ICal($this->range_end);
+                foreach ($dates as $date) {
+                    if (CaldavRessource::datecmp($start, $date) < 0 &&
+                        CaldavRessource::datecmp($end, $date) > 0)
+                        array_push($res, $date);
+                }
+            }
+            return $res;
+        }
+
+        function getFreq() {
+            return strtoupper($this->rules['freq'][0]);
+        }
+
+        function getUntil() {
+            return strtoupper($this->rules['until'][0]);
+        }
+
+        function getCount() {
+            return strtoupper($this->rules['count'][0]);
+        }
+
+        function getInterval() {
+            return strtoupper($this->rules['interval'][0]);
+        }
+
+        function getBySecond() {
+            $l = $this->rules['bysecond'];
+            if ($l) {
+                foreach ($l as $val)
+                    $list[] = strtoupper($val);
+            }
+            return $list;
+        }
+
+        function getByMinute() {
+            $l = $this->rules['byminute'];
+            if ($l) {
+                foreach ($l as $val)
+                    $list[] = strtoupper($val);
+            }
+            return $list;
+        }
+
+        function getByHour() {
+            $l = $this->rules['byhour'];
+            if ($l) {
+                foreach ($l as $val)
+                    $list[] = strtoupper($val);
+            }
+            return $list;
+        }
+
+        function getByDay() {
+            $l = $this->rules['byday'];
+            if ($l) {
+                foreach ($l as $val)
+                    $list[] = strtoupper($val);
+            }
+            return $list;
+        }
+
+        function getByMonthDay() {
+            $l = $this->rules['bymonthday'];
+            if ($l) {
+                foreach ($l as $val)
+                    $list[] = strtoupper($val);
+            }
+            return $list;
+        }
+
+        function getByYearDay() {
+            $l = $this->rules['byyearday'];
+            if ($l) {
+                foreach ($l as $val)
+                    $list[] = strtoupper($val);
+            }
+            return $list;
+        }
+
+        function getByWeekNo() {
+            $l = $this->rules['byweekno'];
+            if ($l) {
+                foreach ($l as $val)
+                    $list[] = strtoupper($val);
+            }
+            return $list;
+        }
+
+        function getByMonth() {
+            $l = $this->rules['bymonth'];
+            if ($l) {
+                foreach ($l as $val)
+                    $list[] = strtoupper($val);
+            }
+            return $list;
+        }
+
+        function getBySetPos() {
+            return strtoupper($this->rules['bysetpos'][0]);
+        }
+
+        function getWKST() {
+            return strtoupper($this->rules['wkst'][0]);
+        }
+
+        function getAll() {
+            return $this->rules;
+        }
+
+        function getEventDates($startDate = NULL, $endDate = NULL) {
+            $dates  = array();
+
+            $freq = $this->getFreq();
+            //print "$freq\n";
+            if (! in_array($freq, $this->freq))
+                return $dates;
+            if ($startDate && $endDate) {
+                $this->range_start = CaldavRessource::iCal2Timestamp($startDate);
+                $this->range_end = CaldavRessource::iCal2Timestamp($endDate);
+                if ($this->start > $this->range_end)
+                    return $dates;
+            }
+            else if ($startDate) {
+                $this->range_start = CaldavRessource::iCal2Timestamp($startDate);
+                $this->range_end = NULL;
+            }
+            else {
+                $this->range_start = NULL;
+                $this->range_end = NULL;
+            }
+            switch ($freq) {
+                case 'HOURLY': $dates = $this->hourly(); break;
+                case 'DAILY': $dates = $this->daily(); break;
+                case 'WEEKLY': $dates = $this->weekly(); break;
+                case 'MONTHLY': $dates = $this->monthly(); break;
+                case 'YEARLY': $dates = $this->yearly(); break;
+                default: break;
+            }
+            //print_r($dates);
+            return (count($dates) > 0) ? $this->limitRange($dates) : $dates;
+        }
+
+        function getStartAndEnd() {
+            $res['start'] = $this->start;
+            $res['end'] = $this->end;
+            
+            return $res;
+        }
+        
+        function __toString() {
+            $str = "FREQ=" . $this->getFreq();
+            $until = $this->getUntil();
+            $count = $this->getCount();
+            if ($until)
+                $str .= ';UNTIL=' . $until;
+            if ($count)
+                $str .= ';COUNT=' . $count;
+            foreach ($this->rules as $k => $v) {
+                if ($k == 'freq' || $k == 'count' || $k == 'until')
+                    continue;
+                $str .= ';' . strtoupper($k) . '=';
+                foreach($v as $rule) {
+                    if ($str[strlen($str) - 1] != '=')
+                        $str .= ',';
+                    $str .=  strtoupper($rule);
+                }
+            }
+            return $str;
+        }
+
+    }
diff --git a/caldav/vevent.class.php b/caldav/vevent.class.php
new file mode 100644 (file)
index 0000000..45b6b04
--- /dev/null
@@ -0,0 +1,155 @@
+<?php
+/* $Id$ */
+
+    require_once 'awl/iCalendar.php';
+    require_once 'caldavresource.class.php';
+    require_once 'icomponent.class.php';
+    require_once 'rruleparser.class.php';
+
+    class VEvent extends IComponent {
+
+        private $rulesParser;
+
+        function __construct($etag, $url, VTYPE $type, iCalendar $item, $new) {
+            parent::__construct($etag, $url, $type, $item, $new);
+            $this->rulesParser = new RRuleParser();
+        }
+
+        function isActive($start, $end) {
+            $res = FALSE;
+            if (!($start && $end))
+                return TRUE;
+            if (! CaldavRessource::isDateTime($start) ||
+                ! CaldavRessource::isDateTime($end))
+                throw new Exception(
+                    "[$start,$end] Invalid CalDAV DateTime format");
+            $event = $this->getBaseComponent();
+            if ($start && !$end) {
+                if (CaldavRessource::datecmp(
+                    $start, $event->GetPValue('DTSTART')) < 0)
+                    $res = TRUE;
+            }
+            else {
+                if (CaldavRessource::datecmp(
+                        $start, $event->GetPValue('DTSTART')) < 0 &&
+                    CaldavRessource::datecmp(
+                        $end, $event->GetPValue('DTEND')) > 0)
+                    $res = TRUE;
+            }
+            return $res;
+        }
+
+        function getActiveDates($range_start = NULL, $range_end = NULL) {
+            $res = array();
+            $event = $this->getBaseComponent();
+            //print_r($event);
+            $start = $event->GetPValue('DTSTART');
+            $end = $event->GetPValue('DTEND');
+            //print "$start:$end<br/>";
+            if (! ($start && $end))
+                return $res;
+            $rrule = $event->GetPValue('RRULE');
+            if ($rrule) {
+                $this->rulesParser->setRule($rrule, $start, $end);
+                //print $this->rulesParser->__toString()."\n";
+                $res = $this->rulesParser->getEventDates(
+                                    $range_start, $range_end);
+                //print_r($res);
+            }
+            else {
+                if ($this->isActive($range_start, $range_end))
+                    array_push($res, $start);
+            }
+            //var_dump($res);
+            return $res;
+        }
+
+        function getRRule() {
+            return $this->rulesParser;
+        }
+
+        function getAlarm() {
+            $alarm = $this->getComponent(VTYPE::VALARM);
+//            print_r($alarm);
+            if ($alarm)
+                $alarm = $alarm[0];
+            return $alarm;
+        }
+
+        function setProperty($name, $value) {
+            $component = $this->getBaseComponent();
+            $properties = $component->GetProperties();
+            $match = FALSE;
+            $update = FALSE;
+            
+            if (count($properties) > 0) {
+                foreach ($properties as $property) {
+                    //echo "B: " . $property->Name(). ":" . $property->Value() . "<br/>";
+                    $test1 = explode(';', $name);
+                    $test2 = explode(';', $property->Name());
+                    if (strcasecmp($test1[0], $test2[0]) === 0) {
+                        if (strcmp($property->Value(), $value) !== 0) {
+                            $property->Value($value);
+                            //echo "B: " . $property->Name(). ":" . $property->Value() . "<br/>";
+                            $update = TRUE;
+                        }
+                        $match = TRUE;
+                    }
+                }
+            }
+            if ($match == FALSE) {
+                $component->AddProperty(strtoupper($name), $value);
+                $update = TRUE;
+            }
+            else {
+                if ($update)
+                    $component->SetProperties($properties);
+            }
+            if ($update) {
+                $this->addDefault($component);
+                $this->setDirty();
+            }
+            //$properties = $component->GetProperties();
+            //foreach ($properties as $property) {
+            //    echo "A: " . $property->Name(). ":" . $property->Value() . "<br/>";
+            //}
+            //echo "<br/>";
+            //exit;
+        }
+        
+        private function AddDefault(iCalComponent $component) {
+            $properties = $component->GetProperties();;
+            $now = gmdate("Ymd\THis\Z");
+            $a = array(1,1,1);
+            foreach ($properties as $property) {
+                //echo "D: " . $property->Name(). ":" . $property->Value() . "<br/>";
+                if (strcasecmp('DTSTAMP', $property->Name()) === 0) {
+                    $property->Value($now);
+                    $a[0] = 0;
+                }
+                if (strcasecmp('LAST-MODIFIED', $property->Name()) === 0) {
+                    $property->Value($now);
+                    $a[1] = 0;
+                }
+                if (strcasecmp('X-WEBCAL-GENERATION', $property->Name()) === 0) {
+                    $property->Value('1');
+                    $a[2] = 0;
+                }
+            }
+            for ($i = 0; $i < count($a); $i++) {
+                //echo $i.':'.$a[$i]."<br/>";
+                if ($a[$i]) {
+                    switch ($i) {
+                        case 0: $c['DTSTAMP'] = $now; break;
+                        case 1: $c['LAST-MODIFIED'] = $now; break;
+                        case 2: $c['X-WEBCAL-GENERATION'] = 1; break;
+                        default: continue;
+                    }
+                    $key = key($c);
+                    $val = $c[$key];
+                    $component->AddProperty($key, $val);
+                    $c = NULL;
+                }
+            }
+        }
+    }
diff --git a/css/top_level.css b/css/top_level.css
new file mode 100644 (file)
index 0000000..eac8ffa
--- /dev/null
@@ -0,0 +1,277 @@
+/* $Id$ */
+
+body {
+    background-color: #FAFAFA;
+    color: #000000;
+    font-family: 'bitstream vera sans', 'vera sans', sans-serif;
+    font-size:  0.8em;
+}
+
+a:link {
+    color: #000099;
+    text-decoration: none;
+}
+
+a:visited {
+    color: #000099;
+    text-decoration: none;
+}
+
+a:active {
+    color: black;
+    text-decoration: none;
+}
+
+a:hover {
+    color: #000099;
+}
+
+img.floatleft {
+    float: left;
+    margin: 2px;
+}
+
+img.floatright {
+    float: right;
+    margin: 2px;
+}
+
+#menu {
+    background-color: #FFFFC4;
+    color: inherit;
+    margin-top: 0px;
+    font-size: 0.8em;
+}
+
+#menu table {
+    width: 100%;
+}
+
+#menu td {
+    width: 25px;
+    text-align: center;
+}
+
+#menu img {
+    border: 0;
+    display: block;
+    margin-right: auto;
+    margin-left: auto;
+}
+
+#ui {
+/*  background-color: transparent;*/
+    background-color: #F1F9FF;
+    color: #000000;
+    margin: 20px;
+    border: 2px ridge #1D388C;
+    padding: 1%;
+    min-height: 450px;
+    height: auto;
+/*    <!--[if lt IE 7]>
+        _height: 450px;
+    <![endif]-->*/
+}
+
+#ui a:link {
+    color: green;
+    text-decoration: none;
+}
+
+#ui a:visited {
+    color: green;
+    text-decoration: none;
+}
+
+#ui a:active {
+    color: green;
+    text-decoration: none;
+}
+
+#ui a:hover {
+    color: green;
+    text-decoration: underline;
+}
+
+#ui table.config {
+    border-width: 1px;
+    border-style: solid;
+    margin-right: auto;
+    margin-left: auto;
+}
+
+#ui th {
+    border-bottom: 3px solid;
+}
+
+#ui td.config {
+    padding: 2px;
+}
+
+#ui p.usermanage {
+    text-align: center;
+}
+
+#footer {
+    text-align: left;
+    font-size: 0.8em;
+}
+
+#footer img {
+    border: 0;
+    float: right;
+}
+
+#cal {
+    font-family: 'bitstream vera sans', 'vera sans', sans-serif;
+    font-size:  1em;
+}
+
+#cal input {
+    font-size:  1em;
+}
+
+#cal td {
+    font-size:  1em;
+}
+
+#cal table {
+    border-width: 1px;
+    border-style: solid;
+    margin-right: auto;
+    margin-left: auto;
+}
+
+#events {
+}
+
+#events th {
+    text-align: center;
+    border-width: 1px 1px 1px 1px;
+    padding: 2px 2px 2px 2px;
+    border-style: solid solid solid solid;
+    border-color: #2C9185 #2C9185 #2C9185 #2C9185;
+    background-color: #CEFFFA;
+}
+
+#events th.event {
+    width: 13.5%;
+}
+
+#events th.day_event {
+    width: 90%;
+}
+
+#events td {
+    border-width: 1px 1px 1px 1px;
+    padding: 2px 2px 2px 2px;
+    border-style: solid solid solid solid;
+    border-color: #2C9185 #2C9185 #2C9185 #2C9185;
+    background-color: #FFFFFF;
+}
+
+#events td.weeknum, td.time {
+    background-color: #8FCDC7;
+    text-align: center;
+    font-weight: bold;
+}
+
+#events td.notused {
+    background-color: #BFBFBF;
+}
+
+#events td.today {
+    background-color: #FAF8D2;
+}
+
+#events .date {
+    border-left-width: 1px;
+    border-bottom-width: 0px;
+    border-right-width: 0px;
+    border-top-width: 1px;
+    border-style: solid;
+    padding: 1px;
+    font-size: x-small;
+    float: right;
+}
+
+#events table {
+    width: 100%;
+    border-width: 2px 2px 2px 2px;
+    border-spacing: 0px;
+    border-style: solid solid solid solid;
+    border-color: #2C9185 #2C9185 #2C9185 #2C9185;
+    border-collapse: separate;
+    background-color: transparent;
+}
+
+#error_msg {
+    display: block;
+    margin-right: auto;
+    margin-left: auto;
+    margin-top: 10%;
+    padding: 5px;
+    width: 400px;
+    border-width: 5px;
+    border-spacing: 0px;
+    border-style: solid solid solid solid;
+    border-color: black;
+    border-collapse: separate;
+    background-color: red;
+    color: black;
+}
+
+#update_msg {
+    display: block;
+    margin-right: auto;
+    margin-left: auto;
+    margin-top: 10%;
+    padding: 5px;
+    width: 400px;
+    border-width: 5px;
+    border-spacing: 0px;
+    border-style: solid solid solid solid;
+    border-color: black;
+    border-collapse: separate;
+    background-color: yellow;
+    color: black;
+}
+
+#login_msg {
+    display: block;
+    margin-right: auto;
+    margin-left: auto;
+    margin-top: 10%;
+    padding: 5px;
+    width: 400px;
+    border-width: 5px;
+    border-spacing: 0px;
+    border-style: solid solid solid solid;
+    border-color: black;
+    border-collapse: separate;
+    background-color: #F1F9FF;
+    color: black;
+}
+
+#login_msg table {
+    margin-right: auto;
+    margin-left: auto;
+}
+
+#event {
+    padding: 1%;
+}
+
+#event input, select {
+    font-family: 'bitstream vera sans', 'vera sans', sans-serif;
+    font-size:  1.0em;
+}
+
+#event table {
+    margin-right: auto;
+    margin-left: auto;
+}
+
+.bold {
+    font-weight: bold;
+}
\ No newline at end of file
diff --git a/error.html b/error.html
new file mode 100644 (file)
index 0000000..09ec9ed
--- /dev/null
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- $Id$ -->
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+    <head>
+        <title>Web Calendar</title>
+        <link rel="stylesheet" type="text/css" href="css/top_level.css" />
+        <link rel="shortcut icon" href="pixmaps/favicon.ico" />
+    </head>
+    <body>
+        <div id="error_msg">
+            <h1 style="text-align: center">Redirecting Error occured</h1>
+            <p style="text-align: center">
+                Your session has expired<br/>
+                or you have tried to by-pass the login system.<br/>
+                You can increase your session timeout in the config menu.
+            </p>
+            <p style="text-align: center">
+                Please login <a href="login.php">here</a>
+            </p>
+        </div>
+    </body>
+</html>
diff --git a/events/delete_event.php b/events/delete_event.php
new file mode 100644 (file)
index 0000000..3fbc045
--- /dev/null
@@ -0,0 +1,64 @@
+<?php
+require_once 'calendar.class.php';
+require_once 'user_validate.php';
+require_once 'helper.php';
+
+valid_user();
+
+include_once 'config.inc.php';
+
+if (! defined($_SESSION['__ROOT__']) && empty($_SESSION['__ROOT__'])) {
+    session_destroy();
+    header('Location: ' . WEB_ROOT . 'error.html');
+    exit;
+}
+
+$query = construct_URL($_SERVER['QUERY_STRING'],
+               array('etag', 'referer', 'cal'));
+//print_r($query);
+//echo '<br/>';
+//print_r($_SESSION['all_events']);
+//exit;
+if (isset($query['cal']) && ! empty($query['cal']) &&
+       isset($query['etag']) && ! empty($query['etag'])) {
+       $_SESSION['calendar'] = $_SESSION['all_events'][$query['cal']];
+       if (! is_object($_SESSION['calendar']))
+               throw new Exception("Missing calendar object");
+
+       foreach ($_SESSION['calendar'] as $vevent) {
+               if ($vevent->getEtag() == $query['etag']) {
+                       $res = $_SESSION['calendar']->delete($vevent->getUrl(), $vevent->getEtag());
+               }
+       }
+       //print $query['referer'];
+       header('Location: '. urldecode($query['referer']));
+       exit;
+}
+else {
+       $text = '<h3 style="text-align: center">Choose an event from one of the listed calendars</h3>';
+       $text .= '<p>';
+       $text .= '<form id="cals" style="text-align: center" action="'.
+                       $_SERVER['PHP_SELF'].'" method="get">';
+       $text .= '<input type="hidden" name="referer" value="'.urldecode($query['referer']).'" />';
+       $text .= '<input type="hidden" id="cal" name="cal" value="" />';
+       $calendar = '';
+       foreach ($_SESSION['all_events'] as $name => $cal) {
+               //$calendar .= "$name<br/>".var_export($cal, true)."<br/>";
+        $text .= $name.': <select name="etag"
+                       onchange="setCalendar(\'cals\', \'cal\', '."'$name'".')">';
+               $text .= '<option selected="selected"></option>';
+               foreach ($cal as $vevent) {
+                       $calendar .= $vevent->getEtag()."<br/>";
+                       $text .= '<option value="'.$vevent->getEtag().'">'.
+                               $vevent->getDetails().'</option>';
+               } 
+               $text .= '</select><br/><br/>';
+       }
+       $text .= '<input type="submit" value="Continue" />';
+       $text .= '</form></p>';
+       //print urldecode($query['referer']);
+       //exit;
+    print popup_window($text, $query['referer']);
+       exit;
+}
+?>
\ No newline at end of file
diff --git a/events/edit_event.php b/events/edit_event.php
new file mode 100644 (file)
index 0000000..2fd52f8
--- /dev/null
@@ -0,0 +1,218 @@
+<?php
+/* $Id$ */
+
+require_once 'calendar.class.php';
+require_once 'user_validate.php';
+require_once 'helper.php';
+
+valid_user();
+
+include_once 'config.inc.php';
+include_once 'eventgui.class.php';
+
+if (! defined($_SESSION['__ROOT__']) && empty($_SESSION['__ROOT__'])) {
+    session_destroy();
+    header('Location: ' . WEB_ROOT . 'error.html');
+    exit;
+}
+
+if (isset($_SESSION['authenticate']) && $_SESSION['authenticate']->validUser()) {
+    $view_style = ($_SESSION['user_settings']->getViewStyle()) ?
+        $_SESSION['user_settings']->getViewStyle() : VIEW_STYLE;
+//    $timeout = ($_SESSION['user_settings']->getTimeout()) ?
+//        (int) $_SESSION['user_settings']->getTimeout() : TIMEOUT;
+    $week_start_sunday = ($_SESSION['user_settings']->getStartWeek() !== NULL) ?
+        $_SESSION['user_settings']->getStartWeek() : WEEK_START_SUNDAY;
+    $start_hour = ($_SESSION['user_settings']->getStartHour()) ?
+        (int) $_SESSION['user_settings']->getStartHour() : START_HOUR;
+    $end_hour = ($_SESSION['user_settings']->getEndHour()) ?
+        (int) $_SESSION['user_settings']->getEndHour() : END_HOUR;
+}
+
+$pwd = WEB_ROOT;
+if ($pwd[strlen($pwd)-1] == '/')
+    $pwd = substr($pwd, 0, -1);
+
+if (isset($_POST) && count($_POST) > 0) {
+    $exclude = array('tzid','range','times','untildate');
+    $start = array('date' => NULL, 'time' => NULL);
+    $end = array('date' => NULL, 'time' => NULL);
+    $referer = $_POST['referer'];
+    $allDay = false;
+    
+    foreach ($_POST as $key => $value) {
+        //echo "$key => $value<br/>";
+        if (strcmp($key, 'referer') == 0)
+            continue;
+        if (in_array($key, $exclude))
+            continue;
+        if (strcasecmp($key, 'startdate') == 0) {
+            $start['date'] = join('', explode('-',$value));
+            continue;
+        }
+        if (strcasecmp($key, 'starttime') == 0) {
+            $start['time'] = $value;
+            if (strlen($start['time']) < 5)
+                $start['time'] .= '00';
+            continue;
+        }
+        if (strcasecmp($key, 'enddate') == 0) {
+            $end['date'] = join('', explode('-',$value));
+            continue;
+        }
+        if (strcasecmp($key, 'endtime') == 0) {
+            $end['time'] = $value;
+            if (strlen($end['time']) < 5)
+                $end['time'] .= '00';
+            continue;
+        }
+        if (strcasecmp("allDay", $key) == 0) {
+            $allDay = true;
+            continue;
+        }
+        if (strcasecmp("recurrence", $key) == 0) {
+            $value = "FREQ=$value";
+            $key = "RRULE";
+        }
+        //echo "$key => $value<br/>";
+        $_SESSION['current_event']->setProperty($key, $value);
+    }
+    //echo 'DTSTART: '. $start['date'].'T'.$start['time'];
+    //echo '<br/>DTEND: '. $end['date'].'T'.$end['time'];
+    //exit;
+    if ($allDay) {
+        $_SESSION['current_event']->setProperty(
+                'DTSTART;VALUE=DATE', $start['date']);
+        $_SESSION['current_event']->setProperty(
+                'DTEND;VALUE=DATE', $end['date']);
+        //echo "<br/>".$start['date']."<br/>";
+        //echo $end['date']."<br/>";
+    }
+    else {
+        $start = strtotime($start['date'].'T'.$start['time']);
+        $end = strtotime($end['date'].'T'.$end['time']);
+        //echo "<br/>".Calendar::timestamp2ICal($start, FALSE)."<br/>";
+        //echo Calendar::timestamp2ICal($end, FALSE)."<br/>";
+        //exit;
+        $_SESSION['current_event']->setProperty('DTSTART', 
+                Calendar::timestamp2ICal($start, FALSE));
+        $_SESSION['current_event']->setProperty('DTEND',
+                Calendar::timestamp2ICal($end, FALSE));
+    }
+    //exit;
+    if (isset($_SESSION['calendar'])) {
+        $res = $_SESSION['calendar']->update(
+            $_SESSION['current_event']->getUrl(),
+            $_SESSION['current_event']->getEtag());
+        //print_r($res);
+        //exit;
+        unset($_SESSION['current_event']);
+        unset($_SESSION['calendar']);
+        unset($_SESSION['EVENT_ACTION']);
+        if (count($res) == 0) {
+            header('Location: ' . $referer);
+            exit;
+        }
+        $msg = '';
+        foreach ($res as $elem) {
+            $k = key($elem);
+            $msg .= "$k: " . $elem[$k];
+        }
+    }
+    else
+        throw new Exception("Error updating event");
+    //print "$msg\n";
+    $msg = join("", explode("\n", $msg));
+    $title = TITLE;
+    $pwd = WEB_ROOT;
+$head = <<<__HEAD
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+<title>$title</title>
+<meta http-equiv="content-type" content="application/xhtml+xml; charset=UTF-8"/>
+<meta http-equiv="content-style-type" content="text/css"/>
+<link rel="stylesheet" type="text/css" href="$pwd/css/top_level.css" />
+<link rel="shortcut icon" href="$pwd/pixmaps/favicon.ico" />
+<script type="text/javascript" src="$pwd/js/helper.js"></script>
+</head>
+<body onload="error('$msg')">
+<table style="width: 100%;">
+<tr>
+<td style="width: 33%"><img src="$pwd/pixmaps/calendar.png" alt="calendar.png" /></td>
+<td style="width: 33%; text-align: center;"><a href="$pwd/logout.php"><img style="border: 0" src="$pwd/pixmaps/exit.png" width="32" height="32" alt="Logout" /><br/>Logout</a></td>
+<td style="text-align: right"><span style="font-size: 1.6em">DAViCal Web Calendar</span><br/> - A Web Interface for <a href="javascript: newwin('http://www.davical.org/');">DAViCal</a></td>
+</tr>
+</table>
+__HEAD;
+    print "$head";
+    include TOP_FOLDER.'/include/menu.inc.php';
+    print "<div id=\"ui\"></div>";
+    include TOP_FOLDER.'/include/footer.inc.php';
+    exit;
+}
+else {
+    $query = construct_URL($_SERVER['QUERY_STRING'],
+            array('etag', 'referer', 'cal'));
+    //print_r($_SESSION['all_events'][$query['cal']]);
+    //exit;
+    if (isset($query['cal']) && ! empty($query['cal'])) {
+        $_SESSION['calendar'] = $_SESSION['all_events'][$query['cal']];
+        if (! is_object($_SESSION['calendar']))
+            throw new Exception("Missing calendar object");
+    
+        if ($_SESSION['EVENT_ACTION'] == 'NEW') {
+            $event = $_SESSION['calendar']->newComponent('VEVENT');
+            $_SESSION['current_event'] = $event;
+        }
+        else {
+            foreach ($_SESSION['calendar'] as $vevent) {
+                if ($vevent->getEtag() == $query['etag']) {
+                    $event = $vevent;
+                    break;
+                }
+            }
+            $_SESSION['current_event'] = $event;
+        }
+        //print $query['referer'];
+        //exit;
+        $gui = new EventGUI($event, $query['referer'], $query['cal']);
+        //unset($_GET['referer']);
+        $calendar = $gui->getView();
+    }
+    else {
+        $text = '<h3 style="text-align: center">Choose an event from one of the listed calendars</h3>';
+        $text .= '<p>';
+        $text .= '<form id="cals" style="text-align: center" action="'.
+                $_SERVER['PHP_SELF'].'" method="get">';
+        $text .= '<input type="hidden" name="referer" value="'.urldecode($query['referer']).'" />';
+        $text .= '<input type="hidden" id="cal" name="cal" value="" />';
+        $calendar = '';
+        foreach ($_SESSION['all_events'] as $name => $cal) {
+            //$calendar .= "$name<br/>".var_export($cal, true)."<br/>";
+               $text .= $name.': <select name="etag"
+                    onchange="setCalendar(\'cals\', \'cal\', '."'$name'".')">';
+            $text .= '<option selected="selected"></option>';
+            foreach ($cal as $vevent) {
+                $calendar .= $vevent->getEtag()."<br/>";
+                $text .= '<option value="'.$vevent->getEtag().'">'.
+                    $vevent->getDetails().'</option>';
+            } 
+            $text .= '</select><br/><br/>';
+        }
+        $text .= '<input type="submit" value="Continue" />';
+        $text .= '</form></p>';
+        //print "{$_GET['referer']}";
+        //exit;
+           print popup_window($text, $query['referer']);
+        exit;
+    }
+}
+include TOP_FOLDER.'/include/header.inc.php';
+include TOP_FOLDER.'/include/menu.inc.php';
+
+print "<div id=\"ui\">$calendar</div>";
+include TOP_FOLDER.'/include/footer.inc.php';
+?>
diff --git a/events/eventgui.class.php b/events/eventgui.class.php
new file mode 100644 (file)
index 0000000..db2df37
--- /dev/null
@@ -0,0 +1,354 @@
+<?php
+/* $Id$ */
+
+include_once 'config.inc.php';
+include_once 'dateformat.class.php';
+include_once 'caldavresource.class.php';
+require_once 'calendar.class.php';
+require_once 'helper.php';
+
+class EventGUI {
+
+    private $event;
+    private $referer;
+    private $summary;
+    private $location;
+    private $description;
+    private $start;
+    private $end;
+    private $timezone;
+    private $dates;
+    private $rrule;
+    private $startdate;
+    private $enddate;
+    private $until;
+    private $cal;
+
+    function __construct(VEvent $event = NULL, $referer = '', $cal = NULL) {
+        $this->event = $event;
+        $this->referer = urldecode($referer);
+        $this->cal = $cal;
+        //print $this->referer;
+        //exit;
+        $this->summary = '';
+        $this->location = '';
+        $this->description = '';
+        $this->start = '';
+        $this->end = '';
+        $this->timezone = '';
+        $this->localtime = ($_SESSION['user_settings']->getTimeZone() !== NULL) ?
+            $_SESSION['user_settings']->getTimeZone() : TIMEZONE;
+        $root = WEB_ROOT;
+        $week_start_sunday = ($_SESSION['user_settings']->getStartWeek() !== NULL) ?
+            $_SESSION['user_settings']->getStartWeek() : WEEK_START_SUNDAY;
+        $week_start_sunday = ($week_start_sunday) ? 0 : 1;
+        $this->startdate = <<< __CAL1
+    <script type="text/javascript">
+        A_TCALDEF['imgpath'] = '{$root}js/img/';
+        A_TCALDEF['weekstart'] = $week_start_sunday;
+        new tcal ({
+        'formname': 'event_data',
+        'controlname': 'startdate'
+    });
+    </script>
+__CAL1;
+        $this->enddate = <<< __CAL1
+    <script type="text/javascript">
+        A_TCALDEF['imgpath'] = '{$root}js/img/';
+        A_TCALDEF['weekstart'] = $week_start_sunday;
+        new tcal ({
+        'formname': 'event_data',
+        'controlname': 'enddate'
+    });
+    </script>
+__CAL1;
+        $this->until = <<< __CAL1
+    <script type="text/javascript">
+        A_TCALDEF['imgpath'] = '{$root}js/img/';
+        A_TCALDEF['weekstart'] = $week_start_sunday;
+        new tcal ({
+        'formname': 'event_data',
+        'controlname': 'untildate'
+    });
+    </script>
+__CAL1;
+    }
+
+    private function parseEvent() {
+        if (! $this->event)
+            return FALSE;
+        $comp = $this->event->getBaseComponent();
+        $this->summary = $comp->GetPValue('SUMMARY');
+        $this->location = $comp->GetPValue('LOCATION');
+        $this->description = $comp->GetPValue('DESCRIPTION');
+        $this->start = $comp->GetPValue('DTSTART');
+        $this->end = $comp->GetPValue('DTEND');
+        $this->timezone = $this->event->getTZID();
+        $rrule = $this->event->getRRule();
+        $this->dates = $rrule->getEventDates();
+        $this->rrule = $rrule->getAll();
+        //echo $this->start .':'. $this->end .'<br/>';
+        return TRUE;
+    }
+
+    private function createDT($elem, $name) {
+        $res = array();
+
+        //print_r($elem);
+        //echo '<br/>';
+        if (count($elem) > 1) {
+            $date = $elem[0];
+            $time = substr($elem[1], 0, 4);
+        }
+        else {
+            $date = $elem[0];
+            $time = '2359';
+        }
+        for ($i = 0; $i < substr($time, 2); $i += 15);
+        if ($i < 10)
+            $i = "0$i";
+        $time = substr($time, 0, 2) . $i;
+        //echo "$time<br/>";
+        $options = array();
+        for ($i = 0; $i < 24; $i++) {
+            $h = ($i < 10) ? "0$i" : $i;
+            $stop = ($i == 23) ? 61 : 60;
+            for ($j = 0; $j < $stop; $j += 15) {
+                $m = ($j < 10) ? "0$j" : $j;
+                if ($m == 60)
+                    $m = 59;
+                $hm = "$h$m";
+                //echo "$hm<br/>";
+                $option = "<option value=\"$hm\"";
+                if ($hm == $time)
+                    $option .= ' selected="selected"';
+                $option .= ">$hm</option>";
+                array_push($options, $option);
+            }
+        }
+        $select = "<select name=\"$name\" id=\"$name\">";
+        foreach ($options as $option)
+            $select .= $option;
+        $select .= '</select>';
+        $date = substr($date, 0, 4).'-'.substr($date, 4, 2).'-'.substr($date, 6);
+        //echo "$date<br/>";
+        $res['date'] = $date;
+        $res['time'] = $select;
+        return $res;
+    }
+
+    private function createTime() {
+        include_once 'timezone.php';
+
+        if ($this->start == '' || $this->end == '') {
+            $this->start = date("Ymd\THis");
+            $this->end = date("Ymd\THis", strtotime("+1 hour"));
+            $this->timezone = $_SESSION['user_settings']->getTimeZone();
+        }
+        //echo $this->start .':'. $this->end .'<br/>';
+        $tz = '<select name="tzid">';
+        foreach ($timezones as $timezone) {
+            $tz .= "<option value=\"$timezone\"";
+            if ($this->localtime == $timezone)
+                $tz .= ' selected="selected"';
+            $tz .= ">$timezone</option>";
+        }
+        $tz .= "</select>";
+        $dateFormat = new DateFormat();
+        $UTCStart = $dateFormat->UTC2Client($this->start);
+        $UTCEnd = $dateFormat->UTC2Client($this->end);
+        //echo $UTCStart .':'. $UTCEnd .'<br/>';
+        $startArray = explode('T', $UTCStart);
+        //echo "Client: ".$dateFormat->client2UTC($dateFormat->UTC2Client($this->start))."<br/>";
+        //echo "Local: ".$dateFormat->UTC2Local($this->start)."<br/>";
+        $start = $this->createDT($startArray, 'starttime');
+        $endArray = explode('T', $UTCEnd);
+        $end = $this->createDT($endArray, 'endtime');
+        $allDay = CaldavRessource::allDayEvent($UTCStart, $UTCEnd);
+        $allDay = ($allDay) ? 'checked="checked"' : '';
+        $datetime = "<table><caption style=\"font-weight: bold\">Date &amp; Time</caption>
+        <tr>
+            <td>All Day Event</td>
+            <td>
+            <script type=\"text/javascript\">
+                var observable1 = ['starttime', 'tcalico_1', 'enddate', 'endtime'];
+                document.write('<input type=\"checkbox\" name=\"allDay\" $allDay' +
+                        'id=\"allDay\" onchange=\"stateSwitch(this, observable1, 0)\"/>');
+            </script>
+            </td>
+            <td>&nbsp;</td>
+        </tr>
+        <tr>
+            <td>Start</td>
+            <td>
+            <script type=\"text/javascript\">
+                document.write('<input type=\"text\" name=\"startdate\" ' +
+                'id=\"startdate\" size=\"12\" onchange=\"dateAlign(' +
+                'this)\" value=\"{$start['date']}\"/>');
+            </script>
+                $this->startdate
+                <!--{$start['date']}-->
+            </td>
+            <td>{$start['time']}</td>
+            <td rowspan=\"2\">$tz</td>
+        </tr>
+        <tr>
+            <td>End</td>
+            <td>
+            <script type=\"text/javascript\">
+                document.write('<input name=\"enddate\" type=\"text\" ' +
+                'id=\"enddate\" size=\"12\" onchange=\"dateAlign(' +
+                'this)\" value=\"{$end['date']}\"/>');
+            </script>
+                $this->enddate
+                <!--{$end['date']}-->
+            </td>
+            <td>
+                {$end['time']}
+                <script type=\"text/javascript\">
+                    var observable4 = ['starttime', 'tcalico_1', 'enddate', 'endtime'];
+                    stateSwitch(document.getElementById('allDay'), observable4, 1);
+                </script>
+            </td>
+        </tr>
+        </table>";
+        return $datetime;
+    }
+
+    private function recurRule($checked) {
+        $choices = array('DAILY', 'WEEKLY', 'MONTHLY', 'YEARLY');
+        $rule = '<span style="font-weight: bold">Recurrence Types</span><br/>';
+        foreach ($choices as $choice) {
+            $rule .= "<input type=\"radio\" name=\"recurrence\" value=\"$choice\"";
+            if (strtoupper($checked) == $choice)
+                $rule .= ' checked="checked"';
+            $rule .= "/>$choice<br/>";
+        }
+        return $rule;
+    }
+
+    private function rangeRule($rules) {
+        $until = '';
+        $times = '';
+        $count_check = '';
+        $until_check = '';
+        $no_check = '';
+
+        $range = $rules['until'][0];
+        if (! $range) {
+            $range = $rules['count'][0];
+            if ($range) {
+                $times = $range;
+                $count_check = 'checked="checked"';
+            }
+            else {
+                $no_check = 'checked="checked"';
+            }
+        }
+        else {
+            $until = substr($range, 0, 4).'-'.substr($range, 4, 2).
+                '-'.substr($range, 6, 2);
+            $until_check = 'checked="checked"';
+        }
+        $rule = <<<_RULE
+        <span style="font-weight: bold">Recurrence Range</span><br/>
+        <input type="radio" name="range" value="UNLIMITED" $no_check/>
+            No ending date<br/>
+        <input type="radio" name="range" value="COUNT" $count_check/>
+            End after
+        <input type="text" name="times" size="4"
+            value="$times"/>&nbsp;Occurrence(s)<br/>
+        <input type="radio" name="range" value="UNTIL" $until_check/>
+            End on
+        <input type="text" name="untildate" size="12" value="$until"/>
+            {$this->until}
+_RULE;
+        return $rule;
+    }
+
+    private function createRecur() {
+        $recur = NULL;
+        $rrule = NULL;
+        if ($this->rrule) {
+            $rrule = array_diff_key($this->rrule, array('freq' => ''));
+            switch ($this->rrule['freq'][0]) {
+                case 'DAILY': $recur = 'DAILY'; break;
+                case 'WEEKLY': $recur = 'WEEKLY'; break;
+                case 'MONTHLY': $recur = 'MONTHLY'; break;
+                case 'YEARLY': $recur = 'YEARLY'; break;
+            }
+        }
+        $recur = $this->recurRule($recur);
+        $range = $this->rangeRule($rrule);
+        //print htmlentities($recur);
+        $res = <<<_TABLE
+        <table cellpadding="10">
+            <caption style="font-weight: bold">Recurrence Rule</caption>
+            <tr>
+                <td valign="top">$recur</td>
+                <td valign="top">$range</td>
+            </tr>
+        </table>
+_TABLE;
+        return $res;
+    }
+
+    function getView() {
+        $title = '<input name="summary" size="53" value="_SUM_"/>';
+        $location = '<input name="location" size="53" value="_LOC_"/>';
+        $description =
+            '<textarea name="description" cols="60" rows="5">_DESC_</textarea>';
+        if ($this->parseEvent()) {
+            $title = str_replace('_SUM_', $this->summary, $title);
+            $location = str_replace('_LOC_', $this->location, $location);
+            $description = str_replace('_DESC_', $this->description, $description);
+        }
+        else {
+            $title = str_replace('_SUM_', '', $title);
+            $location = str_replace('_LOC_', '', $location);
+            $description = str_replace('_DESC_', '', $description);
+        }
+        $btn_label = ($_SESSION['EVENT_ACTION'] == 'NEW') ? "Create" : "Update";
+        unset($_SESSION['EVENT_ACTION']);
+        // for delete button
+        $etag = $this->event->getEtag();
+        $url = $this->event->getUrl();
+        $view = dirname($_SERVER['PHP_SELF']).'/delete_event.php';
+        //['starttime', 'enddate', 'endtime']
+        $view = "<div id=\"event\">
+        <form id=\"event_data\" method=\"post\" action=\"{$_SERVER['PHP_SELF']}\" 
+            onsubmit=\"return enableAll(['starttime', 'enddate', 'endtime'])\">
+            <table>
+                <caption style=\"font-weight: bold\">General</caption>
+               <tr>
+                   <td>Title</td><td>$title</td>
+               </tr>
+               <tr>
+                    <td>Location</td><td>$location</td>
+               </tr>
+               <tr>
+                    <td>Description</td><td>$description</td>
+               </tr>
+            </table><p/>".
+            $this->createTime()."<p/>".
+            $this->createRecur().
+        "<p style=\"text-align: center\">
+            <input type=\"hidden\" value=\"$this->referer\" name=\"referer\"/>
+            <input type=\"button\" value=\"Cancel\"
+                onclick=\"document.location.href='$this->referer'\"/>
+            <input type=\"submit\" value=\"$btn_label\"/>
+                <!--onclick=\"document.getElementById('event_data').submit()\"/>-->
+            <input type=\"button\" value=\"Delete\"
+                onclick=\"document.location.href='$view?etag=$etag&url=$url'+
+                '&cal=$this->cal&referer=$this->referer'\"/>
+            <!--onclick=\"javascript: alert('Update not available')\"/>-->
+            <!--<input type=\"submit\" name=\"update\" value=\"Update\"/>-->
+        </p>
+        </form>
+        </div>";
+        return $view;
+    }
+
+}
+
+?>
diff --git a/events/new_event.php b/events/new_event.php
new file mode 100644 (file)
index 0000000..90994dc
--- /dev/null
@@ -0,0 +1,49 @@
+<?php
+require_once 'calendar.class.php';
+require_once 'user_validate.php';
+
+valid_user();
+
+include_once 'config.inc.php';
+require_once 'helper.php';
+
+if (! defined($_SESSION['__ROOT__']) && empty($_SESSION['__ROOT__'])) {
+    session_destroy();
+    header('Location: ' . WEB_ROOT . 'error.html');
+    exit;
+}
+$path = dirname($_SERVER['PHP_SELF']);
+$url = "$path/edit_event.php";
+$_SESSION['EVENT_ACTION'] = 'NEW';
+$calendars = $_SESSION['user_settings']->getCalendars();
+$query = construct_URL($_SERVER['QUERY_STRING'],
+               array('etag', 'referer', 'cal'));
+
+if (count($calendars) < 1) {
+       header('Location: ' . WEB_ROOT . "utils/configure.php");
+       exit;
+}
+else if (count($calendars) == 1) {
+       $options = "?referer=".$query['referer'];
+       $options .= "&cal=" . current($calendars)->name;
+       header('Location: ' . $url . $options);
+       //print "$url$options<br/>";
+       exit;
+}
+else {
+    $cal = current($calendars)->name;
+       $text = '<p>';
+       $text .= '<form style="text-align: center" action="'.$url.'" method="get">';
+       $text .= '<input type="hidden" name="referer" value="'.$query['referer'].'" />';
+       $text .= 'Choose Calender: ';
+       $text .= '<select name="cal">';
+       foreach ($calendars as $calendar) {
+               $text .= '<option value="'.$calendar->name.'">'.$calendar->name.'</option>';
+       }
+    $text .= '</select><br/><br/>';
+    $text .= '<input type="submit" value="Continue" />';
+    $text .= '</form></p>';
+       print popup_window($text, $query['referer']."?cal=$cal");
+       exit;
+}
+?>
\ No newline at end of file
diff --git a/events/update.php b/events/update.php
new file mode 100644 (file)
index 0000000..e670a51
--- /dev/null
@@ -0,0 +1,33 @@
+<?php
+require_once 'calendar.class.php';
+require_once 'user_validate.php';
+require_once 'helper.php';
+
+valid_user();
+
+include_once 'config.inc.php';
+
+if (! defined($_SESSION['__ROOT__']) && empty($_SESSION['__ROOT__'])) {
+    session_destroy();
+    header('Location: ' . WEB_ROOT . 'error.html');
+    exit;
+}
+$calendars = $_SESSION['user_settings']->getCalendars();
+$query = construct_URL($_SERVER['QUERY_STRING'],
+               array('etag', 'referer', 'cal'));
+//print_r(urldecode($query['referer']));
+//exit;
+$url = $query['referer'].'?';
+foreach ($query as $key => $value) {
+    if (strcasecmp($key, 'referer') === 0)
+        continue;
+    if ($url[strlen($url) - 1] == '?')
+        $url .= "$key=$value";
+    else
+        $url .= "&$key=$value";
+}
+if ($url[strlen($url) - 1] == '?')
+    $url = substr($url, 0, -1);
+header('Location: ' . urldecode($url));
+exit;
+?>
\ No newline at end of file
diff --git a/include/footer.inc.php b/include/footer.inc.php
new file mode 100644 (file)
index 0000000..5a0f6ed
--- /dev/null
@@ -0,0 +1,31 @@
+<?php
+/* $Id$ */
+require_once 'config.inc.php';
+require_once 'persistens.php';
+require_once 'helper.php';
+
+$con = Persistens::getInstance(DBDRIVER);
+if (! is_array($res = $con->getVersion())) {
+    ErrorReport($res);
+} 
+$title = TITLE;
+$foot = <<<_FOOTER
+<div id="footer">
+<p>
+    $title {$res[0]} - &copy; 2010, 2011 Michael Rasmussen<br/>Released under GPLv3
+    <a href="http://validator.w3.org/check?uri=referer">
+        <img src="http://www.w3.org/Icons/valid-xhtml10-blue.png"
+        alt="Valid XHTML 1.0 Strict" height="31" width="88" />
+    </a>
+    <a href="http://jigsaw.w3.org/css-validator/check/referer">
+        <img src="http://www.w3.org/Icons/valid-css2-blue.png"
+        alt="Valid CSS 2.0" height="31" width="88" />
+    </a>
+</p>
+</div>
+</body>
+</html>
+_FOOTER;
+
+//file_put_contents('/tmp/davical.log', $foot, FILE_APPEND);
+print $foot;
diff --git a/include/header.inc.php b/include/header.inc.php
new file mode 100644 (file)
index 0000000..e96729b
--- /dev/null
@@ -0,0 +1,49 @@
+<?php
+/* $Id$ */
+require_once 'config.inc.php';
+require_once 'helper.php';
+
+$pwd = WEB_ROOT;
+if ($pwd[strlen($pwd)-1] == '/')
+    $pwd = substr($pwd, 0, -1);
+$title = TITLE;
+
+$head = <<<_HEAD
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+<title>$title</title>
+<meta name="generator" content="Bluefish 1.0.7"/>
+<meta name="author" content="Michael Rasmussen"/>
+<meta name="date" content="2009-03-23T20:45:48+0100"/>
+<meta name="copyright" content="© 2007, Michael Rasmussen. Licensed under GPL"/>
+<meta name="keywords" content=""/>
+<meta name="description" content=""/>
+<meta name="ROBOTS" content="NOINDEX, NOFOLLOW"/>
+<meta http-equiv="content-type" content="application/xhtml+xml; charset=UTF-8"/>
+<meta http-equiv="content-style-type" content="text/css"/>
+<meta http-equiv="expires" content="-1"/>
+<meta http-equiv="Pragma" content="no-cache"/>
+<link rel="stylesheet" type="text/css" href="$pwd/css/top_level.css" />
+<link rel="shortcut icon" href="$pwd/pixmaps/favicon.ico" />
+<script type="text/javascript" src="$pwd/js/calendar_db.js"></script>
+<link rel="stylesheet" type="text/css" href="$pwd/js/calendar.css" />
+<script type="text/javascript" src="$pwd/js/helper.js"></script>
+</head>
+<body>
+<table style="width: 100%;">
+<tr>
+<td style="width: 33%"><img src="$pwd/pixmaps/calendar.png" alt="calendar.png" /></td>
+<td style="width: 33%; text-align: center;"><a href="$pwd/logout.php">
+    <img onclick="window.location.href='$pwd/logout.php'" style="border: 0" src="$pwd/pixmaps/exit.png" width="32" height="32" alt="Logout" /><br/>Logout</a></td>
+<td style="text-align: right"><span style="font-size: 1.6em">DAViCal Web Calendar</span><br/> - A Web Interface for <a href="javascript: newwin('http://www.davical.org/');">DAViCal</a></td>
+</tr>
+</table>
+_HEAD;
+
+//file_put_contents('/tmp/davical.log', $head);
+
+print $head;
+
diff --git a/include/menu.inc.php b/include/menu.inc.php
new file mode 100644 (file)
index 0000000..fa5236d
--- /dev/null
@@ -0,0 +1,58 @@
+<?php
+/* $Id$ */
+require_once 'config.inc.php';
+require_once 'helper.php';
+
+$pwd = WEB_ROOT;
+if ($pwd[strlen($pwd)-1] == '/')
+    $pwd = substr($pwd, 0, -1);
+$pix = $pwd.'/pixmaps';
+
+$history = $_SERVER['PHP_SELF'];
+$query = construct_URL($_SERVER['QUERY_STRING'], array('etag', 'referer', 'cal'));
+if (count($query) > 0) {
+    $part = '';
+    foreach ($query as $key => $value) {
+        if (strcasecmp($key, 'referer') === 0)
+            continue;
+        $part .= "&$key=$value";
+    }
+    //$history .= substr($part, 0, -1);
+    $history .= $part;
+}
+//print $history;
+//exit;
+$menu = <<<_MENU
+<div id="menu">
+<table>
+<tr>
+    <td><a href="$pwd/events/new_event.php?referer=$history">
+    <img src="$pix/add-event.png" width="21" height="19" alt="new event" />New</a></td>
+    <td><a href="$pwd/events/edit_event.php?referer=$history">
+    <img src="$pix/edit-event.png" width="21" height="19"
+    alt="Edit event" />Edit</a></td>
+    <td><a href="$pwd/events/delete_event.php?referer=$history">
+    <img src="$pix/delete-event.png" width="21" height="19"
+    alt="Delete event" />Delete</a></td>
+    <td colspan="2"></td>
+    <td></td>
+    <td><a href="$pwd/navigate/goto_today.php"><img src="$pix/goto-today.png" width="21" height="19" alt="Goto today" />Today</a></td>
+    <td><a href="$pwd/navigate/show_day.php"><img src="$pix/day.png" width="21" height="19" alt="Show one day" />Day</a></td>
+    <td><a href="$pwd/navigate/show_week.php"><img src="$pix/week.png" width="21" height="19" alt="Show one week" />Week</a></td>
+    <td><a href="$pwd/navigate/show_month.php"><img src="$pix/month.png" width="21" height="19" alt="Show one month" />Month</a></td>
+    <td></td>
+    <td colspan="2"></td>
+    <td><a href="$pwd/utils/print.php" onclick="javascript: alert('Printing not available'); return false;">
+    <img src="$pix/print.png" width="21" height="19" alt="Print" />Print</a></td>
+    <td><a href="$pwd/events/update.php?referer=$history">
+    <img src="$pix/reload.png" width="21" height="19"
+    alt="Update events" />Update</a></td>
+    <td><a href="$pwd/utils/configure.php"><img src="$pix/configuration.png" width="21" height="19" alt="Configure Web Calendar" />Config</a></td>
+</tr>
+</table>
+</div>
+_MENU;
+
+//file_put_contents('/tmp/davical.log', $menu, FILE_APPEND);
+print $menu;
+
diff --git a/include/user_settings.inc.php b/include/user_settings.inc.php
new file mode 100644 (file)
index 0000000..b1b3169
--- /dev/null
@@ -0,0 +1,132 @@
+<?php
+/* $Id$ */
+require_once 'helper.php';
+
+class CalendarInfo {
+    public $name;
+    public $color;
+    public $config;
+}
+
+class UserSettings {
+    private $calendars;
+    private $uid;
+    private $viewStyle;
+    private $startHour;
+    private $endHour;
+    private $startWeek;
+    private $role;
+    private $timeout;
+    private $timezone;
+    private $session_start;
+
+    public function __construct($uid) {
+        $this->calendars = array();
+        $this->uid = $uid;
+        $this->session_start = time();
+    }
+
+    public function setCalendars($calendars) {
+        $this->calendars = $calendars;
+    }
+
+    public function addCalendar($id, CalendarInfo $calendar) {
+        $this->calendars[$id] = $calendar;
+    }
+
+    public function removeCalendar($id) {
+        unset($this->calendars[$id]);
+    }
+
+    public function setSettings($settings) {
+        //file_put_contents('/tmp/davical.log',
+        //   __FILE__ . ": " . var_export($settings, TRUE), FILE_APPEND);
+        $first = TRUE;
+        foreach ($settings as $row) {
+            if ($first) {
+                $this->viewStyle = $row['userview'];
+                $this->startWeek = ($row['weekstart']) ? TRUE : FALSE;
+                $this->startHour = $row['daystart'];
+                $this->endHour = $row['dayend'];
+                $this->role = (int) $row['userrole'];
+                $this->timeout = (int) $row['timeout'];
+                $this->timezone = $row['timezone'];
+                $first = FALSE;
+            }
+            if (isset($row['id']) && $row['id'] >= 0) {
+                $calendar = new CalendarInfo();
+                $calendar->name = $row['name'];
+                $calendar->color = $row['color'];
+                $calendar->config = $row['config'];
+                $this->calendars[$row['id']] = $calendar;
+            }
+        }
+        $this->isTimeout(TRUE);
+    }
+
+    public function getCalendars() {
+        return $this->calendars;
+    }
+
+    public function getCalendar($name) {
+        foreach ($this->calendars as $calendar) {
+            if ($calendar->name == $name)
+                return $calendar->config;
+        }
+        return NULL;
+    }
+
+    public function getViewStyle() {
+        return $this->viewStyle;
+    }
+
+    public function getStartWeek() {
+        return $this->startWeek;
+    }
+
+    public function getStartHour() {
+        return $this->startHour;
+    }
+
+    public function getEndHour() {
+        return $this->endHour;
+    }
+
+    public function getRole() {
+        return (int) $this->role;
+    }
+
+    public function getTimeout() {
+        return $this->timeout;
+    }
+
+    public function isTimeout($reset = FALSE) {
+        $terminate = TRUE;
+        $now = time();
+        
+        if ($reset)
+            $this->session_start = $now;
+
+        //echo "$this->session_start -> $this->timeout -> $now<br/>";
+        $_SESSION['isTimeout'] = TRUE;
+        if ($this->session_start + $this->timeout > $now) {
+            $this->session_start = $now;
+            $terminate = FALSE;
+            $remain = $this->timeout / 60;
+            setcookie('WEBCAL_EXPIRE', $remain, 0, $_SESSION['WEB_ROOT']);
+        }
+        else {
+            header('Location: ' . $_SESSION['WEB_ROOT'] . 'logout.php');
+            exit;
+        }
+    }
+
+    public function getUid() {
+        return $this->uid;
+    }
+
+    public function getTimezone() {
+        return $this->timezone;
+    }
+
+}
diff --git a/index.php b/index.php
new file mode 100644 (file)
index 0000000..52341cb
--- /dev/null
+++ b/index.php
@@ -0,0 +1,70 @@
+<?php
+/* $Id$ */
+if (! file_exists('config.inc.php') || filesize('config.inc.php') == 0) {
+    header("Location: install/install.php");
+    exit();
+}
+
+/* remove pgsql.php from root. Accidentally placed there in release 0.7.4 */
+$cwd = dirname(__FILE__);
+if (file_exists("$cwd/pgsql.php")) {
+    if (! unlink("$cwd/pgsql.php")) {
+        echo "An unrecoverable error has occured call the administrator an inform
+            him that he should read the error log file for further assistans";
+        error_log("$cwd/pgsql.php: Must be deleted.\nI tried but it failed due to
+                missing permissions. You must delete it manually");
+        exit;
+    }
+}
+
+$timezone_offset = date("Z");
+
+include_once 'config.inc.php';
+require_once 'user_validate.php';
+require_once 'utils/helper.php';
+
+$_SESSION['timezone_offset'] = $timezone_offset;
+
+$_SESSION['__ROOT__'] = TOP_FOLDER;
+
+if (PHP_VERSION < 5.1) {
+    die("PHP >= 5.1 is required to run this application");
+}
+
+valid_user();
+
+if (strtoupper($_SESSION['user_settings']->getUid()) == 'ADMIN' && newUpdates()) {
+    $pwd = WEB_ROOT;
+    if ($pwd[strlen($pwd)-1] == '/')
+        $pwd = substr($pwd, 0, -1);
+    
+    print '<?xml version="1.0" encoding="utf-8"?>
+    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+    <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+        <head>
+            <title>Web Calendar</title>
+            <link rel="stylesheet" type="text/css"
+                href="'.$pwd.'/css/top_level.css" />
+            <link rel="shortcut icon"
+                href="'.$pwd.'/pixmaps/favicon.ico" />
+            <script type="text/javascript" src="'.$pwd.'/js/helper.js"></script>
+        </head>
+        <body>
+            <div id="update_msg">
+                <h3 style="text-align: center">
+                New updates available for the database
+                </h3>
+                <div style="text-align: center">
+                <button type="button" name="cancel" 
+                    onclick="javascript:redirect('."'navigate/goto_today.php'".')">Skip</button>
+                <button type="button" value="upgrade"
+                    onclick="javascript:redirect('."'utils/upgrade_db.php'".')">Upgrade</button>
+                </div>
+            </div>
+        </body>
+    </html>';
+}
+else
+    include 'navigate/goto_today.php';
+?>
diff --git a/install/config.inc.php.patch b/install/config.inc.php.patch
new file mode 100644 (file)
index 0000000..5f077dd
--- /dev/null
@@ -0,0 +1,42 @@
+--- ../0.7.4/config.inc.php    2011-03-03 02:26:11.565256656 +0100
++++ ../config.inc.php  2011-03-11 21:57:11.855624358 +0100
+@@ -35,15 +35,17 @@
+     require_once 'utils/authenticate.php';
+     require_once 'utils/helper.php';
++    session_cache_limiter('private');
++    if (isset($_COOKIE['WEBCAL_EXPIRE'])) {
++        $expire = $_COOKIE['WEBCAL_EXPIRE'];
++        session_cache_expire($expire);
++        unset($_COOKIE['WEBCAL_EXPIRE']);
++        setcookie('WEBCAL_EXPIRE', FALSE);
++    }
++    else
++        session_cache_expire(TIMEOUT);
+     session_start();
+-
+-    if (isset($_SESSION['user_settings']))
+-        $timeout = $_SESSION['user_settings']->getTimeout();
+-
+-    if (isset($timeout))
+-        ini_set('session.gc_maxlifetime', $timeout);
+-    else if (defined('TIMEOUT'))
+-        ini_set('session.gc_maxlifetime', TIMEOUT);
++    $_SESSION['WEB_ROOT'] = WEB_ROOT;
+     error_reporting(E_ALL ^ E_NOTICE ^ E_USER_NOTICE);
+@@ -69,4 +71,11 @@
+         ini_set('log_errors', 'on');
+         ini_set('error_log', LOG_FILE);
+     }
+-?>
++
++    if (! isset($_SESSION['isTimeout'])) {
++      if (isset($_SESSION['user_settings']))
++              $_SESSION['user_settings']->isTimeout();
++    }
++    else
++      unset($_SESSION['isTimeout']);
++?>
+\ No newline at end of file
diff --git a/install/config.inc.php1.patch b/install/config.inc.php1.patch
new file mode 100644 (file)
index 0000000..f754357
--- /dev/null
@@ -0,0 +1,15 @@
+--- ../../0.8.0/config.inc.php 2011-03-19 17:23:46.950510408 +0100
++++ ../config.inc.php  2011-03-22 00:57:37.957420952 +0100
+@@ -45,6 +45,7 @@
+     else
+         session_cache_expire(TIMEOUT);
+     session_start();
++    header('Cache-Control: private');
+     $_SESSION['WEB_ROOT'] = WEB_ROOT;
+     error_reporting(E_ALL ^ E_NOTICE ^ E_USER_NOTICE);
+@@ -78,4 +79,3 @@
+     }
+     else
+       unset($_SESSION['isTimeout']);
+-?>
diff --git a/install/install.php b/install/install.php
new file mode 100644 (file)
index 0000000..3b0454f
--- /dev/null
@@ -0,0 +1,667 @@
+<?php
+/* $Id$ */
+
+if (version_compare(PHP_VERSION, '5.1.3') === -1) {
+    die("PHP >= 5.1.3 is required to run this application");
+}
+
+error_reporting(E_ALL);
+ini_set('display_errors', 0);
+
+session_start();
+
+$root = getcwd();
+chdir('..');
+$top = getcwd();
+chdir($root);
+
+ini_set('include_path', "$top:$top/navigate:$top/utils:$top/include:$top/caldav:$top/caldav/awl:$top/templates:$top/events");
+
+include_once "$top/utils/timezone.php";
+include_once "$top/utils/helper.php";
+
+$db_drivers = array('sqlite','pgsql');
+
+/* Test for an existing config.inc.php */
+function remove_config() {
+    if (file_exists("../config.inc.php")) {
+        $postfix = date("c");
+        copy("../config.inc.php", "../$postfix-config.inc.php");
+        unlink("../config.inc.php");
+    }
+}
+
+$config_inc = <<<_CONFIG
+    /*******************************************************************
+     * Change only below this line.
+     * But in generel you should not changes these settings
+     * except via the setup script.
+     ******************************************************************/
+
+    define('TITLE', __TITLE__);
+    define('TIMEZONE', __TIMEZONE__);
+    define('ERROR_DISPLAY', __ERROR_DISPLAY__);
+    define('ERROR_LOG', __ERROR_LOG__);
+    define('LOG_FILE', __LOG_FILE__);
+    define('TOP_FOLDER', __TOP_FOLDER__);
+    /* / at the end of WEB_ROOT is mandatory */
+    define('WEB_ROOT', __WEB_ROOT__);
+    define('DBDRIVER', __DBDRIVER__);
+    define('DNS', __DNS__);
+    define('ADMIN_MAIL', __ADMIN_MAIL__);
+
+    /* Settings which can be change by individual users.
+     * Settings are change in config from the menu
+     */
+    define('VIEW_STYLE', __VIEW_STYLE__);
+    define('TIMEOUT', __TIMEOUT__);
+    define('WEEK_START_SUNDAY', __WEEK_START_SUNDAY__);
+    define('START_HOUR', __START_HOUR__);
+    define('END_HOUR', __END_HOUR__);
+
+    /*******************************************************************
+     * Do not change below this line
+     * All changes should be done above
+     ******************************************************************/
+
+    require_once 'include/user_settings.inc.php';
+    require_once 'utils/authenticate.php';
+    require_once 'utils/helper.php';
+
+    session_cache_limiter('private');
+    if (isset(\$_COOKIE['WEBCAL_EXPIRE'])) {
+        \$expire = \$_COOKIE['WEBCAL_EXPIRE'];
+        session_cache_expire(\$expire);
+        unset(\$_COOKIE['WEBCAL_EXPIRE']);
+        setcookie('WEBCAL_EXPIRE', FALSE);
+    }
+    else
+        session_cache_expire(TIMEOUT);
+    session_start();
+    header('Cache-Control: private');
+    \$_SESSION['WEB_ROOT'] = WEB_ROOT;
+
+    error_reporting(E_ALL ^ E_NOTICE ^ E_USER_NOTICE);
+
+    if (isset(\$_SESSION['user_settings'])) {
+        ini_set('date.timezone', \$_SESSION['user_settings']->getTimezone());
+    }
+    else if (defined('TIMEZONE')) {
+        ini_set('date.timezone', TIMEZONE);
+    }
+    else {
+        ini_set('date.timezone', 'Etc/UTC');
+    }
+
+    if (defined('ERROR_DISPLAY')) {
+        if (ERROR_DISPLAY) {
+            ini_set('display_errors', 'on');
+        }
+        else {
+            ini_set('display_errors', 'off');
+        }
+    }
+    if (defined('ERROR_LOG') && ERROR_LOG) {
+        ini_set('log_errors', 'on');
+        ini_set('error_log', LOG_FILE);
+    }
+
+    if (! isset(\$_SESSION['isTimeout'])) {
+       if (isset(\$_SESSION['user_settings']))
+               \$_SESSION['user_settings']->isTimeout();
+    }
+    else
+       unset(\$_SESSION['isTimeout']);
+_CONFIG;
+
+$config_apache = <<<_CONFIG
+    Alias __ALIAS__ "__TOP__"
+    <Directory "__TOP__">
+        Options Multiviews SymLinksIfOwnerMatch
+        php_value include_path '.:__TOP__:__TOP__navigate:__TOP__utils:__TOP__include:__TOP__caldav:__TOP__caldav/awl:__TOP__templates:__TOP__events'
+    </Directory>
+_CONFIG;
+
+$htaccess = <<<_HTACCESS
+        Options None
+        Order deny,allow
+        Deny from all
+        Allow from 127.0.0.1/32 ::1/128
+_HTACCESS;
+
+$head = <<<_HEAD
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+<title>Web Calendar Installation</title>
+<meta name="generator" content="Bluefish 1.0.7"/>
+<meta name="author" content="Michael Rasmussen"/>
+<meta name="date" content="2009-03-23T20:45:48+0100"/>
+<meta name="copyright" content="© 2007, Michael Rasmussen. Licensed under GPL"/>
+<meta name="keywords" content=""/>
+<meta name="description" content=""/>
+<meta name="ROBOTS" content="NOINDEX, NOFOLLOW"/>
+<meta http-equiv="content-type" content="application/xhtml+xml; charset=UTF-8"/>
+<meta http-equiv="content-style-type" content="text/css"/>
+<meta http-equiv="expires" content="-1"/>
+<meta http-equiv="Pragma" content="no-cache"/>
+<link rel="stylesheet" type="text/css" href="../css/top_level.css" />
+<link rel="shortcut icon" href="../pixmaps/favicon.ico" />
+</head>
+<body>
+<table style="width: 100%;">
+<tr>
+<td style="width: 33%"><img src="../pixmaps/calendar.png" alt="calendar.png" /></td>
+<td style="width: 33%; text-align: center;"><img style="border: 0" src="../pixmaps/stock_init.png" width="48" height="48" alt="Setup"/></td>
+<td style="text-align: right"><span style="font-size: 1.6em">DAViCal Web Calendar</span><br/> - A Web Interface for <a href="javascript: newwin('http://rscds.sourceforge.net/');">DAViCal</a></td>
+</tr>
+</table>
+_HEAD;
+
+$foot = <<<_FOOTER
+<div id="footer">
+<p>
+    <a href="http://validator.w3.org/check?uri=referer">
+        <img src="http://www.w3.org/Icons/valid-xhtml10-blue.png"
+        alt="Valid XHTML 1.0 Strict" height="31" width="88" />
+    </a>
+    <a href="http://jigsaw.w3.org/css-validator/check/referer">
+        <img src="http://www.w3.org/Icons/valid-css2-blue.png"
+        alt="Valid CSS 2.0" height="31" width="88" />
+    </a>
+</p>
+</div>
+</body>
+</html>
+_FOOTER;
+
+function shutdown() {
+    global $head, $foot;
+
+    $isError = false;
+    if ($error = error_get_last()) {
+        switch($error['type']) {
+            case E_ERROR:
+            case E_CORE_ERROR:
+            case E_COMPILE_ERROR:
+            case E_USER_ERROR:
+                $isError = true;
+                break;
+        }
+    }
+
+    if ($isError) {
+        print $head;
+        print '<div id=\"ui\">';
+        if ($error['message'] == "Class 'PDO' not found") {
+            echo '<p style="text-align: center; font-size: 1.5em">
+                This install cannot continue because the module PDO is not installed</p>';
+        }
+        else
+            echo "<p style=\"text-align: center; font-size: 1.5em\">Script execution halted ({$error['message']})</p>";
+        print '</div>';
+        print $foot;
+    }
+    remove_config();
+}
+
+register_shutdown_function('shutdown');
+
+function get_server_timezone() {
+    $tz_abbr = date('T');
+    return timezone_name_from_abbr($tz_abbr);
+}
+
+function stage_1() {
+    switch ($_POST['driver']) {
+        case 'sqlite':
+            $dns = <<<_DNS
+            <tr>
+                <td class="config">Database file</td>
+                <td class="config">
+                    <input type="text" name="dns"/>
+                    <input type="hidden" name="driver" value="{$_POST['driver']}"/>
+                </td>
+            </tr>
+_DNS;
+            break;
+        case 'pgsql':
+            $dns = <<<_DNS
+            <tr>
+                <td class="config" colspan="3">
+                    Below you will find reasonable default values for most.<br/>
+                    <span style="font-weight: bold">database name</span>: Name of an exiting database used as template<br/>
+                    on Postgresql this database is normally named template1<br/>
+                    You should normally not need any other information than the following:<br/>
+                    <span style="font-weight: bold">postgresql dba</span>: An existing postgresql user with permission to create<br/>
+                    new databases and users.<br/>
+                    <span style="font-weight: bold">postgresql dba password</span>: Password for this user<br/>
+                    <span style="font-weight: bold">Password for webcal admin</span>: Password for the webcal admin user<br/>
+                </td>
+            </tr>
+            <tr>
+                <td class="config">host</td>
+                <td class="config">
+                    <input type="text" name="dns[]" value="localhost"/>
+                </td>
+            </tr>
+            <tr>
+                <td class="config">port</td>
+                <td class="config">
+                    <input type="text" name="dns[]" value="5432"/>
+                </td>
+            </tr>
+            <tr>
+                <td class="config">database name</td>
+                <td class="config">
+                    <input type="text" name="dns[]" value="template1"/>
+                </td>
+            </tr>
+            <tr>
+                <td class="config">postgresql dba</td>
+                <td class="config">
+                    <input type="text" name="dns[]" value="root"/>
+                </td>
+            </tr>
+            <tr>
+                <td class="config">postgresql dba password</td>
+                <td class="config">
+                    <input type="password" name="dns[]" value="password"/>
+                    <input type="hidden" name="driver" value="{$_POST['driver']}"/>
+                </td>
+            </tr>
+_DNS;
+            break;
+    }
+    return <<<__FORM
+    <h1 style="text-align: center">Setup: Stage 1/4</h1>
+    <form action="{$_SERVER['PHP_SELF']}" method="post">
+        <table class="config">
+            $dns
+            <tr>
+                <td class="config">Password for webcal admin</td>
+                <td class="config"><input name="pwd1" type="password"/></td>
+            </tr>
+            <tr>
+                <td class="config">Repeat Password</td>
+                <td class="config"><input name="pwd2" type="password"/></td>
+            </tr>
+            <tr>
+                <td class="config" colspan="2" style="text-align: center">
+                    <input type="hidden" name="stage" value="2"/>
+                    <input type="submit" name="action" value=">> Next"/>
+                </td>
+            </tr>
+        </table>
+    </form>
+__FORM;
+}
+
+function stage_2($top, $root) {
+    include_once "$top/utils/persistens.php";
+    global $timezones, $VERSION;
+    $dns = '';
+    $tz = '';
+
+    try {
+        if ($_POST['pwd1'] != $_POST['pwd2'])
+            throw new Exception("Password does not compare");
+        if ($_POST['driver'] == 'sqlite') {
+            $dns = $_POST['dns'];
+            $db = Persistens::getInstance($_POST['driver'], "$top/$dns");
+            $name = NULL;
+            $pwd = NULL;
+        }
+        else {
+            $dns = "host={$_POST['dns'][0]} port={$_POST['dns'][1]} dbname={$_POST['dns'][2]} user={$_POST['dns'][3]} password={$_POST['dns'][4]}";
+            $db = Persistens::getInstance($_POST['driver'], $dns);
+            $name = "webcal";
+            $user = "webcal";
+            $pwd = $_POST['pwd1'];
+            $db->initDatabase($name, $pwd);
+            $db = NULL;
+            $dns = "host={$_POST['dns'][0]} port={$_POST['dns'][1]} dbname=$name user=$user password=$pwd";
+            $db = Persistens::getInstance($_POST['driver'], $dns);
+            //throw new Exception(var_export($dns, true));
+        }
+        $db->createDatabase($name);
+        $db->execute("insert into about (version) values ('$VERSION')");
+        $data['uid'] = 'admin';
+        $data['pwd'] = sha1($_POST['pwd1']);
+        $data['timezone'] = get_server_timezone();
+        $data['userrole'] = 0;
+        $data['seckey'] = '';
+        $data['pubkey'] = '';
+        $data['timeout'] = 15 * 60;
+        $data['view'] = 'week';
+        $data['week_start'] = false;
+        $data['start'] = 8;
+        $data['end'] = 17;
+        $db->newUser($data);
+    }
+    catch (Exception $ex) {
+        remove_config();
+        $result = "<p style=\"text-align: center\">stage2: {$ex->getMessage()}</p>";
+        $result .= "<p style=\"text-align: center\">
+            <input type=\"button\" value=\"Back\"
+             onclick=\"javascript: history.back()\"/></p>";
+        return $result;
+    }
+    $config['DBDRIVER'] = $_POST['driver'];
+    $config['DNS'] = $dns;
+    $_SESSION['config'] = $config;
+    foreach ($timezones as $timezone) {
+        $tz .= "<option value=\"$timezone\"";
+        if ($timezone == get_server_timezone())
+            $tz .= ' selected="true"';
+        $tz .= ">$timezone</option>";
+    }
+    return <<<__FORM
+    <h1 style="text-align: center">Setup: Stage 2/4</h1>
+    <form action="{$_SERVER['PHP_SELF']}" method="post">
+        <table class="config">
+            <tr>
+                <td class="config">Application web root</td>
+                <td class="config"><input name="web_root" type="text"/></td>
+            </tr>
+            <tr>
+                <td class="config">Application title</td>
+                <td class="config"><input name="title" type="text"/></td>
+            </tr>
+            <tr>
+                <td class="config">Server timezone</td>
+                <td class="config"><select name="timezone">$tz</select></td>
+            </tr>
+            <tr>
+                <td class="config">Display errors in browser</td>
+                <td class="config">
+                    <select name="display_error">
+                        <option value="no">No</option>
+                        <option value="yes">Yes</option>
+                    </select>
+                </td>
+            </tr>
+            <tr>
+                <td class="config">Log errors</td>
+                <td class="config">
+                    <select name="log_error">
+                        <option value="no">No</option>
+                        <option value="yes">Yes</option>
+                    </select>
+                </td>
+            </tr>
+            <tr>
+                <td class="config">Log file</td>
+                <td class="config"><input name="log_file" type="text"/></td>
+            </tr>
+            <tr>
+                <td class="config">Administrator email</td>
+                <td class="config"><input name="admin_email" type="text"/></td>
+            </tr>
+            <tr>
+                <td class="config" colspan="2" style="text-align: center">
+                    <input type="hidden" name="stage" value="3"/>
+                    <input type="submit" name="action" value=">> Next"/>
+                </td>
+            </tr>
+        </table>
+    </form>
+__FORM;
+}
+
+function stage_3($top, $root) {
+    $config = $_SESSION['config'];
+    $config['WEB_ROOT'] = $_POST['web_root'];
+    $config['TITLE'] = $_POST['title'];
+    $config['TIMEZONE'] = $_POST['timezone'];
+    $config['ERROR_DISPLAY'] = ($_POST['display_error'] == 'yes')? 'TRUE' : 'FALSE';
+    $config['ERROR_LOG'] = ($_POST['log_error'] == 'yes')? 'TRUE' : 'FALSE';
+    $config['LOG_FILE'] = $_POST['log_file'];
+    $config['ADMIN_MAIL'] = $_POST['admin_email'];
+    $_SESSION['config'] = $config;
+    return <<<__FORM
+    <h1 style="text-align: center">Setup: Stage 3/4</h1>
+    <form action="{$_SERVER['PHP_SELF']}" method="post">
+        <table class="config">
+            <tr>
+                <td class="config">Default view</td>
+                <td class="config">
+                    <select name="view_style">
+                        <option value="day">Day</option>
+                        <option value="week" selected="true">Week</option>
+                        <option value="month">Month</option>
+                    </select>
+                </td>
+            </tr>
+            <tr>
+                <td class="config">Default session timeout in minuts</td>
+                <td class="config"><input name="timeout" type="text" value="60"/></td>
+            </tr>
+            <tr>
+                <td class="config">Default start of week Sunday</td>
+                <td class="config">
+                    <select name="week_start_sunday">
+                        <option value="no" selected="true">No</option>
+                        <option value="yes">Yes</option>
+                    </select>
+                </td>
+            </tr>
+            <tr>
+                <td class="config">Default day start</td>
+                <td class="config"><input name="start_day" type="text" value="8"/></td>
+            </tr>
+            <tr>
+                <td class="config">Default day end</td>
+                <td class="config"><input name="end_day" type="text" value="17"/></td>
+            </tr>
+            <tr>
+                <td class="config" colspan="2" style="text-align: center">
+                    <input type="hidden" name="stage" value="4"/>
+                    <input type="submit" name="action" value=">> Next"/>
+                </td>
+            </tr>
+        </table>
+    </form>
+__FORM;
+}
+
+function stage_4($top, $root) {
+    include_once "$top/utils/persistens.php";
+    global $config_inc, $config_apache, $htaccess;
+
+    $config = $_SESSION['config'];
+    $config['VIEW_STYLE'] = $_POST['view_style'];
+    $config['TIMEOUT'] = $_POST['timeout'] * 60;
+    $config['WEEK_START_SUNDAY'] =
+        ($_POST['week_start_sunday'] == 'yes')? 'TRUE' : 'FALSE';
+    $config['START_HOUR'] = $_POST['start_day'];
+    $config['END_HOUR'] = $_POST['end_day'];
+    $config['TOP_FOLDER'] = $top;
+    $start = strpos($config['WEB_ROOT'], "/");
+    if ($start != 0 || $start === FALSE)
+        $config['WEB_ROOT'] = '/'.$config['WEB_ROOT'];
+    $end = strrpos($config['WEB_ROOT'], "/");
+    if ($end != strlen($config['WEB_ROOT']) - 1 || $end == FALSE)
+        $config['WEB_ROOT'] = $config['WEB_ROOT'].'/';
+    foreach ($config as $key => $value) {
+        if ($value !== 'TRUE' && $value !== 'FALSE')
+            $value = "'$value'";
+        $config_inc = str_replace('__'.$key.'__', $value, $config_inc);
+    }
+    // Create config.inc.php
+    $file = "<?php\n" . $config_inc . "\n?>\n";
+    file_put_contents("$top/config.inc.php", $file);
+    // Create .htaccess in install
+    if (! file_exists("$root/.htaccess"))
+        file_put_contents("$root/.htaccess", $htaccess);
+    // Create webcal in conf.d
+    $root = $config['WEB_ROOT'];
+    $root = substr($root, 0, -1);
+    $config_apache = str_replace('__ALIAS__', $root, $config_apache);
+    $config_apache =
+        str_replace('__TOP__', $config['TOP_FOLDER'].'/', $config_apache);
+    $result = '<p style="text-align: center">If you haven\'t done basic setup
+        by executing the setup script you must Copy/paste the following
+        to /etc/[apache config]/conf.d/webcal.conf and restart apache<br/>';
+    $result .= '<textarea rows="10" cols="60">';
+    $result .= htmlspecialchars($config_apache);
+    $result .= '</textarea></p>';
+    $result .= '<p style="text-align: center">After restart of
+        apache follow this link to webcal';
+    $url = getServerUrl($config['WEB_ROOT']);
+    $webroot = getServerUrl($config['WEB_ROOT'], $root);
+    $result .= " <a href=\"$url\">webcal</a></p>";
+    $result .= '<p style="text-align: center">Remember to disable ' .
+        'write access to ' . "$top/install";
+    // update database
+    if ($config['DBDRIVER'] == 'sqlite') {
+        $db = Persistens::getInstance($config['DBDRIVER'], $top."/".$config['DNS']);
+    }
+    else {
+        $db = Persistens::getInstance($config['DBDRIVER'], $config['DNS']);
+    }
+    $db->changeDefault($config);
+    $_SESSION = array();
+    if (isset($_COOKIE[session_name()])) {
+        setcookie(session_name(), '', time()-42000, '/');
+    }
+    session_destroy();
+    return $result;
+}
+
+if (count($_POST) > 0) {
+    if (isset($_POST['stage'])) {
+        switch ($_POST['stage']) {
+            case '1': $page = stage_1(); break;
+            case '2': $page = stage_2($top, $root); break;
+            case '3': $page = stage_3($top, $root); break;
+            case '4': $page = stage_4($top, $root); break;
+            case '5': $page = stage_4($top, $root); break;
+            default: trigger_error("Unknown stage", E_USER_ERROR);
+        }
+    }
+    else {
+        remove_config();
+        trigger_error("Unknown stage", E_USER_ERROR);
+    }
+}
+else {
+    remove_config();
+
+    $error = FALSE;
+    $page = '<h1 style="text-align: center">Setup: Stage 0/4</h1>';
+    $page .= '<table class="config">';
+    $page .= '<tr><td>Checking if module mcrypt is available</td>';
+    try {
+        $mcrypt = MCRYPT_ENCRYPT;
+        if (is_integer($mcrypt)) {
+            $page .= '<td><span style="font-size: 1.2em; font-weight: bold;
+             background: green">OK</span></td></tr>';
+        }
+        else {
+            $page .= '<td><span style="font-size: 1.2em; font-weight: bold;
+             background: red">NO</span></td></tr>';
+            $error = TRUE;
+        }
+    } catch (ErrorException $ex) {
+        $page .= '<td><span style="font-size: 1.2em; font-weight: bold;
+         background: red">NO</span></td></tr>';
+        $error = TRUE;
+    }
+    $page .= '<tr><td>Checking if module PDO is available</td>';
+    try {
+        $avail = PDO::getAvailableDrivers();
+        $page .= '<td><span style="font-size: 1.2em; font-weight: bold;
+         background: green">OK</span></td></tr>';
+    }
+    catch (PDOException $ex) {
+        $page .= '<td><span style="font-size: 1.2em; font-weight: bold;
+         background: red">NO</span></td></tr>';
+        $error = TRUE;
+    }
+    //$page .= "<tr><td cols=\"2\">".var_export($avail, true)."</td></tr>";
+    $page .= "<tr><td>Checking if a suitable PDO driver is available</td>";
+    $dbms = NULL;
+    foreach ($db_drivers as $driver) {
+        $driver = strtolower($driver);
+        if (in_array($driver, $avail))
+            $dbms .= "<option value=\"$driver\">$driver</option>";
+    }
+    if ($dbms)
+        $page .= '<td><span style="font-size: 1.2em; font-weight: bold;
+         background: green">OK</span></td></tr>';
+    else {
+        $page .= '<td><span style="font-size: 1.2em; font-weight: bold;
+         background: red">NO</span></td></tr>';
+        $error = TRUE;
+    }
+    $page .= "<tr><td>Checking if application root is writeable</td>";
+    try {
+        if (@file_put_contents("$top/config.inc.php", "") === FALSE)
+            throw new Exception();
+        $page .= '<td><span style="font-size: 1.2em; font-weight: bold;
+         background: green">OK</span></td></tr>';
+    }
+    catch (Exception $ex) {
+        $page .= '<td><span style="font-size: 1.2em; font-weight: bold;
+         background: red">NO</span></td></tr>';
+        $error = TRUE;
+    }
+    $page .= "<tr><td>Checking if install folder is writeable</td>";
+    try {
+       $test_file = hash("md5", date("r", time()));
+        if (@file_put_contents("$root/$test_file", "") === FALSE)
+            throw new Exception();
+        if (unlink("$root/$test_file") === FALSE)
+            throw new Exception();
+        $page .= '<td><span style="font-size: 1.2em; font-weight: bold;
+         background: green">OK</span></td></tr>';
+    }
+    catch (Exception $ex) {
+        $page .= '<td><span style="font-size: 1.2em; font-weight: bold;
+         background: red">NO</span></td></tr>';
+        $error = TRUE;
+    }
+    $page .= '</table><br/><br/>';
+
+    if (! $error) {
+        $page .= <<<__FORM
+        <form action="{$_SERVER['PHP_SELF']}" method="post">
+            <table class="config">
+                <tr>
+                    <td class="config">
+                        Choose Database system
+                    </td>
+                    <td class="config">
+                        <select name="driver">
+                            $dbms
+                        </select>
+                    </td>
+                </tr>
+                <tr>
+                    <td class="config" colspan="2" style="text-align: center">
+                        <input type="hidden" name="stage" value="1"/>
+                        <input type="submit" name="action" value=">> Next"/>
+                    </td>
+                </tr>
+            </table>
+        </form>
+__FORM;
+    }
+    else {
+               remove_config();
+        $page .= '<p style="text-align: center">Install missing modules or correct
+        errors  marked by
+        <span style="font-size: 1.2em; font-weight: bold;background: red">NO</span> to
+        continue installation</p>';
+    }
+}
+
+print $head;
+
+echo "<div id=\"ui\">$page</div>";
+
+print $foot;
+?>
diff --git a/install/pgsql_db_upgrade_0_7_3.sql b/install/pgsql_db_upgrade_0_7_3.sql
new file mode 100644 (file)
index 0000000..bcacc02
--- /dev/null
@@ -0,0 +1,11 @@
+-- db_upgrade_0_7_3.sql
+-- $Id$
+
+create table about (
+       id integer default 1,
+       version varchar(256) default ''
+);
+insert into about (version) values ('0.7.3');
+
+alter table calendar alter column config type varchar(4000);
+alter table calendar alter column config set default '';
diff --git a/install/pgsql_db_upgrade_0_7_4.sql b/install/pgsql_db_upgrade_0_7_4.sql
new file mode 100644 (file)
index 0000000..063f4bf
--- /dev/null
@@ -0,0 +1,5 @@
+-- db_upgrade_0_7_4.sql
+-- $Id$
+
+update about set version='0.7.4';
+
diff --git a/install/pgsql_db_upgrade_0_7_5.sql b/install/pgsql_db_upgrade_0_7_5.sql
new file mode 100644 (file)
index 0000000..45926da
--- /dev/null
@@ -0,0 +1,12 @@
+-- db_upgrade_0_7_5.sql
+-- $Id$
+
+create table ldap (
+    enable integer default 0,
+    dns varchar(256) default '',
+    tls integer default 0,
+    base_dn varchar(256) default '',
+    user_attr varchar(256) default 'uid');
+
+update about set version='0.7.5';
+
diff --git a/install/pgsql_db_upgrade_0_8_0.sql b/install/pgsql_db_upgrade_0_8_0.sql
new file mode 100644 (file)
index 0000000..a5c0f1b
--- /dev/null
@@ -0,0 +1,5 @@
+-- db_upgrade_0_8_0.sql
+-- $Id$
+
+update about set version='0.8.0';
+
diff --git a/install/pgsql_db_upgrade_0_8_1.sql b/install/pgsql_db_upgrade_0_8_1.sql
new file mode 100644 (file)
index 0000000..aa6b945
--- /dev/null
@@ -0,0 +1,5 @@
+-- db_upgrade_0_8_1.sql
+-- $Id$
+
+update about set version='0.8.1';
+
diff --git a/install/setup b/install/setup
new file mode 100644 (file)
index 0000000..42390bf
--- /dev/null
@@ -0,0 +1,168 @@
+#!/usr/bin/env perl
+# $Id$
+
+use strict;
+use warnings;
+
+BEGIN {
+    eval {
+        use File::Basename;
+        use File::Copy;
+        use File::Find;
+        use File::Path;
+        use Cwd;
+    }; die $@ if $@;
+}
+
+die "Only root may run this" if $> != 0;
+die "Script must be executed from its own folder"
+    unless -e getcwd.'/install.php';
+
+my (@apache_dir, @application);
+
+find(\&find_apache, '/etc', '/usr/local/etc');
+
+sub find_apache {
+    return unless -d;
+    return unless $File::Find::name =~ /\.*etc\/([apache2|httpd]+\/conf.d)\.*/;
+    my $path = "$File::Find::dir";
+    push @apache_dir, $path;
+}
+
+sub find_appl {
+    push @application, $File::Find::name;
+}
+
+sub copy_files {
+    my $path = shift;
+    if (-e $path) {
+        print "Directory exists. Continue [y/n]? ";
+        my $continue = <STDIN>;
+        chomp $continue;
+        exit unless $continue eq 'y';
+    }
+    else {
+        umask 022;
+        mkpath ($path) or die $!;
+    }
+    find(\&find_appl, '..');
+    foreach (@application) {
+        next if /^..$/;
+        my $from = $_;
+        /^..\/(.+)/;
+        do {
+            mkdir ("$path/$1");
+            next;
+        } if -d $from;
+        my ($file, $dir) = fileparse($1);
+        $dir = '' if $dir =~ /^.\//;
+        print STDERR "Copying: $from -> $path/$dir\n";
+        copy($from, "$path/$dir") or die "Copy failed: $!";
+    }
+}
+
+my $config = <<__CONF;
+    Alias __ALIAS__ "__TOP__"
+    <Directory "__TOP__">
+        Options Multiviews SymLinksIfOwnerMatch
+        php_value include_path '.:__TOP__:__TOP__navigate:__TOP__utils:__TOP__include:__TOP__caldav:__TOP__caldav/awl:__TOP__templates:__TOP__events'
+    </Directory>
+__CONF
+
+print <<__HELP;
+Davical Web Calender (C) 2009, Michael Rasmussen.
+Released under GNU General Public License version 3+.
+This license applies to all files except where a different license
+state otherwise.
+
+This script will create the basic installation of Davical web calendar.
+You only need to run this script if you want to install the application
+outside the Apache's web scope.
+
+You will be asked a number of questions which must all be answered.
+The script will try to figure out must of the answers for you and
+present the found information inside []. Should the script not find
+the answers for you (you will see []) or you disaggree to the suggested
+setting you must provide the required setting yourself. If you aggree
+to the found setting you simply press enter.
+
+__HELP
+
+print "Apache2 configuration folder [";
+print $apache_dir[0] if scalar @apache_dir > 0;
+print "]? ";
+my $folder = <STDIN>;
+chomp $folder;
+$folder = $apache_dir[0] unless $folder;
+die "Missing Apache configuration folder\n" unless $folder;
+
+my $url = 'webcal';
+print "Application URL [$url]? ";
+my $url1 = <STDIN>;
+chomp $url1;
+$url1 = $url unless $url1;
+die "Missing application web root\n" unless $url1;
+
+my $cwd = getcwd;
+my ($file, $dir) = fileparse($cwd);
+$cwd = ($dir =~ /(.*)\/$/) ? $1 : $dir;
+print "Application web root [$cwd]? ";
+my $root = <STDIN>;
+chomp $root;
+do {
+    my $home = $ENV{'HOME'};
+    $root =~ /^~(.*)$/;
+    $root = $home.$1;
+} if $root =~ m/^~/;
+$root = $cwd unless $root;
+die "Missing application web root\n" unless $root;
+
+$config =~ s/__TOP__/$root\//g;
+$config =~ s/__ALIAS__/\/$url1/;
+
+print <<_RESULT;
+
+This setup will be used:
+Apache configuration folder:\t$folder
+Application URL:\t\t/$url1
+Application top folder:\t\t$root
+
+Installing this configuration in Apache:
+$config
+_RESULT
+
+print "Continue [Y/n]? ";
+my $continue = <STDIN>;
+chomp $continue unless $continue =~ m/^\n$/;
+exit unless $continue =~ m/^[Yy\n]$/;
+
+copy_files($root) unless $root eq $cwd;
+
+my $config_file = "$folder/conf.d/webcal.conf";
+do {
+    print "$config_file: File exists, overwrite [y/n]? ";
+    my $continue = <STDIN>;
+    chomp $continue unless $continue =~ m/^\n$/;
+    exit unless $continue =~ m/^[Yy\n]$/;
+} if -e $config_file;
+
+open CONF, "> $config_file" or die $!;
+print CONF $config;
+close CONF;
+
+print <<_INFO;
+
+Setup has completed. You need to restart Apache before
+you continue with the installation of WebCal.
+
+After Apache has been restarted point your web browser
+to this address: http://localhost/$url1
+Then simply follow the wizard.
+
+For securety reasons I strongly advice you to only allow
+access to the application via secure HTTP.
+
+Happy hacking:-)
+_INFO
+
+exit 0;
diff --git a/install/sqlite_db_upgrade_0_7_3.sql b/install/sqlite_db_upgrade_0_7_3.sql
new file mode 100644 (file)
index 0000000..433347d
--- /dev/null
@@ -0,0 +1,9 @@
+-- db_upgrade_0_7_3.sql
+-- $Id$
+
+create table about (
+       id integer default 1,
+       version text default ''
+);
+insert into about (version) values ('0.7.3');
+
diff --git a/install/sqlite_db_upgrade_0_7_4.sql b/install/sqlite_db_upgrade_0_7_4.sql
new file mode 100644 (file)
index 0000000..063f4bf
--- /dev/null
@@ -0,0 +1,5 @@
+-- db_upgrade_0_7_4.sql
+-- $Id$
+
+update about set version='0.7.4';
+
diff --git a/install/sqlite_db_upgrade_0_7_5.sql b/install/sqlite_db_upgrade_0_7_5.sql
new file mode 100644 (file)
index 0000000..9cfb4b7
--- /dev/null
@@ -0,0 +1,12 @@
+-- db_upgrade_0_7_5.sql
+-- $Id$
+
+create table ldap (
+    enable integer default 0,
+    dns text default '',
+    tls integer default 0,
+    base_dn text default '',
+    user_attr text default 'uid');
+
+update about set version='0.7.5';
+
diff --git a/install/sqlite_db_upgrade_0_8_0.sql b/install/sqlite_db_upgrade_0_8_0.sql
new file mode 100644 (file)
index 0000000..a5c0f1b
--- /dev/null
@@ -0,0 +1,5 @@
+-- db_upgrade_0_8_0.sql
+-- $Id$
+
+update about set version='0.8.0';
+
diff --git a/install/sqlite_db_upgrade_0_8_1.sql b/install/sqlite_db_upgrade_0_8_1.sql
new file mode 100644 (file)
index 0000000..aa6b945
--- /dev/null
@@ -0,0 +1,5 @@
+-- db_upgrade_0_8_1.sql
+-- $Id$
+
+update about set version='0.8.1';
+
diff --git a/install/timezone.txt b/install/timezone.txt
new file mode 100644 (file)
index 0000000..4455db3
--- /dev/null
@@ -0,0 +1,560 @@
+    { "Africa/Abidjan"                    , 0x000000 },
+    { "Africa/Accra"                      , 0x000055 },
+    { "Africa/Addis_Ababa"                , 0x0000FD },
+    { "Africa/Algiers"                    , 0x000153 },
+    { "Africa/Asmara"                     , 0x00027E },
+    { "Africa/Asmera"                     , 0x0002D4 },
+    { "Africa/Bamako"                     , 0x00032A },
+    { "Africa/Bangui"                     , 0x000395 },
+    { "Africa/Banjul"                     , 0x0003EA },
+    { "Africa/Bissau"                     , 0x000461 },
+    { "Africa/Blantyre"                   , 0x0004C7 },
+    { "Africa/Brazzaville"                , 0x00051C },
+    { "Africa/Bujumbura"                  , 0x000571 },
+    { "Africa/Cairo"                      , 0x0005B5 },
+    { "Africa/Casablanca"                 , 0x00097C },
+    { "Africa/Ceuta"                      , 0x000A58 },
+    { "Africa/Conakry"                    , 0x000D5F },
+    { "Africa/Dakar"                      , 0x000DCA },
+    { "Africa/Dar_es_Salaam"              , 0x000E30 },
+    { "Africa/Djibouti"                   , 0x000E9D },
+    { "Africa/Douala"                     , 0x000EF2 },
+    { "Africa/El_Aaiun"                   , 0x000F47 },
+    { "Africa/Freetown"                   , 0x000FAD },
+    { "Africa/Gaborone"                   , 0x0010BC },
+    { "Africa/Harare"                     , 0x001117 },
+    { "Africa/Johannesburg"               , 0x00116C },
+    { "Africa/Kampala"                    , 0x0011DA },
+    { "Africa/Khartoum"                   , 0x001259 },
+    { "Africa/Kigali"                     , 0x00136C },
+    { "Africa/Kinshasa"                   , 0x0013C1 },
+    { "Africa/Lagos"                      , 0x00141C },
+    { "Africa/Libreville"                 , 0x001471 },
+    { "Africa/Lome"                       , 0x0014C6 },
+    { "Africa/Luanda"                     , 0x00150A },
+    { "Africa/Lubumbashi"                 , 0x00155F },
+    { "Africa/Lusaka"                     , 0x0015BA },
+    { "Africa/Malabo"                     , 0x00160F },
+    { "Africa/Maputo"                     , 0x001675 },
+    { "Africa/Maseru"                     , 0x0016CA },
+    { "Africa/Mbabane"                    , 0x001732 },
+    { "Africa/Mogadishu"                  , 0x001788 },
+    { "Africa/Monrovia"                   , 0x0017E3 },
+    { "Africa/Nairobi"                    , 0x001849 },
+    { "Africa/Ndjamena"                   , 0x0018C8 },
+    { "Africa/Niamey"                     , 0x001934 },
+    { "Africa/Nouakchott"                 , 0x0019A7 },
+    { "Africa/Ouagadougou"                , 0x001A12 },
+    { "Africa/Porto-Novo"                 , 0x001A67 },
+    { "Africa/Sao_Tome"                   , 0x001ACD },
+    { "Africa/Timbuktu"                   , 0x001B22 },
+    { "Africa/Tripoli"                    , 0x001B8D },
+    { "Africa/Tunis"                      , 0x001C87 },
+    { "Africa/Windhoek"                   , 0x001EB1 },
+    { "America/Adak"                      , 0x0020F8 },
+    { "America/Anchorage"                 , 0x00246E },
+    { "America/Anguilla"                  , 0x0027E2 },
+    { "America/Antigua"                   , 0x002837 },
+    { "America/Araguaina"                 , 0x00289D },
+    { "America/Argentina/Buenos_Aires"    , 0x0029F8 },
+    { "America/Argentina/Catamarca"       , 0x002CC3 },
+    { "America/Argentina/ComodRivadavia"  , 0x002E84 },
+    { "America/Argentina/Cordoba"         , 0x00302A },
+    { "America/Argentina/Jujuy"           , 0x00331C },
+    { "America/Argentina/La_Rioja"        , 0x0034D0 },
+    { "America/Argentina/Mendoza"         , 0x003688 },
+    { "America/Argentina/Rio_Gallegos"    , 0x003848 },
+    { "America/Argentina/Salta"           , 0x0039FD },
+    { "America/Argentina/San_Juan"        , 0x003BA9 },
+    { "America/Argentina/San_Luis"        , 0x003D61 },
+    { "America/Argentina/Tucuman"         , 0x004035 },
+    { "America/Argentina/Ushuaia"         , 0x00430E },
+    { "America/Aruba"                     , 0x0044C9 },
+    { "America/Asuncion"                  , 0x00452F },
+    { "America/Atikokan"                  , 0x004814 },
+    { "America/Atka"                      , 0x0048EA },
+    { "America/Bahia"                     , 0x004C50 },
+    { "America/Barbados"                  , 0x004DD9 },
+    { "America/Belem"                     , 0x004E73 },
+    { "America/Belize"                    , 0x004F6E },
+    { "America/Blanc-Sablon"              , 0x0050EA },
+    { "America/Boa_Vista"                 , 0x00519E },
+    { "America/Bogota"                    , 0x0052A7 },
+    { "America/Boise"                     , 0x005313 },
+    { "America/Buenos_Aires"              , 0x0056AA },
+    { "America/Cambridge_Bay"             , 0x005960 },
+    { "America/Campo_Grande"              , 0x005C88 },
+    { "America/Cancun"                    , 0x005F77 },
+    { "America/Caracas"                   , 0x0061B9 },
+    { "America/Catamarca"                 , 0x006220 },
+    { "America/Cayenne"                   , 0x0063C6 },
+    { "America/Cayman"                    , 0x006428 },
+    { "America/Chicago"                   , 0x00647D },
+    { "America/Chihuahua"                 , 0x006994 },
+    { "America/Coral_Harbour"             , 0x006BE3 },
+    { "America/Cordoba"                   , 0x006C75 },
+    { "America/Costa_Rica"                , 0x006F38 },
+    { "America/Cuiaba"                    , 0x006FC2 },
+    { "America/Curacao"                   , 0x0072A0 },
+    { "America/Danmarkshavn"              , 0x007306 },
+    { "America/Dawson"                    , 0x00744A },
+    { "America/Dawson_Creek"              , 0x007767 },
+    { "America/Denver"                    , 0x007941 },
+    { "America/Detroit"                   , 0x007CC7 },
+    { "America/Dominica"                  , 0x008026 },
+    { "America/Edmonton"                  , 0x00807B },
+    { "America/Eirunepe"                  , 0x008433 },
+    { "America/El_Salvador"               , 0x008546 },
+    { "America/Ensenada"                  , 0x0085BB },
+    { "America/Fort_Wayne"                , 0x008A62 },
+    { "America/Fortaleza"                 , 0x008924 },
+    { "America/Glace_Bay"                 , 0x008CCC },
+    { "America/Godthab"                   , 0x009043 },
+    { "America/Goose_Bay"                 , 0x009307 },
+    { "America/Grand_Turk"                , 0x0097C4 },
+    { "America/Grenada"                   , 0x009A73 },
+    { "America/Guadeloupe"                , 0x009AC8 },
+    { "America/Guatemala"                 , 0x009B1D },
+    { "America/Guayaquil"                 , 0x009BA6 },
+    { "America/Guyana"                    , 0x009C03 },
+    { "America/Halifax"                   , 0x009C84 },
+    { "America/Havana"                    , 0x00A19A },
+    { "America/Hermosillo"                , 0x00A50D },
+    { "America/Indiana/Indianapolis"      , 0x00A5EB },
+    { "America/Indiana/Knox"              , 0x00A87C },
+    { "America/Indiana/Marengo"           , 0x00AC13 },
+    { "America/Indiana/Petersburg"        , 0x00AEB9 },
+    { "America/Indiana/Tell_City"         , 0x00B406 },
+    { "America/Indiana/Vevay"             , 0x00B69F },
+    { "America/Indiana/Vincennes"         , 0x00B8DA },
+    { "America/Indiana/Winamac"           , 0x00BB8E },
+    { "America/Indianapolis"              , 0x00B19C },
+    { "America/Inuvik"                    , 0x00BE47 },
+    { "America/Iqaluit"                   , 0x00C13E },
+    { "America/Jamaica"                   , 0x00C460 },
+    { "America/Jujuy"                     , 0x00C525 },
+    { "America/Juneau"                    , 0x00C6CF },
+    { "America/Kentucky/Louisville"       , 0x00CA4D },
+    { "America/Kentucky/Monticello"       , 0x00CE6B },
+    { "America/Knox_IN"                   , 0x00D1F0 },
+    { "America/La_Paz"                    , 0x00D561 },
+    { "America/Lima"                      , 0x00D5C8 },
+    { "America/Los_Angeles"               , 0x00D670 },
+    { "America/Louisville"                , 0x00DA81 },
+    { "America/Maceio"                    , 0x00DE76 },
+    { "America/Managua"                   , 0x00DFB0 },
+    { "America/Manaus"                    , 0x00E063 },
+    { "America/Marigot"                   , 0x00E165 },
+    { "America/Martinique"                , 0x00E1BA },
+    { "America/Mazatlan"                  , 0x00E226 },
+    { "America/Mendoza"                   , 0x00E493 },
+    { "America/Menominee"                 , 0x00E647 },
+    { "America/Merida"                    , 0x00E9C8 },
+    { "America/Mexico_City"               , 0x00EC03 },
+    { "America/Miquelon"                  , 0x00EE7E },
+    { "America/Moncton"                   , 0x00F0F0 },
+    { "America/Monterrey"                 , 0x00F587 },
+    { "America/Montevideo"                , 0x00F7CE },
+    { "America/Montreal"                  , 0x00FAE0 },
+    { "America/Montserrat"                , 0x00FFF6 },
+    { "America/Nassau"                    , 0x01004B },
+    { "America/New_York"                  , 0x010390 },
+    { "America/Nipigon"                   , 0x01089B },
+    { "America/Nome"                      , 0x010BEC },
+    { "America/Noronha"                   , 0x010F6A },
+    { "America/North_Dakota/Center"       , 0x01109A },
+    { "America/North_Dakota/New_Salem"    , 0x01142E },
+    { "America/Panama"                    , 0x0117D7 },
+    { "America/Pangnirtung"               , 0x01182C },
+    { "America/Paramaribo"                , 0x011B62 },
+    { "America/Phoenix"                   , 0x011BF4 },
+    { "America/Port-au-Prince"            , 0x011CA2 },
+    { "America/Port_of_Spain"             , 0x011EBD },
+    { "America/Porto_Acre"                , 0x011DBE },
+    { "America/Porto_Velho"               , 0x011F12 },
+    { "America/Puerto_Rico"               , 0x012008 },
+    { "America/Rainy_River"               , 0x012073 },
+    { "America/Rankin_Inlet"              , 0x0123AB },
+    { "America/Recife"                    , 0x012691 },
+    { "America/Regina"                    , 0x0127BB },
+    { "America/Resolute"                  , 0x012979 },
+    { "America/Rio_Branco"                , 0x012C72 },
+    { "America/Rosario"                   , 0x012D75 },
+    { "America/Santarem"                  , 0x013038 },
+    { "America/Santiago"                  , 0x01313D },
+    { "America/Santo_Domingo"             , 0x0134E6 },
+    { "America/Sao_Paulo"                 , 0x0135AC },
+    { "America/Scoresbysund"              , 0x0138BB },
+    { "America/Shiprock"                  , 0x013BA9 },
+    { "America/St_Barthelemy"             , 0x013F38 },
+    { "America/St_Johns"                  , 0x013F8D },
+    { "America/St_Kitts"                  , 0x0144E0 },
+    { "America/St_Lucia"                  , 0x014535 },
+    { "America/St_Thomas"                 , 0x01458A },
+    { "America/St_Vincent"                , 0x0145DF },
+    { "America/Swift_Current"             , 0x014634 },
+    { "America/Tegucigalpa"               , 0x014755 },
+    { "America/Thule"                     , 0x0147D4 },
+    { "America/Thunder_Bay"               , 0x014A1B },
+    { "America/Tijuana"                   , 0x014D64 },
+    { "America/Toronto"                   , 0x0150D9 },
+    { "America/Tortola"                   , 0x0155F0 },
+    { "America/Vancouver"                 , 0x015645 },
+    { "America/Virgin"                    , 0x015A82 },
+    { "America/Whitehorse"                , 0x015AD7 },
+    { "America/Winnipeg"                  , 0x015DF4 },
+    { "America/Yakutat"                   , 0x016234 },
+    { "America/Yellowknife"               , 0x01659F },
+    { "Antarctica/Casey"                  , 0x0168AF },
+    { "Antarctica/Davis"                  , 0x016923 },
+    { "Antarctica/DumontDUrville"         , 0x0169A0 },
+    { "Antarctica/Mawson"                 , 0x016A32 },
+    { "Antarctica/McMurdo"                , 0x016AA1 },
+    { "Antarctica/Palmer"                 , 0x016DA3 },
+    { "Antarctica/Rothera"                , 0x0170BF },
+    { "Antarctica/South_Pole"             , 0x017135 },
+    { "Antarctica/Syowa"                  , 0x01743D },
+    { "Antarctica/Vostok"                 , 0x0174AB },
+    { "Arctic/Longyearbyen"               , 0x017520 },
+    { "Asia/Aden"                         , 0x017852 },
+    { "Asia/Almaty"                       , 0x0178A7 },
+    { "Asia/Amman"                        , 0x017A26 },
+    { "Asia/Anadyr"                       , 0x017CE6 },
+    { "Asia/Aqtau"                        , 0x017FD4 },
+    { "Asia/Aqtobe"                       , 0x0181D3 },
+    { "Asia/Ashgabat"                     , 0x01838B },
+    { "Asia/Ashkhabad"                    , 0x0184A8 },
+    { "Asia/Baghdad"                      , 0x0185C5 },
+    { "Asia/Bahrain"                      , 0x01873A },
+    { "Asia/Baku"                         , 0x0187A0 },
+    { "Asia/Bangkok"                      , 0x018A88 },
+    { "Asia/Beirut"                       , 0x018ADD },
+    { "Asia/Bishkek"                      , 0x018DEA },
+    { "Asia/Brunei"                       , 0x018F96 },
+    { "Asia/Calcutta"                     , 0x018FF8 },
+    { "Asia/Choibalsan"                   , 0x019071 },
+    { "Asia/Chongqing"                    , 0x0191EA },
+    { "Asia/Chungking"                    , 0x0192D9 },
+    { "Asia/Colombo"                      , 0x019388 },
+    { "Asia/Dacca"                        , 0x019424 },
+    { "Asia/Damascus"                     , 0x0194B3 },
+    { "Asia/Dhaka"                        , 0x019803 },
+    { "Asia/Dili"                         , 0x019892 },
+    { "Asia/Dubai"                        , 0x01991B },
+    { "Asia/Dushanbe"                     , 0x019970 },
+    { "Asia/Gaza"                         , 0x019A73 },
+    { "Asia/Harbin"                       , 0x019DBC },
+    { "Asia/Ho_Chi_Minh"                  , 0x019EA3 },
+    { "Asia/Hong_Kong"                    , 0x019F1B },
+    { "Asia/Hovd"                         , 0x01A0D1 },
+    { "Asia/Irkutsk"                      , 0x01A249 },
+    { "Asia/Istanbul"                     , 0x01A530 },
+    { "Asia/Jakarta"                      , 0x01A91D },
+    { "Asia/Jayapura"                     , 0x01A9C7 },
+    { "Asia/Jerusalem"                    , 0x01AA4B },
+    { "Asia/Kabul"                        , 0x01AD7A },
+    { "Asia/Kamchatka"                    , 0x01ADCB },
+    { "Asia/Karachi"                      , 0x01B0B0 },
+    { "Asia/Kashgar"                      , 0x01B165 },
+    { "Asia/Kathmandu"                    , 0x01B236 },
+    { "Asia/Katmandu"                     , 0x01B29C },
+    { "Asia/Kolkata"                      , 0x01B302 },
+    { "Asia/Krasnoyarsk"                  , 0x01B37B },
+    { "Asia/Kuala_Lumpur"                 , 0x01B664 },
+    { "Asia/Kuching"                      , 0x01B721 },
+    { "Asia/Kuwait"                       , 0x01B80F },
+    { "Asia/Macao"                        , 0x01B864 },
+    { "Asia/Macau"                        , 0x01B99F },
+    { "Asia/Magadan"                      , 0x01BADA },
+    { "Asia/Makassar"                     , 0x01BDBD },
+    { "Asia/Manila"                       , 0x01BE76 },
+    { "Asia/Muscat"                       , 0x01BEFB },
+    { "Asia/Nicosia"                      , 0x01BF50 },
+    { "Asia/Novosibirsk"                  , 0x01C238 },
+    { "Asia/Omsk"                         , 0x01C52C },
+    { "Asia/Oral"                         , 0x01C814 },
+    { "Asia/Phnom_Penh"                   , 0x01C9E4 },
+    { "Asia/Pontianak"                    , 0x01CA5C },
+    { "Asia/Pyongyang"                    , 0x01CB1D },
+    { "Asia/Qatar"                        , 0x01CB8A },
+    { "Asia/Qyzylorda"                    , 0x01CBF0 },
+    { "Asia/Rangoon"                      , 0x01CDC6 },
+    { "Asia/Riyadh"                       , 0x01CE3E },
+    { "Asia/Saigon"                       , 0x01CE93 },
+    { "Asia/Sakhalin"                     , 0x01CF0B },
+    { "Asia/Samarkand"                    , 0x01D20B },
+    { "Asia/Seoul"                        , 0x01D341 },
+    { "Asia/Shanghai"                     , 0x01D3E5 },
+    { "Asia/Singapore"                    , 0x01D4C5 },
+    { "Asia/Taipei"                       , 0x01D57C },
+    { "Asia/Tashkent"                     , 0x01D694 },
+    { "Asia/Tbilisi"                      , 0x01D7C5 },
+    { "Asia/Tehran"                       , 0x01D97F },
+    { "Asia/Tel_Aviv"                     , 0x01DBED },
+    { "Asia/Thimbu"                       , 0x01DF1C },
+    { "Asia/Thimphu"                      , 0x01DF82 },
+    { "Asia/Tokyo"                        , 0x01DFE8 },
+    { "Asia/Ujung_Pandang"                , 0x01E071 },
+    { "Asia/Ulaanbaatar"                  , 0x01E0ED },
+    { "Asia/Ulan_Bator"                   , 0x01E248 },
+    { "Asia/Urumqi"                       , 0x01E395 },
+    { "Asia/Vientiane"                    , 0x01E45C },
+    { "Asia/Vladivostok"                  , 0x01E4D4 },
+    { "Asia/Yakutsk"                      , 0x01E7C1 },
+    { "Asia/Yekaterinburg"                , 0x01EAA7 },
+    { "Asia/Yerevan"                      , 0x01EDB3 },
+    { "Atlantic/Azores"                   , 0x01F0B7 },
+    { "Atlantic/Bermuda"                  , 0x01F5BA },
+    { "Atlantic/Canary"                   , 0x01F89B },
+    { "Atlantic/Cape_Verde"               , 0x01FB71 },
+    { "Atlantic/Faeroe"                   , 0x01FBEA },
+    { "Atlantic/Faroe"                    , 0x01FE8E },
+    { "Atlantic/Jan_Mayen"                , 0x020132 },
+    { "Atlantic/Madeira"                  , 0x020464 },
+    { "Atlantic/Reykjavik"                , 0x02096D },
+    { "Atlantic/South_Georgia"            , 0x020B26 },
+    { "Atlantic/St_Helena"                , 0x020E3E },
+    { "Atlantic/Stanley"                  , 0x020B6A },
+    { "Australia/ACT"                     , 0x020E93 },
+    { "Australia/Adelaide"                , 0x0211B0 },
+    { "Australia/Brisbane"                , 0x0214DC },
+    { "Australia/Broken_Hill"             , 0x0215A3 },
+    { "Australia/Canberra"                , 0x0218E1 },
+    { "Australia/Currie"                  , 0x021BFE },
+    { "Australia/Darwin"                  , 0x021F31 },
+    { "Australia/Eucla"                   , 0x021FB7 },
+    { "Australia/Hobart"                  , 0x02208C },
+    { "Australia/LHI"                     , 0x0223EA },
+    { "Australia/Lindeman"                , 0x022685 },
+    { "Australia/Lord_Howe"               , 0x022766 },
+    { "Australia/Melbourne"               , 0x022A11 },
+    { "Australia/North"                   , 0x022D36 },
+    { "Australia/NSW"                     , 0x022DAA },
+    { "Australia/Perth"                   , 0x0230C7 },
+    { "Australia/Queensland"              , 0x02319F },
+    { "Australia/South"                   , 0x02324B },
+    { "Australia/Sydney"                  , 0x023568 },
+    { "Australia/Tasmania"                , 0x0238A5 },
+    { "Australia/Victoria"                , 0x023BEA },
+    { "Australia/West"                    , 0x023F07 },
+    { "Australia/Yancowinna"              , 0x023FBD },
+    { "Brazil/Acre"                       , 0x0242DF },
+    { "Brazil/DeNoronha"                  , 0x0243DE },
+    { "Brazil/East"                       , 0x0244FE },
+    { "Brazil/West"                       , 0x0247DB },
+    { "Canada/Atlantic"                   , 0x0248D3 },
+    { "Canada/Central"                    , 0x024DBB },
+    { "Canada/East-Saskatchewan"          , 0x0256C5 },
+    { "Canada/Eastern"                    , 0x0251D5 },
+    { "Canada/Mountain"                   , 0x02584E },
+    { "Canada/Newfoundland"               , 0x025BC4 },
+    { "Canada/Pacific"                    , 0x0260EF },
+    { "Canada/Saskatchewan"               , 0x026508 },
+    { "Canada/Yukon"                      , 0x026691 },
+    { "CET"                               , 0x026994 },
+    { "Chile/Continental"                 , 0x026C9D },
+    { "Chile/EasterIsland"                , 0x027038 },
+    { "CST6CDT"                           , 0x02737A },
+    { "Cuba"                              , 0x0276CB },
+    { "EET"                               , 0x027A3E },
+    { "Egypt"                             , 0x027CF1 },
+    { "Eire"                              , 0x0280B8 },
+    { "EST"                               , 0x0285C9 },
+    { "EST5EDT"                           , 0x02860D },
+    { "Etc/GMT"                           , 0x02895E },
+    { "Etc/GMT+0"                         , 0x028A2A },
+    { "Etc/GMT+1"                         , 0x028AB4 },
+    { "Etc/GMT+10"                        , 0x028B41 },
+    { "Etc/GMT+11"                        , 0x028BCF },
+    { "Etc/GMT+12"                        , 0x028C5D },
+    { "Etc/GMT+2"                         , 0x028D78 },
+    { "Etc/GMT+3"                         , 0x028E04 },
+    { "Etc/GMT+4"                         , 0x028E90 },
+    { "Etc/GMT+5"                         , 0x028F1C },
+    { "Etc/GMT+6"                         , 0x028FA8 },
+    { "Etc/GMT+7"                         , 0x029034 },
+    { "Etc/GMT+8"                         , 0x0290C0 },
+    { "Etc/GMT+9"                         , 0x02914C },
+    { "Etc/GMT-0"                         , 0x0289E6 },
+    { "Etc/GMT-1"                         , 0x028A6E },
+    { "Etc/GMT-10"                        , 0x028AFA },
+    { "Etc/GMT-11"                        , 0x028B88 },
+    { "Etc/GMT-12"                        , 0x028C16 },
+    { "Etc/GMT-13"                        , 0x028CA4 },
+    { "Etc/GMT-14"                        , 0x028CEB },
+    { "Etc/GMT-2"                         , 0x028D32 },
+    { "Etc/GMT-3"                         , 0x028DBE },
+    { "Etc/GMT-4"                         , 0x028E4A },
+    { "Etc/GMT-5"                         , 0x028ED6 },
+    { "Etc/GMT-6"                         , 0x028F62 },
+    { "Etc/GMT-7"                         , 0x028FEE },
+    { "Etc/GMT-8"                         , 0x02907A },
+    { "Etc/GMT-9"                         , 0x029106 },
+    { "Etc/GMT0"                          , 0x0289A2 },
+    { "Etc/Greenwich"                     , 0x029192 },
+    { "Etc/UCT"                           , 0x0291D6 },
+    { "Etc/Universal"                     , 0x02921A },
+    { "Etc/UTC"                           , 0x02925E },
+    { "Etc/Zulu"                          , 0x0292A2 },
+    { "Europe/Amsterdam"                  , 0x0292E6 },
+    { "Europe/Andorra"                    , 0x029724 },
+    { "Europe/Athens"                     , 0x0299A0 },
+    { "Europe/Belfast"                    , 0x029CE3 },
+    { "Europe/Belgrade"                   , 0x02A21A },
+    { "Europe/Berlin"                     , 0x02A4E3 },
+    { "Europe/Bratislava"                 , 0x02A839 },
+    { "Europe/Brussels"                   , 0x02AB6B },
+    { "Europe/Bucharest"                  , 0x02AFA2 },
+    { "Europe/Budapest"                   , 0x02B2CC },
+    { "Europe/Chisinau"                   , 0x02B63F },
+    { "Europe/Copenhagen"                 , 0x02B9CD },
+    { "Europe/Dublin"                     , 0x02BCD7 },
+    { "Europe/Gibraltar"                  , 0x02C1E8 },
+    { "Europe/Guernsey"                   , 0x02C63F },
+    { "Europe/Helsinki"                   , 0x02CB76 },
+    { "Europe/Isle_of_Man"                , 0x02CE2C },
+    { "Europe/Istanbul"                   , 0x02D363 },
+    { "Europe/Jersey"                     , 0x02D750 },
+    { "Europe/Kaliningrad"                , 0x02DC87 },
+    { "Europe/Kiev"                       , 0x02DFEA },
+    { "Europe/Lisbon"                     , 0x02E301 },
+    { "Europe/Ljubljana"                  , 0x02E805 },
+    { "Europe/London"                     , 0x02EACE },
+    { "Europe/Luxembourg"                 , 0x02F005 },
+    { "Europe/Madrid"                     , 0x02F45B },
+    { "Europe/Malta"                      , 0x02F821 },
+    { "Europe/Mariehamn"                  , 0x02FBDA },
+    { "Europe/Minsk"                      , 0x02FE90 },
+    { "Europe/Monaco"                     , 0x03019B },
+    { "Europe/Moscow"                     , 0x0305D6 },
+    { "Europe/Nicosia"                    , 0x030928 },
+    { "Europe/Oslo"                       , 0x030C10 },
+    { "Europe/Paris"                      , 0x030F42 },
+    { "Europe/Podgorica"                  , 0x031388 },
+    { "Europe/Prague"                     , 0x031651 },
+    { "Europe/Riga"                       , 0x031983 },
+    { "Europe/Rome"                       , 0x031CC8 },
+    { "Europe/Samara"                     , 0x03208B },
+    { "Europe/San_Marino"                 , 0x0323B7 },
+    { "Europe/Sarajevo"                   , 0x03277A },
+    { "Europe/Simferopol"                 , 0x032A43 },
+    { "Europe/Skopje"                     , 0x032D6E },
+    { "Europe/Sofia"                      , 0x033037 },
+    { "Europe/Stockholm"                  , 0x03333F },
+    { "Europe/Tallinn"                    , 0x0335EE },
+    { "Europe/Tirane"                     , 0x033928 },
+    { "Europe/Tiraspol"                   , 0x033C2E },
+    { "Europe/Uzhgorod"                   , 0x033FBC },
+    { "Europe/Vaduz"                      , 0x0342D3 },
+    { "Europe/Vatican"                    , 0x034566 },
+    { "Europe/Vienna"                     , 0x034929 },
+    { "Europe/Vilnius"                    , 0x034C56 },
+    { "Europe/Volgograd"                  , 0x034F95 },
+    { "Europe/Warsaw"                     , 0x03529E },
+    { "Europe/Zagreb"                     , 0x03567F },
+    { "Europe/Zaporozhye"                 , 0x035948 },
+    { "Europe/Zurich"                     , 0x035C89 },
+    { "Factory"                           , 0x035F38 },
+    { "GB"                                , 0x035FA9 },
+    { "GB-Eire"                           , 0x0364E0 },
+    { "GMT"                               , 0x036A17 },
+    { "GMT+0"                             , 0x036AE3 },
+    { "GMT-0"                             , 0x036A9F },
+    { "GMT0"                              , 0x036A5B },
+    { "Greenwich"                         , 0x036B27 },
+    { "Hongkong"                          , 0x036B6B },
+    { "HST"                               , 0x036D21 },
+    { "Iceland"                           , 0x036D65 },
+    { "Indian/Antananarivo"               , 0x036F1E },
+    { "Indian/Chagos"                     , 0x036F92 },
+    { "Indian/Christmas"                  , 0x036FF4 },
+    { "Indian/Cocos"                      , 0x037038 },
+    { "Indian/Comoro"                     , 0x03707C },
+    { "Indian/Kerguelen"                  , 0x0370D1 },
+    { "Indian/Mahe"                       , 0x037126 },
+    { "Indian/Maldives"                   , 0x03717B },
+    { "Indian/Mauritius"                  , 0x0371D0 },
+    { "Indian/Mayotte"                    , 0x037373 },
+    { "Indian/Reunion"                    , 0x0373C8 },
+    { "Iran"                              , 0x03741D },
+    { "Israel"                            , 0x03768B },
+    { "Jamaica"                           , 0x0379BA },
+    { "Japan"                             , 0x037A7F },
+    { "Kwajalein"                         , 0x037B08 },
+    { "Libya"                             , 0x037B6B },
+    { "MET"                               , 0x037C65 },
+    { "Mexico/BajaNorte"                  , 0x037F6E },
+    { "Mexico/BajaSur"                    , 0x0382D7 },
+    { "Mexico/General"                    , 0x03851C },
+    { "MST"                               , 0x03877A },
+    { "MST7MDT"                           , 0x0387BE },
+    { "Navajo"                            , 0x038B0F },
+    { "NZ"                                , 0x038E88 },
+    { "NZ-CHAT"                           , 0x039206 },
+    { "Pacific/Apia"                      , 0x0394EE },
+    { "Pacific/Auckland"                  , 0x039555 },
+    { "Pacific/Chatham"                   , 0x0398E1 },
+    { "Pacific/Easter"                    , 0x039BD8 },
+    { "Pacific/Efate"                     , 0x039F36 },
+    { "Pacific/Enderbury"                 , 0x039FFC },
+    { "Pacific/Fakaofo"                   , 0x03A06A },
+    { "Pacific/Fiji"                      , 0x03A0AE },
+    { "Pacific/Funafuti"                  , 0x03A124 },
+    { "Pacific/Galapagos"                 , 0x03A168 },
+    { "Pacific/Gambier"                   , 0x03A1E0 },
+    { "Pacific/Guadalcanal"               , 0x03A245 },
+    { "Pacific/Guam"                      , 0x03A29A },
+    { "Pacific/Honolulu"                  , 0x03A2F0 },
+    { "Pacific/Johnston"                  , 0x03A384 },
+    { "Pacific/Kiritimati"                , 0x03A3D6 },
+    { "Pacific/Kosrae"                    , 0x03A441 },
+    { "Pacific/Kwajalein"                 , 0x03A49E },
+    { "Pacific/Majuro"                    , 0x03A50A },
+    { "Pacific/Marquesas"                 , 0x03A569 },
+    { "Pacific/Midway"                    , 0x03A5D0 },
+    { "Pacific/Nauru"                     , 0x03A65A },
+    { "Pacific/Niue"                      , 0x03A6D2 },
+    { "Pacific/Norfolk"                   , 0x03A730 },
+    { "Pacific/Noumea"                    , 0x03A785 },
+    { "Pacific/Pago_Pago"                 , 0x03A815 },
+    { "Pacific/Palau"                     , 0x03A89E },
+    { "Pacific/Pitcairn"                  , 0x03A8E2 },
+    { "Pacific/Ponape"                    , 0x03A937 },
+    { "Pacific/Port_Moresby"              , 0x03A98C },
+    { "Pacific/Rarotonga"                 , 0x03A9D0 },
+    { "Pacific/Saipan"                    , 0x03AAAC },
+    { "Pacific/Samoa"                     , 0x03AB0F },
+    { "Pacific/Tahiti"                    , 0x03AB98 },
+    { "Pacific/Tarawa"                    , 0x03ABFD },
+    { "Pacific/Tongatapu"                 , 0x03AC51 },
+    { "Pacific/Truk"                      , 0x03ACDD },
+    { "Pacific/Wake"                      , 0x03AD36 },
+    { "Pacific/Wallis"                    , 0x03AD86 },
+    { "Pacific/Yap"                       , 0x03ADCA },
+    { "Poland"                            , 0x03AE0F },
+    { "Portugal"                          , 0x03B1F0 },
+    { "PRC"                               , 0x03B6EC },
+    { "PST8PDT"                           , 0x03B79D },
+    { "ROC"                               , 0x03BAEE },
+    { "ROK"                               , 0x03BC06 },
+    { "Singapore"                         , 0x03BCAA },
+    { "Turkey"                            , 0x03BD61 },
+    { "UCT"                               , 0x03C14E },
+    { "Universal"                         , 0x03C192 },
+    { "US/Alaska"                         , 0x03C1D6 },
+    { "US/Aleutian"                       , 0x03C53F },
+    { "US/Arizona"                        , 0x03C8A5 },
+    { "US/Central"                        , 0x03C933 },
+    { "US/East-Indiana"                   , 0x03D33D },
+    { "US/Eastern"                        , 0x03CE3E },
+    { "US/Hawaii"                         , 0x03D5A7 },
+    { "US/Indiana-Starke"                 , 0x03D635 },
+    { "US/Michigan"                       , 0x03D9A6 },
+    { "US/Mountain"                       , 0x03DCDD },
+    { "US/Pacific"                        , 0x03E056 },
+    { "US/Pacific-New"                    , 0x03E45B },
+    { "US/Samoa"                          , 0x03E860 },
+    { "UTC"                               , 0x03E8E9 },
+    { "W-SU"                              , 0x03EBE0 },
+    { "WET"                               , 0x03E92D },
+    { "Zulu"                              , 0x03EF1B },
diff --git a/install/tz.pl b/install/tz.pl
new file mode 100644 (file)
index 0000000..4217af0
--- /dev/null
@@ -0,0 +1,26 @@
+#!/usr/bin/env perl
+use strict;
+use warnings;
+my ($tz, @zones);
+
+open FILE, "timezone.txt" or die $!;
+while (<FILE>) {
+    if ($_ =~ /"(\w*\/?\w*)"/) {
+        push @zones, $1;
+    }
+}
+close FILE;
+
+my $size = scalar @zones;
+$tz = "<?php\n\$timezone = array(\n";
+foreach (@zones) {
+    $size--;
+    $tz .= "\t\"$_\"";
+    $tz .= ($size) ? ",\n" : "\n";
+}
+$tz .= ");\n?>\n";
+
+open FILE, ">timezone.php" or die $!;
+print FILE $tz;
+
+exit 0;
diff --git a/install/upgrade.sh b/install/upgrade.sh
new file mode 100644 (file)
index 0000000..89e2cd9
--- /dev/null
@@ -0,0 +1,81 @@
+#!/bin/sh
+
+PATH=/usr/bin:/usr/sbin:/bin:/sbin
+DIR=/tmp
+USR=user.sql
+CAL=calendar.sql
+USR_DUMP=".mode insert user\n.output user.sql"
+CAL_DUMP=".mode insert calendar\n.output calendar.sql"
+
+function export_db() {
+       echo -n 'Please enter name of database to export from: '
+       read DB
+       if [ ! -e "$DB" ]; then
+               echo $DB: File does not exist
+               exit 1
+       fi
+
+       echo -e $USR_DUMP > "$DIR/$USR"
+       sqlite3 -init "$DIR/$USR" "$DB" 'select * from user where id <> 1'
+       rm -f "$DIR/$USR"
+
+       echo -e $CAL_DUMP > "$DIR/$CAL"
+       sqlite3 -init "$DIR/$CAL" "$DB" 'select * from calendar'
+       rm -f "$DIR/$CAL"
+}
+
+function import_db() {
+       echo -n 'Please enter name of database to import into: '
+       read DB
+       if [ ! -e "$DB" ]; then
+               echo $DB: File does not exist
+               exit 1
+       fi
+       if [ ! -e "./$USR" ]; then
+               echo $USR: File does not exist
+               exit 1
+       fi
+       if [ ! -e "./$CAL" ]; then
+               echo $CAL: File does not exist
+               exit 1
+       fi
+
+       sqlite3 "$DB" < "./$USR"
+       sqlite3 "$DB" < "./$CAL"
+
+       echo -n "Delete files containing backup [y/N]: "
+       read DEL
+       if [ ! -z "$DEL" ]; then
+               if [ "$DEL" = "y" -o "$DEL" = "Y" ]; then
+                       rm -f "./$USR"
+                       rm -f "./$CAL"
+               fi
+       fi
+}
+
+if [ $UID -ne '0' ]; then
+       echo You must run this script as root
+       exit 1
+fi
+
+case "$1" in
+       export)
+               export_db
+               echo Database was succesfully exported
+               echo You should now perform standard install
+               echo and run this script with command import
+               exit 0
+               ;;
+       import)
+               import_db
+               echo Database was succesfully imported
+               exit 0
+               ;;
+       *)
+               echo -e "usage: $0 CMD\n
+CMD is:
+\texport\texport data from a database
+\timport\timport data to a database"
+               exit 0
+               ;;
+esac
diff --git a/js/ajax_wrapper.js b/js/ajax_wrapper.js
new file mode 100644 (file)
index 0000000..26ad499
--- /dev/null
@@ -0,0 +1,83 @@
+var url = "http://localhost/~mir/test.php?name=";
+var output = new Array();
+var httpRequest;
+var fields = new Object();
+
+/**
+* Get the XMLHttpRequest Object
+* \return XMLHttpRequst, or <i>false</i> if fail
+*/
+function getXMLHttpRequest() {
+    if (window.XMLHttpRequest) { // Mozilla, Safari, ...
+        httpRequest = new XMLHttpRequest();
+        if (httpRequest.overrideMimeType) {
+            httpRequest.overrideMimeType('text/xml');
+        }
+    } 
+    else if (window.ActiveXObject) { // IE
+        try {
+            httpRequest = new ActiveXObject("Msxml2.XMLHTTP");
+        } 
+        catch (e) {
+            try {
+                httpRequest = new ActiveXObject("Microsoft.XMLHTTP");
+            } 
+            catch (e) {}
+        }
+    }
+       if (!httpRequest) {
+        alert('Giving up :( Cannot create an XMLHTTP instance');
+        return false;
+    }
+       return true;
+}
+
+function handleHttpResponse(response, xml) {
+       if (xml == true) {
+               var text = response.getElementsByTagName('param')[0].firstChild.data
+               text = text + " : " + response.getElementsByTagName('greeting')[0].firstChild.data
+               document.getElementById("textarea").value = text
+       }
+       else {
+               document.getElementById("textarea").value = response.value
+       }
+}
+
+function setField(field, value) {
+       fields[field] = value
+}
+
+function getContactByName(name) {
+       if (httpRequest == null) {
+               alert("Could not initiate AJAX Session")
+               return null
+       }
+       httpRequest.onreadystatechange = function() {
+        if (httpRequest.readyState == 4) {
+                       if (httpRequest.status == 200)
+                               handleHttpResponse(httpRequest.responseXML, true)
+               }
+       }
+       httpRequest.open("GET", url + name, true)
+       httpRequest.send(null)
+}
+
+function handleEvent(field) {
+       try {
+               var f = fields[field]
+               if (f == "search_name") {
+                       var name = document.getElementById(f).value
+                       getContactByName(name)
+               }
+       }
+       catch (e) {
+               return false
+       }
+}
+
+if (!getXMLHttpRequest()) {
+       document.location = 'error.html';
+}
+window.onload = setField("name", "search_name");
+
+
diff --git a/js/calendar.css b/js/calendar.css
new file mode 100644 (file)
index 0000000..a3ddcae
--- /dev/null
@@ -0,0 +1,95 @@
+/* calendar icon */\r
+img.tcalIcon {\r
+       cursor: pointer;\r
+       margin-left: 1px;\r
+       vertical-align: middle;\r
+}\r
+/* calendar container element */\r
+div#tcal {\r
+       position: absolute;\r
+       visibility: hidden;\r
+       z-index: 100;\r
+       width: 158px;\r
+       padding: 2px 0 0 0;\r
+}\r
+/* all tables in calendar */\r
+div#tcal table {\r
+       width: 100%;\r
+       border: 1px solid silver;\r
+       border-collapse: collapse;\r
+       background-color: white;\r
+}\r
+/* navigation table */\r
+div#tcal table.ctrl {\r
+       border-bottom: 0;\r
+}\r
+/* navigation buttons */\r
+div#tcal table.ctrl td {\r
+       width: 15px;\r
+       height: 20px;\r
+}\r
+/* month year header */\r
+div#tcal table.ctrl th {\r
+       background-color: white;\r
+       color: black;\r
+       border: 0;\r
+}\r
+/* week days header */\r
+div#tcal th {\r
+       border: 1px solid silver;\r
+       border-collapse: collapse;\r
+       text-align: center;\r
+       padding: 3px 0;\r
+       font-family: tahoma, verdana, arial;\r
+       font-size: 10px;\r
+       background-color: gray;\r
+       color: white;\r
+}\r
+/* date cells */\r
+div#tcal td {\r
+       border: 0;\r
+       border-collapse: collapse;\r
+       text-align: center;\r
+       padding: 2px 0;\r
+       font-family: tahoma, verdana, arial;\r
+       font-size: 11px;\r
+       width: 22px;\r
+       cursor: pointer;\r
+}\r
+/* date highlight\r
+   in case of conflicting settings order here determines the priority from least to most important */\r
+div#tcal td.othermonth {\r
+       color: silver;\r
+}\r
+div#tcal td.weekend {\r
+       background-color: #ACD6F5;\r
+}\r
+div#tcal td.today {\r
+       border: 1px solid red;\r
+}\r
+div#tcal td.selected {\r
+       background-color: #FFB3BE;\r
+}\r
+/* iframe element used to suppress windowed controls in IE5/6 */\r
+iframe#tcalIF {\r
+       position: absolute;\r
+       visibility: hidden;\r
+       z-index: 98;\r
+       border: 0;\r
+}\r
+/* transparent shadow */\r
+div#tcalShade {\r
+       position: absolute;\r
+       visibility: hidden;\r
+       z-index: 99;\r
+}\r
+div#tcalShade table {\r
+       border: 0;\r
+       border-collapse: collapse;\r
+       width: 100%;\r
+}\r
+div#tcalShade table td {\r
+       border: 0;\r
+       border-collapse: collapse;\r
+       padding: 0;\r
+}\r
diff --git a/js/calendar_db.js b/js/calendar_db.js
new file mode 100644 (file)
index 0000000..ade479c
--- /dev/null
@@ -0,0 +1,329 @@
+// Tigra Calendar v4.0.2 (2009-01-12) Database (yyyy-mm-dd)\r
+// http://www.softcomplex.com/products/tigra_calendar/\r
+// Public Domain Software... You're welcome.\r
+\r
+// default settins\r
+var A_TCALDEF = {\r
+       'months' : ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],\r
+       'weekdays' : ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'],\r
+       'yearscroll': true, // show year scroller\r
+       'weekstart': 0, // first day of week: 0-Su or 1-Mo\r
+       'centyear'  : 70, // 2 digit years less than 'centyear' are in 20xx, othewise in 19xx.\r
+       'imgpath' : 'img/' // directory with calendar images\r
+}\r
+// date parsing function\r
+function f_tcalParseDate (s_date) {\r
+\r
+       var re_date = /^\s*(\d{2,4})\-(\d{1,2})\-(\d{1,2})\s*$/;\r
+       if (!re_date.exec(s_date))\r
+               return alert ("Invalid date: '" + s_date + "'.\nAccepted format is yyyy-mm-dd.")\r
+       var n_day = Number(RegExp.$3),\r
+               n_month = Number(RegExp.$2),\r
+               n_year = Number(RegExp.$1);\r
+       \r
+       if (n_year < 100)\r
+               n_year += (n_year < this.a_tpl.centyear ? 2000 : 1900);\r
+       if (n_month < 1 || n_month > 12)\r
+               return alert ("Invalid month value: '" + n_month + "'.\nAllowed range is 01-12.");\r
+       var d_numdays = new Date(n_year, n_month, 0);\r
+       if (n_day > d_numdays.getDate())\r
+               return alert("Invalid day of month value: '" + n_day + "'.\nAllowed range for selected month is 01 - " + d_numdays.getDate() + ".");\r
+\r
+       return new Date (n_year, n_month - 1, n_day);\r
+}\r
+// date generating function\r
+function f_tcalGenerDate (d_date) {\r
+       return (\r
+               d_date.getFullYear() + "-"\r
+               + (d_date.getMonth() < 9 ? '0' : '') + (d_date.getMonth() + 1) + "-"\r
+               + (d_date.getDate() < 10 ? '0' : '') + d_date.getDate()\r
+       );\r
+}\r
+\r
+// implementation\r
+function tcal (a_cfg, a_tpl) {\r
+\r
+       // apply default template if not specified\r
+       if (!a_tpl)\r
+               a_tpl = A_TCALDEF;\r
+\r
+       // register in global collections\r
+       if (!window.A_TCALS)\r
+               window.A_TCALS = [];\r
+       if (!window.A_TCALSIDX)\r
+               window.A_TCALSIDX = [];\r
+       \r
+       this.s_id = a_cfg.id ? a_cfg.id : A_TCALS.length;\r
+       window.A_TCALS[this.s_id] = this;\r
+       window.A_TCALSIDX[window.A_TCALSIDX.length] = this;\r
+       \r
+       // assign methods\r
+       this.f_show = f_tcalShow;\r
+       this.f_hide = f_tcalHide;\r
+       this.f_toggle = f_tcalToggle;\r
+       this.f_update = f_tcalUpdate;\r
+       this.f_relDate = f_tcalRelDate;\r
+       this.f_parseDate = f_tcalParseDate;\r
+       this.f_generDate = f_tcalGenerDate;\r
+       \r
+       // create calendar icon\r
+       this.s_iconId = 'tcalico_' + this.s_id;\r
+       this.e_icon = f_getElement(this.s_iconId);\r
+       if (!this.e_icon) {\r
+               document.write('<img src="' + a_tpl.imgpath + 'cal.gif" id="' + this.s_iconId + '" onclick="A_TCALS[\'' + this.s_id + '\'].f_toggle()" class="tcalIcon" alt="Open Calendar" />');\r
+               this.e_icon = f_getElement(this.s_iconId);\r
+       }\r
+       // save received parameters\r
+       this.a_cfg = a_cfg;\r
+       this.a_tpl = a_tpl;\r
+}\r
+\r
+function f_tcalShow (d_date) {\r
+\r
+       // find input field\r
+       if (!this.a_cfg.controlname)\r
+               throw("TC: control name is not specified");\r
+       if (this.a_cfg.formname) {\r
+               var e_form = document.forms[this.a_cfg.formname];\r
+               if (!e_form)\r
+                       throw("TC: form '" + this.a_cfg.formname + "' can not be found");\r
+               this.e_input = e_form.elements[this.a_cfg.controlname];\r
+       }\r
+       else\r
+               this.e_input = f_getElement(this.a_cfg.controlname);\r
+\r
+       if (!this.e_input || !this.e_input.tagName || this.e_input.tagName != 'INPUT')\r
+               throw("TC: element '" + this.a_cfg.controlname + "' does not exist in "\r
+                       + (this.a_cfg.formname ? "form '" + this.a_cfg.controlname + "'" : 'this document'));\r
+\r
+       // dynamically create HTML elements if needed\r
+       this.e_div = f_getElement('tcal');\r
+       if (!this.e_div) {\r
+               this.e_div = document.createElement("DIV");\r
+               this.e_div.id = 'tcal';\r
+               document.body.appendChild(this.e_div);\r
+       }\r
+       this.e_shade = f_getElement('tcalShade');\r
+       if (!this.e_shade) {\r
+               this.e_shade = document.createElement("DIV");\r
+               this.e_shade.id = 'tcalShade';\r
+               document.body.appendChild(this.e_shade);\r
+       }\r
+       this.e_iframe =  f_getElement('tcalIF')\r
+       if (b_ieFix && !this.e_iframe) {\r
+               this.e_iframe = document.createElement("IFRAME");\r
+               this.e_iframe.style.filter = 'alpha(opacity=0)';\r
+               this.e_iframe.id = 'tcalIF';\r
+               this.e_iframe.src = this.a_tpl.imgpath + 'pixel.gif';\r
+               document.body.appendChild(this.e_iframe);\r
+       }\r
+       \r
+       // hide all calendars\r
+       f_tcalHideAll();\r
+\r
+       // generate HTML and show calendar\r
+       this.e_icon = f_getElement(this.s_iconId);\r
+       if (!this.f_update())\r
+               return;\r
+\r
+       this.e_div.style.visibility = 'visible';\r
+       this.e_shade.style.visibility = 'visible';\r
+       if (this.e_iframe)\r
+               this.e_iframe.style.visibility = 'visible';\r
+\r
+       // change icon and status\r
+       this.e_icon.src = this.a_tpl.imgpath + 'no_cal.gif';\r
+       this.e_icon.title = 'Close Calendar';\r
+       this.b_visible = true;\r
+}\r
+\r
+function f_tcalHide (n_date) {\r
+       if (n_date) {\r
+               this.e_input.value = this.f_generDate(new Date(n_date));\r
+               if (this.e_input.onchange) {\r
+                       this.e_input.onchange(this.e_input);\r
+               }\r
+       }\r
+\r
+       // no action if not visible\r
+       if (!this.b_visible)\r
+               return;\r
+\r
+       // hide elements\r
+       if (this.e_iframe)\r
+               this.e_iframe.style.visibility = 'hidden';\r
+       if (this.e_shade)\r
+               this.e_shade.style.visibility = 'hidden';\r
+       this.e_div.style.visibility = 'hidden';\r
+       \r
+       // change icon and status\r
+       this.e_icon = f_getElement(this.s_iconId);\r
+       this.e_icon.src = this.a_tpl.imgpath + 'cal.gif';\r
+       this.e_icon.title = 'Open Calendar';\r
+       this.b_visible = false;\r
+}\r
+\r
+function f_tcalToggle () {\r
+       return this.b_visible ? this.f_hide() : this.f_show();\r
+}\r
+\r
+function f_tcalUpdate (d_date) {\r
+       \r
+       var d_client = new Date();\r
+       d_client.setHours(0);\r
+       d_client.setMinutes(0);\r
+       d_client.setSeconds(0);\r
+       d_client.setMilliseconds(0);\r
+       \r
+       var d_today = this.a_cfg.today ? this.f_parseDate(this.a_cfg.today) : d_client;\r
+       var d_selected = this.e_input.value == ''\r
+               ? (this.a_cfg.selected ? this.f_parseDate(this.a_cfg.selected) : d_today)\r
+               : this.f_parseDate(this.e_input.value);\r
+\r
+       // figure out date to display\r
+       if (!d_date)\r
+               // selected by default\r
+               d_date = d_selected;\r
+       else if (typeof(d_date) == 'number')\r
+               // get from number\r
+               d_date = new Date(d_date);\r
+       else if (typeof(d_date) == 'string')\r
+               // parse from string\r
+               this.f_parseDate(d_date);\r
+               \r
+       if (!d_date) return false;\r
+\r
+       // first date to display\r
+       var d_firstday = new Date(d_date);\r
+       d_firstday.setDate(1);\r
+       d_firstday.setDate(1 - (7 + d_firstday.getDay() - this.a_tpl.weekstart) % 7);\r
+       \r
+       var a_class, s_html = '<table class="ctrl"><tbody><tr>'\r
+               + (this.a_tpl.yearscroll ? '<td' + this.f_relDate(d_date, -1, 'y') + ' title="Previous Year"><img src="' + this.a_tpl.imgpath + 'prev_year.gif" /></td>' : '')\r
+               + '<td' + this.f_relDate(d_date, -1) + ' title="Previous Month"><img src="' + this.a_tpl.imgpath + 'prev_mon.gif" /></td><th>'\r
+               + this.a_tpl.months[d_date.getMonth()] + ' ' + d_date.getFullYear()\r
+                       + '</th><td' + this.f_relDate(d_date, 1) + ' title="Next Month"><img src="' + this.a_tpl.imgpath + 'next_mon.gif" /></td>'\r
+               + (this.a_tpl.yearscroll ? '<td' + this.f_relDate(d_date, 1, 'y') + ' title="Next Year"><img src="' + this.a_tpl.imgpath + 'next_year.gif" /></td></td>' : '')\r
+               + '</tr></tbody></table><table><tbody><tr class="wd">';\r
+\r
+       // print weekdays titles\r
+       for (var i = 0; i < 7; i++)\r
+               s_html += '<th>' + this.a_tpl.weekdays[(this.a_tpl.weekstart + i) % 7] + '</th>';\r
+       s_html += '</tr>' ;\r
+\r
+       // print calendar table\r
+       var d_current = new Date(d_firstday);\r
+       while (d_current.getMonth() == d_date.getMonth() ||\r
+               d_current.getMonth() == d_firstday.getMonth()) {\r
+       \r
+               // print row heder\r
+               s_html +='<tr>';\r
+               for (var n_wday = 0; n_wday < 7; n_wday++) {\r
+\r
+                       a_class = [];\r
+                       // other month\r
+                       if (d_current.getMonth() != d_date.getMonth())\r
+                               a_class[a_class.length] = 'othermonth';\r
+                       // weekend\r
+                       if (d_current.getDay() == 0 || d_current.getDay() == 6)\r
+                               a_class[a_class.length] = 'weekend';\r
+                       // today\r
+                       if (d_current.valueOf() == d_today.valueOf())\r
+                               a_class[a_class.length] = 'today';\r
+                       // selected\r
+                       if (d_current.valueOf() == d_selected.valueOf())\r
+                               a_class[a_class.length] = 'selected';\r
+\r
+                       s_html += '<td onclick="A_TCALS[\'' + this.s_id + '\'].f_hide(' + d_current.valueOf() + ')"' + (a_class.length ? ' class="' + a_class.join(' ') + '">' : '>') + d_current.getDate() + '</td>'\r
+                       d_current.setDate(d_current.getDate() + 1);\r
+               }\r
+               // print row footer\r
+               s_html +='</tr>';\r
+       }\r
+       s_html +='</tbody></table>';\r
+       \r
+       // update HTML, positions and sizes\r
+       this.e_div.innerHTML = s_html;\r
+\r
+       var n_width  = this.e_div.offsetWidth;\r
+       var n_height = this.e_div.offsetHeight;\r
+       var n_top  = f_getPosition (this.e_icon, 'Top') + this.e_icon.offsetHeight;\r
+       var n_left = f_getPosition (this.e_icon, 'Left') - n_width + this.e_icon.offsetWidth;\r
+       if (n_left < 0) n_left = 0;\r
+       \r
+       this.e_div.style.left = n_left + 'px';\r
+       this.e_div.style.top  = n_top + 'px';\r
+\r
+       this.e_shade.style.width = (n_width + 8) + 'px';\r
+       this.e_shade.style.left = (n_left - 1) + 'px';\r
+       this.e_shade.style.top = (n_top - 1) + 'px';\r
+       this.e_shade.innerHTML = b_ieFix\r
+               ? '<table><tbody><tr><td rowspan="2" colspan="2" width="6"><img src="' + this.a_tpl.imgpath + 'pixel.gif"></td><td width="7" height="7" style="filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src=\'' + this.a_tpl.imgpath + 'shade_tr.png\', sizingMethod=\'scale\');"><img src="' + this.a_tpl.imgpath + 'pixel.gif"></td></tr><tr><td height="' + (n_height - 7) + '" style="filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src=\'' + this.a_tpl.imgpath + 'shade_mr.png\', sizingMethod=\'scale\');"><img src="' + this.a_tpl.imgpath + 'pixel.gif"></td></tr><tr><td width="7" style="filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src=\'' + this.a_tpl.imgpath + 'shade_bl.png\', sizingMethod=\'scale\');"><img src="' + this.a_tpl.imgpath + 'pixel.gif"></td><td style="filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src=\'' + this.a_tpl.imgpath + 'shade_bm.png\', sizingMethod=\'scale\');" height="7" align="left"><img src="' + this.a_tpl.imgpath + 'pixel.gif"></td><td style="filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src=\'' + this.a_tpl.imgpath + 'shade_br.png\', sizingMethod=\'scale\');"><img src="' + this.a_tpl.imgpath + 'pixel.gif"></td></tr><tbody></table>'\r
+               : '<table><tbody><tr><td rowspan="2" width="6"><img src="' + this.a_tpl.imgpath + 'pixel.gif"></td><td rowspan="2"><img src="' + this.a_tpl.imgpath + 'pixel.gif"></td><td width="7" height="7"><img src="' + this.a_tpl.imgpath + 'shade_tr.png"></td></tr><tr><td background="' + this.a_tpl.imgpath + 'shade_mr.png" height="' + (n_height - 7) + '"><img src="' + this.a_tpl.imgpath + 'pixel.gif"></td></tr><tr><td><img src="' + this.a_tpl.imgpath + 'shade_bl.png"></td><td background="' + this.a_tpl.imgpath + 'shade_bm.png" height="7" align="left"><img src="' + this.a_tpl.imgpath + 'pixel.gif"></td><td><img src="' + this.a_tpl.imgpath + 'shade_br.png"></td></tr><tbody></table>';\r
+       \r
+       if (this.e_iframe) {\r
+               this.e_iframe.style.left = n_left + 'px';\r
+               this.e_iframe.style.top  = n_top + 'px';\r
+               this.e_iframe.style.width = (n_width + 6) + 'px';\r
+               this.e_iframe.style.height = (n_height + 6) +'px';\r
+       }\r
+       return true;\r
+}\r
+\r
+function f_getPosition (e_elemRef, s_coord) {\r
+       var n_pos = 0, n_offset,\r
+               e_elem = e_elemRef;\r
+\r
+       while (e_elem) {\r
+               n_offset = e_elem["offset" + s_coord];\r
+               n_pos += n_offset;\r
+               e_elem = e_elem.offsetParent;\r
+       }\r
+       // margin correction in some browsers\r
+       if (b_ieMac)\r
+               n_pos += parseInt(document.body[s_coord.toLowerCase() + 'Margin']);\r
+       else if (b_safari)\r
+               n_pos -= n_offset;\r
+       \r
+       e_elem = e_elemRef;\r
+       while (e_elem != document.body) {\r
+               n_offset = e_elem["scroll" + s_coord];\r
+               if (n_offset && e_elem.style.overflow == 'scroll')\r
+                       n_pos -= n_offset;\r
+               e_elem = e_elem.parentNode;\r
+       }\r
+       return n_pos;\r
+}\r
+\r
+function f_tcalRelDate (d_date, d_diff, s_units) {\r
+       var s_units = (s_units == 'y' ? 'FullYear' : 'Month');\r
+       var d_result = new Date(d_date);\r
+       d_result['set' + s_units](d_date['get' + s_units]() + d_diff);\r
+       if (d_result.getDate() != d_date.getDate())\r
+               d_result.setDate(0);\r
+       return ' onclick="A_TCALS[\'' + this.s_id + '\'].f_update(' + d_result.valueOf() + ')"';\r
+}\r
+\r
+function f_tcalHideAll () {\r
+       for (var i = 0; i < window.A_TCALSIDX.length; i++)\r
+               window.A_TCALSIDX[i].f_hide();\r
+}      \r
+\r
+f_getElement = document.all ?\r
+       function (s_id) { return document.all[s_id] } :\r
+       function (s_id) { return document.getElementById(s_id) };\r
+\r
+if (document.addEventListener)\r
+       window.addEventListener('scroll', f_tcalHideAll, false);\r
+if (window.attachEvent)\r
+       window.attachEvent('onscroll', f_tcalHideAll);\r
+       \r
+// global variables\r
+var s_userAgent = navigator.userAgent.toLowerCase(),\r
+       re_webkit = /WebKit\/(\d+)/i;\r
+var b_mac = s_userAgent.indexOf('mac') != -1,\r
+       b_ie5 = s_userAgent.indexOf('msie 5') != -1,\r
+       b_ie6 = s_userAgent.indexOf('msie 6') != -1 && s_userAgent.indexOf('opera') == -1;\r
+var b_ieFix = b_ie5 || b_ie6,\r
+       b_ieMac  = b_mac && b_ie5,\r
+       b_safari = b_mac && re_webkit.exec(s_userAgent) && Number(RegExp.$1) < 500;\r
diff --git a/js/helper.js b/js/helper.js
new file mode 100644 (file)
index 0000000..46964e1
--- /dev/null
@@ -0,0 +1,152 @@
+// $Id$
+
+var onClickDisabled = [];
+var allDay = false;
+var observable_startdate = ['enddate'];
+var observable_enddate = ['startdate'];
+
+function newwin(url) {
+       window.open(url);
+}
+
+function focus(element) {
+       document.getElementById(element).focus();
+}
+
+function show_the_overlay() {
+       document.getElementById('display_overlay_holder').style.display = 'block';
+}
+
+function hide_the_overlay(referer) {
+       document.getElementById('display_overlay_holder').style.display = 'none';
+document.location.href=referer;
+}
+
+function setCalendar(form, input, cal) {
+       var id = document.getElementById(input);
+       var theForm = document.getElementById(form);
+       
+       if (id == null || theForm == null)
+               return;
+       id.value = cal;
+}
+
+function error(msg) {
+       alert(msg);
+       document.location.href="$referer";
+}
+
+function redirect(url) {
+       window.location = url;
+}
+
+function is_array(obj) {
+   return obj && !(obj.propertyIsEnumerable('length')) && 
+   typeof obj === 'object' && typeof obj.length === 'number';
+}
+
+function getDateString(date, add) {
+       var m = (date.getMonth() + 1);
+       if (m < 10)
+               m = '0' + m;
+       var d = (date.getDate() + add);
+       if (d < 10)
+               d = '0' + d;
+       var s = date.getFullYear() + '-' + m + '-' + d;
+       return s;
+}
+
+function getTimeString(date, end) {
+       var m = date.getMinutes();
+       var h = 0;
+       if (m < 8)
+               m = '00';
+       else if (m < 23)
+               m = '15';
+       else if (m < 38)
+               m = '30';
+       else if (m < 43)
+               m = '45';
+       else {
+               m = '00';
+               h = 1;
+       }
+       var h = date.getHours() + h + end;
+       if (h < 10)
+               h = '0' + h;
+       return  h + m;
+}
+
+function stateSwitch(btn, elements, init) {
+       if (btn == null || elements == null || ! is_array(elements) || elements.length < 1)
+               return;
+
+       var starttime = document.getElementById('starttime');
+       var endtime = document.getElementById('endtime');
+       var startdate = document.getElementById('startdate');
+       var enddate = document.getElementById('enddate');
+       allDay = btn.checked;
+       for (var i = 0; i < elements.length; i++) {
+               var elem = document.getElementById(elements[i]);
+               if (btn.checked == true) {
+                       elem.disabled = true;
+                       if (elem.onclick) {
+                               onClickDisabled[elem.id] = elem.onclick;
+                               elem.onclick = null;
+                       }
+               }
+               else {
+                       var tmp = null;
+                       if (onClickDisabled[elem.id])
+                               var tmp = onClickDisabled[elem.id];
+                       if (tmp) {
+                               elem.onclick = tmp;
+                               onClickDisabled[elem.id] = null;
+                       }
+                       elem.disabled = false;
+               }
+       }
+       if (! init) {
+               if (allDay) {
+                       starttime.value = '0000';
+                       endtime.value = '0000';
+                       var s_date = new Date(startdate.value);
+                       var e_date = new Date(getDateString(s_date, 1));
+                       enddate.value = getDateString(e_date, 0);
+               }
+               else {
+                       var now = new Date();
+                       var time = getTimeString(now, 0);
+                       var s_date = new Date(startdate.value);
+                       enddate.value = getDateString(s_date, 0);
+                       starttime.value = time;
+                       endtime.value = getTimeString(now, 1);
+               }
+       }
+}
+
+function enableAll(elements) {
+       if (elements == null || ! is_array(elements) || elements.length < 1)
+               return true;
+
+       for (var i = 0; i < elements.length; i++) {
+               var elem = document.getElementById(elements[i]);
+               elem.disabled = false;
+       }
+       return true;
+}
+
+function dateAlign(hasDate) {
+       if (hasDate == null)
+               return;
+       
+       if (hasDate.id == 'startdate')
+               elements = observable_startdate;
+       else
+               elements = observable_enddate;
+
+       for (var i = 0; i < elements.length; i++) {
+               var elem = document.getElementById(elements[i]);
+               elem.value = hasDate.value;
+       }
+}
\ No newline at end of file
diff --git a/js/img/cal.gif b/js/img/cal.gif
new file mode 100644 (file)
index 0000000..8526cf5
Binary files /dev/null and b/js/img/cal.gif differ
diff --git a/js/img/next_mon.gif b/js/img/next_mon.gif
new file mode 100644 (file)
index 0000000..14c622f
Binary files /dev/null and b/js/img/next_mon.gif differ
diff --git a/js/img/next_year.gif b/js/img/next_year.gif
new file mode 100644 (file)
index 0000000..b66f288
Binary files /dev/null and b/js/img/next_year.gif differ
diff --git a/js/img/no_cal.gif b/js/img/no_cal.gif
new file mode 100644 (file)
index 0000000..adc58e2
Binary files /dev/null and b/js/img/no_cal.gif differ
diff --git a/js/img/pixel.gif b/js/img/pixel.gif
new file mode 100644 (file)
index 0000000..46a2cf0
Binary files /dev/null and b/js/img/pixel.gif differ
diff --git a/js/img/prev_mon.gif b/js/img/prev_mon.gif
new file mode 100644 (file)
index 0000000..12ce7ff
Binary files /dev/null and b/js/img/prev_mon.gif differ
diff --git a/js/img/prev_year.gif b/js/img/prev_year.gif
new file mode 100644 (file)
index 0000000..c726b0e
Binary files /dev/null and b/js/img/prev_year.gif differ
diff --git a/js/img/shade_bl.png b/js/img/shade_bl.png
new file mode 100644 (file)
index 0000000..29bd554
Binary files /dev/null and b/js/img/shade_bl.png differ
diff --git a/js/img/shade_bm.png b/js/img/shade_bm.png
new file mode 100644 (file)
index 0000000..5c4e0af
Binary files /dev/null and b/js/img/shade_bm.png differ
diff --git a/js/img/shade_br.png b/js/img/shade_br.png
new file mode 100644 (file)
index 0000000..ce8a2fa
Binary files /dev/null and b/js/img/shade_br.png differ
diff --git a/js/img/shade_mr.png b/js/img/shade_mr.png
new file mode 100644 (file)
index 0000000..4594bc4
Binary files /dev/null and b/js/img/shade_mr.png differ
diff --git a/js/img/shade_tr.png b/js/img/shade_tr.png
new file mode 100644 (file)
index 0000000..2e598c9
Binary files /dev/null and b/js/img/shade_tr.png differ
diff --git a/js/license.txt b/js/license.txt
new file mode 100644 (file)
index 0000000..07bc26c
--- /dev/null
@@ -0,0 +1,16 @@
+Tigra Calendar v4.0 LICENSE:
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
\ No newline at end of file
diff --git a/license.txt b/license.txt
new file mode 100644 (file)
index 0000000..c41f283
--- /dev/null
@@ -0,0 +1,28 @@
+/* $Id$ */
+
+WebCal is a web based CalDAV enabled calendar. WebCal is mainly
+developed as a frontend to DAViCAL but should be able to connect to
+any standard conforming CalDAV calender server.
+
+Copyright (C) 2009  Michael Rasmussen <mir@datanom.net>
+
+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 3 of the License, 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, see <http://www.gnu.org/licenses/>.
+
+Part of the application is the copyright of other persons.
+
+- caldav-client.php and awl is copyright Andrew McMillan added minor
+changes by me. caldav-client.php and awl is licensed under GPL 2+
+
+- Tigra Calendar is copyright www.softcomplex.com. Tigra Calendar is
+licensed under public domain.
diff --git a/login.php b/login.php
new file mode 100644 (file)
index 0000000..ee01806
--- /dev/null
+++ b/login.php
@@ -0,0 +1,79 @@
+<?php
+/* $Id$ */
+
+if (! file_exists('config.inc.php'))
+    throw new Exception("The application is not configured yet");
+
+include_once 'config.inc.php';
+require_once 'helper.php';
+
+if (isset($_SESSION['attemps']))
+    $_SESSION['attemps']++;
+else
+    $_SESSION['attemps'] = 1;
+
+if ($_SESSION['attemps'] == 1) {
+    $_SESSION['authenticate'] = new Authenticate(DBDRIVER);
+}
+
+/*
+ * To avoid bots, script kiddeis or other password harvester
+ * add a 5 seconds delay for every 5 failed attemps
+ */
+if ($_SESSION['attemps'] > 5) {
+    sleep(5);
+    header('Location: logout.php');
+}
+
+if (isset($_POST['uid']) && isset($_POST['pwd'])) {
+    $auth = $_SESSION['authenticate'];
+    
+    $auth->login($_POST['uid'], $_POST['pwd']);
+    if ($auth->validUser()) {
+        unset($_SESSION['attemps']);
+        $_SESSION['user_settings'] = new UserSettings($_POST['uid']);
+        $_SESSION['user_settings']->setSettings(
+            $_SESSION['authenticate']->getSettings());
+        //var_dump($_SESSION['user_settings']);
+        //var_dump($_SESSION['authenticate']->getSettings());
+        header('Location: index.php');
+        exit();
+    }
+}
+
+include 'include/header.inc.php';
+$email = ADMIN_MAIL;
+$action = $_SERVER['PHP_SELF'];
+
+print <<< _HTML
+<div id="login_msg">
+    <form action="$action" method="post">
+        <table>
+            <tr>
+                <td>Username</td><td><input id="uid" type="text" name="uid"/></td>
+            </tr>
+            <tr>
+                <td>Password</td><td><input type="password" name="pwd"/></td>
+            </tr>
+            <tr>
+                <td colspan="2" style="text-align: center">
+                    <input type="reset" value="Clear"/>
+                    <input type="submit" name="submit" value="Login"/>
+                </td>
+            </tr>
+            <tr>
+                <td colspan="2" style="text-align: center">If you don't have an account
+                    <a href="mailto:$email?subject=Requesting an account">apply</a>
+                </td>
+            </tr>
+        </table>
+    </form>
+</div>
+<script type="text/javascript">
+    focus("uid");
+</script>
+_HTML;
+
+include 'include/footer.inc.php';
+
+?>
diff --git a/logout.php b/logout.php
new file mode 100644 (file)
index 0000000..4180096
--- /dev/null
@@ -0,0 +1,33 @@
+<?php
+/* $Id$ */
+require_once 'config.inc.php';
+require_once 'authenticate.php';
+require_once 'helper.php';
+
+$root = WEB_ROOT;
+
+/* $_SESSION['caldav_server']->save(); */
+if (isset($_SESSION['authenticate']) && is_object($_SESSION['authenticate']))
+    $_SESSION['authenticate']->logout();
+
+setcookie('WEBCAL_EXPIRE', '', time()-3600, $_SESSION['WEB_ROOT']);
+
+// Unset all of the session variables.
+$_SESSION = array();
+
+if (ini_get("session.use_cookies")) {
+    $params = session_get_cookie_params();
+    setcookie(session_name(), '', time() - 42000,
+        $params["path"], $params["domain"],
+        $params["secure"], $params["httponly"]
+    );
+}
+
+// Finally, destroy the session.
+session_destroy();
+session_write_close();
+session_unset();
+
+header("Location: " . getServerUrl($root) . "/index.php");
+exit;
+?>
\ No newline at end of file
diff --git a/makedoc b/makedoc
new file mode 100644 (file)
index 0000000..0863f39
--- /dev/null
+++ b/makedoc
@@ -0,0 +1,30 @@
+#!/bin/sh
+
+CP="cp --parents"
+if [ -e doc_source ]; then
+    rm -rf doc_source
+fi
+
+if [ -e doc ]; then
+    rm -rf doc
+fi
+
+mkdir doc_source
+mkdir doc
+
+$CP caldav/*.php doc_source
+$CP events/*.php doc_source
+$CP include/*.php doc_source
+$CP install/*.php doc_source
+$CP navigate/*.php doc_source
+$CP templates/*.php doc_source
+$CP utils/*.php doc_source
+$CP *.php doc_source
+
+doxygen Doxyfile
+
+cd doc/latex
+make pdf
+cd ../../
+
+exit 0
diff --git a/navigate/goto_today.php b/navigate/goto_today.php
new file mode 100644 (file)
index 0000000..120901c
--- /dev/null
@@ -0,0 +1,61 @@
+<?php
+/* $Id$ */
+require_once 'config.inc.php';
+require_once 'user_validate.php';
+require_once 'helper.php';
+
+valid_user();
+
+if (! defined($_SESSION['__ROOT__']) && empty($_SESSION['__ROOT__'])) {
+    session_destroy();
+    header('Location: ' . WEB_ROOT . 'error.html');
+    exit;
+}
+
+if (isset($_SESSION['authenticate']) && $_SESSION['authenticate']->validUser()) {
+    $view_style = ($_SESSION['user_settings']->getViewStyle()) ?
+        $_SESSION['user_settings']->getViewStyle() : VIEW_STYLE;
+    $week_start_sunday = ($_SESSION['user_settings']->getStartWeek() !== NULL) ?
+        $_SESSION['user_settings']->getStartWeek() : WEEK_START_SUNDAY;
+    $start_hour = ($_SESSION['user_settings']->getStartHour() != NULL) ?
+        (int) $_SESSION['user_settings']->getStartHour() : START_HOUR;
+    $end_hour = ($_SESSION['user_settings']->getEndHour() != NULL) ?
+        (int) $_SESSION['user_settings']->getEndHour() : END_HOUR;
+}
+
+$pwd = WEB_ROOT;
+if ($pwd[strlen($pwd)-1] == '/')
+    $pwd = substr($pwd, 0, -1);
+
+include TOP_FOLDER.'/templates/view.class.php';
+$pageView = new PageView($pwd, $start_hour, $end_hour, $week_start_sunday);
+
+switch ($view_style) {
+    case 'month':
+        $view = $pageView->createView(PageView::MONTH);
+        $year = date('Y');
+        $month = date('n');
+        $calendar = $view->getView($year, $month);
+        break;
+    case 'week':
+        $view = $pageView->createView(PageView::WEEK);
+        $year = date('Y');
+        $week = $view->get_week_number(time());
+        $calendar = $view->getView($year, $week);
+        break;
+    case 'day':
+        $view = $pageView->createView(PageView::DAY);
+        $year = date('Y');
+        $day = date('z');
+        $calendar = $view->getView($year, $day);
+        break;
+    default: trigger_error(VIEW_STYLE.": ['month', 'week', 'day']", E_USER_ERROR);
+}
+
+include TOP_FOLDER.'/include/header.inc.php';
+include TOP_FOLDER.'/include/menu.inc.php';
+
+//file_put_contents('/tmp/davical.log', "<div id=\"ui\">$calendar</div>", FILE_APPEND);
+print "<div id=\"ui\">$calendar</div>";
+include TOP_FOLDER.'/include/footer.inc.php';
+?>
diff --git a/navigate/show_day.php b/navigate/show_day.php
new file mode 100644 (file)
index 0000000..30f0113
--- /dev/null
@@ -0,0 +1,45 @@
+<?php
+/* $Id$ */
+require_once 'config.inc.php';
+require_once 'user_validate.php';
+require_once 'helper.php';
+
+valid_user();
+
+if (! defined($_SESSION['__ROOT__']) && empty($_SESSION['__ROOT__'])) {
+    session_destroy();
+    header('Location: ' . WEB_ROOT . 'error.html');
+    exit;
+}
+
+if (isset($_SESSION['authenticate']) && $_SESSION['authenticate']->validUser()) {
+    $view_style = ($_SESSION['user_settings']->getViewStyle()) ?
+        $_SESSION['user_settings']->getViewStyle() : VIEW_STYLE;
+    $week_start_sunday = ($_SESSION['user_settings']->getStartWeek() !== NULL) ?
+        $_SESSION['user_settings']->getStartWeek() : WEEK_START_SUNDAY;
+    $start_hour = ($_SESSION['user_settings']->getStartHour() != NULL) ?
+        (int) $_SESSION['user_settings']->getStartHour() : START_HOUR;
+    $end_hour = ($_SESSION['user_settings']->getEndHour() != NULL) ?
+        (int) $_SESSION['user_settings']->getEndHour() : END_HOUR;
+}
+
+$pwd = WEB_ROOT;
+if ($pwd[strlen($pwd)-1] == '/')
+    $pwd = substr($pwd, 0, -1);
+
+include TOP_FOLDER.'/include/header.inc.php';
+include TOP_FOLDER.'/include/menu.inc.php';
+include TOP_FOLDER.'/templates/view.class.php';
+$pageView = new PageView($pwd, $start_hour, $end_hour, $week_start_sunday);
+$day = $pageView->createView(PageView::DAY);
+
+if (isset($_GET['year']) && isset($_GET['day'])) {
+    $calendar = $day->getView($_GET['year'], $_GET['day']);
+}
+else {
+    $calendar = $day->getView();
+}
+print "<div id=\"ui\">$calendar</div>";
+include TOP_FOLDER.'/include/footer.inc.php';
+
+?>
diff --git a/navigate/show_month.php b/navigate/show_month.php
new file mode 100644 (file)
index 0000000..8f686eb
--- /dev/null
@@ -0,0 +1,45 @@
+<?php
+/* $Id$ */
+require_once 'config.inc.php';
+require_once 'user_validate.php';
+require_once 'helper.php';
+
+valid_user();
+
+if (! defined($_SESSION['__ROOT__']) && empty($_SESSION['__ROOT__'])) {
+    session_destroy();
+    header('Location: ' . WEB_ROOT . 'error.html');
+    exit;
+}
+
+if (isset($_SESSION['authenticate']) && $_SESSION['authenticate']->validUser()) {
+    $view_style = ($_SESSION['user_settings']->getViewStyle()) ?
+        $_SESSION['user_settings']->getViewStyle() : VIEW_STYLE;
+    $week_start_sunday = ($_SESSION['user_settings']->getStartWeek() !== NULL) ?
+        $_SESSION['user_settings']->getStartWeek() : WEEK_START_SUNDAY;
+    $start_hour = ($_SESSION['user_settings']->getStartHour() != NULL) ?
+        (int) $_SESSION['user_settings']->getStartHour() : START_HOUR;
+    $end_hour = ($_SESSION['user_settings']->getEndHour() != NULL) ?
+        (int) $_SESSION['user_settings']->getEndHour() : END_HOUR;
+}
+
+$pwd = WEB_ROOT;
+if ($pwd[strlen($pwd)-1] == '/')
+    $pwd = substr($pwd, 0, -1);
+
+include TOP_FOLDER.'/include/header.inc.php';
+include TOP_FOLDER.'/include/menu.inc.php';
+include TOP_FOLDER.'/templates/view.class.php';
+$pageView = new PageView($pwd, $start_hour, $end_hour, $week_start_sunday);
+$month = $pageView->createView(PageView::MONTH);
+
+if (isset($_GET['year']) && isset($_GET['month'])) {
+    $calendar = $month->getView($_GET['year'], $_GET['month']);
+}
+else {
+    $calendar = $month->getView();
+}
+print "<div id=\"ui\">$calendar</div>";
+include TOP_FOLDER.'/include/footer.inc.php';
+
+?>
diff --git a/navigate/show_week.php b/navigate/show_week.php
new file mode 100644 (file)
index 0000000..0b8d7cf
--- /dev/null
@@ -0,0 +1,45 @@
+<?php
+/* $Id$ */
+require_once 'config.inc.php';
+require_once 'user_validate.php';
+require_once 'helper.php';
+
+valid_user();
+
+if (! defined($_SESSION['__ROOT__']) && empty($_SESSION['__ROOT__'])) {
+    session_destroy();
+    header('Location: ' . WEB_ROOT . 'error.html');
+    exit;
+}
+
+if (isset($_SESSION['authenticate']) && $_SESSION['authenticate']->validUser()) {
+    $view_style = ($_SESSION['user_settings']->getViewStyle()) ?
+        $_SESSION['user_settings']->getViewStyle() : VIEW_STYLE;
+    $week_start_sunday = ($_SESSION['user_settings']->getStartWeek() !== NULL) ?
+        $_SESSION['user_settings']->getStartWeek() : WEEK_START_SUNDAY;
+    $start_hour = ($_SESSION['user_settings']->getStartHour() != NULL) ?
+        (int) $_SESSION['user_settings']->getStartHour() : START_HOUR;
+    $end_hour = ($_SESSION['user_settings']->getEndHour() != NULL) ?
+        (int) $_SESSION['user_settings']->getEndHour() : END_HOUR;
+}
+
+$pwd = WEB_ROOT;
+if ($pwd[strlen($pwd)-1] == '/')
+    $pwd = substr($pwd, 0, -1);
+
+include TOP_FOLDER.'/include/header.inc.php';
+include TOP_FOLDER.'/include/menu.inc.php';
+include TOP_FOLDER.'/templates/view.class.php';
+$pageView = new PageView($pwd, $start_hour, $end_hour, $week_start_sunday);
+$week = $pageView->createView(PageView::WEEK);
+
+if (isset($_GET['year']) && isset($_GET['week'])) {
+    $calendar = $week->getView($_GET['year'], $_GET['week']);
+}
+else {
+    $calendar = $week->getView();
+}
+print "<div id=\"ui\">$calendar</div>";
+include TOP_FOLDER.'/include/footer.inc.php';
+
+?>
diff --git a/pixmaps/add-event.png b/pixmaps/add-event.png
new file mode 100644 (file)
index 0000000..ef54089
Binary files /dev/null and b/pixmaps/add-event.png differ
diff --git a/pixmaps/calendar.png b/pixmaps/calendar.png
new file mode 100644 (file)
index 0000000..b85918e
Binary files /dev/null and b/pixmaps/calendar.png differ
diff --git a/pixmaps/configuration.png b/pixmaps/configuration.png
new file mode 100644 (file)
index 0000000..da6246f
Binary files /dev/null and b/pixmaps/configuration.png differ
diff --git a/pixmaps/day.png b/pixmaps/day.png
new file mode 100644 (file)
index 0000000..3562373
Binary files /dev/null and b/pixmaps/day.png differ
diff --git a/pixmaps/delete-event.png b/pixmaps/delete-event.png
new file mode 100644 (file)
index 0000000..5b96edc
Binary files /dev/null and b/pixmaps/delete-event.png differ
diff --git a/pixmaps/edit-event.png b/pixmaps/edit-event.png
new file mode 100644 (file)
index 0000000..c836d3a
Binary files /dev/null and b/pixmaps/edit-event.png differ
diff --git a/pixmaps/exit.png b/pixmaps/exit.png
new file mode 100644 (file)
index 0000000..7445887
Binary files /dev/null and b/pixmaps/exit.png differ
diff --git a/pixmaps/favicon.ico b/pixmaps/favicon.ico
new file mode 100644 (file)
index 0000000..8acf109
Binary files /dev/null and b/pixmaps/favicon.ico differ
diff --git a/pixmaps/goto-today.png b/pixmaps/goto-today.png
new file mode 100644 (file)
index 0000000..37943ba
Binary files /dev/null and b/pixmaps/goto-today.png differ
diff --git a/pixmaps/left.png b/pixmaps/left.png
new file mode 100644 (file)
index 0000000..a6c4668
Binary files /dev/null and b/pixmaps/left.png differ
diff --git a/pixmaps/month.png b/pixmaps/month.png
new file mode 100644 (file)
index 0000000..740d05f
Binary files /dev/null and b/pixmaps/month.png differ
diff --git a/pixmaps/print.png b/pixmaps/print.png
new file mode 100644 (file)
index 0000000..81e89ff
Binary files /dev/null and b/pixmaps/print.png differ
diff --git a/pixmaps/reload.png b/pixmaps/reload.png
new file mode 100644 (file)
index 0000000..6955ec6
Binary files /dev/null and b/pixmaps/reload.png differ
diff --git a/pixmaps/right.png b/pixmaps/right.png
new file mode 100644 (file)
index 0000000..8356b71
Binary files /dev/null and b/pixmaps/right.png differ
diff --git a/pixmaps/stock_init.png b/pixmaps/stock_init.png
new file mode 100644 (file)
index 0000000..12ca146
Binary files /dev/null and b/pixmaps/stock_init.png differ
diff --git a/pixmaps/week.png b/pixmaps/week.png
new file mode 100644 (file)
index 0000000..7ae8826
Binary files /dev/null and b/pixmaps/week.png differ
diff --git a/templates/day_view.class.php b/templates/day_view.class.php
new file mode 100644 (file)
index 0000000..a26132a
--- /dev/null
@@ -0,0 +1,202 @@
+<?php
+/* $Id$ */
+include_once 'view.class.php';
+require_once 'caldavresource.class.php';
+include_once 'dateformat.class.php';
+require_once 'helper.php';
+
+class DayView extends View {
+
+    private $url;
+    private $p_year;
+    private $privious;
+    private $year_and_day;
+    private $n_year;
+    private $next;
+    private $year;
+    private $day;
+    private $range;
+    private $calendar;
+    private $etags = array();
+    private $top = "<table style=\"width: 100%\">
+    <tr>
+        <td style=\"width: 33%; text-align: left;\"><a href=
+        \"__URL__?year=__P_YEAR__&amp;day=__PRIVIOUS__\">
+        <img style=\"border: 0\" src=\"__WEB_ROOT__/pixmaps/left.png\"
+        alt=\"Privious\"/></a></td>
+        <td style=\"width: 33%; font-size: 2.5em; font-weight: bold;
+        text-align: center\">__YEAR_AND_DAY__</td>
+        <td style=\"width: 33%; text-align: right;\">
+        <a href=\"__URL__?year=__N_YEAR__&amp;day=__NEXT__\">
+        <img style=\"border: 0\" src=\"__WEB_ROOT__/pixmaps/right.png\"
+        alt=\"next\"/></a></td>
+    </tr></table>";
+    private $bottom;
+
+    function __construct() {
+        $this->url = parent::$root.'/navigate/show_day.php';
+        $this->calendar = $calendar;
+        $this->range = array();
+    }
+
+    protected function parseDate() {
+        if ($this->year && $this->day >= 0) {
+            $day = $this->day;
+            if ($day < 0 || $day > 365) {
+                $day = date('z');
+                $year = date('Y');
+            }
+            else
+                $year = $this->year;
+        }
+        else {
+            $day = date('z');
+            $year = date('Y');
+        }
+        $now = mktime(0, 0, 0, 1, 1, $year);
+        $this->year_and_day = "$year day ".(date('z', strtotime("+$day days", $now))+1);
+
+        $this->privious = $day - 1;
+        $this->p_year = $year;
+        if ($this->privious < 0) {
+            $this->p_year -= 1;
+            $this->privious = date('z', mktime(0, 0, 0, 12, 31, $this->p_year));
+        }
+        $this->next = $day + 1;
+        $this->n_year = $year;
+        if ($this->next > date('z', mktime(0, 0, 0, 12, 31, $this->year))) {
+            $this->next = 0;
+            $this->n_year += 1;
+        }
+
+        $this->year = $year;
+        $this->day = $day;
+    }
+
+    protected function getHead() {
+        $this->parseDate();
+        $head = str_replace('__URL__', $this->url, $this->top);
+        $head = str_replace('__P_YEAR__', $this->p_year, $head);
+        $head = str_replace('__PRIVIOUS__', $this->privious, $head);
+        $head = str_replace('__WEB_ROOT__', parent::$root, $head);
+        $head = str_replace('__YEAR_AND_DAY__', $this->year_and_day, $head);
+        $head = str_replace('__N_YEAR__', $this->n_year, $head);
+        $head = str_replace('__NEXT__', $this->next, $head);
+        $this->bottom = $head;
+        return $this->applyLegend() . $head;
+    }
+
+    private function getCell($events, $day, $progress, $excluded = FALSE) {
+        $cell = '';
+        //$etags = array();
+        if ($events) {
+            $now = mktime(0, 0, 0, 1, 1, $this->year);
+            $ts = strtotime("+$day days +$progress hours", $now);
+            $ical_start = CaldavRessource::timestamp2ICal($ts, TRUE);
+            $ts = strtotime("+$day days +".($progress+1)." hours", $now);
+            $ical_end = CaldavRessource::timestamp2ICal($ts, TRUE);
+            $dateFormat = new dateFormat();
+            foreach ($events as $etag =>$infos) {
+                foreach ($infos['dates'] as $time) {
+                    CaldavRessource::fix_allday_event($time['start'],$time['end']);
+                    $time['start'] = $dateFormat->UTC2Client($time['start']);
+                    $time['end'] = $dateFormat->UTC2Client($time['end']);
+                    if ($excluded) {
+                        $ts = strtotime(
+                            "+$day days +".parent::$start_hour." hours", $now);
+                        $parent_start = CaldavRessource::timestamp2ICal($ts, TRUE);
+                        $end = (parent::$end_hour == 24) ? 23 : parent::$end_hour;
+                        $ts = strtotime(
+                            "+$day days +".$end." hours", $now);
+                        $parent_end = CaldavRessource::timestamp2ICal($ts, TRUE);
+                        $time_start = (strrchr($time['start'], 'Z')) ?
+                            trim($time['start'], "Z") : $time['start'];
+                        $time_end = (strrchr($time['end'], 'Z')) ?
+                            trim($time['end'], "Z") : $time['end'];
+                        $parts = explode("T", $time_end);
+                        if (substr($parts[1], 0, 2) == "00") {
+                            $parts[1] =  "24" . substr($parts[1], 2, 4);
+                            $time_end = $parts[0] . "T" . $parts[1];
+                        }
+                        //print substr($time_end, 0, 2)."b[$parent_start : $parent_end] [$time_start : $time_end]" . CaldavRessource::cmptime(
+                        //        $parent_end, $time_end) ."<br/>";
+                        /*if ((CaldavRessource::cmptime(
+                                $parent_start, $time_start) > 0  &&
+                            CaldavRessource::cmpdate($parent_start, $time_start) == 0)
+                            ||
+                            (CaldavRessource::cmptime(
+                                $parent_end, $time_end) < 0 &&
+                            CaldavRessource::cmpdate($parent_end, $time_end) == 0)) {*/
+                        if ((CaldavRessource::cmptime(
+                                $parent_start, $time_start) > 0  &&
+                            CaldavRessource::cmpdate($parent_start, $time_end) == 0)
+                            ||
+                            (CaldavRessource::cmptime(
+                                $parent_end, $time_end) < 0 &&
+                            CaldavRessource::cmpdate($parent_end, $time_start) == 0)) {
+                            if (! in_array($etag, $this->etags)) {
+                                array_push($this->etags, $etag);
+                                $cell = $this->makeEvent($cell, $etag, $infos);
+                                //print "$cell<br/>";
+                            }
+                        }
+                        //print "a[$parent_start : $parent_end] [$time_start : $time_end] parent_start:time_start" . CaldavRessource::cmptime(
+                        //    $parent_start, $time_start) ."<br/>";
+                        //print "a[$parent_start : $parent_end] [$time_start : $time_end] parent_end:time_end" . CaldavRessource::cmptime(
+                        //    $parent_end, $time_end) ."<br/>";
+                    }
+                    else {
+                        //print "$ical_start:$ical_end:{$time['start']}:{$time['end']}<br/>";
+                        $s_cmp = CaldavRessource::datecmp($ical_start, $time['start']);
+                        $e_cmp = CaldavRessource::datecmp($ical_start, $time['end']);
+                        if ($s_cmp >= 0 && $e_cmp < 0) {
+                        //    if (! in_array($etag, $this->etags)) {
+                                $cell = $this->makeEvent($cell, $etag, $infos);
+                                //print "$cell";
+                        //    }
+                        }
+                    }
+                }
+            }
+        }
+        return ($cell == '') ? '&nbsp;' : $cell;
+    }
+
+    public function getView($year = NULL, $day = NULL) {
+        $this->year = $year;
+        $this->day = $day;
+        $calendar = $this->getHead();
+        $calendar .= '<br />';
+        $calendar .=  str_replace('__DAY__', $this->dateFromDayInYear(
+            $this->year, $this->day, $head), $this->getViewHead('day'));
+        $now = mktime(0, 0, 0, 1, 1, $this->year);
+        $this->range['start'] = strtotime("+".($this->day - 1)." days", $now);
+        $this->range['end'] = strtotime("+".$this->day." days", $now);
+        $events = $this->getEvents($this->range);
+        $calendar .= "<tr><td class=\"time\">Excluded</td>\n";
+        $cell = $this->getCell($events, $this->day, 0, TRUE);
+        if ($this->day == date('z')) {
+            $calendar .= "<td class=\"today\">$cell</td>\n";
+        }
+        else
+            $calendar .= "<td>$cell</td>\n";
+        $calendar .= '</tr>';
+        for ($i = parent::$start_hour; $i < parent::$end_hour; $i++) {
+            $s_time = ($i < 10) ? "0$i" : $i;
+            $e_time = ($i < 9) ? '0'.($i + 1) : ($i + 1);
+            $calendar .= "<tr><td class=\"time\">$s_time - $e_time</td>\n";
+            $cell = $this->getCell($events, $this->day, $i);
+            if ($this->day == date('z')) {
+                $calendar .= "<td class=\"today\">$cell</td>\n";
+            }
+            else
+                $calendar .= "<td>$cell</td>\n";
+            $calendar .= '</tr>';
+        }
+        $calendar .= '</table></div>';
+        $calendar .= '<br />';
+        $calendar .= $this->bottom;
+        return $calendar;
+    }
+
+}
diff --git a/templates/month_view.class.php b/templates/month_view.class.php
new file mode 100644 (file)
index 0000000..a1687de
--- /dev/null
@@ -0,0 +1,214 @@
+<?php
+/* $Id$ */
+include_once 'view.class.php';
+require_once 'helper.php';
+
+class MonthView extends View {
+
+    private $view;
+    private $url;
+    private $p_year;
+    private $privious;
+    private $year_and_month;
+    private $n_year;
+    private $next;
+    private $year;
+    private $month;
+    private $range;
+    private $calendar;
+    private $top = "<table style=\"width: 100%\">
+    <tr>
+        <td style=\"width: 33%; text-align: left;\"><a href=
+        \"__URL__?year=__P_YEAR__&amp;month=__PRIVIOUS__\">
+        <img style=\"border: 0\" src=\"__WEB_ROOT__/pixmaps/left.png\"
+        alt=\"Privious\"/></a></td><td style=\"width: 33%;
+        font-size: 2.5em; font-weight: bold;text-align: center\">
+        __YEAR_AND_MONTH__</td><td style=\"width: 33%;
+        text-align: right;\"><a href=
+        \"__URL__?year=__N_YEAR__&amp;month=__NEXT__\">
+        <img style=\"border: 0\" src=\"__WEB_ROOT__/pixmaps/right.png\"
+        alt=\"next\"/></a></td></tr></table>";
+    private $bottom;
+
+    function __construct(Calendar $calendar = NULL) {
+        $this->url = parent::$root.'/navigate/show_month.php';
+        $this->calendar = $calendar;
+        $this->range = array();
+    }
+
+    protected function parseDate() {
+        if ($this->year && $this->month) {
+            $month = $this->month;
+            if ($month < 0 || $month > 12) {
+                $month = date('n');
+            }
+            $year = $this->year;
+        }
+        else {
+            $month = date('n');
+            $year = date('Y');
+        }
+
+        $this->privious = $month - 1;
+        $this->p_year = $year;
+        if ($this->privious < 1) {
+            $this->privious = 12;
+            $this->p_year -= 1;
+        }
+        $this->next = $month + 1;
+        $this->n_year = $year;
+        if ($this->next > 12) {
+            $this->next = 1;
+            $this->n_year += 1;
+        }
+        $this->year_and_month = date('F Y', mktime(0,0,0,$month,28,$year));
+        $this->year = $year;
+        $this->month = $month;
+    }
+
+    protected function getHead() {
+        $this->parseDate();
+        $head = str_replace('__URL__', $this->url, $this->top);
+        $head = str_replace('__P_YEAR__', $this->p_year, $head);
+        $head = str_replace('__PRIVIOUS__', $this->privious, $head);
+        $head = str_replace('__WEB_ROOT__', parent::$root, $head);
+        $head = str_replace('__YEAR_AND_MONTH__', $this->year_and_month, $head);
+        $head = str_replace('__N_YEAR__', $this->n_year, $head);
+        $head = str_replace('__NEXT__', $this->next, $head);
+        $this->bottom = $head;
+        return $this->applyLegend() . $head;
+    }
+
+    private function getCell($events, $day) {
+        $ts = gmmktime(0,0,0,$this->month,$day,$this->year);
+        //print date('c', $ts).'<br/>';
+        $icalDate = CaldavRessource::timestamp2ICal($ts, FALSE);
+        $cell = '';
+        if ($events) {
+            $dateFormat = new dateFormat();
+            foreach ($events as $etag =>$infos) {
+                foreach ($infos['dates'] as $time) {
+                    //if ('20100619T144500' == $time['start'])
+                    //print "$icalDate:{$time['start']}:{$time['end']}<br/>";
+                    $time['start'] = $dateFormat->UTC2Client($time['start']);
+                    $time['end'] = $dateFormat->UTC2Client($time['end']);
+                    $s_cmp = CaldavRessource::cmpdate(
+                        $icalDate, $time['start']);
+                    $e_cmp = CaldavRessource::cmpdate(
+                        $icalDate, $time['end']);
+                    if (($s_cmp >= 0 && $e_cmp <= 0) ||
+                        ($s_cmp == 0 && $e_cmp > 0) ||
+                        ($s_cmp > 0 && $e_cmp == 0)) {
+                        $cell = $this->makeEvent($cell, $etag, $infos);
+                        //print "$cell<br/>";
+                    }
+                }
+            }
+        }
+        return ($cell == '') ? '&nbsp;' : $cell;
+    }
+
+    public function getView($year = NULL, $month = NULL) {
+        $this->year = $year;
+        $this->month = $month;
+        $calendar = $this->getHead();
+        $calendar .= '<br />';
+        $calendar .= $this->getViewHead('month');
+        $days = date('t', gmmktime(0,0,0,$this->month,1,$this->year));
+        $this->range['start'] = gmmktime(0,0,0,$this->month,1,$this->year);
+        $this->range['end'] = gmmktime(23,59,59,$this->month,$days,$this->year);
+        $adjust = (parent::$start_sunday) ? 0 : 1;
+        $day = 1;
+        $start = $this->getStartDay($this->year, $this->month);
+        if ($start == 0 && ! parent::$start_sunday) {
+            switch ($days) {
+                case 28:
+                case 29: $stop = 5; break;
+                default: $stop = 6;
+            }
+        }
+        else if ($start == 1 && ! parent::$start_sunday) {
+            switch ($days) {
+                case 28: $stop = 4; break;
+                default: $stop = 5;
+            }
+        }
+        else if ($start == 0 && parent::$start_sunday) {
+            switch ($days) {
+                case 28: $stop = 4; break;
+                default: $stop = 5;
+            }
+        }
+        else if ($start > 4 && parent::$start_sunday) {
+            if ($days > 29)
+                $stop = 6;
+            else
+                $stop = 5;
+        }
+        else $stop = 5;
+        $events = $this->getEvents($this->range);
+        for ($i = 0; $i < $stop; $i++) {
+            $week = $this->get_week_number(
+                mktime(0,0,0,$this->month,($i * 7)+1,$this->year));
+            $calendar .= "<tr><td class=\"weeknum\">$week</td>";
+            if ($i == 0) {
+                if ($adjust) {
+                    if ($start > 0) {
+                        for ($notused = 0; $notused < $start - 1; $notused++)
+                            $calendar .= "<td class=\"notused\">&nbsp;</td>";
+                        for ($notused = 7 - $notused; $notused > 0; $notused--, $day++) {
+                            $cell = $this->getCell($events, $day);
+                            if ($this->isToDay($this->year, $this->month, $day))
+                                $calendar .= "<td class=\"today\">$cell<span class=\"date\">$day</span></td>";
+                            else
+                                $calendar .= "<td>$cell<span class=\"date\">$day</span></td>";
+                        }
+                    }
+                    else {
+                        for ($notused = 0; $notused < 6; $notused++)
+                            $calendar .= "<td class=\"notused\">&nbsp;</td>";
+                        $cell = $this->getCell($events, $day);
+                        if ($this->isToDay($this->year, $this->month, $day))
+                            $calendar .= "<td class=\"today\">$cell<span class=\"date\">$day</span></td>";
+                        else
+                            $calendar .= "<td>$cell<span class=\"date\">$day</span></td>";
+                        $day++;
+                    }
+                }
+                else {
+                    for ($notused = $start; $notused > 0; $notused--)
+                        $calendar .= "<td class=\"notused\"></td>";
+                    for ($notused = 7 - $start; $notused > 0; $notused--, $day++) {
+                        $cell = $this->getCell($events, $day);
+                        if ($this->isToDay($this->year, $this->month, $day))
+                            $calendar .= "<td class=\"today\">$cell<span class=\"date\">$day</span></td>";
+                        else
+                            $calendar .= "<td>$cell<span class=\"date\">$day</span></td>";
+                    }
+                }
+            }
+            else {
+                $j = $adjust + ($i * 7);
+                for (; $j < ($i * 7) + (7 + $adjust); $j++, $day++) {
+                    if ($day > $days)
+                        break;
+                    $cell = $this->getCell($events, $day);
+                    if ($this->isToDay($this->year, $this->month, $day))
+                        $calendar .= "<td class=\"today\">$cell<span class=\"date\">$day</span></td>";
+                    else
+                        $calendar .= "<td>$cell<span class=\"date\">$day</span></td>";
+                }
+                while ($j < ($i * 7) + (7 + $adjust)) {
+                    $calendar .= "<td class=\"notused\">&nbsp;</td>";
+                    $j++;
+                }
+            }
+            $calendar .= "</tr>\n";
+        }
+        $calendar .= '</table></div>';
+        $calendar .= '<br />';
+        $calendar .= $this->bottom;
+        return $calendar;
+    }
+
+}
diff --git a/templates/view.class.php b/templates/view.class.php
new file mode 100644 (file)
index 0000000..592224c
--- /dev/null
@@ -0,0 +1,313 @@
+<?php
+/* $Id$ */
+
+require_once 'user_validate.php';
+
+valid_user();
+
+include_once 'config.inc.php';
+require_once 'helper.php';
+
+include_once 'month_view.class.php';
+include_once 'week_view.class.php';
+include_once 'day_view.class.php';
+include_once 'calendar.class.php';
+
+abstract class View {
+    protected static $start_sunday;
+    protected static $root;
+    protected static $start_hour;
+    protected static $end_hour;
+    private $heading = "<div id=\"events\"><table><tr>";
+    private $view_head_sunday = "
+        <th class=\"event\">Sunday</th>
+        <th class=\"event\">Monday</th>
+        <th class=\"event\">Tuesday</th>
+        <th class=\"event\">Wednedsday</th>
+        <th class=\"event\">Thursday</th>
+        <th class=\"event\">Friday</th>
+        <th class=\"event\">Saturday</th>
+    </tr>";
+    private $view_head_monday = "
+        <th class=\"event\">Monday</th>
+        <th class=\"event\">Tuesday</th>
+        <th class=\"event\">Wednedsday</th>
+        <th class=\"event\">Thursday</th>
+        <th class=\"event\">Friday</th>
+        <th class=\"event\">Saturday</th>
+        <th class=\"event\">Sunday</th>
+    </tr>";
+    private $view_head_day = "
+        <th class=\"day_event\">__DAY__</th>
+    </tr>";
+
+    function __construct($root, $start_hour, $end_hour, $start_sunday = FALSE) {
+        self::$start_sunday = $start_sunday;
+        self::$root = $root;
+        self::$start_hour = $start_hour;
+        self::$end_hour = $end_hour;
+    }
+
+    protected function applyLegend() {
+        $calenders = $_SESSION['user_settings']->getCalendars();
+        $legend = '<p style="text-align: center">';
+        foreach ($calenders as $calender) {
+            $legend .= '[<span style="background: ' . $calender->color .
+                '">&nbsp;&nbsp;&nbsp;</span>:' . $calender->name . ']';
+        }
+        $legend .= '</p>';
+        $legend .= <<<_HELP
+            <p style="text-align: center">
+                Events outside configured range is only displayed
+                when Day or Week view is active
+            </p>
+_HELP;
+        return $legend;
+    }
+
+    protected function getViewHead($kindOf) {
+        switch ($kindOf) {
+            case 'month':
+                $return = $this->heading.'<th>Week</th>';
+                if (self::$start_sunday)
+                    $return .= $this->view_head_sunday;
+                else
+                    $return .= $this->view_head_monday;
+                return $return;
+                break;
+            case 'week':
+                if (self::$start_sunday) {
+                    $head = str_replace('</th>', '<br/>__DATE__</th>',
+                        $this->view_head_sunday);
+                }
+                else {
+                    $head = str_replace('</th>', '<br/>__DATE__</th>',
+                        $this->view_head_monday);
+                }
+                $head = '<th>Time</th>' . $head;
+                return $this->heading . $head;
+                break;
+            case 'day':
+                $return = $this->heading . '<th>Time</th>';
+                return $return . $this->view_head_day;
+                break;
+            default: trigger_error("$kindOf: ['month', 'week', 'day']", E_USER_ERROR);
+        }
+    }
+
+    /*
+     * Function get_week_number which returns the week number of the
+     * given date according to ISO 8601-1988
+     * http://www.php.happycodings.com/Date_Time/code22.html
+     */
+    protected function is_leap_year($year) {
+        if ((($year % 4) == 0 and ($year % 100)!=0) or ($year % 400)==0) {
+                return 1;
+        } else {
+                return 0;
+        }
+    }
+
+    /* define ISO_WEEK_START_WDAY 1 // Monday
+     * define ISO_WEEK1_WDAY 4 // Thursday
+     * define YDAY_MINIMUM (-366)
+     * int big_enough_multiple_of_7 = (-YDAY_MINIMUM / 7 + 2) * 7;
+     * return (yday - (yday - wday + ISO_WEEK1_WDAY +
+     * big_enough_multiple_of_7) % 7 + ISO_WEEK1_WDAY -
+     * ISO_WEEK_START_WDAY);
+     */
+    private function iso_week_days($yday, $wday) {
+        return $yday - (($yday - $wday + 382) % 7) + 3;
+    }
+
+    protected function get_week_number($timestamp) {
+        $d = getdate($timestamp);
+        $days = $this->iso_week_days($d[ "yday"], $d[ "wday"]);
+
+        if ($days < 0) {
+            $d[ "yday"] += 365 + $this->is_leap_year(--$d[ "year"]);
+            $days = $this->iso_week_days($d[ "yday"], $d[ "wday"]);
+        } else {
+            $d[ "yday"] -= 365 + $this->is_leap_year($d[ "year"]);
+            $d2 = $this->iso_week_days($d[ "yday"], $d[ "wday"]);
+            if (0 <= $d2) {
+                /* $d["year"]++; */
+                $days = $d2;
+            }
+        }
+        $adjust = 1;
+        $day = date('w');
+        if ($_SESSION['user_settings']->getStartWeek() === true && $day == 0)
+            $adjust = 2;
+        return (int)($days / 7) + $adjust;
+    }
+
+    protected function getStartDay($year, $month) {
+        /* date('w') returns start day counting from Sunday */
+        $start = date('w', mktime(0,0,0,$month,1,$year));
+        return $start;
+    }
+
+    protected function isToDay($year, $month, $day) {
+        return ($year == date('Y') && $month == date('n') && $day == date('j'));
+    }
+
+    protected function lastDayInWeek($year, $week) {
+        $weekEndDay = 0;  // 0 = Sunday.
+        $dayOfYear = 4 + (($week - 1) * 7);
+        $date = mktime(0, 0, 0, 1, $dayOfYear, $year);
+        // Find the last day of this week.
+        $dayOfWeek = date("w", $date);
+        $daysToAdd = ($weekEndDay - $dayOfWeek + 7) % 7;
+        $date += $daysToAdd * 24*60*60;
+        return (self::$start_sunday) ? $date - (24*60*60) : $date;
+    }
+
+    protected function dateFromDayInYear($year, $day) {
+        $now = mktime(0, 0, 0, 1, 1, $year);
+        return date('l, j F', strtotime("+$day days", $now));
+    }
+
+    protected function makeSummary($infos) {
+        $summary = 'Title: ';
+        $summary .= (isset($infos['summary']) && ! empty($infos['summary'])) ?
+                $infos['summary'] : 'Untitled';
+        $summary .= "\n";
+        $summary .= 'Date: ';
+        $start = strtotime($infos['dates'][0]['start']);
+        $date = date("D j M Y", $start); 
+        $end = strtotime($infos['dates'][0]['end']);
+        $summary .= $date . ' ' . date("G:i", $start) . ' - ' . date("G:i", $end);
+        
+        return $summary;
+    }
+    
+    protected function makeEvent($cell, $uri, $infos) {
+        $history = $_SERVER['PHP_SELF'];
+        $summary = (isset($infos['summary']) && ! empty($infos['summary'])) ?
+                $infos['summary'] : 'Untitled';
+        $history .= (empty($_SERVER['QUERY_STRING'])) ? '' : '?'.$_SERVER['QUERY_STRING'];
+        //$url = WEB_ROOT . 'events/edit_event.php?etag='. $uri;
+        $url = 'etag='. $uri.'&referer='.$history.'&cal='.$infos['cal'];
+        $url = WEB_ROOT . 'events/edit_event.php?' . urlencode($url);
+        $c = '<span style="background: '.$infos['color'].';color: #E6E6E6;
+        font-weigth: bold;"><a href="'.$url.
+        '" title="'.$this->makeSummary($infos).'">'.$summary.'</a></span>';
+        return ($cell != '') ? "$cell<br/>$c" : $c;
+    }
+
+    protected function getEvents($range) {
+        $list = array();
+        $all_events = array();
+
+        $cals = $_SESSION['user_settings']->getCalendars();
+        //file_put_contents('/tmp/davical.log',
+        //    __FILE__ . ": " . var_export($cals, TRUE), FILE_APPEND);
+        if (! $cals)
+            return NULL;
+        $start = CaldavRessource::timestamp2ICal($range['start'], TRUE);
+        if ($_SESSION['user_settings']->getStartWeek() === FALSE) {
+            $datetime = explode('T', $start);
+            //print_r($datetime);
+            $day = $datetime[0] + 1;
+            $start = $day.'T'.$datetime[1].'Z';
+        }
+        $end = CaldavRessource::timestamp2ICal($range['end'], TRUE);
+        $datetime = explode('T', $end);
+        $end = $datetime[0].'T'.'235959Z';
+        //print "$start:$end<br/>";
+        foreach ($cals as $cal) {
+            //var_dump($cal);
+               $conf = implode_cal(decode($cal->config));
+               //var_dump($conf);
+            $events = new Calendar($conf['url'], $conf['uid'], $conf['pwd']);
+            //print_r($events);
+               $events->getComponents($start, $end);
+               //print_r($events);
+            $all_events[$cal->name] = $events;
+            foreach ($events as $event) {
+                $dates = $event->getActiveDates();
+                //file_put_contents('/tmp/davical.log',
+                //__FILE__ . ": " . var_export($dates, TRUE), FILE_APPEND);
+                //var_dump($dates);
+                $comp = $event->getBaseComponent();
+                //print_r($comp);
+                $summary = $comp->GetPValue('SUMMARY');
+                $item = array();
+                       $dtend = explode('T', $comp->GetPValue('DTEND'));
+                //print_r($comp->GetPValue('DTEND'));
+                //print "<br/>";
+                //print_r($dtend);
+                //print "<br/>";
+                //print_r($dates);
+                //print "<br/>--------------------------<br/>";
+                if (count($dates) > 0) {
+                    foreach ($dates as $date) {
+                        //print_r($date);
+                        //print "<br/>--------------------------<br/>";
+                        //$datetime = explode('T', $date);
+                        //print_r($dtend);
+                        //print "<br/>--------------------------<br/>";
+                                   //if (count($datetime) > 1) {
+                        if (count($dtend) > 1) {
+                                       //if (count($dtend) < 2)
+                                           //    array_push($dtend, '235959');
+                                       //$endtime = $datetime[0].'T'.$dtend[1];
+                            $endtime = $dtend[0].'T'.$dtend[1];
+                                   }
+                        else {
+                            //$endtime = $datetime[0];
+                            $day = strtotime($dtend[0]);
+                            $day = strtotime("-1 day", $day);
+                            $endtime = date("Ymd\T235959", $day);
+                            //$endtime = $endtime."T235959";
+                            //$endtime = $dtend[0]."T000000";
+                        }
+                        //print "$date:$endtime<br/>";
+                        array_push($item, array('start' => $date, 'end' => $endtime));
+                    }
+                    $list[$event->getEtag()] = array('color' => $cal->color,
+                    'summary' => $summary, 'dates' => $item, 'cal' => $cal->name);
+                }
+            }
+        }
+        $_SESSION['all_events'] = $all_events;
+        //var_dump($list);
+        return $list;
+    }
+
+    protected abstract function parseDate();
+    protected abstract function getHead();
+    public abstract function getView($year = NULL, $other = NULL);
+}
+
+class PageView extends View {
+    const DAY = 0;
+    const WEEK = 1;
+    const MONTH = 2;
+
+    function __construct($root, $start_hour, $end_hour, $start_sunday = FALSE) {
+        parent::__construct($root, $start_hour, $end_hour, $start_sunday);
+    }
+
+    public function createView($view) {
+        switch ($view) {
+            case (self::DAY): return new DayView(); break;
+            case (self::WEEK): return new WeekView(); break;
+            case (self::MONTH): return new MonthView(); break;
+            default: trigger_error("$view: ['MONTH', 'WEEK', 'DAY']", E_USER_ERROR);
+        }
+    }
+
+    protected function parseDate() {
+        throw new Exception("Function can only be called from a subclass");
+    }
+
+    protected function getHead() {
+        throw new Exception("Function can only be called from a subclass");
+    }
+
+    public function getView($year = NULL, $other = NULL) {
+        throw new Exception("Function can only be called from a subclass");
+    }
+}
diff --git a/templates/week_view.class.php b/templates/week_view.class.php
new file mode 100644 (file)
index 0000000..08e1216
--- /dev/null
@@ -0,0 +1,247 @@
+<?php
+/* $Id$ */
+include_once 'view.class.php';
+require_once 'caldavresource.class.php';
+include_once 'dateformat.class.php';
+require_once 'helper.php';
+
+class WeekView extends View {
+
+    private $url;
+    private $p_year;
+    private $privious;
+    private $year_and_week;
+    private $n_year;
+    private $next;
+    private $year;
+    private $week;
+    private $range;
+    private $calendar;
+    private $etags = array();
+    private $top = "<table style=\"width: 100%\">
+    <tr>
+        <td style=\"width: 33%; text-align: left;\"><a href=
+        \"__URL__?year=__P_YEAR__&amp;week=__PRIVIOUS__\">
+        <img style=\"border: 0\" src=\"__WEB_ROOT__/pixmaps/left.png\"
+        alt=\"Privious\"/></a></td>
+        <td style=\"width: 33%; font-size: 2.5em; font-weight: bold;
+        text-align: center\">__YEAR_AND_WEEK__</td>
+        <td style=\"width: 33%; text-align: right;\">
+        <a href=\"__URL__?year=__N_YEAR__&amp;week=__NEXT__\">
+        <img style=\"border: 0\" src=\"__WEB_ROOT__/pixmaps/right.png\"
+        alt=\"next\"/></a></td>
+    </tr></table>";
+    private $bottom;
+
+    function __construct(Calendar $calendar = NULL) {
+        $this->url = parent::$root.'/navigate/show_week.php';
+        $this->calendar = $calendar;
+        $this->range = array();
+    }
+
+    protected function parseDate() {
+        if ($this->year && $this->week) {
+            $week = $this->week;
+            if ($week < 0 || $week > 53) {
+                $week = $this->get_week_number(time());
+            }
+            $year = $this->year;
+        }
+        else {
+            $week = $this->get_week_number(time());
+            $year = date('Y');
+        }
+        $this->privious = $week - 1;
+        $this->p_year = $year;
+        if ($this->privious == 0) {
+            $this->p_year -= 1;
+            if (date("w", strtotime($this->p_year."/12/31")) > 3)
+                $this->privious = 53;
+            else
+                $this->privious = 52;
+        }
+        $this->next = $week + 1;
+        $this->n_year = $year;
+        if ($this->next > 51) {
+            if ($this->get_week_number(strtotime("$year/12/31")) == 1 ||
+                $this->next > 53) {
+                $this->next = 1;
+                $this->n_year += 1;
+            }
+        }
+
+        $this->year_and_week = "$year Week $week";
+        $this->year = $year;
+        $this->week = $week;
+    }
+
+    protected function getHead() {
+        $this->parseDate();
+        $head = str_replace('__URL__', $this->url, $this->top);
+        $head = str_replace('__P_YEAR__', $this->p_year, $head);
+        $head = str_replace('__PRIVIOUS__', $this->privious, $head);
+        $head = str_replace('__WEB_ROOT__', parent::$root, $head);
+        $head = str_replace('__YEAR_AND_WEEK__', $this->year_and_week, $head);
+        $head = str_replace('__N_YEAR__', $this->n_year, $head);
+        $head = str_replace('__NEXT__', $this->next, $head);
+        $this->bottom = $head;
+        return $this->applyLegend() . $head;
+    }
+
+    private function addDates($head) {
+        $timestamp = $this->lastDayInWeek($this->year, $this->week);
+        $i = 7;
+        $this->range['end'] = $timestamp;
+        do {
+            $date = date('Y/m/d', $timestamp);
+            $timestamp = strtotime('-1 day', $timestamp);
+            $head = substr_replace($head, $date, strrpos($head, '__DATE__'), 8);
+            $i--;
+        } while ($i > 0);
+        $this->range['start'] = $timestamp;
+        return $head;
+    }
+
+    private function getCell($events, $day, $progress, $excluded = FALSE) {
+        $cell = '';
+        //$etags = array();
+        if ($events) {
+            //print var_export($events,true)."<br/>";
+            $dateFormat = new dateFormat();
+            foreach ($events as $etag =>$infos) {
+                foreach ($infos['dates'] as $time) {
+                    //print "{$time['start']}:{$time['end']}<br/>";
+                    //print $time['end']."<br/>";
+                    CaldavRessource::fix_allday_event($time['start'],$time['end']);
+                    //print $time['end']."<br/>";
+                    //print "{$time['start']}:{$time['end']}<br/>";
+                    $time['start'] = $dateFormat->UTC2Client($time['start']);
+                    $time['end'] = $dateFormat->UTC2Client($time['end']);
+                    //print $time['end']."<br/>";
+                    if ($excluded) {
+                        $ts = strtotime("+$day day +".parent::$start_hour." hour",
+                            $this->range['start']);
+                        $ical_start = CaldavRessource::timestamp2ICal($ts);
+                        //print parent::$end_hour;
+                        $end = (parent::$end_hour == 24) ? 23 : parent::$end_hour;
+                        $ts = strtotime("+$day day +".$end." hour",
+                            $this->range['start']);
+                        $ical_end = CaldavRessource::timestamp2ICal($ts);
+                        $time_start = (strrchr($time['start'], 'Z')) ?
+                            trim($time['start'], "Z") : $time['start'];
+                        $time_end = (strrchr($time['end'], 'Z')) ?
+                            trim($time['end'], "Z") : $time['end'];
+                        //print $time_end."\n";
+                        $parts = explode("T", $time_end);
+                        if (substr($parts[1], 0, 2) == "00") {
+                            $parts[1] =  "24" . substr($parts[1], 2, 4);
+                            $time_end = $parts[0] . "T" . $parts[1];
+                        }
+                        //print "b[$time_start : $time_end]<br/>";
+                        //print "a[$ical_start : $ical_end] [$time_start : $time_end]<br/>";
+                        if ((CaldavRessource::cmptime(
+                                $ical_start, $time_start) > 0  &&
+                            CaldavRessource::cmpdate($ical_start, $time_end) == 0)
+                            ||
+                            (CaldavRessource::cmptime(
+                                $ical_end, $time_end) < 0 &&
+                            CaldavRessource::cmpdate($ical_end, $time_start) == 0)) {
+                            //print "b[$ical_start : $ical_end] [$time_start : $time_end]<br/>";
+                            if (! in_array($etag, $this->etags)) {
+                                array_push($this->etags, $etag);
+                                $cell = $this->makeEvent($cell, $etag, $infos);
+                                //print "$cell<br/>";
+                                //print "exc: cell: $etag<br/>";
+                                //print "exc: ".var_export($this->etags, true)."<br/>";
+                            }
+                        }
+                    }
+                    else {
+                        $ts = strtotime("+$day day +$progress hour",
+                            $this->range['start']);
+                        $icalDate = CaldavRessource::timestamp2ICal($ts, TRUE);
+                        //print "$icalDate {$time['start']}:{$time['end']}<br/>";
+                        $s_cmp = CaldavRessource::datecmp($icalDate, $time['start']);
+                        $e_cmp = CaldavRessource::datecmp($icalDate, $time['end']);
+                        //print "$s_cmp:$e_cmp<br/>";
+                        if ($s_cmp >= 0 && $e_cmp < 0) {
+                            //if (! in_array($etag, $this->etags)) {
+                                $cell = $this->makeEvent($cell, $etag, $infos);
+                                //print "$cell<br/>";
+                                //print "inc: cell: $etag<br/>";
+                                //print "inc: ".var_export($this->etags, true)."<br/>";
+                            //}
+                        }
+                    }
+                }
+            }
+        }
+        return ($cell == '') ? '&nbsp;' : $cell;
+    }
+
+    public function getView($year = NULL, $week = NULL) {
+        $this->year = $year;
+        $this->week = $week;
+        $calendar = $this->getHead();
+        $calendar .= '<br />';
+        $calendar .= $this->addDates($this->getViewHead('week'));
+        if ($this->get_week_number(time()) == $week || $week == NULL) {
+            $day = date('w');
+            if ($_SESSION['user_settings']->getStartWeek() === FALSE) {
+                if ($day == 0)
+                    $day = 6;
+                else
+                    $day--;
+            }
+        }
+        else
+            $day = -1;
+        $events = $this->getEvents($this->range);
+        //var_dump($events);
+        //var_dump($_SESSION['all_events']);
+        $calendar .= "<tr><td class=\"time\">Excluded</td>\n";
+        $excluded = array();
+        for ($d = 0; $d < 7; $d++) {
+            $cell = $this->getCell($events, $d + 1, 0, TRUE);
+            if ($d == $day && ($year == date('Y') || $year == NULL)) {
+                $calendar .= "<td class=\"today\">$cell</td>\n";
+            }
+            else
+                $calendar .= "<td>$cell</td>\n";
+            //print_r($cell);
+            //if ($d < 6) {
+            //    print "clean etags<br/>";
+            //    $this->etags = array();
+            //}
+            array_push($excluded, $this->etags);
+            $this->etags = array();
+        }
+        $calendar .= '</tr>';
+        for ($i = parent::$start_hour; $i < parent::$end_hour; $i++) {
+            $s_time = ($i < 10) ? "0$i" : $i;
+            $e_time = ($i < 9) ? '0'.($i + 1) : ($i + 1);
+            $calendar .= "<tr><td class=\"time\">$s_time - $e_time</td>\n";
+            for ($d = 0; $d < 7; $d++) {
+                $this->etags = $excluded[$d];
+                $progress = $i;
+                $cell = $this->getCell($events, $d + 1, $progress);
+                //print_r($cell);
+                if ($d == $day && ($year == date('Y') || $year == NULL)) {
+                    $calendar .= "<td class=\"today\">$cell</td>\n";
+                }
+                else
+                    $calendar .= "<td>$cell</td>\n";
+            }
+            $calendar .= '</tr>';
+        }
+        $calendar .= '</table></div>';
+        $calendar .= '<br />';
+        $calendar .= $this->bottom;
+        //file_put_contents('/tmp/davical.log', $calendar);
+        return $calendar;
+    }
+
+    function get_week_number($timestamp) {
+        return parent::get_week_number($timestamp);
+    }
+}
diff --git a/user_exist_error.php b/user_exist_error.php
new file mode 100644 (file)
index 0000000..8fef2c0
--- /dev/null
@@ -0,0 +1,29 @@
+<?php
+/* $Id$ */
+include_once 'config.inc.php';
+
+$page = <<<__HTML
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+    <head>
+        <title>Web Calendar</title>
+        <link rel="stylesheet" type="text/css" href="css/top_level.css" />
+        <link rel="shortcut icon" href="pixmaps/favicon.ico" />
+    </head>
+    <body>
+        <div id="error_msg">
+            <h1 style="text-align: center">Cannot create user account</h1>
+            <p style="text-align: center">
+                A user with your username already exists.<br/>
+                You should contact your system administrator with this message 
+                and inform him or her of your username
+            </p>
+        </div>
+    </body>
+</html>
+__HTML;
+
+echo $page;
+?>
\ No newline at end of file
diff --git a/user_validate.php b/user_validate.php
new file mode 100644 (file)
index 0000000..b4fb759
--- /dev/null
@@ -0,0 +1,28 @@
+<?php
+/* $Id$ */
+    require_once 'config.inc.php';
+    require_once 'user_settings.inc.php';
+    require_once 'helper.php';
+
+    function valid_user() {
+        if (! isset($_SESSION['authenticate']) ||
+            ! is_object($_SESSION['authenticate'])) {
+            header('Location: ' . WEB_ROOT . 'login.php');
+            exit();
+        }
+
+        $auth = $_SESSION['authenticate'];
+        if (! $auth->validUser()) {
+            header('Location: ' . WEB_ROOT . 'login.php');
+            exit();
+        }
+    }
+    
+    function has_admin_role() {
+        if (! isset($_SESSION['user_settings']) ||
+            ! is_object($_SESSION['user_settings'])) {
+            return false;
+        }
+        return ($_SESSION['user_settings']->getRole() == 0);
+    }
+
diff --git a/utils/authenticate.php b/utils/authenticate.php
new file mode 100644 (file)
index 0000000..d6d70ba
--- /dev/null
@@ -0,0 +1,180 @@
+<?php
+/* $Id$ */
+require_once 'config.inc.php';
+require_once 'persistens.php';
+require_once 'helper.php';
+
+class Authenticate {
+
+    private $valid_user;
+    private $settings;
+    private $db;
+    private $secKey;
+    private $pubKey;
+    private $ldap;
+
+    public function __construct($db) {
+        $this->valid_user = FALSE;
+        $this->settings = array();
+        $this->db = $db;
+        $this->secKey = NULL;
+        $this->pubKey = NULL;
+        $this->ldap = NULL;
+    }
+
+    public function login($uid, $pwd) {
+        $con = Persistens::getInstance($this->db);
+        if ($this->useLDAP() && $uid != 'admin') {
+            if ($this->authLDAP($uid, $pwd)) {
+                // check user exists. Internal password sha1 hash of uid
+                $pwd = sha1($uid);
+                //echo "$uid:$pwd<br/>";
+                //exit;
+                $settings = array_change_key_case(
+                        $con->authenticate($uid, $pwd));
+                //print_r($settings);
+                //exit;
+                if (is_array($settings) && count($settings) > 0) {
+                    // user found
+                    $this->valid_user = TRUE;
+                }
+                else if (is_array($settings) && count($settings) == 0) {
+                    // User not found
+                    if ($con->getRole($uid)) {
+                        // A user with this uid exists. We cannot create
+                        $this->valid_user = FALSE;
+                        if (session_id())
+                            session_destroy();
+                        header('Location: ' . WEB_ROOT . 'user_exist_error.php');
+                        exit;
+                    }
+                    else {
+                        // User does not exist so create a normal user
+                        $data = create_user_data($uid, $pwd, 2);
+                        if ($con->newUser($data) === FALSE) {
+                            $this->valid_user = FALSE;
+                            if (session_id())
+                                session_destroy();
+                            header('Location: ' . WEB_ROOT . 'error.html');
+                            exit;
+                        }
+                        $settings = array_change_key_case(
+                                $con->authenticate($uid, $pwd));
+                        if (count($settings) == 0) {
+                            $this->valid_user = FALSE;
+                        }
+                        else {
+                            $this->valid_user = TRUE;
+                        }
+                    }
+                }
+                else {
+                    $this->valid_user = FALSE;
+                    if (session_id())
+                        session_destroy();
+                    header('Location: ' . WEB_ROOT . 'error.html');
+                    exit;
+                }
+            }
+            else {
+                $this->valid_user = FALSE;
+            }
+        }
+        else {
+            $settings = array_change_key_case(
+                    $con->authenticate($uid, $pwd));
+            if (count($settings) == 0) {
+                $this->valid_user = FALSE;
+            }
+            else {
+                $this->valid_user = TRUE;
+            }
+        }
+        if ($this->valid_user == TRUE) {
+            $setting = array();
+            $this->settings = array();
+            foreach ($settings as $row) {
+                $this->secKey = $row['seckey'];
+                $this->pubKey = $row['pubkey'];
+                foreach ($row as $key => $val) {
+                    if ($key != 'seckey' || $key != 'pubkey')
+                        $setting[$key] = $val;
+                }
+                array_push($this->settings, $setting);
+            }
+        }
+    }
+
+    public function logout() {
+        $this->valid_user = false;
+        $this->key = NULL;
+    }
+
+    public function validUser() {
+        return $this->valid_user;
+    }
+
+    public function getSettings() {
+        return $this->settings;
+    }
+
+    public function getSecretKey() {
+        return $this->secKey;
+    }
+
+    public function getPublicKey() {
+        return $this->pubKey;
+    }
+
+    private function useLDAP() {
+        $con = Persistens::getInstance($this->db);
+        $version = $con->getVersion();
+        $version = string2int($version['version']);
+        //print_r($version);
+        if ($version < 175) {
+            // no LDAP before 0.7.5
+            return FALSE;
+        }
+        $this->ldap = $con->getLdapConfig();
+           if (! is_array($this->ldap) && $this->ldap) {
+            $this->ldap = NULL;
+            if (session_id())
+                session_destroy();
+            header('Location: ' . WEB_ROOT . 'error.html');
+            exit;
+        }
+
+           return ($this->ldap && $this->ldap['enable'] !== 0);
+    }
+    
+    private function authLDAP($uid, $pwd) {
+        $res = false;
+        $ver = 3;
+
+        // ldap_bind always accepts login if password is empty since and
+        // empty password will be considered a try to make an anonymous login
+        if ($this->ldap && $uid && $pwd && !empty($pwd)) {
+            $dns = $this->ldap['dns'];
+            $dn = $this->ldap['user_attr'] . "=$uid," . $this->ldap['base_dn'];
+            $lc = ldap_connect($dns);
+            if ($lc) {
+                if (ldap_set_option($lc, LDAP_OPT_PROTOCOL_VERSION, 3) === false) {
+                    if (ldap_set_option($lc, LDAP_OPT_PROTOCOL_VERSION, 2) === FALSE)
+                        return $res;
+                    $ver = 2;
+                }
+                if ($this->ldap['tls']) {
+                    if ($ver < 3)
+                        return $res;
+                    if (ldap_start_tls($lc) === false)
+                        return $res;
+                }
+                //echo "$ver: $dn\n";
+                if (@ldap_bind($lc, $dn, $pwd))
+                    $res = true;
+                ldap_close($lc);
+            }
+        }
+        return $res;
+    }
+}
diff --git a/utils/calendar.php b/utils/calendar.php
new file mode 100644 (file)
index 0000000..e603f83
--- /dev/null
@@ -0,0 +1,188 @@
+<?php
+/* $Id$ */
+require_once 'config.inc.php';
+require_once 'user_settings.inc.php';
+require_once 'user_validate.php';
+require_once 'helper.php';
+
+valid_user();
+
+if (! defined($_SESSION['__ROOT__']) && empty($_SESSION['__ROOT__'])) {
+    if (session_id())
+        session_destroy();
+    header('Location: ' . WEB_ROOT . 'error.html');
+    exit;
+}
+
+require_once 'persistens.php';
+//var_dump($_POST);
+//exit();
+include TOP_FOLDER.'/include/header.inc.php';
+include TOP_FOLDER.'/include/menu.inc.php';
+
+function colors($selected = null) {
+    $list = array('aqua', 'black', 'blue', 'fuchsia', 'gray', 'green',
+        'lime', 'maroon', 'navy', 'olive', 'purple', 'red', 'silver',
+        'teal', 'white', 'yellow');
+
+    $colors = '';
+    foreach ($list as $color) {
+        $colors .= "<option value=\"$color\" style=\"background: $color\"";
+        if ($selected && $selected == $color)
+            $colors .= ' selected="true"';
+        $colors .= ">$color</option>";
+    }
+    return $colors;
+}
+
+$user = $_SESSION['user_settings'];
+if (count($_POST) > 0 && isset($_POST['action'])) {
+    $db = Persistens::getInstance(DBDRIVER);
+    $calconf = $_POST['calendar'];
+    if ($_POST['action'] == 'new') {
+        $row = $calconf;
+        $text = "url={$row['url']}\nuid={$row['uid']}\npwd={$row['pwd']}";
+        $text = encode($text);
+        $calendar = new CalendarInfo();
+        $calendar->name = $row['name'];
+        $calendar->color = $row['color'];
+        $calendar->config = $text;
+        $id = $db->addCalendar($user->getUid(), $calendar);
+        if (is_numeric($id)) {
+            $user->addCalendar($id, $calendar);
+            $pageView = "<p class=\"usermanage\">Calendar was added with id $id</p>";
+        }
+        else {
+            $pageView = "<p class=\"usermanage\">$id</p>";
+        }
+    }
+    else {
+        foreach ($calconf as $id => $row) {
+            if (! isset($row[check]))
+                continue;
+            if ($_POST['action'] == 'update') {
+                $text = "url={$row['url']}\nuid={$row['uid']}\npwd={$row['pwd']}";
+                $text = encode($text);
+                $calendar = new CalendarInfo();
+                $calendar->name = $row['name'];
+                $calendar->color = $row['color'];
+                $calendar->config = $text;
+                $res = $db->updateCalendar($user->getUid(), $id, $calendar);
+                if ($res === TRUE) {
+                    $user->addCalendar($id, $calendar);
+                    $pageView = "<p class=\"usermanage\">Calendar with id $id was updated</p>";
+                }
+                else {
+                    $pageView = "<p class=\"usermanage\">$id</p>";
+                }
+            }
+            else {
+                $res = $db->deleteCalendar($user->getUid(), $id);
+                if ($res === TRUE) {
+                    $user->removeCalendar($id);
+                    $pageView = "<p class=\"usermanage\">Calendar with id $id was removed</p>";
+                }
+                else {
+                    $pageView = "<p class=\"usermanage\">$id</p>";
+                }
+            }
+        }
+    }
+    $pageView .= '</table>';
+}
+else if (count($_POST) > 0 && isset($_POST['new'])) {
+    $colors = colors();
+    $pageView = '<form id="form" action="'.$_SERVER[PHP_SELF].'" method="post">
+                 <table class="config">';
+    $pageView .= "<tr><td>Name</td>
+                <td><input name=\"calendar[name]\" type=\"text\" size=\"20\"/></td>
+                </tr>
+                <tr>
+                <td>Color</td>
+                <td>
+                    <select name=\"calendar[color]\">
+                       $colors
+                    </select>
+                </td>
+                </tr>
+                <tr>
+                <td>URL</td>
+                <td><input name=\"calendar[url]\" type=\"text\" size=\"50\"/></td>
+                </tr>
+                <tr>
+                <td>Username</td>
+                <td><input name=\"calendar[uid]\" type=\"text\" size=\"20\"/></td>
+                </tr>
+                <tr>
+                <td>Password</td>
+                <td><input name=\"calendar[pwd]\" type=\"password\" size=\"20\"/></td>
+                </tr>";
+    $pageView .= '<tr>
+                    <td colspan="6" style="text-align: center">
+                        <input type="button" value="Submit"
+                        onclick="document.getElementById(\'form\').submit()">
+                        <input type="hidden" name="action" value="new"/>
+                    </td>
+                </tr>';
+    $pageView .= '</table></form>';
+}
+else {
+    //var_dump($_SESSION['authenticate']);
+    //var_dump($user);
+    //exit();
+    $calendars = $user->getCalendars();
+    //var_dump($calendars);
+    //exit();
+    $pageView = '<form id="form" action="'.$_SERVER[PHP_SELF].'" method="post">
+                 <table id="cal">';
+
+    if (count($calendars) > 0) {
+        foreach ($calendars as $id => $cal) {
+            $text = decode($cal->config);
+            $color = colors($cal->color);
+            $info = implode_cal($text);
+            $pageView .= "<tr><td>Name</td>
+                <td><input name=\"calendar[$id][name]\" type=\"text\" size=\"8\" value=\"{$cal->name}\"/></td>
+                <td>Color</td>
+                <td>
+                    <select name=\"calendar[$id][color]\">
+                        $color
+                    </select>
+                </td>
+                <td>URL</td>
+                <td><input name=\"calendar[$id][url]\" type=\"text\" size=\"40\" value=\"{$info['url']}\"/>
+                </td>
+                <td>Username</td>
+                <td><input name=\"calendar[$id][uid]\" type=\"text\" size=\"8\" value=\"{$info['uid']}\"/></td>
+                <td>Password</td>
+                <td><input name=\"calendar[$id][pwd]\" type=\"password\" size=\"8\" value=\"{$info['pwd']}\"/></td>
+                <td><input type=\"checkbox\" name=\"calendar[$id][check]\" value=\"$id\"/></td>
+                </tr>";
+        }
+        $pageView .= '<tr>
+                        <td colspan="11" style="text-align: center">
+                            <input type="radio" name="action" value="delete">Delete</input>
+                            <input type="radio" name="action" value="update">Update</input>
+                        </td>
+                    </tr>
+                    <tr>
+                        <td colspan="11" style="text-align: center">
+                            <input type="button" value="Submit"
+                            onclick="document.getElementById(\'form\').submit()">
+                            <input type="submit" name="new" value="New Calendar"/>
+                        </td>
+                    </tr>';
+    }
+    else {
+        $pageView .= '<tr>
+                            <td>
+                                <input type="submit" name="new" value="New Calendar"/>
+                            </td>
+                      </tr>';
+    }
+    $pageView .= '</table></form>';
+}
+print "<div id=\"ui\">$pageView</div>";
+
+include TOP_FOLDER.'/include/footer.inc.php';
+?>
diff --git a/utils/configure.php b/utils/configure.php
new file mode 100644 (file)
index 0000000..efaa2f2
--- /dev/null
@@ -0,0 +1,257 @@
+<?php
+/* $Id$ */
+require_once 'config.inc.php';
+require_once 'user_validate.php';
+require_once 'timezone.php';
+require_once 'helper.php';
+
+valid_user();
+
+if (! defined($_SESSION['__ROOT__']) && empty($_SESSION['__ROOT__'])) {
+    if (session_id())
+        session_destroy();
+    header('Location: ' . WEB_ROOT . 'error.html');
+    exit;
+}
+
+require_once 'persistens.php';
+
+$user = $_SESSION['user_settings'];
+
+if (count($_POST) > 0 && isset($_POST['action'])) {
+    $db = Persistens::getInstance(DBDRIVER);
+
+    if ($_POST['action'] == "password") {
+        if ($_POST['pwd1'] == $_POST['pwd2']) {
+            $res = $db->changePassword($user->getUid(), sha1($_POST['pwd1']));
+            if ($res === TRUE) {
+                header("Location: " . WEB_ROOT . "logout.php");
+                exit();
+            }
+            //else {
+            //    $res = "uid: ".$user->getUid()." ".$_POST['pwd1']." ".$_POST['pwd2']." -> $res";
+            //}
+        }
+        else {
+            $res = "Passwords did not compare";
+        }
+        $pageView =  "<p style=\"text-align: center\">$res</p>";
+    }
+    else if ($_POST['action'] == "settings") {
+        file_put_contents('/tmp/davical.log',
+            __FILE__ . ": " . var_export($_POST, TRUE), FILE_APPEND);
+        $conf = array();
+        if ($_POST['endDay'] > $_POST['startDay']) {
+            $conf['daystart'] = $_POST['startDay'];
+            $conf['dayend'] = $_POST['endDay'];
+        }
+        else {
+            $conf['daystart'] = $user->getStartHour();
+            $conf['dayend'] = $user->getEndHour();
+        }
+        $conf['timeout'] =
+            ($_POST['timeout'] > 0) ? $_POST['timeout'] * 60 :
+                $user->getTimeout() / 60;
+        $conf['userview'] = $_POST['viewStyle'];
+        $conf['weekstart'] = ($_POST['startWeek'] == 'SU') ? 1 : 0;
+        $conf['timezone'] = $_POST['timezone'];
+        $conf['userrole'] = $user->getRole();
+        $res = $db->setUserSettings($user->getUid(), $conf);
+        if ($res === TRUE) {
+            $user->setSettings(array($conf));
+            header("Location: " . $_SERVER['PHP_SELF']);
+            exit();
+        }
+        $pageView =  "<p style=\"text-align: center\">$res</p>";
+    }
+}
+else {
+    $timeout = $user->getTimeout() / 60;
+    $viewStyle = $user->getViewStyle();
+    switch ($viewStyle) {
+        case 'day':
+            $viewStyle = <<<_SELECT
+                <select name="viewStyle">
+                    <option value="day" selected="true">Day</option>
+                    <option value="week">Week</option>
+                    <option value="month">Month</option>
+                </select>
+_SELECT;
+            break;
+        case 'week':
+            $viewStyle = <<<_SELECT
+                <select name="viewStyle">
+                    <option value="day">Day</option>
+                    <option value="week" selected="true">Week</option>
+                    <option value="month">Month</option>
+                </select>
+_SELECT;
+            break;
+        case 'month':
+            $viewStyle = <<<_SELECT
+                <select name="viewStyle">
+                    <option value="day">Day</option>
+                    <option value="week">Week</option>
+                    <option value="month" selected="true">Month</option>
+                </select>
+_SELECT;
+            break;
+    }
+    $weekStart = $user->getStartWeek();
+    if ($weekStart)
+        $weekStart = <<<_SELECT
+            <select name="startWeek">
+                <option value="SU" selected="true">Sunday</option>
+                <option value="MO">Monday</option>
+            </select>
+_SELECT;
+    else
+        $weekStart = <<<_SELECT
+            <select name="startWeek">
+                <option value="SU">Sunday</option>
+                <option value="MO" selected="true">Monday</option>
+            </select>
+_SELECT;
+
+    $dayStart = (int) $user->getStartHour();
+    $start = '<select name="startDay">';
+    for ($i = 0; $i < 25; $i++) {
+        $hour = ($i < 10) ? "0$i:00" : "$i:00";
+        $start .= "<option value=\"$i\"";
+        if ($i == $dayStart)
+            $start .= " selected=\"true\"";
+        $start .= ">$hour</option>";
+    }
+    $start .= '</select>';
+    $dayEnd = (int) $user->getEndHour();
+    $end = '<select name="endDay">';
+    for ($i = 0; $i < 25; $i++) {
+        $hour = ($i < 10) ? "0$i:00" : "$i:00";
+        $end .= "<option value=\"$i\"";
+        if ($i == $dayEnd)
+            $end .= " selected=\"true\"";
+        $end .= ">$hour</option>";
+    }
+    $end .= '</select>';
+
+    $current = $user->getTimezone();
+    foreach ($timezones as $timezone) {
+        $tz .= "<option value=\"$timezone\"";
+        if ($current == $timezone)
+            $tz .= ' selected="true"';
+        $tz .= ">$timezone</option>";
+    }
+
+    $url = getServerUrl();
+    if ($url[strlen($url)-1] == '/')
+        $url = substr($url, 0, -1);
+
+    $pageView = <<< __EOF
+    <p>
+        <form action="{$_SERVER['PHP_SELF']}" method="post">
+            <table class="config">
+                <tr>
+                    <th>Setting</th><th>Current</th>
+                </tr>
+                <tr>
+                    <td class="config">Session timeout in minuts</td>
+                    <td class="config">
+                        <input size="6" name="timeout" type="text" value="$timeout"/>
+                    </td>
+                </tr>
+                <tr>
+                    <td class="config">Default view</td><td>$viewStyle</td>
+                </tr>
+                <tr>
+                    <td class="config">Timezone</td>
+                    <td class="config">
+                        <select name="timezone">
+                            $tz
+                        </select>
+                    </td>
+                </tr>
+                <tr>
+                    <td class="config">Week start on</td><td>$weekStart</td>
+                </tr>
+                <tr>
+                    <td class="config">Day start</td>
+                    <td class="config">$start</td>
+                </tr>
+                <tr>
+                    <td class="config">Day end</td>
+                    <td class="config">$end</td>
+                </tr>
+                <tr>
+                    <input type="hidden" name="action" value="settings"/>
+                    <td class="config" colspan="2" style="text-align: center">
+                        <input type="submit" name="setting" value="Submit changes"/>
+                    </td>
+                </tr>
+            </table>
+        </form>
+    </p>
+__EOF;
+    if (strtoupper($user->getUid()) != 'ADMIN') {
+    $pageView .= <<< __EOF
+    <p style="text-align: center">
+        <button type="button" id="calconfig"
+                onclick="document.location.href='$url/utils/calendar.php'">
+            Configure Calendars
+        </button>
+    </p>
+__EOF;
+    }
+    if ($user->getRole() === 0) {
+    $pageView .= <<<__EOF
+    <p style="text-align: center">
+        <button type="button" id="useradm"
+                onclick="document.location.href='$url/utils/users.php'">
+            Manage users
+        </button>
+        &nbsp;&nbsp;
+        <button type="button" id="ldapadm"
+                onclick="document.location.href='$url/utils/ldap.php'">
+            Configure LDAP
+        </button>
+    </p>
+__EOF;
+    }
+    $pageView .= <<<__EOF
+    <p>
+        <form action="{$_SERVER['PHP_SELF']}" method="post">
+            <table class="config">
+                <tr>
+                    <th colspan="2" style="text-align: center">Change password</th>
+                </tr>
+                <tr>
+                    <td colspan="2"><span style="background: red; font-weight: bold;">
+                        &nbsp;After changing the password a relogin is required&nbsp;
+                    </span></td>
+                </tr>
+                <tr>
+                    <td class="config">New Password</td>
+                    <td class="config"><input name="pwd1" type="password"/></td>
+                </tr>
+                <tr>
+                    <td class="config">Repeat Password</td>
+                    <td class="config"><input name="pwd2" type="password"/></td>
+                </tr>
+                <tr>
+                    <input type="hidden" name="action" value="password"/>
+                    <td class="config" colspan="2" style="text-align: center">
+                        <input type="submit" name="password" value="Submit"/>
+                    </td>
+                </tr>
+            </table>
+        </form>
+    </p>
+__EOF;
+}
+
+include TOP_FOLDER.'/include/header.inc.php';
+include TOP_FOLDER.'/include/menu.inc.php';
+
+print "<div id=\"ui\">$pageView</div>";
+
+include TOP_FOLDER.'/include/footer.inc.php';
+?>
diff --git a/utils/dateformat.class.php b/utils/dateformat.class.php
new file mode 100644 (file)
index 0000000..75ebb45
--- /dev/null
@@ -0,0 +1,58 @@
+<?php
+
+include_once 'config.inc.php';
+require_once 'helper.php';
+
+class DateFormat {
+/*     private $tz;
+       
+       function __construct($tz = '') {
+               if (empty($tz)) {
+                       $tz = ($_SESSION['user_settings']->getTimeZone() !== NULL) ?
+            $_SESSION['user_settings']->getTimeZone() : TIMEZONE;
+               }
+               $timezone = date_default_timezone_get();
+               $tz_server = date("Z")/3600;
+               date_default_timezone_set($tz);
+               $tz_client = date("Z")/3600;
+               date_default_timezone_set($timezone);
+               $this->tz = $tz_client - $tz_server;
+               print "$timezone:$tz_server:$tz_client<br/>";
+       }*/
+       
+       public function client2UTC($datetime) {
+               if (strcasecmp(substr($datetime, -1), "Z") === 0) {
+                       // Time is in UTC already
+                       return $datetime;
+               }
+               
+       $timestamp = strtotime($datetime);
+       
+       return gmdate("Ymd\THis\Z", $timestamp);
+    }
+    
+    public function UTC2Client($datetime) {
+               //print "UTC2Client: $datetime\n";
+               if (strcasecmp(substr($datetime, -1), "Z") !== 0) {
+                       // Time is not in UTC
+                       return $datetime;
+               }
+               
+       $timestamp = strtotime($datetime);
+       
+       return date("Ymd\THis", $timestamp);
+       }
+
+    public function UTC2Local($datetime) {
+               if (strcasecmp(substr($datetime, -1), "Z") !== 0) {
+                       // Time is not in UTC
+                       return $datetime;
+               }
+
+               $offset = $_SESSION['timezone_offset'] - date("Z");
+       $timestamp = strtotime($datetime) + $offset;
+       
+       return date("Ymd\THis", $timestamp);
+       }
+
+}
\ No newline at end of file
diff --git a/utils/db_create.postgresql.php b/utils/db_create.postgresql.php
new file mode 100644 (file)
index 0000000..b4390d8
--- /dev/null
@@ -0,0 +1,59 @@
+<?php
+/* $Id$ */
+
+function make_sql_stm() {
+    $db = array();
+
+    array_push($db, "create sequence roles_pk_seq");
+    array_push($db, "create table roles (
+    id integer primary key default nextval('roles_pk_seq'),
+    name varchar(256) not null
+    )");
+    array_push($db, "insert into roles values(0,'administrator')");
+    array_push($db, "insert into roles values(1,'power user')");
+    array_push($db, "insert into roles values(2,'user')");
+
+    array_push($db, "create sequence users_pk_seq");
+    array_push($db, "create table users (
+    id integer primary key default nextval('users_pk_seq'),
+    uid varchar(256) not null,
+    pwd varchar(256) not null,
+    userrole integer not null,
+    timeout integer default 3600,
+    userview varchar(256) default 'week',
+    weekstart integer default 0,
+    daystart numeric(4,2) default 8.00,
+    dayend numeric(4,2) default 17.00,
+    timezone varchar(256) default 'Etc/UTC',
+    seckey varchar(256) default '',
+    pubkey varchar(256) default '',
+    constraint uid_index unique (uid),
+    constraint userrole_fk foreign key (userrole) references roles (id)
+    on update cascade
+    on delete restrict
+    )");
+
+    array_push($db, "create sequence calendar_pk_seq");
+    array_push($db, "create table calendar (
+    id integer default nextval('calendar_pk_seq'),
+    uid integer not null,
+    name varchar(256) not null,
+    color varchar(256) default 'navy',
+    config varchar(4000) default '',
+    constraint calendar_pk primary key (id, uid),
+    constraint uid_fk foreign key (uid) references users (id)
+    on update cascade
+    on delete cascade
+    )");
+    array_push($db, "create table about (
+    id integer default 1,
+    version varchar(256) default '')");
+    array_push($db, "create table ldap (
+    enable integer default 0,
+    dns varchar(256) default '',
+    tls integer default 0,
+    base_dn varchar(256) default '',
+    user_attr varchar(256) default 'uid')");
+
+    return $db;
+}
diff --git a/utils/db_create.sql b/utils/db_create.sql
new file mode 100644 (file)
index 0000000..9aade84
--- /dev/null
@@ -0,0 +1,94 @@
+BEGIN TRANSACTION;
+DELETE FROM sqlite_sequence;
+INSERT INTO "sqlite_sequence" VALUES('roles',2);
+INSERT INTO "sqlite_sequence" VALUES('user',5);
+CREATE TABLE roles (
+id integer primary key autoincrement,
+name text not null
+);
+INSERT INTO "roles" VALUES(0,'administrator');
+INSERT INTO "roles" VALUES(1,'power user');
+INSERT INTO "roles" VALUES(2,'user');
+CREATE TABLE user (
+id integer primary key autoincrement,
+uid text not null,
+pwd text not null,
+userrole integer not null,
+timeout integer default 3600,
+userview text default 'week',
+weekstart integer default 0,
+daystart real default 8.00,
+dayend real default 17.00,
+seckey text default '',
+pubkey text default '',
+constraint uid_index unique (uid)
+constraint userrole_fk foreign key (userrole) references roles (id)
+on delete restrict
+on update cascade
+on insert cascade
+);
+INSERT INTO "user" VALUES(1,'admin','d033e22ae348aeb5660fc2140aec35850c4da997',0,1200,'week',0,8.0,17.0,'','');
+INSERT INTO "user" VALUES(2,'test','a94a8fe5ccb19ba61c4c0873d391e987982fbbd3',1,3600,'week',0,8.0,17.0,'da830961dc3af47fff6d1af3be3d66d6','\93¢yQ è\85§¸\90d\8e\9c±gaÙ8(\r<\a\vk\9dÂy\86*\19:ð');
+CREATE TABLE calendar (
+id integer default -1,
+uid integer not null,
+name text not null,
+color text default 'navy',
+config text default '',
+constraint calendar_pk primary key (id, uid),
+constraint uid_fk foreign key (uid) references user (id)
+on delete cascade
+on update cascade
+on insert cascade
+);
+CREATE TRIGGER compute_id after insert on calendar
+begin
+    update calendar set id = (select max(id) + 1 from calendar) where id = -1;
+end;
+CREATE TRIGGER genfkey2_insert_referencing BEFORE INSERT ON "calendar" WHEN 
+    new."uid" IS NOT NULL AND NOT EXISTS (SELECT 1 FROM "user" WHERE new."uid" == "id")
+BEGIN
+  SELECT RAISE(ABORT, 'constraint failed');
+END;
+CREATE TRIGGER genfkey2_update_referencing BEFORE
+    UPDATE OF uid ON "calendar" WHEN 
+    new."uid" IS NOT NULL AND 
+    NOT EXISTS (SELECT 1 FROM "user" WHERE new."uid" == "id")
+BEGIN
+  SELECT RAISE(ABORT, 'constraint failed');
+END;
+CREATE TRIGGER genfkey2_delete_referenced BEFORE DELETE ON "user" WHEN
+    EXISTS (SELECT 1 FROM "calendar" WHERE old."id" == "uid")
+BEGIN
+  DELETE FROM "calendar" WHERE "uid" = old."id";
+END;
+CREATE TRIGGER genfkey2_update_referenced AFTER
+    UPDATE OF id ON "user" WHEN 
+    EXISTS (SELECT 1 FROM "calendar" WHERE old."id" == "uid")
+BEGIN
+  UPDATE "calendar" SET "uid" = new."id" WHERE "uid" = old."id";
+END;
+CREATE TRIGGER genfkey1_insert_referencing BEFORE INSERT ON "user" WHEN 
+    new."userrole" IS NOT NULL AND NOT EXISTS (SELECT 1 FROM "roles" WHERE new."userrole" == "id")
+BEGIN
+  SELECT RAISE(ABORT, 'constraint failed');
+END;
+CREATE TRIGGER genfkey1_update_referencing BEFORE
+    UPDATE OF userrole ON "user" WHEN 
+    new."userrole" IS NOT NULL AND 
+    NOT EXISTS (SELECT 1 FROM "roles" WHERE new."userrole" == "id")
+BEGIN
+  SELECT RAISE(ABORT, 'constraint failed');
+END;
+CREATE TRIGGER genfkey1_delete_referenced BEFORE DELETE ON "roles" WHEN
+    EXISTS (SELECT 1 FROM "user" WHERE old."id" == "userrole")
+BEGIN
+  SELECT RAISE(ABORT, 'constraint failed');
+END;
+CREATE TRIGGER genfkey1_update_referenced AFTER
+    UPDATE OF id ON "roles" WHEN 
+    EXISTS (SELECT 1 FROM "user" WHERE old."id" == "userrole")
+BEGIN
+  UPDATE "user" SET "userrole" = new."id" WHERE "userrole" = old."id";
+END;
+COMMIT;
diff --git a/utils/db_create.sqlite.php b/utils/db_create.sqlite.php
new file mode 100644 (file)
index 0000000..dc07591
--- /dev/null
@@ -0,0 +1,105 @@
+<?php
+/* $Id$ */
+
+function make_sql_stm() {
+    $db = array();
+
+    array_push($db, "drop table if exists roles");
+    array_push($db, "CREATE TABLE roles (
+    id integer primary key autoincrement,
+    name text not null
+    )");
+    array_push($db, "INSERT INTO roles VALUES(0,'administrator')");
+    array_push($db, "INSERT INTO roles VALUES(1,'power user')");
+    array_push($db, "INSERT INTO roles VALUES(2,'user')");
+
+    array_push($db, "drop table if exists user");
+    array_push($db, "CREATE TABLE user (
+    id integer primary key autoincrement,
+    uid text not null,
+    pwd text not null,
+    userrole integer not null,
+    timeout integer default 3600,
+    userview text default 'week',
+    weekstart integer default 0,
+    daystart real default 8.00,
+    dayend real default 17.00,
+    timezone text default 'Etc/UTC',
+    seckey text default '',
+    pubkey text default '',
+    constraint uid_index unique (uid)
+    )");
+
+    array_push($db, "drop table if exists calendar");
+    array_push($db, "CREATE TABLE calendar (
+    id integer default -1,
+    uid integer not null,
+    name text not null,
+    color text default 'navy',
+    config text default '',
+    constraint calendar_pk primary key (id, uid)
+    )");
+    array_push($db, "CREATE TRIGGER compute_id after insert on calendar
+    begin
+        update calendar set id = (select max(id) + 1 from calendar) where id = -1;
+    end;");
+    array_push($db, "CREATE TRIGGER genfkey2_insert_referencing BEFORE INSERT ON calendar WHEN
+        new.uid IS NOT NULL AND NOT EXISTS (SELECT 1 FROM user WHERE new.uid == id)
+    BEGIN
+      SELECT RAISE(ABORT, 'constraint failed');
+    END");
+    array_push($db, "CREATE TRIGGER genfkey2_update_referencing BEFORE
+        UPDATE OF uid ON calendar WHEN
+        new.uid IS NOT NULL AND
+        NOT EXISTS (SELECT 1 FROM user WHERE new.uid == id)
+    BEGIN
+      SELECT RAISE(ABORT, 'constraint failed');
+    END");
+    array_push($db, "CREATE TRIGGER genfkey2_delete_referenced BEFORE DELETE ON
+    user WHEN EXISTS (SELECT 1 FROM calendar WHERE old.id == uid)
+    BEGIN
+      DELETE FROM calendar WHERE uid = old.id;
+    END");
+    array_push($db, "CREATE TRIGGER genfkey2_update_referenced AFTER
+        UPDATE OF id ON user WHEN
+        EXISTS (SELECT 1 FROM calendar WHERE old.id == uid)
+    BEGIN
+      UPDATE calendar SET uid = new.id WHERE uid = old.id;
+    END");
+    array_push($db, "CREATE TRIGGER genfkey1_insert_referencing BEFORE INSERT ON
+    user WHEN new.userrole IS NOT NULL AND NOT EXISTS (SELECT 1 FROM roles WHERE
+    new.userrole == id)
+    BEGIN
+      SELECT RAISE(ABORT, 'constraint failed');
+    END");
+    array_push($db, "CREATE TRIGGER genfkey1_update_referencing BEFORE
+        UPDATE OF userrole ON user WHEN new.userrole IS NOT NULL AND
+        NOT EXISTS (SELECT 1 FROM roles WHERE new.userrole == id)
+    BEGIN
+      SELECT RAISE(ABORT, 'constraint failed');
+    END");
+    array_push($db, "CREATE TRIGGER genfkey1_delete_referenced BEFORE DELETE ON
+    roles WHEN EXISTS (SELECT 1 FROM user WHERE old.id == userrole)
+    BEGIN
+      SELECT RAISE(ABORT, 'constraint failed');
+    END");
+    array_push($db, "CREATE TRIGGER genfkey1_update_referenced AFTER
+        UPDATE OF id ON roles WHEN
+        EXISTS (SELECT 1 FROM user WHERE old.id == userrole)
+    BEGIN
+      UPDATE user SET userrole = new.id WHERE userrole = old.id;
+    END");
+    array_push($db, "drop table if exists about");
+    array_push($db, "create table about (
+    id integer default 1,
+    version text default '')");
+    array_push($db, "drop table if exists ldap");
+    array_push($db, "create table ldap (
+    enable integer default 0,
+    dns text default '',
+    tls integer default 0,
+    base_dn text default '',
+    user_attr text default 'uid')");
+
+    return $db;
+}
diff --git a/utils/helper.php b/utils/helper.php
new file mode 100644 (file)
index 0000000..f1c2dc6
--- /dev/null
@@ -0,0 +1,615 @@
+<?php
+/* $Id$ */
+include_once 'config.inc.php';
+require_once 'persistens.php';
+
+define('MICRO', 1);
+define('MINOR', 8);
+define('MAJOR', 0);
+
+$VERSION = MAJOR.".".MINOR.".".MICRO;
+$CURRENT_VERSION = $VERSION;
+$PREFIX = TOP_FOLDER."/install/".DBDRIVER;
+$PATCH_SET = array(
+    "173" => "db_upgrade_0_7_3.sql",
+    "174" => "db_upgrade_0_7_4.sql",
+    "175" => "db_upgrade_0_7_5.sql",
+    "180" => "db_upgrade_0_8_0.sql",
+    "181" => "db_upgrade_0_8_1.sql",
+);
+
+$pwd = WEB_ROOT;
+if ($pwd[strlen($pwd)-1] == '/')
+    $pwd = substr($pwd, 0, -1);
+
+function string2int($str) {
+    if (is_numeric($str))
+        return $str;
+    if (is_string($str)) {
+        $parts = explode(".", $str);
+        $str = 0;
+        for ($i = 0; $i < count($parts); $i++) {
+            $num = ($i == 0) ? $parts[$i] + 1 : $parts[$i];
+            $str += (pow(10, count($parts) - $i - 1) * $num);
+        }
+    }
+    return $str;
+}
+
+function getServerUrl($root = WEB_ROOT, $cwd = "") {
+    $root = (! empty($root) && $root[0] == '/') ? substr($root, 1) : $root;
+    $root = (! empty($root) && $root[strlen($root) - 1] == '/') ? substr($root, 0, -1) : $root;
+    $cwd = (! empty($cwd) && $cwd[0] == '/') ? substr($cwd, 1) : $cwd;
+    $cwd = (! empty($cwd) && $cwd[strlen($cwd) - 1] == '/') ? substr($cwd, 0, -1) : $cwd;
+    
+    //echo var_export($_SERVER, true);
+    $protocol = ((isset($_SERVER['HTTPS']) && ! empty($_SERVER['HTTPS'])) || (isset($_SERVER['HTTPS']) && strcasecmp("on", $_SERVER['HTTPS']) === 0)
+        || (isset($_SERVER['HTTP_SCHEME']) && strcasecmp("https", $_SERVER['HTTP_SCHEME']) === 0)) ? "https" : "http";
+    //echo "$protocol<br/>";
+    $server = ($_SERVER['SERVER_PORT'] == 80 || $_SERVER['SERVER_PORT'] == 443)?
+        $_SERVER['SERVER_NAME'] : $_SERVER['SERVER_NAME'].':'.$_SERVER['SERVER_PORT'];
+    $url = (! empty($cwd)) ? "$protocol://$server/$cwd" : "$protocol://$server";
+        
+    return "$url/$root";
+}
+
+function getServerUri() {
+    return TOP_FOLDER;
+}
+
+function createKey($key) {
+    $td = mcrypt_module_open('rijndael-256', '', 'ofb', '');
+    $public = mcrypt_create_iv(mcrypt_enc_get_iv_size($td), MCRYPT_DEV_RANDOM);
+    $ks = mcrypt_enc_get_key_size($td);
+    mcrypt_module_close($td);
+
+    /* Create key */
+    $secret = substr($key, 0, $ks);
+    return array($secret, $public);
+}
+
+function encode($text) {
+    /* Intialize encryption */
+    $td = mcrypt_module_open('rijndael-256', '', 'ofb', '');
+    $iv = $_SESSION['authenticate']->getPublicKey();
+    $key = $_SESSION['authenticate']->getSecretKey();
+
+    mcrypt_generic_init($td, $key, $iv);
+
+    /* Encrypt data */
+    $ciffer = mcrypt_generic($td, $text);
+    $encrypted = quoted_printable_encode($ciffer);
+
+    /* Terminate encryption handler */
+    mcrypt_generic_deinit($td);
+    mcrypt_module_close($td);
+
+    return $encrypted;
+}
+
+function decode($text) {
+    $td = mcrypt_module_open('rijndael-256', '', 'ofb', '');
+    $iv = $_SESSION['authenticate']->getPublicKey();
+    $key = $_SESSION['authenticate']->getSecretKey();
+
+    /* Initialize encryption module for decryption */
+    mcrypt_generic_init($td, $key, $iv);
+
+    /* Decrypt encrypted string */
+    $ciffer = quoted_printable_decode($text);
+    $decrypted = mdecrypt_generic($td, $ciffer);
+
+    /* Terminate decryption handle and close module */
+    mcrypt_generic_deinit($td);
+    mcrypt_module_close($td);
+
+    return $decrypted;
+}
+
+function implode_cal($text) {
+    $infos = explode("\n", $text);
+    foreach ($infos as $info) {
+        $row = explode('=', $info);
+        $result[$row[0]] = $row[1];
+    }
+    return $result;
+}
+
+// taken from roundcubemail
+if (!function_exists("quoted_printable_encode")) {
+       function quoted_printable_encode($text) {
+               $length=strlen($text); 
+               for ($whitespace= "", $line=0,$encode= "",
+                               $index=0;$index<$length;$index++) { 
+                       $character=substr($text,$index,1); 
+                       $order=Ord($character); 
+                       $encode=0; 
+                       switch($order) { 
+                               case 9: 
+                               case 32: 
+                                       if ($header_charset== "") {
+                                               $previous_whitespace=$whitespace;
+                                               $whitespace=$character;
+                                               $character= "";
+                                       } 
+                                       else { 
+                                               if ($order==32)
+                                                       $character= "_";
+                                               else
+                                                       $encode=1;
+                                       }
+                                       break;
+                               case 10: 
+                               case 13: 
+                                       if ($whitespace!= "") {
+                                               if ($line+3>75) {
+                                                       $encoded.= "=\n";
+                                                       $line=0;
+                                               }
+                                               $encoded.=sprintf( "=%02X",Ord($whitespace));
+                                               $line+=3;
+                                               $whitespace= "";
+                                       }
+                                       $encoded.=$character;
+                                       $line=0;
+                                       continue 2;
+                               default:
+                                       if ($order > 127 || $order < 32 || $character == "=" 
+                                               || ($character == "?" || $character == "_" ||
+                                                       $character == "(" || $character== ")")) 
+                                               $encode=1; 
+                                               break; 
+                       } 
+                       if ($whitespace!= "") { 
+                               if ($line+1>75) {
+                                       $encoded.= "=\n";
+                                       $line=0;
+                               } 
+                               $encoded.=$whitespace; 
+                               $line++; 
+                               $whitespace= ""; 
+                       } 
+                       if ($character!= "") { 
+                               if($encode) {
+                                       $character=sprintf( "=%02X",$order);
+                                       $encoded_length=3;
+                               } 
+                               else
+                                       $encoded_length=1;
+                               if ($line+$encoded_length > 75) {
+                                       $encoded.= "=\n";
+                                       $line=0;
+                               }
+                               $encoded.=$character;
+                               $line+=$encoded_length;
+                       } 
+               } 
+               if ($whitespace!= "") {
+                       if ($line+3>75)
+                               $encoded.= "=\n";
+                       $encoded.=sprintf( "=%02X",Ord($whitespace));
+               }
+               return $encoded;
+       }
+}
+
+function newUpdates() {
+    global $VERSION, $CURRENT_VERSION;
+    
+    $con = Persistens::getInstance(DBDRIVER);
+    try {
+        $res = $con->getVersion();
+    }
+    catch (Exception $e) {
+        ReportError($e);
+    }
+    $version = $res['version'];
+    $version = string2int($version);
+    if (! is_numeric($version)) {
+        ReportError("Invalid version format");
+        exit;
+    }
+    $CURRENT_VERSION = $version;
+    //file_put_contents('/tmp/updates', string2int($VERSION)." > $version", FILE_APPEND);
+    return string2int($VERSION) > $version;
+}
+
+function upgrade() {
+    global $VERSION, $PATCH_SET, $CURRENT_VERSION, $PREFIX;
+       $table = 'backup';
+       
+    $version = string2int($CURRENT_VERSION);
+    $con = Persistens::getInstance(DBDRIVER);
+    try {
+        $res = $con->getVersion();
+    }
+    catch (Exception $e) {
+        ReportError($e);
+        exit;
+    }
+    if (($num = $con->nextTableNumber('backup')) > 0)
+       $table .= $num;
+
+    if ($version < 173) {
+               if (($res = $con->execute(
+                       "create table $table as select * from calendar")) !== true) {
+                       ReportError($res);
+                       exit;
+               }
+               $rows = $con->getCalendarConfig();
+               if (! is_array($rows)) {
+                       ReportError($rows);
+                       exit;
+               }
+               foreach($rows as $row) {
+                       if (function_exists(quoted_printable_encode))
+                               $ciffer = quoted_printable_encode($row['config']);
+                       else
+                               $ciffer = webcal_quoted_printable_encode($row['config']);
+            if (($res = $con->execute("begin transaction")) !== true) {
+                ReportError($res);
+                exit;
+                       }
+                       if (($res = $con->execute(
+                               "update calendar set config = '$ciffer' where id = " .
+                               $row['id'])) !== true) {
+                               $con->execute("rollback");
+                               ReportError($res."<br/>A backup of your calendar data can be found in table backup");
+                               exit;
+                       }
+               }
+               $con->execute("commit");
+       }
+    if ($version >= 174) {
+        // remove pgsql.php from root. Accidentally placed there in release 0.7.4
+        if (file_exists(TOP_FOLDER."/pgsql.php"))
+            unlink(TOP_FOLDER."/pgsql.php");
+               // patch config.inc.php
+               if (substr(PHP_OS, 0, 3) !== 'WIN') {
+               $oldcwd = getcwd();
+            $cwd = TOP_FOLDER.'/install';
+            chdir($cwd);
+            if ($version < 181) {
+                       exec("patch -p0 < config.inc.php.patch");
+               }
+               if ($version > 180) {
+                       exec("patch -p0 < config.inc.php1.patch");
+               }
+               chdir($oldcwd);
+               }
+    }
+    foreach ($PATCH_SET as $set => $file) {
+        $content = array();
+        //echo "$set -> $file<br/>";
+        if ($version < $set) {
+            //echo $PREFIX."_".$file."<br/>";
+            if (! file_exists($PREFIX."_".$file))
+                continue;
+            $sql = file_get_contents($PREFIX."_".$file);
+            $raw_lines = explode("\n", $sql);
+            foreach ($raw_lines as $line) {
+                if (preg_match("/^--/", $line))
+                    continue;
+                $content[] = $line;
+            }
+            $lines = explode(";", join("\n", $content));
+            if (count($lines) > 0) {       
+                if (($res = $con->execute("begin transaction")) !== true) {
+                    ReportError($res);
+                    exit;
+                               }
+                foreach ($lines as $line) {
+                    $line = trim($line);
+                    if (preg_match("/^\s*$/", $line) || preg_match("/^--/", $line))
+                        continue;
+                    if (($res = $con->execute($line)) !== true) {
+                        ReportError($res);
+                        $con->execute("rollback");
+                        exit;
+                    }
+                }
+                $con->execute("commit");
+            }
+        }
+        else
+            continue;
+    }
+}
+
+function ReportError($msg) {
+    // be sure that the supplied parameter is a string and not empty
+    if (empty ($msg) || !is_string ($msg)) {
+        throw new ErrorException ('Invalid parameter supplied to ReportError', 0, E_ERROR);
+    }
+
+    // retrieve error settings
+    $display    = strtolower(ini_get('display_errors'));
+    $log        = strtolower(ini_get('log_errors'));
+
+    // check if we're displaying errors
+    if ($display === 'on' || $display === '1' || $display === 1 || $display === 'true' || $display === true) {
+        echo $msg;
+    }
+    else {
+        $pwd = WEB_ROOT;
+        if ($pwd[strlen($pwd)-1] == '/')
+            $pwd = substr($pwd, 0, -1);
+        print '<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+            <head>
+                <title>Web Calendar</title>
+                <link rel="stylesheet" type="text/css"
+                    href="'.$pwd.'/css/top_level.css" />
+                <link rel="shortcut icon"
+                    href="'.$pwd.'/pixmaps/favicon.ico" />
+            </head>
+            <body>
+                <div id="error_msg">
+                    <p>
+                        The application has triggered an error which is not
+                        recoverable. The execution has therefore been cancelled.
+                        <br/><br/>
+                        The error is logged and the webmaster will handle
+                        the error in due time.
+                        <br/><br/>
+                        To help fix the application you are welcome to notify
+                        the <a href="mailto:'.$_SERVER['SERVER_ADMIN'].'
+                        ?subject='.TITLE.': error report['.
+                        $_SERVER['SERVER_NAME'].']">webmaster</a>
+                        by email with a detailed description containing the
+                        following information:
+                        <br/><br/>
+                        1) What did you do just before this message occurred<br/>
+                        2) What did you expect to happen<br/>
+                        3) Your browser name and version number.<br/>
+                    </p>
+                </div>
+            </body>
+</html>';
+    }
+
+    // check if we're logging errors
+    if ($log === 'on' || $log === '1' || $log === 1 || $log === 'true' || $log === true) {
+        $result = error_log ($msg);
+
+        // check for error while logging
+        if (!$result) {
+            throw new ErrorException ('Attempt to write message to error log failed in ReportError', 0, E_ERROR);
+        }
+    }
+}
+
+function create_user_data($uid, $pwd, $role) {
+    $data = array();
+    
+    $data['uid'] = $uid;
+    $data['pwd'] = sha1($pwd);
+    $data['userrole'] = $role;
+    $keys = createKey(sha1("{$data['uid']}{$data['pwd']}"));
+    $data['seckey'] = $keys[0];
+    $data['pubkey'] = $keys[1];
+    $data['timezone'] = TIMEZONE;
+    $data['view'] = VIEW_STYLE;
+    $data['timeout'] = TIMEOUT;
+    $data['week_start'] = WEEK_START_SUNDAY;
+    $data['start'] = START_HOUR;
+    $data['end'] = END_HOUR;
+    
+    return $data;
+}
+
+function popup_window($text = '', $redirect = NULL, $form_elem = array(), $title = NULL) {
+    $con = Persistens::getInstance(DBDRIVER);
+    if (! is_array($res = $con->getVersion())) {
+        ErrorReport($res);
+    }
+    
+    $title = ($title) ? $title : TITLE;
+    /* deprecated */
+    if (count($form_elem) < 1) {
+        $form = NULL;
+        $input = NULL;
+    }
+    else {
+        $form = key($form_elem);
+        $input = $form_elem[$form];
+    }
+    /* end deprecated */
+    $referer = ($redirect) ? urldecode($redirect) : WEB_ROOT;
+    //print "$referer<br/>";
+    //exit;
+    $pwd = WEB_ROOT;
+$head = <<<__HEAD
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+<title>$title</title>
+<meta http-equiv="content-type" content="application/xhtml+xml; charset=UTF-8"/>
+<meta http-equiv="content-style-type" content="text/css"/>
+<link rel="stylesheet" type="text/css" href="$pwd/css/top_level.css" />
+<link rel="shortcut icon" href="$pwd/pixmaps/favicon.ico" />
+<style type="text/css">
+<!--
+#display_overlay_holder {
+       width: 100%;
+       height: 100%;
+       position: absolute;
+       top: 0px;
+       left: 0px;
+       display:none;
+}
+#display_overlay_bg {
+       position: absolute;
+       left: 0px;
+       top: 0px;
+       width: 100%;
+       height: 100%;
+       background-color:#000000;
+       opacity: 0.55;
+       filter: alpha(opacity=85);
+}
+#display_horizon {
+       position: absolute;
+       top: 45%;
+       left: 0px;
+       width: 100%;
+       height: 1px;
+       overflow: visible;
+}
+#display_content {
+       position: absolute;
+       width: 400px;
+       height: 250px;
+       left: 50%;
+       margin-left: -200px;
+       top: -125px;
+       background-color: #CCCCCC;
+}
+#text_content {
+    font-size: 0.8em;
+    color: blue;
+}
+-->
+</style>
+<script type="text/javascript" src="$pwd/js/helper.js"></script>
+</head>
+<body onload="show_the_overlay()">
+<table style="width: 100%;">
+<tr>
+<td style="width: 33%"><img src="$pwd/pixmaps/calendar.png" alt="calendar.png" /></td>
+<td style="width: 33%; text-align: center;"><a href="$pwd/logout.php"><img style="border: 0" src="$pwd/pixmaps/exit.png" width="32" height="32" alt="Logout" /><br/>Logout</a></td>
+<td style="text-align: right"><span style="font-size: 1.6em">DAViCal Web Calendar</span><br/> - A Web Interface for <a href="javascript: newwin('http://www.davical.org/');">DAViCal</a></td>
+</tr>
+</table>
+__HEAD;
+$display = <<<_DISPLAY
+<div id="display_overlay_holder">
+       <div id="display_overlay_bg" />
+       <div id="display_horizon">
+               <div id="display_content">
+        <div id="text_content">
+               $text
+        <br/><br/><br/>
+        <p style="text-align: center;">
+        <button type="button" onclick="hide_the_overlay('$referer')">Cancel</button>
+        </p>
+        </div>
+               </div>
+       </div>
+</div>
+_DISPLAY;
+$foot = <<<_FOOTER
+<div id="footer">
+<p>
+    $title {$res[0]} - &copy; 2010, 2011 Michael Rasmussen<br/>Released under GPLv3
+    <a href="http://validator.w3.org/check?uri=referer">
+        <img src="http://www.w3.org/Icons/valid-xhtml10-blue.png"
+        alt="Valid XHTML 1.0 Strict" height="31" width="88" />
+    </a>
+    <a href="http://jigsaw.w3.org/css-validator/check/referer">
+        <img src="http://www.w3.org/Icons/valid-css2-blue.png"
+        alt="Valid CSS 2.0" height="31" width="88" />
+    </a>
+</p>
+</div>
+_FOOTER;
+    ob_start();
+    print $head;
+       include TOP_FOLDER.'/include/menu.inc.php';
+       print "<div id=\"ui\">&nbsp;</div>";
+    print $foot;
+    print $display;
+    print "</body></html>";
+    
+    return ob_get_clean();
+}
+
+function construct_URL($query_string, $attributes = array()) {
+    $test = trim($query_string);
+    if (empty($test))
+        return array();
+    $current = NULL;
+    $query = array();
+    $query_str = urldecode($query_string);
+    //print "$query_str<br/>";
+    $q = explode('&', $query_str);
+    //print_r($q); echo "<br/>";
+    foreach ($q as $elem) {
+        $s = explode('=', $elem);
+        //print_r($s); echo "<br/>";
+        if (! $current && in_array($s[0], $attributes)) {
+            if (count($s) > 2) {
+                $key = array_shift($s);
+                $value = array_shift($s);
+                $value .= '=';
+                //print "kv: $key $value<br/>";
+                while ($s) {
+                    $value .= array_shift($s);
+                    $value .= '&';
+                    $value .= array_shift($s);
+                }
+                $current = $key;
+            }
+            else {
+                $key = array_shift($s);
+                $value = array_shift($s);
+            }
+            $query[$key] = $value;
+            //print_r($query); echo "<br/>";
+        }
+        else if ($current) {
+            $value = $query[$current];
+            while ($s) {
+                $value .= array_shift($s);
+                $value .= '=';
+                $value .= array_shift($s);
+            }
+            $query[$current] = $value;
+            $current = NULL;
+            //print_r($query); echo "<br/>";
+        }
+        else {
+            $key = array_shift($s);
+            $value = array_shift($s);
+            $query[$key] = $value;
+            //print_r($query); echo "<br/>";
+        }
+    }
+    //print_r($query); echo "<br/>";
+    //exit;
+    // urlencode if nessasary
+    foreach ($query as $key => $value) {
+        if (strpos($value, '&') !== FALSE)
+            $value = urlencode($value);
+        $query_array[$key] = $value;
+    }
+    return $query_array;
+}
+
+//Begin main function definition
+function ErrorsAsExceptions($level, $msg, $fileName, $lineNumber) {
+    // do nothing if error reporting is turned off
+    if (error_reporting() === 0) {
+        return;
+    }
+
+    // be sure received error is supposed to be reported
+    if (error_reporting() & $level) {
+        try {
+            // report error to appropriate channels
+            ReportError(/*$reportMsg*/$msg);
+        }
+        catch (ErrorException $e) {
+            // ignore errors while reporting
+        }
+
+        // go ahead and throw the exception
+        throw new ErrorException($msg, 0, $level, $fileName, $lineNumber);
+    }
+}
+
+set_error_handler('ErrorsAsExceptions');
diff --git a/utils/ldap.php b/utils/ldap.php
new file mode 100644 (file)
index 0000000..7b84e68
--- /dev/null
@@ -0,0 +1,135 @@
+<?php
+/* $Id$ */
+require_once 'config.inc.php';
+require_once 'user_validate.php';
+require_once 'helper.php';
+
+valid_user();
+
+if (! defined($_SESSION['__ROOT__']) && empty($_SESSION['__ROOT__'])) {
+    if (session_id())
+        session_destroy();
+    header('Location: ' . WEB_ROOT . 'error.html');
+    exit;
+}
+
+require_once 'persistens.php';
+
+$user = $_SESSION['user_settings'];
+
+if ($user->getRole() !== 0) {
+    if (session_id())
+        session_destroy();
+    header('Location: ' . WEB_ROOT . 'error.html');
+    exit;
+}
+
+$db = Persistens::getInstance(DBDRIVER);
+
+if (count($_POST) > 0) {
+       $config = array(
+               (isset($_POST['enable']) && $_POST['enable'] == 'on') ? 1 : 0,
+               $_POST['dns'],
+               (isset($_POST['tls']) && $_POST['tls'] == 'on') ? 1 : 0,
+               $_POST['base_dn'],
+               $_POST['user_attr']
+       );
+       if (($res = $db->setLdapConfig($config)) === true)              
+               $pageView = "<p style=\"text-align: center\">
+                       LDAP configuration was successfully updated</p>";
+       else
+               $pageView = "<p style=\"text-align: center\">$res</p>";
+}
+else {
+       $config = $db->getLdapConfig();
+       if (! is_array($config) && $config) {
+               if (session_id())
+                       session_destroy();
+               header('Location: ' . WEB_ROOT . 'error.html');
+               exit;
+       }
+
+       $enable = ($config['enable']) ? ' checked="checked"' : '';
+       $tls = ($config['tls']) ? ' checked="checked"' : '';
+       $dns = $config['dns'];
+       $base_dn = $config['base_dn'];
+       $user_attr = $config['user_attr'];
+    $pageView = <<< __EOF
+        <form action="{$_SERVER['PHP_SELF']}" method="post">
+            <table class="config">
+                <tr>
+                    <th>Setting</th><th>Current</th>
+                </tr>
+                <tr>
+                    <td class="config">LDAP enabled</td>
+                    <td class="config">
+                        <input name="enable" type="checkbox"$enable/>
+                    </td>
+                </tr>
+                <tr>
+                    <td class="config">URL</td>
+                    <td class="config">
+                        <input size="30" name="dns" type="text" value="$dns"/>
+                    </td>
+                </tr>
+                <tr>
+                    <td class="config">Use TLS</td>
+                    <td class="config">
+                        <input name="tls" type="checkbox"$tls/>
+                    </td>
+                </tr>
+                <tr>
+                    <td class="config">Base DN</td>
+                    <td class="config">
+                        <input size="30" name="base_dn" type="text" value="$base_dn"/>
+                    </td>
+                </tr>
+                <tr>
+                    <td class="config">User ATTR</td>
+                    <td class="config">
+                        <input name="user_attr" type="text" value="$user_attr"/>
+                    </td>
+                </tr>
+                <tr>
+                    <td class="config" colspan="2" style="text-align: center">
+                        <input type="submit" name="setting" value="Submit changes"/>
+                    </td>
+                </tr>
+            </table>
+        </form>
+       <p>
+               After enabling LDAP users will be authenticated by LDAP and when
+               a user logs on for the first time an account will automatically
+               be created using the username provided by LDAP. If the user
+               account is removed from LDAP the account will be deactivated but
+               remain in webcal until deleted by the admin user.<br/>
+               <br/>
+               If your LDAP server is located here: <span class="bold">ldap.foo.tld
+               </span> on either port
+               389 or 636 then URL is:<br/>
+               Without SSL (389): <span class="bold">ldap://</span>ldap.foo.tld<br/>
+               With SSL (636): <span class="bold">ldaps://</span>ldap.foo.tld<br/>
+               With TLS (389): <span class="bold">ldap://</span>ldap.foo.tld and 
+               <span class="bold">check "Use TLS"</span><br/>
+               If your LDAP server is listen on 8389 then this port number must
+               be added to the URL like ldap://ldap.foo.tld<span class="bold">:8389</span><br/>
+               <br/>
+               If your Bind DN is the following: uid=username,ou=people,dc=foo,
+               dc=tld then "Base DN" and "User ATTR" has to look like this:<br/>
+               Base DN: <span class="bold">ou=people,dc=foo,dc=tld</span><br/>
+               User ATTR: <span class="bold">uid</span><br/>
+               If your Bind DN is the following: cn=username,dc=foo,dc=tld then
+               "Base DN" and "User ATTR" has to look like this:<br/>
+               Base DN: <span class="bold">dc=foo,dc=tld</span><br/>
+               User ATTR: <span class="bold">cn</span>
+    </p>
+__EOF;
+}
+
+include TOP_FOLDER.'/include/header.inc.php';
+include TOP_FOLDER.'/include/menu.inc.php';
+
+print "<div id=\"ui\">$pageView</div>";
+
+include TOP_FOLDER.'/include/footer.inc.php';
+?>
\ No newline at end of file
diff --git a/utils/newuser.php b/utils/newuser.php
new file mode 100644 (file)
index 0000000..0a85e59
--- /dev/null
@@ -0,0 +1,98 @@
+<?php
+/* $Id$ */
+require_once 'config.inc.php';
+require_once 'user_validate.php';
+require_once 'helper.php';
+
+valid_user();
+
+if (! defined($_SESSION['__ROOT__']) && empty($_SESSION['__ROOT__'])) {
+    if (session_id())
+        session_destroy();
+    header('Location: ' . WEB_ROOT . 'error.html');
+    exit;
+}
+
+if (! has_admin_role()) {
+    if (session_id())
+        session_destroy();
+    header('Location: ' . WEB_ROOT . 'error.html');
+    exit;
+}
+
+require_once 'persistens.php';
+
+include TOP_FOLDER.'/include/header.inc.php';
+include TOP_FOLDER.'/include/menu.inc.php';
+$self =$_SERVER['PHP_SELF'];
+$db = Persistens::getInstance(DBDRIVER);
+
+if (count($_POST) > 0 && isset($_POST['action'])) {
+
+    if (strtolower($_POST['action']) == 'add') {
+        if ($_POST['pwd1'] != $_POST['pwd2']) {
+            $result = "Password does not match";
+        }
+        else {
+            $data = create_user_data(
+                    $_POST['uid'], $_POST['pwd1'], $_POST['role']);
+            $result = $db->newUser($data);
+            if ($result === TRUE) {
+                $name = $db->getRoleName((int) $_POST['role']);
+                $result = "{$_POST['uid']} with role: {$name['name']} added";
+            }
+            $result = nl2br($result);
+        }
+    }
+    $pageView .= <<<__EOF
+<p style="text-align: center">
+    $result
+</p>
+__EOF;
+}
+else {
+    $roles = $db->getRoles();
+    $select = '<select name="role">';
+    foreach ($roles as $role) {
+        $select .= "<option value=\"{$role['id']}\">{$role['name']}</option>";
+    }
+    $select .= "</select>";
+    $pageView .= <<<__EOF
+    <form action="$self" method="post" id="new_form">
+    <p>
+        <table class="config">
+            <tr>
+                <th colspan="2" style="text-align: center">New user</th>
+            </tr>
+            <tr>
+                <td class="config">Username</td>
+                <td class="config"><input name="uid" type="text"/></td>
+            </tr>
+            <tr>
+                <td class="config">User role</td>
+                <td class="config">$select</td>
+            </tr>
+            <tr>
+                <td class="config">Password</td>
+                <td class="config"><input name="pwd1" type="password"/></td>
+            </tr>
+            <tr>
+                <td class="config">Repeat Password</td>
+                <td class="config"><input name="pwd2" type="password"/></td>
+            </tr>
+            <tr>
+                <td class="config" colspan="2" style="text-align: center">
+                    <input type="submit" name="action" value="Add"/>
+                </td>
+            </tr>
+        </table>
+    </p>
+    </form>
+__EOF;
+}
+
+print "<div id=\"ui\">$pageView</div>";
+
+include TOP_FOLDER.'/include/footer.inc.php';
+
+?>
diff --git a/utils/persistens.php b/utils/persistens.php
new file mode 100644 (file)
index 0000000..85591db
--- /dev/null
@@ -0,0 +1,194 @@
+<?php
+/* $Id$ */
+
+include_once 'config.inc.php';
+require_once 'helper.php';
+
+require_once 'sqlite.php';
+require_once 'pgsql.php';
+
+class DefaultConfig {
+    public $viewStyle;
+    public $timeout;
+    public $startWeek;
+    public $startHour;
+    public $endHour;
+    public $title;
+    public $timezone;
+    public $errorDisplay;
+    public $errorLog;
+    public $logFile;
+    public $topFolder;
+    public $webRoot;
+    public $dns;
+    public $role;
+}
+
+interface WebcalSupport {
+    function getViewStyle($uid);
+    function getTimeout($uid);
+    function getRole($uid);
+    function getStartWeek($uid);
+    function getStartHour($uid);
+    function getEndHour($uid);
+    function getTimezone($uid);
+    function authenticate($uid, $pwd);
+    function createDatabase($name);
+    function initDatabase($name, $pwd, $uid);
+    function addUser($data);
+    function setUserSettings($uid, $data);
+    function deleteUser($uid);
+    function addCalendar($uid, CalendarInfo $calendar);
+    function deleteCalendar($uid, $id);
+    function updateCalendar($uid, $id, CalendarInfo $calendar);
+    function changePassword($uid, $pwd);
+    function getAllUsers($limit, $offset);
+    function getRoles();
+    function getRoleName($id);
+    function changeDefault($data);
+    function getVersion();
+    function execute($sql);
+    function getCalendarConfig($id);
+    function nextTableNumber($name);
+    function getLdapConfig();
+    function setLdapConfig(array $config);
+}
+
+class Persistens {
+
+    private static $instance = NULL;
+    private $con;
+
+    private function __construct($driver, $dns) {
+        $this->con = new $driver($dns);
+    }
+
+    private function __clone() {}
+
+    static function getInstance($db = NULL, $dns = NULL) {
+        if (! self::$instance) {
+            if (! $db)
+                throw new Exception('Missing database driver');
+            switch (strtolower($db)) {
+                case 'sqlite':
+                    self::$instance = new Persistens('SQLite', $dns); break;
+                    break;
+                case 'pgsql':
+                    self::$instance = new Persistens('Pgsql', $dns); break;
+                    break;
+                default: throw new Exception("$db: Unsupported driver");
+            }
+        }
+        return self::$instance;
+    }
+
+    function getViewStyle($uid) {
+        return $this->con->getViewStyle($uid);
+    }
+
+    function getStartWeek($uid) {
+        return $this->con->getStartWeek($uid);
+    }
+
+    function getStartHour($uid) {
+        return $this->con->getStartHour($uid);
+    }
+
+    function getEndHour($uid) {
+        return $this->con->getEndHour($uid);
+    }
+
+    function authenticate($uid, $pwd) {
+        return $this->con->authenticate($uid, sha1($pwd));
+    }
+
+    function getTimeout($uid) {
+        return $this->con->getTimeout($uid);
+    }
+
+    function getRole($uid) {
+        return $this->con->getRole($uid);
+    }
+
+    function createDatabase($name) {
+        $this->con->createDatabase($name);
+    }
+
+    function initDatabase($name, $pwd, $uid = 'webcal') {
+        $this->con->initDatabase($name, $pwd, $uid = 'webcal');
+        $this->con = NULL;
+        self::$instance = NULL;
+    }
+
+    function newUser($data) {
+        return $this->con->addUser($data);
+    }
+
+    function setUserSettings($uid, $settings) {
+        return $this->con->setUserSettings($uid, $settings);
+    }
+
+    function deleteUser($uid) {
+        return $this->con->deleteUser($uid);
+    }
+
+    function addCalendar($uid, CalendarInfo $calendar) {
+        return $this->con->addCalendar($uid, $calendar);
+    }
+
+    function deleteCalendar($uid, $id) {
+        return $this->con->deleteCalendar($uid, $id);
+    }
+
+    function updateCalendar($uid, $id, CalendarInfo $calendar) {
+        return $this->con->updateCalendar($uid, $id, $calendar);
+    }
+
+    function changePassword($uid, $pwd) {
+        return $this->con->changePassword($uid, $pwd);
+    }
+
+    function getAllUsers($limit = -1, $offset = 0) {
+        return $this->con->getAllUsers($limit, $offset);
+    }
+
+    function getRoles() {
+        return $this->con->getRoles();
+    }
+
+    function getRoleName($id) {
+        return $this->con->getRoleName($id);
+    }
+
+    function changeDefault($data) {
+        return $this->con->changeDefault($data);
+    }
+    
+    function getVersion() {
+        $res = $this->con->getVersion();
+        if (! is_array($res))
+            throw new Exception($res);
+        return $res;
+    }
+
+    function execute($sql) {
+        return $this->con->execute($sql);
+    }
+    
+    function getCalendarConfig($id = -1) {
+               return $this->con->getCalendarConfig($id);
+       }
+
+       function nextTableNumber($name) {
+               return $this->con->nextTableNumber($name);
+       }
+       
+    function getLdapConfig() {
+               return $this->con->getLdapConfig();
+       }
+
+    function setLdapConfig(array $config) {
+               return $this->con->setLdapConfig($config);
+       }
+
+}
diff --git a/utils/pgsql.php b/utils/pgsql.php
new file mode 100644 (file)
index 0000000..a2b3320
--- /dev/null
@@ -0,0 +1,724 @@
+<?php
+/* $Id$ */
+
+include_once 'config.inc.php';
+require_once 'persistens.php';
+require_once 'helper.php';
+
+class Pgsql extends PDO implements WebcalSupport {
+
+       private $user;
+
+    function __construct($dns = NULL) {
+               if ($dns)
+                       $db_dns = "pgsql:$dns";
+               else
+                       $db_dns = 'pgsql:'.DNS;
+        try {
+            parent::__construct($db_dns);
+            $locale = iconv_get_encoding('internal_encoding');
+            parent::exec("set client_encoding = '$locale'");
+        }
+        catch (PDOException $ex) {
+            throw new Exception($ex->getMessage());
+        }
+    }
+
+       private function getMsg($resource) {
+               $err = $resource->errorInfo();
+               if (count($err) > 2)
+                       return $err[2];
+               else
+                       return "";
+       }  
+
+       function initDatabase($name, $pwd, $uid) {
+        $locale = "select pg_encoding_to_char(encoding) as encoding from pg_database where datname='template0'";
+           $usr = "create user $uid with encrypted password '$pwd'";
+       $init = "create database $name with encoding 'utf8' template template0 owner $uid";
+               try {
+            $sth = $this->prepare($locale);
+            $sth->execute();
+            $res = $sth->fetch();
+            if (strcasecmp($res['encoding'], 'UTF8') !== 0)
+                throw new Exception("Template0 encoding [".$res['encoding']."]. Must be UTF8");
+                       $this->exec($usr);
+                       $this->exec($init);
+               }
+               catch (PDOException $ex) {
+            throw new Exception($ex->getMessage());
+        }
+       }
+
+    function createDatabase($name) {
+        $sql = "select count(*) as exist from pg_database where datname=?";
+        $sth = $this->prepare($sql);
+        $sth->execute(array($name));
+        $res = $sth->fetch();
+        if ($res['exist'] != 0) {
+                       $this->beginTransaction();
+            include_once 'db_create.postgresql.php';
+            $db = make_sql_stm();
+            foreach ($db as $sql) {
+                $sth->closeCursor();
+                $sth = $this->prepare($sql);
+                if (! $sth) {
+                    $this->rollBack();
+                    $err = "$sql\n";
+                    foreach ($this->errorInfo() as $info)
+                        $err .= "$info\n";
+                    throw new Exception($err);
+                }
+               $sth->execute();
+                //throw new Exception(var_export($sth, true));
+                if ($sth->errorCode() && $sth->errorCode() != '00000') {
+                    $this->rollBack();
+                    $err = "$sql\n";
+                    foreach ($this->errorInfo() as $info)
+                        $err .= "$info\n";
+                    throw new Exception($err);
+                }
+                       }
+            $this->commit();
+        }
+        else
+            throw new Exception("$name: Database does not exist");
+    }
+
+    function getViewStyle($uid) {
+        $sql = "select userview from users u where u.uid = ?";
+        $sth = $this->prepare($sql);
+        $sth->execute(array($uid));
+        $res = $sth->fetch();
+        return $res[0];
+    }
+
+    function getRole($uid) {
+        $sql = "select userrole from users u where u.uid = ?";
+        $sth = $this->prepare($sql);
+        $sth->execute(array($uid));
+        $res = $sth->fetch();
+        return $res[0];
+    }
+
+    function getTimeout($uid) {
+        $sql = "select timeout from users u where u.uid = ?";
+        $sth = $this->prepare($sql);
+        $sth->execute(array($uid));
+        $res = $sth->fetch();
+        return $res[0];
+    }
+
+    function getTimezone($uid) {
+        $sql = "select timezone from users u where u.uid = ?";
+        $sth = $this->prepare($sql);
+        $sth->execute(array($uid));
+        $res = $sth->fetch();
+        return $res[0];
+    }
+
+    function getStartWeek($uid) {
+        $sql = "select weekstart from users u where u.uid = ?";
+        $sth = $this->prepare($sql);
+        $sth->execute(array($uid));
+        $res = $sth->fetch();
+        return ($res[0]) ? 'MO' : 'SU';
+    }
+
+    function getStartHour($uid) {
+        $sql = "select daystart from users u where u.uid = ?";
+        $sth = $this->prepare($sql);
+        $sth->execute(array($uid));
+        $res = $sth->fetch();
+        return $res[0];
+    }
+
+    function getEndHour($uid) {
+        $sql = "select daystart from users u where u.uid = ?";
+        $sth = $this->prepare($sql);
+        $sth->execute(array($uid));
+        $res = $sth->fetch();
+        return $res[0];
+    }
+
+    function authenticate($uid, $pwd) {
+        $res = array();
+        $sql = "select userrole, timeout, userview, weekstart, daystart,
+        dayend, timezone, seckey, pubkey, c.id as id, name, color, config
+        from users u left join calendar c on c.uid = u.id where
+        u.uid = ? and u.pwd = ?";
+        $sth = $this->prepare($sql);
+        if (! $sth) {
+            $err = "$sql\n";
+            foreach ($this->errorInfo() as $info)
+                $err .= "$info\n";
+            throw new Exception($err);
+        }
+        $sth->execute(array($uid,$pwd));
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $err = "$sql\n";
+            foreach ($this->errorInfo() as $info)
+                $err .= "$info\n";
+            throw new Exception($err);
+        }
+        $res = $sth->fetchAll(PDO::FETCH_ASSOC);
+        
+        /*
+        if ($res[0]['config']) {
+            //$res[0]['config'] = base64_decode($res[0]['config']);
+            //$res[0]['config'] = quoted_printable_decode($res[0]['config']);
+            $res[0]['config'] = $res[0]['config'];
+               }
+               */
+
+        return $res;
+    }
+
+    function addUser($data) {
+        //$fp = fopen('/tmp/davical.log', 'a');
+        //fwrite($fp, "New user\nuid: {$data['uid']}\npwd: {$data['pwd']}\ntimezone: {$data['timezone']}\nuserrole: {$data['userrole']}\nseckey: {$data['seckey']}\npubkey: {$data['pubkey']}");
+        //fclose($fp);
+        $sql = "insert into users (uid, pwd, timezone, userrole, seckey, pubkey, " .
+            "timeout, userview, weekstart, daystart, dayend) values (?,?,?,?,?,?,?,?,?,?,?)";
+        $sth = $this->prepare($sql);
+        if (! $sth) {
+            foreach ($this->errorInfo() as $info)
+                $err .= "$info\n";
+            return $err;
+        }
+        $this->beginTransaction();
+        $sth->execute(array($data['uid'],$data['pwd'],$data['timezone'],
+            $data['userrole'],$data['seckey'],$data['pubkey'],
+            $data['timeout'], $data['view'], ($data['week_start'] == false) ? 0 : 1,
+            $data['start'], $data['end']));
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $this->rollBack();
+            $err = "$sql\n";
+            foreach ($this->errorInfo() as $info)
+                $err .= "$info\n";
+            return $err;
+        }
+        $this->commit();
+        return TRUE;
+    }
+
+    function setUserSettings($uid, $data) {
+        $sql = "update users set userrole = ?, timeout = ?, userview = ?,
+            weekstart = ?, daystart = ?, dayend = ?, timezone = ?
+            where uid = ?";
+        $sth = $this->prepare($sql);
+        if (! $sth) {
+            $err = "$sql\n";
+            foreach ($this->errorInfo() as $info)
+                $err .= "$info\n";
+            return $err;
+        }
+        $this->beginTransaction();
+        $sth->execute(array($data['userrole'],$data['timeout'],
+            $data['userview'],$data['weekstart'],$data['daystart'],
+            $data['dayend'],$data['timezone'],$uid));
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $this->rollBack();
+            $err = "$sql\n";
+            foreach ($this->errorInfo() as $info)
+                $err .= "$info\n";
+            return $err;
+        }
+        $this->commit();
+        return TRUE;
+    }
+
+    function changeDefault($data) {
+        $this->beginTransaction();
+        foreach ($data as $column => $default) {
+            $sql = "alter table users alter $column set default $default";
+            //echo "$sql<br/>";
+            $res = $this->exec($sql);
+            if ($res === FALSE) {
+                $err = "$sql\n";
+                foreach ($this->errorInfo() as $info)
+                    $err .= "$info\n";
+                $this->rollBack();
+                return $err;
+            }
+        }
+        $this->commit();
+        return TRUE;
+    }
+
+    function deleteUser($uid) {
+        $sql = "delete from users where uid = ?";
+        $sth = $this->prepare($sql);
+        if (! $sth) {
+            $err = "$sql\n";
+            foreach ($this->errorInfo() as $info)
+                $err .= "$info\n";
+            return $err;
+        }
+        $this->beginTransaction();
+        $sth->execute(array($uid));
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $this->rollBack();
+            $err = "$sql\n";
+            foreach ($this->errorInfo() as $info)
+                $err .= "$info\n";
+            return $err;
+        }
+        $this->commit();
+        return TRUE;
+    }
+
+    function addCalendar($uid, CalendarInfo $cal) {
+        //$fp = fopen('/tmp/add_calender.log', 'a');
+        //fwrite($fp, "New calendar\nUID: $uid\nname: {$cal->name}\nColor: {$cal->color}\nConfig: {$cal->config}\n");
+        $this->beginTransaction();
+        $sql = "select id from users where uid = ?";
+        //fwrite($fp, "Get ID: $sql\n");
+        $sth = $this->prepare($sql);
+        if (! $sth) {
+            $err = "$sql\n";
+            foreach ($this->errorInfo() as $info)
+                $err .= "$info\n";
+            $this->rollBack();
+            return $err;
+        }
+        $sth->execute(array($uid));
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $err = "$sql\n";
+            foreach ($this->errorInfo() as $info)
+                $err .= "$info\n";
+            $this->rollBack();
+            return $err;
+        }
+        $sth->bindColumn(1, $id);
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $err = "$sql\n";
+            foreach ($this->errorInfo() as $info)
+                $err .= "$info\n";
+            $this->rollBack();
+            return $err;
+        }
+        $sth->fetch(PDO::FETCH_BOUND);
+        //fwrite($fp, "Returned users ID: $id\n");
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $err = "$sql\n";
+            foreach ($this->errorInfo() as $info)
+                $err .= "$info\n";
+            $this->rollBack();
+            return $err;
+        }
+        $sth->closeCursor();
+        $sql = "insert into calendar (uid, name, color, config)
+            values ($id,?,?,?)";
+        //var_dump($sql);
+        //fwrite($fp, "Insert into calendar: $sql\n");
+        $sth = $this->prepare($sql);
+        if (! $sth) {
+            $err = "$sql\n";
+            foreach ($this->errorInfo() as $info)
+                $err .= "$info\n";
+            $this->rollBack();
+            return $err;
+        }
+        //$sth->execute(array($cal->name,$cal->color,base64_encode($cal->config)));
+        $sth->execute(array($cal->name, $cal->color, $cal->config));
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $err = "$sql\n";
+            foreach ($this->errorInfo() as $info)
+                $err .= "$info\n";
+            $this->rollBack();
+            return $err;
+        }
+        $sth->closeCursor();
+        $sth = $this->prepare('select max(id) from calendar');
+        $sth->execute();
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $err = "$sql\n";
+            foreach ($this->errorInfo() as $info)
+                $err .= "$info\n";
+            $this->rollBack();
+            return $err;
+        }
+        $sth->bindColumn(1, $id);
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $err = "$sql\n";
+            foreach ($this->errorInfo() as $info)
+                $err .= "$info\n";
+            $this->rollBack();
+            return $err;
+        }
+        $sth->fetch(PDO::FETCH_BOUND);
+        //fwrite($fp, "Returned ID for created calendar: $id\n");
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $err = "$sql\n";
+            foreach ($this->errorInfo() as $info)
+                $err .= "$info\n";
+            $this->rollBack();
+            return $err;
+        }
+        $sth->closeCursor();
+        $this->commit();
+/*        $sql = "select * from calendar where id = $id";
+        $sth = $this->prepare($sql);
+        $sth->execute();
+        $result = $sth->fetchAll(PDO::FETCH_ASSOC);
+        if ($result)
+            fwrite($fp, var_export($result[0], TRUE) . "\n");
+        else
+            fwrite($fp, "No date found in calendar relation\n");
+        fclose($fp);*/
+        return $id;
+    }
+
+    function deleteCalendar($uid, $id) {
+        $this->beginTransaction();
+        $sql = "select id from users where uid = ?";
+        $sth = $this->prepare($sql);
+        if (! $sth) {
+            $err = "$sql\n";
+            foreach ($this->errorInfo() as $info)
+                $err .= "$info\n";
+            $this->rollBack();
+            return $err;
+        }
+        $sth->execute(array($uid));
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $err = "$sql\n";
+            foreach ($this->errorInfo() as $info)
+                $err .= "$info\n";
+            $this->rollBack();
+            return $err;
+        }
+        $sth->bindColumn(1, $uid);
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $err = "$sql\n";
+            foreach ($this->errorInfo() as $info)
+                $err .= "$info\n";
+            $this->rollBack();
+            return $err;
+        }
+        $sth->fetch(PDO::FETCH_BOUND);
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $err = "$sql\n";
+            foreach ($this->errorInfo() as $info)
+                $err .= "$info\n";
+            $this->rollBack();
+            return $err;
+        }
+        $sth->closeCursor();
+        $sql = "delete from calendar where id = ? and uid = $uid";
+        $sth = $this->prepare($sql);
+        if (! $sth) {
+            $err = "$sql\n";
+            foreach ($this->errorInfo() as $info)
+                $err .= "$info\n";
+            $this->rollBack();
+            return $err;
+        }
+        $sth->execute(array($id));
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $err = "$sql\n";
+            foreach ($this->errorInfo() as $info)
+                $err .= "$info\n";
+            $this->rollBack();
+            return $err;
+        }
+        $this->commit();
+        return TRUE;
+    }
+
+    function updateCalendar($uid, $id, CalendarInfo $cal) {
+        //echo "$uid:$id:".var_export($cal,true)."<br/>";
+        $this->beginTransaction();
+        $sql = "select id from users where uid = ?";
+        $sth = $this->prepare($sql);
+        if (! $sth) {
+            $err = "$sql\n";
+            foreach ($this->errorInfo() as $info)
+                $err .= "$info\n";
+            $this->rollBack();
+            return $err;
+        }
+        $sth->execute(array($uid));
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $err = "$sql\n";
+            foreach ($this->errorInfo() as $info)
+                $err .= "$info\n";
+            $this->rollBack();
+            return $err;
+        }
+        $sth->bindColumn(1, $uid);
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $err = "$sql\n";
+            foreach ($this->errorInfo() as $info)
+                $err .= "$info\n";
+            $this->rollBack();
+            return $err;
+        }
+        $sth->fetch(PDO::FETCH_BOUND);
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $err = "$sql\n";
+            foreach ($this->errorInfo() as $info)
+                $err .= "$info\n";
+            $this->rollBack();
+            return $err;
+        }
+        $sth->closeCursor();
+        $sql = "update calendar set name = ?, color = ?, config = ?
+        where id = ? and uid = $uid";
+        $sth = $this->prepare($sql);
+        if (! $sth) {
+            $err = "$sql\n";
+            foreach ($this->errorInfo() as $info)
+                $err .= "$info\n";
+            $this->rollBack();
+            return $err;
+        }
+        //$sth->execute(array($cal->name,$cal->color,base64_encode($cal->config),$id));
+        $sth->execute(array($cal->name,$cal->color, $cal->config,$id));
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $err = "$sql\n";
+            foreach ($this->errorInfo() as $info)
+                $err .= "$info\n";
+            $this->rollBack();
+            return $err;
+        }
+        $this->commit();
+        return TRUE;
+    }
+
+    function changePassword($uid, $pwd) {
+        $this->beginTransaction();
+        $sql = "update users set pwd = ? where uid = ?";
+        $sth = $this->prepare($sql);
+        if (! $sth) {
+            $err = "$sql\n";
+            foreach ($this->errorInfo() as $info)
+                $err .= "$info\n";
+            return $err;
+        }
+        $sth->execute(array($pwd,$uid));
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $this->rollBack();
+            $err = "$sql\n";
+            foreach ($this->errorInfo() as $info)
+                $err .= "$info\n";
+            return $err;
+        }
+        $this->commit();
+        return TRUE;
+    }
+
+    function getAllUsers($limit, $offset) {
+               if ($limit == -1) {
+               $sql = "select u.uid, r.name as userrole from users u, roles r
+               where u.userrole = r.id limit all offset ?";
+               }
+               else {
+               $sql = "select u.uid, r.name as userrole from users u, roles r
+               where u.userrole = r.id limit ? offset ?";
+               }
+        $sth = $this->prepare($sql);
+        if (! $sth) {
+            $err = "$sql\n";
+            foreach ($this->errorInfo() as $info)
+                $err .= "$info\n";
+            return $err;
+        }
+        if ($limit == -1)
+               $sth->execute(array($offset));
+        else
+               $sth->execute(array($limit,$offset));
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $err = "$sql\n";
+            foreach ($this->errorInfo() as $info)
+                $err .= "$info\n";
+            return $err;
+        }
+        return $sth->fetchAll(PDO::FETCH_ASSOC);
+    }
+
+    function getRoles() {
+        $sql = "select id, name from roles order by id desc";
+        $sth = $this->prepare($sql);
+        if (! $sth) {
+            $err = "$sql\n";
+            foreach ($this->errorInfo() as $info)
+                $err .= "$info\n";
+            return $err;
+        }
+        $sth->execute();
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $err = "$sql\n";
+            foreach ($this->errorInfo() as $info)
+                $err .= "$info\n";
+            return $err;
+        }
+        return $sth->fetchAll(PDO::FETCH_ASSOC);
+    }
+
+    function getRoleName($id) {
+        $sql = "select name from roles where id = ?";
+        $sth = $this->prepare($sql);
+        if (! $sth) {
+            $err = "$sql\n";
+            foreach ($this->errorInfo() as $info)
+                $err .= "$info\n";
+            return $err;
+        }
+        $sth->execute(array($id));
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $err = "$sql\n";
+            foreach ($this->errorInfo() as $info)
+                $err .= "$info\n";
+            return $err;
+        }
+        return $sth->fetch(PDO::FETCH_ASSOC);
+    }
+
+    function getVersion() {
+        $version = array('version' => 0);
+        $sql = "select count(*) as exist from pg_tables where tablename = 'about'";
+        $sth = $this->prepare($sql);
+        if (! $sth) {
+            $err = "$sql\n";
+            foreach ($this->errorInfo() as $info)
+                $err .= "$info\n";
+            return $err;
+        }
+        $sth->execute();
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $err = "$sql\n";
+            foreach ($this->errorInfo() as $info)
+                $err .= "$info\n";
+            return $err;
+        }
+        $res = $sth->fetch();
+        if ($res['exist'] > 0) {
+            $sql = "select version from about where id = 1";
+            $sth = $this->prepare($sql);
+            if (! $sth) {
+                $err = "$sql\n";
+                foreach ($this->errorInfo() as $info)
+                    $err .= "$info\n";
+                return $err;
+            }
+            $sth->execute();
+            if ($sth->errorCode() && $sth->errorCode() != '00000') {
+                $err = "$sql\n";
+                foreach ($this->errorInfo() as $info)
+                    $err .= "$info\n";
+                return $err;
+            }
+            $res = $sth->fetch();
+            $version = $res;
+        }
+        return $version;
+    }
+
+    function execute($sql) {
+        $sth = $this->prepare($sql);
+        if (! $sth) {
+            $err = "$sql\n";
+            foreach ($this->errorInfo() as $info)
+                $err .= "$info\n";
+            return $err;
+        }
+        $sth->execute();
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $err = "$sql\n";
+            foreach ($this->errorInfo() as $info)
+                $err .= "$info\n";
+            return $err;
+        }
+        return true;
+    }
+
+    function getCalendarConfig($id) {
+               if ($id == -1) {
+                       $sql = "select id, config from calendar";
+               }
+               else {
+                       $sql = "select id, config from calendar where id = ?";
+               }
+        $sth = $this->prepare($sql);
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $err = "$sql\n";
+                       $err .= $this->getMsg($sth)."\n";
+            return $err;
+        }
+        $sth->execute(array($id));
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $err = "$sql\n";
+                       $err .= $this->getMsg($sth)."\n";
+            return $err;
+        }
+        return $sth->fetchAll(PDO::FETCH_ASSOC);
+       }
+
+       function nextTableNumber($name) {
+               $sql = "select count(*) as found from pg_tables where tableowner = 'webcal' " .
+                      "and tablename like ?";
+               $sth = $this->prepare($sql);
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $err = "$sql\n";
+                       $err .= $this->getMsg($sth)."\n";
+            return $err;
+        }
+        $sth->execute(array($name . '%'));
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $err = "$sql\n";
+                       $err .= $this->getMsg($sth)."\n";
+            return $err;
+        }
+        $res = $sth->fetch(PDO::FETCH_ASSOC);
+        return $res['found'];
+       }
+       
+    function getLdapConfig() {
+        $sql = "select enable, dns, tls, base_dn, user_attr from ldap";
+               $sth = $this->prepare($sql);
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $err = "$sql\n";
+                       $err .= $this->getMsg($sth)."\n";
+            return $err;
+        }
+        $sth->execute();
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $err = "$sql\n";
+                       $err .= $this->getMsg($sth)."\n";
+            return $err;
+        }
+        return $sth->fetch(PDO::FETCH_ASSOC);
+    }
+    
+    function setLdapConfig(array $config) {
+               $old_config = $this->getLdapConfig();
+           if (! is_array($old_config) && $old_config)
+                   return $old_config;
+        else if (is_array($old_config))
+            $sql = "update ldap set enable=?, dns=?, tls=?, base_dn=?, user_attr=?";
+        else
+            $sql = "insert into ldap values(?, ?, ?, ?, ?)";
+        $this->beginTransaction();
+               $sth = $this->prepare($sql);
+        if (! $sth) {
+            $err = "$sql\n";
+            foreach ($this->errorInfo() as $info)
+                $err .= "$info\n";
+            $this->rollBack();
+            return $err;
+        }
+        $sth->execute($config);
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $this->rollBack();
+            $err = "$sql\n";
+            foreach ($this->errorInfo() as $info)
+                $err .= "$info\n";
+            return $err;
+        }
+        $this->commit();
+        return true;
+    }
+
+}
diff --git a/utils/sqlite.php b/utils/sqlite.php
new file mode 100644 (file)
index 0000000..55dcbb4
--- /dev/null
@@ -0,0 +1,797 @@
+<?php
+/* $Id$ */
+
+include_once 'config.inc.php';
+require_once 'persistens.php';
+require_once 'helper.php';
+
+class SQLite extends PDO implements WebcalSupport {
+
+    function __construct($dns = NULL) {
+        if ($dns) {
+            $db_dns = "sqlite:$dns";
+        }
+        else {
+            $db_dns = 'sqlite:'.TOP_FOLDER.'/'.DNS;
+        }
+        try {
+               parent::__construct($db_dns, 0600);
+               parent::exec("PRAGMA foreign_keys = OFF");
+                       //parent::setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
+                       //parent::setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT);
+               }
+        catch (PDOException $ex) {
+            throw new Exception($ex->getMessage());
+        }
+    }
+
+       private function getMsg($resource) {
+               $err = $resource->errorInfo();
+               if (count($err) > 2)
+                       return $err[2];
+               else
+                       return "";
+       }  
+
+    function initDatabase($name, $pwd, $uid) {
+    }
+
+    function createDatabase($name) {
+        $sql = "select count(*) as found from sqlite_master where
+                 type = 'table' and name = 'user'";
+        $sth = $this->prepare($sql);
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $err = "$sql\n";
+            $err .= $this->getMsg($sth)."\n";
+            throw new Exception($err);
+        }
+        $sth->execute();
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $err = "$sql\n";
+            $err .= $this->getMsg($sth)."\n";
+            throw new Exception($err);
+        }
+        $res = $sth->fetch();
+        if ($res['found'] == 0) {
+            $this->beginTransaction();
+            include 'db_create.sqlite.php';
+            $db = make_sql_stm();
+            foreach ($db as $sql) {
+                $sth->closeCursor();
+                $sth = $this->prepare($sql);
+                if ($sth->errorCode() && $sth->errorCode() != '00000') {
+                    $this->rollBack();
+                       $err = "$sql\n";
+                           $err .= $this->getMsg($sth)."\n";
+                    throw new Exception($err);
+                }
+                $sth->execute();
+                if ($sth->errorCode() && $sth->errorCode() != '00000') {
+                    $this->rollBack();
+                    $err = "$sql\n";
+                       $err .= $this->getMsg($sth)."\n";
+                    throw new Exception($err);
+                }
+            }
+            $this->commit();
+        }
+        else
+            throw new Exception("Database exists");
+    }
+
+    function getViewStyle($uid) {
+        $sql = "select userview from user u where u.uid = ?";
+        $sth = $this->prepare($sql);
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $err = "$sql\n";
+                       $err .= $this->getMsg($sth)."\n";
+            return $err;
+        }
+        $sth->execute(array($uid));
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $err = "$sql\n";
+                       $err .= $this->getMsg($sth)."\n";
+            return $err;
+        }
+        $res = $sth->fetch();
+        return $res[0];
+    }
+
+    function getRole($uid) {
+        $sql = "select userrole from user u where u.uid = ?";
+        $sth = $this->prepare($sql);
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $err = "$sql\n";
+                       $err .= $this->getMsg($sth)."\n";
+            return $err;
+        }
+        $sth->execute(array($uid));
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $err = "$sql\n";
+                       $err .= $this->getMsg($sth)."\n";
+            return $err;
+        }
+        $res = $sth->fetch();
+        return $res[0];
+    }
+
+    function getTimeout($uid) {
+        $sql = "select timeout from user u where u.uid = ?";
+        $sth = $this->prepare($sql);
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $err = "$sql\n";
+                       $err .= $this->getMsg($sth)."\n";
+            return $err;
+        }
+        $sth->execute(array($uid));
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $err = "$sql\n";
+                       $err .= $this->getMsg($sth)."\n";
+            return $err;
+        }
+        $res = $sth->fetch();
+        return $res[0];
+    }
+
+    function getTimezone($uid) {
+        $sql = "select timezone from user u where u.uid = ?";
+        $sth = $this->prepare($sql);
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $err = "$sql\n";
+                       $err .= $this->getMsg($sth)."\n";
+            return $err;
+        }
+        $sth->execute(array($uid));
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $err = "$sql\n";
+                       $err .= $this->getMsg($sth)."\n";
+            return $err;
+        }
+        $res = $sth->fetch();
+        return $res[0];
+    }
+
+    function getStartWeek($uid) {
+        $sql = "select weekstart from user u where u.uid = ?";
+        $sth = $this->prepare($sql);
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $err = "$sql\n";
+                       $err .= $this->getMsg($sth)."\n";
+            return $err;
+        }
+        $sth->execute(array($uid));
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $err = "$sql\n";
+                       $err .= $this->getMsg($sth)."\n";
+            return $err;
+        }
+        $res = $sth->fetch();
+        return ($res[0]) ? 'MO' : 'SU';
+    }
+
+    function getStartHour($uid) {
+        $sql = "select daystart from user u where u.uid = ?";
+        $sth = $this->prepare($sql);
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $err = "$sql\n";
+                       $err .= $this->getMsg($sth)."\n";
+            return $err;
+        }
+        $sth->execute(array($uid));
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $err = "$sql\n";
+                       $err .= $this->getMsg($sth)."\n";
+            return $err;
+        }
+        $res = $sth->fetch();
+        return $res[0];
+    }
+
+    function getEndHour($uid) {
+        $sql = "select daystart from user u where u.uid = ?";
+        $sth = $this->prepare($sql);
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $err = "$sql\n";
+                       $err .= $this->getMsg($sth)."\n";
+            return $err;
+        }
+        $sth->execute(array($uid));
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $err = "$sql\n";
+                       $err .= $this->getMsg($sth)."\n";
+            return $err;
+        }
+        $res = $sth->fetch();
+        return $res[0];
+    }
+
+    function authenticate($uid, $pwd) {
+        $res = array();
+        $sql = "select userrole, timeout, userview, weekstart, daystart,
+        dayend, timezone, seckey, pubkey, c.id id, name, color, config
+        from user u left join calendar c on c.uid = u.id where
+        u.uid = ? and u.pwd = ?";
+        $sth = $this->prepare($sql);
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $err = "$sql\n";
+                       $err .= $this->getMsg($sth)."\n";
+            throw new Exception($err);
+        }
+        $sth->execute(array($uid,$pwd));
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $err = "$sql\n";
+                       $err .= $this->getMsg($sth)."\n";
+            throw new Exception($err);
+        }
+        $res = $sth->fetchAll(PDO::FETCH_ASSOC);
+        return $res;
+    }
+
+    function addUser($data) {
+        $sql = "insert into user (uid, pwd, timezone, userrole, seckey, pubkey, " .
+            "timeout, userview, weekstart, daystart, dayend) values (?,?,?,?,?,?,?,?,?,?,?)";
+        $sth = $this->prepare($sql);
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $err = "$sql\n";
+            $err .= $this->getMsg($sth)."\n";
+            return $err;
+        }
+        $this->beginTransaction();
+        $sth->execute(array($data['uid'],$data['pwd'],$data['timezone'],
+            $data['userrole'],$data['seckey'],$data['pubkey'],
+            $data['timeout'], $data['view'], ($data['week_start'] == false) ? 0 : 1,
+            $data['start'], $data['end']));
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $this->rollBack();
+            $err = "$sql\n";
+            $err .= $this->getMsg($sth)."\n";
+            return $err;
+        }
+        $this->commit();
+        return TRUE;
+    }
+
+    function setUserSettings($uid, $data) {
+        $sql = "update user set userrole = ?, timeout = ?, userview = ?,
+            weekstart = ?, daystart = ?, dayend = ?, timezone = ?
+            where uid = ?";
+        $sth = $this->prepare($sql);
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $err = "$sql\n";
+                       $err .= $this->getMsg($sth)."\n";
+            return $err;
+        }
+        $this->beginTransaction();
+        $sth->execute(array($data['userrole'],$data['timeout'],
+            $data['userview'],$data['weekstart'],$data['daystart'],
+            $data['dayend'],$data['timezone'],$uid));
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $this->rollBack();
+            $err = "$sql\n";
+                       $err .= $this->getMsg($sth)."\n";
+            return $err;
+        }
+        $this->commit();
+        return TRUE;
+    }
+/*
+timeout integer default 3600,
+userview text default 'week',
+weekstart integer default 0,
+daystart real default 8.00,
+dayend real default 17.00,
+timezone text default 'Etc/UTC',
+*/
+    function changeDefault($data) {
+        $temp = <<<_TEMP
+            CREATE TEMPORARY TABLE tmpuser (
+                id integer,
+                uid text,
+                pwd text,
+                userrole integer,
+                timeout integer,
+                userview text,
+                weekstart integer,
+                daystart real,
+                dayend real,
+                timezone text,
+                seckey text,
+                pubkey text
+            )
+_TEMP;
+        $table = <<<_TABLE
+            CREATE TABLE user (
+                id integer primary key autoincrement,
+                uid text not null,
+                pwd text not null,
+                userrole integer not null,
+                timeout integer default __TIMEOUT__,
+                userview text default '__VIEW__',
+                weekstart integer default __WEEKSTART__,
+                daystart real default __DAYSTART__,
+                dayend real default __DAYEND__,
+                timezone text default '__TIMEZONE__',
+                seckey text default '',
+                pubkey text default '',
+                constraint uid_index unique (uid)
+                constraint userrole_fk foreign key (userrole) references roles (id)
+                on delete restrict
+                on update cascade
+                on insert cascade
+            )
+_TABLE;
+        $table = str_replace('__TIMEOUT__', $data['TIMEOUT'], $table);
+        $table = str_replace('__VIEW__', $data['VIEW_STYLE'], $table);
+        $table = str_replace('__WEEKSTART__', $data['WEEK_START_SUNDAY'], $table);
+        $table = str_replace('__DAYSTART__', $data['START_HOUR'], $table);
+        $table = str_replace('__DAYEND__', $data['END_HOUR'], $table);
+        $table = str_replace('__TIMEZONE__', $data['TIMEZONE'], $table);
+        $this->beginTransaction();
+        if ($this->exec($temp) === FALSE) {
+            $err = "$sql\n";
+                       $err .= $this->getMsg($sth)."\n";
+            $this->rollBack();
+            return $err;
+        }
+        if ($this->exec('insert into tmpuser select * from user') === FALSE) {
+            $err = "cp to tmp table\n";
+                       $err .= $this->getMsg($this)."\n";
+            $this->rollBack();
+            return $err;
+        }
+        if ($this->exec('drop table user') === FALSE) {
+            $err = "drop user table\n";
+                       $err .= $this->getMsg($this)."\n";
+            $this->rollBack();
+            return $err;
+        }
+        if ($this->exec($table) === FALSE) {
+            $err = "create table\n";
+                       $err .= $this->getMsg($this)."\n";
+            $this->rollBack();
+            return $err;
+        }
+        if ($this->exec('insert into user select * from tmpuser') === FALSE) {
+            $err = "cp tmp to user table\n";
+                       $err .= $this->getMsg($this)."\n";
+            $this->rollBack();
+            return $err;
+        }
+        if ($this->exec('drop table tmpuser') === FALSE) {
+            $err = "drop user table\n";
+                       $err .= $this->getMsg($this)."\n";
+            $this->rollBack();
+            return $err;
+        }
+        $this->commit();
+        return TRUE;
+    }
+
+    function deleteUser($uid) {
+        $sql = "delete from user where uid = ?";
+        $sth = $this->prepare($sql);
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $err = "$sql\n";
+                       $err .= $this->getMsg($sth)."\n";
+            return $err;
+        }
+        $this->beginTransaction();
+        $sth->execute(array($uid));
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $this->rollBack();
+            $err = "$sql\n";
+                       $err .= $this->getMsg($sth)."\n";
+            return $err;
+        }
+        $this->commit();
+        return TRUE;
+    }
+
+    function addCalendar($uid, CalendarInfo $cal) {
+        //$fp = fopen('/tmp/add_calender.log', 'a');
+        //fwrite($fp, "New calendar\nUID: $uid\nname: {$cal->name}\nColor: {$cal->color}\nConfig: {$cal->config}\n");
+        $this->beginTransaction();
+        $sql = "select id from user where uid = ?";
+        //fwrite($fp, "Get ID: $sql\n");
+        $sth = $this->prepare($sql);
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $err = "$sql\n";
+                       $err .= $this->getMsg($sth)."\n";
+            $this->rollBack();
+            return $err;
+        }
+        $sth->execute(array($uid));
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $err = "$sql\n";
+                       $err .= $this->getMsg($sth)."\n";
+            $this->rollBack();
+            return $err;
+        }
+        $sth->bindColumn(1, $id);
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $err = "$sql\n";
+                       $err .= $this->getMsg($sth)."\n";
+            $this->rollBack();
+            return $err;
+        }
+        $sth->fetch(PDO::FETCH_BOUND);
+        //fwrite($fp, "Returned user ID: $id\n");
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $err = "$sql\n";
+                       $err .= $this->getMsg($sth)."\n";
+            $this->rollBack();
+            return $err;
+        }
+        $sth->closeCursor();
+        $sql = "insert into calendar (uid, name, color, config)
+            values ($id,?,?,?)";
+        //var_dump($sql);
+        //fwrite($fp, "Insert into calendar: $sql\n");
+        $sth = $this->prepare($sql);
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $err = "$sql\n";
+                       $err .= $this->getMsg($sth)."\n";
+            $this->rollBack();
+            return $err;
+        }
+        $sth->execute(array($cal->name,$cal->color,$cal->config));
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $err = "$sql\n";
+                       $err .= $this->getMsg($sth)."\n";
+            $this->rollBack();
+            return $err;
+        }
+        $sth->closeCursor();
+        $sth = $this->prepare('select max(id) from calendar');
+        $sth->execute();
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $err = "$sql\n";
+                       $err .= $this->getMsg($sth)."\n";
+            $this->rollBack();
+            return $err;
+        }
+        $sth->bindColumn(1, $id);
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $err = "$sql\n";
+                       $err .= $this->getMsg($sth)."\n";
+            $this->rollBack();
+            return $err;
+        }
+        $sth->fetch(PDO::FETCH_BOUND);
+        //fwrite($fp, "Returned ID for created calendar: $id\n");
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $err = "$sql\n";
+                       $err .= $this->getMsg($sth)."\n";
+            $this->rollBack();
+            return $err;
+        }
+        $sth->closeCursor();
+        $this->commit();
+/*        $sql = "select * from calendar where id = $id";
+        $sth = $this->prepare($sql);
+        $sth->execute();
+        $result = $sth->fetchAll(PDO::FETCH_ASSOC);
+        if ($result)
+            fwrite($fp, var_export($result[0], TRUE) . "\n");
+        else
+            fwrite($fp, "No date found in calendar relation\n");
+        fclose($fp);*/
+        return $id;
+    }
+
+    function deleteCalendar($uid, $id) {
+        $this->beginTransaction();
+        $sql = "select id from user where uid = ?";
+        $sth = $this->prepare($sql);
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $err = "$sql\n";
+                       $err .= $this->getMsg($sth)."\n";
+            $this->rollBack();
+            return $err;
+        }
+        $sth->execute(array($uid));
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $err = "$sql\n";
+                       $err .= $this->getMsg($sth)."\n";
+            $this->rollBack();
+            return $err;
+        }
+        $sth->bindColumn(1, $uid);
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $err = "$sql\n";
+                       $err .= $this->getMsg($sth)."\n";
+            $this->rollBack();
+            return $err;
+        }
+        $sth->fetch(PDO::FETCH_BOUND);
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $err = "$sql\n";
+                       $err .= $this->getMsg($sth)."\n";
+            $this->rollBack();
+            return $err;
+        }
+        $sth->closeCursor();
+        $sql = "delete from calendar where id = ? and uid = $uid";
+        $sth = $this->prepare($sql);
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $err = "$sql\n";
+                       $err .= $this->getMsg($sth)."\n";
+            $this->rollBack();
+            return $err;
+        }
+        $sth->execute(array($id));
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $err = "$sql\n";
+                       $err .= $this->getMsg($sth)."\n";
+            $this->rollBack();
+            return $err;
+        }
+        $this->commit();
+        return TRUE;
+    }
+
+    function updateCalendar($uid, $id, CalendarInfo $cal) {
+        $this->beginTransaction();
+        $sql = "select id from user where uid = ?";
+        $sth = $this->prepare($sql);
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $err = "$sql\n";
+                       $err .= $this->getMsg($sth)."\n";
+            $this->rollBack();
+            return $err;
+        }
+        $sth->execute(array($uid));
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $err = "$sql\n";
+                       $err .= $this->getMsg($sth)."\n";
+            $this->rollBack();
+            return $err;
+        }
+        $sth->bindColumn(1, $uid);
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $err = "$sql\n";
+                       $err .= $this->getMsg($sth)."\n";
+            $this->rollBack();
+            return $err;
+        }
+        $sth->fetch(PDO::FETCH_BOUND);
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $err = "$sql\n";
+                       $err .= $this->getMsg($sth)."\n";
+            $this->rollBack();
+            return $err;
+        }
+        $sth->closeCursor();
+        $sql = "update calendar set name = ?, color = ?, config = ?
+        where id = ? and uid = $uid";
+        $sth = $this->prepare($sql);
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $err = "$sql\n";
+                       $err .= $this->getMsg($sth)."\n";
+            $this->rollBack();
+            return $err;
+        }
+        $sth->execute(array($cal->name,$cal->color,$cal->config,$id));
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $err = "$sql\n";
+                       $err .= $this->getMsg($sth)."\n";
+            $this->rollBack();
+            return $err;
+        }
+        $this->commit();
+        return TRUE;
+    }
+
+    function changePassword($uid, $pwd) {
+        $this->beginTransaction();
+        $sql = "update user set pwd = ? where uid = ?";
+        $sth = $this->prepare($sql);
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $err = "$sql\n";
+                       $err .= $this->getMsg($sth)."\n";
+            return $err;
+        }
+        $sth->execute(array($pwd,$uid));
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $this->rollBack();
+            $err = "$sql\n";
+                       $err .= $this->getMsg($sth)."\n";
+            return $err;
+        }
+        $this->commit();
+        return TRUE;
+    }
+
+    function getAllUsers($limit, $offset) {
+        $sql = "select u.uid, r.name as userrole from user u, roles r
+        where u.userrole = r.id limit ? offset ?";
+        $sth = $this->prepare($sql);
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $err = "$sql\n";
+                       $err .= $this->getMsg($sth)."\n";
+            return $err;
+        }
+        $sth->execute(array($limit,$offset));
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $err = "$sql\n";
+                       $err .= $this->getMsg($sth)."\n";
+            return $err;
+        }
+        return $sth->fetchAll(PDO::FETCH_ASSOC);
+    }
+
+    function getRoles() {
+        $sql = "select id, name from roles order by id desc";
+        $sth = $this->prepare($sql);
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $err = "$sql\n";
+                       $err .= $this->getMsg($sth)."\n";
+            return $err;
+        }
+        $sth->execute();
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $err = "$sql\n";
+                       $err .= $this->getMsg($sth)."\n";
+            return $err;
+        }
+        return $sth->fetchAll(PDO::FETCH_ASSOC);
+    }
+
+    function getRoleName($id) {
+        $sql = "select name from roles where id = ?";
+        $sth = $this->prepare($sql);
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $err = "$sql\n";
+                       $err .= $this->getMsg($sth)."\n";
+            return $err;
+        }
+        $sth->execute(array($id));
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $err = "$sql\n";
+                       $err .= $this->getMsg($sth)."\n";
+            return $err;
+        }
+        return $sth->fetch(PDO::FETCH_ASSOC);
+    }
+
+    function getVersion() {
+        $version = array('version' => 0);
+        $sql = "select count(*) as exist from sqlite_master where 
+            type = 'table' and tbl_name = 'about'";
+        $sth = $this->prepare($sql);
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $err = "$sql\n";
+                       $err .= $this->getMsg($sth)."\n";
+            return $err;
+        }
+        $sth->execute();
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $err = "$sql\n";
+                       $err .= $this->getMsg($sth)."\n";
+            return $err;
+        }
+        $res = $sth->fetch();
+        if ($res['exist'] > 0) {
+            $sql = "select version from about where id = 1";
+            $sth = $this->prepare($sql);
+            if ($sth->errorCode() && $sth->errorCode() != '00000') {
+               $err = "$sql\n";
+                       $err .= $this->getMsg($sth)."\n";
+                return $err;
+            }
+            $sth->execute();
+            if ($sth->errorCode() && $sth->errorCode() != '00000') {
+                   $err = "$sql\n";
+                       $err .= $this->getMsg($sth)."\n";
+                return $err;
+            }
+            $res = $sth->fetch();
+            $version = $res;
+        }
+        return $version;
+    }
+
+    function execute($sql) {
+        $sth = $this->prepare($sql);
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $err = "$sql\n";
+                       $err .= $this->getMsg($sth)."\n";
+            return $err;
+        }
+        $sth->execute();
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $err = "$sql\n";
+                       $err .= $this->getMsg($sth)."\n";
+            return $err;
+        }
+        return true;
+    }
+    
+    function getCalendarConfig($id) {
+               if ($id == -1) {
+                       $sql = "select id, config from calendar";
+               }
+               else {
+                       $sql = "select id, config from calendar where id = ?";
+               }
+        $sth = $this->prepare($sql);
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $err = "$sql\n";
+                       $err .= $this->getMsg($sth)."\n";
+            return $err;
+        }
+        $sth->execute(array($id));
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $err = "$sql\n";
+                       $err .= $this->getMsg($sth)."\n";
+            return $err;
+        }
+        return $sth->fetchAll(PDO::FETCH_ASSOC);
+       }
+
+       function nextTableNumber($name) {
+               $sql = "select count(*) as found from sqlite_master where " .
+                          "type = 'table' and name like ?";
+               $sth = $this->prepare($sql);
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $err = "$sql\n";
+                       $err .= $this->getMsg($sth)."\n";
+            return $err;
+        }
+        $sth->execute(array($name . '%'));
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $err = "$sql\n";
+                       $err .= $this->getMsg($sth)."\n";
+            return $err;
+        }
+        $res = $sth->fetch(PDO::FETCH_ASSOC);
+        return $res['found'];
+       }
+
+    function getLdapConfig() {
+        $sql = "select enable, dns, tls, base_dn, user_attr from ldap";
+               $sth = $this->prepare($sql);
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $err = "$sql\n";
+                       $err .= $this->getMsg($sth)."\n";
+            return $err;
+        }
+        $sth->execute();
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $err = "$sql\n";
+                       $err .= $this->getMsg($sth)."\n";
+            return $err;
+        }
+        return $sth->fetch(PDO::FETCH_ASSOC);
+    }
+    
+    function setLdapConfig(array $config) {
+               $old_config = $this->getLdapConfig();
+           if (! is_array($old_config) && $old_config)
+                   return $old_config;
+        else if (is_array($old_config))
+            $sql = "update ldap set enable=?, dns=?, tls=?, base_dn=?, user_attr=?";
+        else
+            $sql = "insert into ldap values(?, ?, ?, ?, ?)";
+        $this->beginTransaction();
+               $sth = $this->prepare($sql);
+        if (! $sth) {
+            $err = "$sql\n";
+            foreach ($this->errorInfo() as $info)
+                $err .= "$info\n";
+            $this->rollBack();
+            return $err;
+        }
+        $sth->execute($config);
+        if ($sth->errorCode() && $sth->errorCode() != '00000') {
+            $this->rollBack();
+            $err = "$sql\n";
+            foreach ($this->errorInfo() as $info)
+                $err .= "$info\n";
+            return $err;
+        }
+        $this->commit();
+        return true;
+    }
+
+}
diff --git a/utils/timezone.php b/utils/timezone.php
new file mode 100644 (file)
index 0000000..02a25b8
--- /dev/null
@@ -0,0 +1,500 @@
+<?php
+/* $Id$ */
+
+$timezones = array(
+    "Africa/Abidjan",
+    "Africa/Accra",
+    "Africa/Addis_Ababa",
+    "Africa/Algiers",
+    "Africa/Asmara",
+    "Africa/Asmera",
+    "Africa/Bamako",
+    "Africa/Bangui",
+    "Africa/Banjul",
+    "Africa/Bissau",
+    "Africa/Blantyre",
+    "Africa/Brazzaville",
+    "Africa/Bujumbura",
+    "Africa/Cairo",
+    "Africa/Casablanca",
+    "Africa/Ceuta",
+    "Africa/Conakry",
+    "Africa/Dakar",
+    "Africa/Dar_es_Salaam",
+    "Africa/Djibouti",
+    "Africa/Douala",
+    "Africa/El_Aaiun",
+    "Africa/Freetown",
+    "Africa/Gaborone",
+    "Africa/Harare",
+    "Africa/Johannesburg",
+    "Africa/Kampala",
+    "Africa/Khartoum",
+    "Africa/Kigali",
+    "Africa/Kinshasa",
+    "Africa/Lagos",
+    "Africa/Libreville",
+    "Africa/Lome",
+    "Africa/Luanda",
+    "Africa/Lubumbashi",
+    "Africa/Lusaka",
+    "Africa/Malabo",
+    "Africa/Maputo",
+    "Africa/Maseru",
+    "Africa/Mbabane",
+    "Africa/Mogadishu",
+    "Africa/Monrovia",
+    "Africa/Nairobi",
+    "Africa/Ndjamena",
+    "Africa/Niamey",
+    "Africa/Nouakchott",
+    "Africa/Ouagadougou",
+    "Africa/Sao_Tome",
+    "Africa/Timbuktu",
+    "Africa/Tripoli",
+    "Africa/Tunis",
+    "Africa/Windhoek",
+    "America/Adak",
+    "America/Anchorage",
+    "America/Anguilla",
+    "America/Antigua",
+    "America/Araguaina",
+    "America/Aruba",
+    "America/Asuncion",
+    "America/Atikokan",
+    "America/Atka",
+    "America/Bahia",
+    "America/Barbados",
+    "America/Belem",
+    "America/Belize",
+    "America/Boa_Vista",
+    "America/Bogota",
+    "America/Boise",
+    "America/Buenos_Aires",
+    "America/Cambridge_Bay",
+    "America/Campo_Grande",
+    "America/Cancun",
+    "America/Caracas",
+    "America/Catamarca",
+    "America/Cayenne",
+    "America/Cayman",
+    "America/Chicago",
+    "America/Chihuahua",
+    "America/Coral_Harbour",
+    "America/Cordoba",
+    "America/Costa_Rica",
+    "America/Cuiaba",
+    "America/Curacao",
+    "America/Danmarkshavn",
+    "America/Dawson",
+    "America/Dawson_Creek",
+    "America/Denver",
+    "America/Detroit",
+    "America/Dominica",
+    "America/Edmonton",
+    "America/Eirunepe",
+    "America/El_Salvador",
+    "America/Ensenada",
+    "America/Fort_Wayne",
+    "America/Fortaleza",
+    "America/Glace_Bay",
+    "America/Godthab",
+    "America/Goose_Bay",
+    "America/Grand_Turk",
+    "America/Grenada",
+    "America/Guadeloupe",
+    "America/Guatemala",
+    "America/Guayaquil",
+    "America/Guyana",
+    "America/Halifax",
+    "America/Havana",
+    "America/Hermosillo",
+    "America/Indianapolis",
+    "America/Inuvik",
+    "America/Iqaluit",
+    "America/Jamaica",
+    "America/Jujuy",
+    "America/Juneau",
+    "America/Knox_IN",
+    "America/La_Paz",
+    "America/Lima",
+    "America/Los_Angeles",
+    "America/Louisville",
+    "America/Maceio",
+    "America/Managua",
+    "America/Manaus",
+    "America/Marigot",
+    "America/Martinique",
+    "America/Mazatlan",
+    "America/Mendoza",
+    "America/Menominee",
+    "America/Merida",
+    "America/Mexico_City",
+    "America/Miquelon",
+    "America/Moncton",
+    "America/Monterrey",
+    "America/Montevideo",
+    "America/Montreal",
+    "America/Montserrat",
+    "America/Nassau",
+    "America/New_York",
+    "America/Nipigon",
+    "America/Nome",
+    "America/Noronha",
+    "America/Panama",
+    "America/Pangnirtung",
+    "America/Paramaribo",
+    "America/Phoenix",
+    "America/Port_of_Spain",
+    "America/Porto_Acre",
+    "America/Porto_Velho",
+    "America/Puerto_Rico",
+    "America/Rainy_River",
+    "America/Rankin_Inlet",
+    "America/Recife",
+    "America/Regina",
+    "America/Resolute",
+    "America/Rio_Branco",
+    "America/Rosario",
+    "America/Santarem",
+    "America/Santiago",
+    "America/Santo_Domingo",
+    "America/Sao_Paulo",
+    "America/Scoresbysund",
+    "America/Shiprock",
+    "America/St_Barthelemy",
+    "America/St_Johns",
+    "America/St_Kitts",
+    "America/St_Lucia",
+    "America/St_Thomas",
+    "America/St_Vincent",
+    "America/Swift_Current",
+    "America/Tegucigalpa",
+    "America/Thule",
+    "America/Thunder_Bay",
+    "America/Tijuana",
+    "America/Toronto",
+    "America/Tortola",
+    "America/Vancouver",
+    "America/Virgin",
+    "America/Whitehorse",
+    "America/Winnipeg",
+    "America/Yakutat",
+    "America/Yellowknife",
+    "Antarctica/Casey",
+    "Antarctica/Davis",
+    "Antarctica/DumontDUrville",
+    "Antarctica/Mawson",
+    "Antarctica/McMurdo",
+    "Antarctica/Palmer",
+    "Antarctica/Rothera",
+    "Antarctica/South_Pole",
+    "Antarctica/Syowa",
+    "Antarctica/Vostok",
+    "Arctic/Longyearbyen",
+    "Asia/Aden",
+    "Asia/Almaty",
+    "Asia/Amman",
+    "Asia/Anadyr",
+    "Asia/Aqtau",
+    "Asia/Aqtobe",
+    "Asia/Ashgabat",
+    "Asia/Ashkhabad",
+    "Asia/Baghdad",
+    "Asia/Bahrain",
+    "Asia/Baku",
+    "Asia/Bangkok",
+    "Asia/Beirut",
+    "Asia/Bishkek",
+    "Asia/Brunei",
+    "Asia/Calcutta",
+    "Asia/Choibalsan",
+    "Asia/Chongqing",
+    "Asia/Chungking",
+    "Asia/Colombo",
+    "Asia/Dacca",
+    "Asia/Damascus",
+    "Asia/Dhaka",
+    "Asia/Dili",
+    "Asia/Dubai",
+    "Asia/Dushanbe",
+    "Asia/Gaza",
+    "Asia/Harbin",
+    "Asia/Ho_Chi_Minh",
+    "Asia/Hong_Kong",
+    "Asia/Hovd",
+    "Asia/Irkutsk",
+    "Asia/Istanbul",
+    "Asia/Jakarta",
+    "Asia/Jayapura",
+    "Asia/Jerusalem",
+    "Asia/Kabul",
+    "Asia/Kamchatka",
+    "Asia/Karachi",
+    "Asia/Kashgar",
+    "Asia/Kathmandu",
+    "Asia/Katmandu",
+    "Asia/Kolkata",
+    "Asia/Krasnoyarsk",
+    "Asia/Kuala_Lumpur",
+    "Asia/Kuching",
+    "Asia/Kuwait",
+    "Asia/Macao",
+    "Asia/Macau",
+    "Asia/Magadan",
+    "Asia/Makassar",
+    "Asia/Manila",
+    "Asia/Muscat",
+    "Asia/Nicosia",
+    "Asia/Novosibirsk",
+    "Asia/Omsk",
+    "Asia/Oral",
+    "Asia/Phnom_Penh",
+    "Asia/Pontianak",
+    "Asia/Pyongyang",
+    "Asia/Qatar",
+    "Asia/Qyzylorda",
+    "Asia/Rangoon",
+    "Asia/Riyadh",
+    "Asia/Saigon",
+    "Asia/Sakhalin",
+    "Asia/Samarkand",
+    "Asia/Seoul",
+    "Asia/Shanghai",
+    "Asia/Singapore",
+    "Asia/Taipei",
+    "Asia/Tashkent",
+    "Asia/Tbilisi",
+    "Asia/Tehran",
+    "Asia/Tel_Aviv",
+    "Asia/Thimbu",
+    "Asia/Thimphu",
+    "Asia/Tokyo",
+    "Asia/Ujung_Pandang",
+    "Asia/Ulaanbaatar",
+    "Asia/Ulan_Bator",
+    "Asia/Urumqi",
+    "Asia/Vientiane",
+    "Asia/Vladivostok",
+    "Asia/Yakutsk",
+    "Asia/Yekaterinburg",
+    "Asia/Yerevan",
+    "Atlantic/Azores",
+    "Atlantic/Bermuda",
+    "Atlantic/Canary",
+    "Atlantic/Cape_Verde",
+    "Atlantic/Faeroe",
+    "Atlantic/Faroe",
+    "Atlantic/Jan_Mayen",
+    "Atlantic/Madeira",
+    "Atlantic/Reykjavik",
+    "Atlantic/South_Georgia",
+    "Atlantic/St_Helena",
+    "Atlantic/Stanley",
+    "Australia/ACT",
+    "Australia/Adelaide",
+    "Australia/Brisbane",
+    "Australia/Broken_Hill",
+    "Australia/Canberra",
+    "Australia/Currie",
+    "Australia/Darwin",
+    "Australia/Eucla",
+    "Australia/Hobart",
+    "Australia/LHI",
+    "Australia/Lindeman",
+    "Australia/Lord_Howe",
+    "Australia/Melbourne",
+    "Australia/North",
+    "Australia/NSW",
+    "Australia/Perth",
+    "Australia/Queensland",
+    "Australia/South",
+    "Australia/Sydney",
+    "Australia/Tasmania",
+    "Australia/Victoria",
+    "Australia/West",
+    "Australia/Yancowinna",
+    "Brazil/Acre",
+    "Brazil/DeNoronha",
+    "Brazil/East",
+    "Brazil/West",
+    "Canada/Atlantic",
+    "Canada/Central",
+    "Canada/Eastern",
+    "Canada/Mountain",
+    "Canada/Newfoundland",
+    "Canada/Pacific",
+    "Canada/Saskatchewan",
+    "Canada/Yukon",
+    "CET",
+    "Chile/Continental",
+    "Chile/EasterIsland",
+    "CST6CDT",
+    "Cuba",
+    "EET",
+    "Egypt",
+    "Eire",
+    "EST",
+    "EST5EDT",
+    "Etc/GMT",
+    "Etc/GMT0",
+    "Etc/Greenwich",
+    "Etc/UCT",
+    "Etc/Universal",
+    "Etc/UTC",
+    "Etc/Zulu",
+    "Europe/Amsterdam",
+    "Europe/Andorra",
+    "Europe/Athens",
+    "Europe/Belfast",
+    "Europe/Belgrade",
+    "Europe/Berlin",
+    "Europe/Bratislava",
+    "Europe/Brussels",
+    "Europe/Bucharest",
+    "Europe/Budapest",
+    "Europe/Chisinau",
+    "Europe/Copenhagen",
+    "Europe/Dublin",
+    "Europe/Gibraltar",
+    "Europe/Guernsey",
+    "Europe/Helsinki",
+    "Europe/Isle_of_Man",
+    "Europe/Istanbul",
+    "Europe/Jersey",
+    "Europe/Kaliningrad",
+    "Europe/Kiev",
+    "Europe/Lisbon",
+    "Europe/Ljubljana",
+    "Europe/London",
+    "Europe/Luxembourg",
+    "Europe/Madrid",
+    "Europe/Malta",
+    "Europe/Mariehamn",
+    "Europe/Minsk",
+    "Europe/Monaco",
+    "Europe/Moscow",
+    "Europe/Nicosia",
+    "Europe/Oslo",
+    "Europe/Paris",
+    "Europe/Podgorica",
+    "Europe/Prague",
+    "Europe/Riga",
+    "Europe/Rome",
+    "Europe/Samara",
+    "Europe/San_Marino",
+    "Europe/Sarajevo",
+    "Europe/Simferopol",
+    "Europe/Skopje",
+    "Europe/Sofia",
+    "Europe/Stockholm",
+    "Europe/Tallinn",
+    "Europe/Tirane",
+    "Europe/Tiraspol",
+    "Europe/Uzhgorod",
+    "Europe/Vaduz",
+    "Europe/Vatican",
+    "Europe/Vienna",
+    "Europe/Vilnius",
+    "Europe/Volgograd",
+    "Europe/Warsaw",
+    "Europe/Zagreb",
+    "Europe/Zaporozhye",
+    "Europe/Zurich",
+    "Factory",
+    "GB",
+    "GMT",
+    "GMT0",
+    "Greenwich",
+    "Hongkong",
+    "HST",
+    "Iceland",
+    "Indian/Antananarivo",
+    "Indian/Chagos",
+    "Indian/Christmas",
+    "Indian/Cocos",
+    "Indian/Comoro",
+    "Indian/Kerguelen",
+    "Indian/Mahe",
+    "Indian/Maldives",
+    "Indian/Mauritius",
+    "Indian/Mayotte",
+    "Indian/Reunion",
+    "Iran",
+    "Israel",
+    "Jamaica",
+    "Japan",
+    "Kwajalein",
+    "Libya",
+    "MET",
+    "Mexico/BajaNorte",
+    "Mexico/BajaSur",
+    "Mexico/General",
+    "MST",
+    "MST7MDT",
+    "Navajo",
+    "NZ",
+    "Pacific/Apia",
+    "Pacific/Auckland",
+    "Pacific/Chatham",
+    "Pacific/Easter",
+    "Pacific/Efate",
+    "Pacific/Enderbury",
+    "Pacific/Fakaofo",
+    "Pacific/Fiji",
+    "Pacific/Funafuti",
+    "Pacific/Galapagos",
+    "Pacific/Gambier",
+    "Pacific/Guadalcanal",
+    "Pacific/Guam",
+    "Pacific/Honolulu",
+    "Pacific/Johnston",
+    "Pacific/Kiritimati",
+    "Pacific/Kosrae",
+    "Pacific/Kwajalein",
+    "Pacific/Majuro",
+    "Pacific/Marquesas",
+    "Pacific/Midway",
+    "Pacific/Nauru",
+    "Pacific/Niue",
+    "Pacific/Norfolk",
+    "Pacific/Noumea",
+    "Pacific/Pago_Pago",
+    "Pacific/Palau",
+    "Pacific/Pitcairn",
+    "Pacific/Ponape",
+    "Pacific/Port_Moresby",
+    "Pacific/Rarotonga",
+    "Pacific/Saipan",
+    "Pacific/Samoa",
+    "Pacific/Tahiti",
+    "Pacific/Tarawa",
+    "Pacific/Tongatapu",
+    "Pacific/Truk",
+    "Pacific/Wake",
+    "Pacific/Wallis",
+    "Pacific/Yap",
+    "Poland",
+    "Portugal",
+    "PRC",
+    "PST8PDT",
+    "ROC",
+    "ROK",
+    "Singapore",
+    "Turkey",
+    "UCT",
+    "Universal",
+    "US/Alaska",
+    "US/Aleutian",
+    "US/Arizona",
+    "US/Central",
+    "US/Eastern",
+    "US/Hawaii",
+    "US/Michigan",
+    "US/Mountain",
+    "US/Pacific",
+    "US/Samoa",
+    "UTC",
+    "WET",
+    "Zulu"
+);
diff --git a/utils/upgrade_db.php b/utils/upgrade_db.php
new file mode 100644 (file)
index 0000000..3c30d02
--- /dev/null
@@ -0,0 +1,23 @@
+<?php
+/* $Id$ */
+require_once 'config.inc.php';
+require_once 'user_validate.php';
+require_once 'helper.php';
+
+valid_user();
+
+$root = WEB_ROOT;
+
+if (! defined($_SESSION['__ROOT__']) && empty($_SESSION['__ROOT__'])) {
+    session_destroy();
+    header('Location: ' . WEB_ROOT . 'error.html');
+    exit;
+}
+
+if (strtoupper($_SESSION['user_settings']->getUid()) == 'ADMIN' && newUpdates()) {
+    upgrade();
+}
+
+header('Location: ' . getServerUrl($root) . '/index.php');
+exit;
+?>
diff --git a/utils/users.php b/utils/users.php
new file mode 100644 (file)
index 0000000..09daa68
--- /dev/null
@@ -0,0 +1,121 @@
+<?php
+/* $Id$ */
+require_once 'config.inc.php';
+require_once 'user_validate.php';
+require_once 'helper.php';
+
+valid_user();
+
+if (! defined($_SESSION['__ROOT__']) && empty($_SESSION['__ROOT__'])) {
+    if (session_id())
+        session_destroy();
+    header('Location: ' . WEB_ROOT . 'error.html');
+    exit;
+}
+
+if (! has_admin_role()) {
+    if (session_id())
+        session_destroy();
+    header('Location: ' . WEB_ROOT . 'error.html');
+    exit;
+}
+
+require_once 'persistens.php';
+
+include TOP_FOLDER.'/include/header.inc.php';
+include TOP_FOLDER.'/include/menu.inc.php';
+$self =$_SERVER['PHP_SELF'];
+$db = Persistens::getInstance(DBDRIVER);
+
+if (count($_POST) > 0 && isset($_POST['action'])) {
+
+    $action = strtolower($_POST['action']);
+    switch ($action) {
+        case 'delete':
+            $error = '';
+            if (isset($_POST['uid']) && ! empty($_POST['uid'])) {
+                foreach ($_POST['uid'] as $uid) {
+                    $result = $db->deleteUser($uid);
+                    if ($result !== TRUE) {
+                        $error .= "$result<br/>";
+                    }
+                }
+            }
+            else
+                $error = 'No user selected for deletion';
+            if ($error == '')
+                $result = "Users was succesfully delete";
+            else
+                $result = $error;
+            break;
+        case 'reset password':
+            $res = '';
+            $error = '';
+            if (isset($_POST['uid']) && ! empty($_POST['uid'])) {
+                foreach ($_POST['uid'] as $uid) {
+                    $pwd = substr(md5($_POST['uid'] . microtime()), 0, 8);
+                    $result = $db->changePassword($uid, sha1($pwd));
+                    if ($result !== TRUE)
+                        $res .= $_POST['uid'] .": No change<br/>";
+                    else
+                        $res .= $uid .": $pwd<br/>";
+                }
+            }
+            else
+                $error = 'No user selected for resetting password';
+            if ($error == '')
+                $result = $res;
+            else
+                $result = $error;
+            break;
+        default:
+            $result = "$action: Unknown action";
+            break;
+    }
+    $pageView .= <<<__EOF
+<p style="text-align: center">
+    $result
+</p>
+__EOF;
+}
+else {
+    $pageView = <<<__EOF
+    <form action="$self" method="post" id="form">
+    <p>
+        <table class="config">
+            <tr>
+                <td colspan="3" style="text-align: center;
+                    font-weight: bold;font-size: 2.0em;">
+                    Manage users
+                </td>
+            </tr>
+            <tr>
+                <th>Username</th><th>Role</th><th>&nbsp;</th>
+            </tr>
+__EOF;
+    $users = $db->getAllUsers();
+    foreach ($users as $user) {
+        if (strtoupper($user['uid']) == 'ADMIN') {
+            $pageView .= "<tr><td>{$user['uid']}</td><td>{$user['userrole']}</td>
+            <td>&nbsp;</td>
+            </tr>";
+        }
+        else {
+            $pageView .= "<tr><td>{$user['uid']}</td><td>{$user['userrole']}</td>
+            <td style=\"text-align: center\"><input type=\"checkbox\" name=\"uid[]\"
+            value=\"{$user['uid']}\"/></td></tr>";
+        }
+    }
+    $pageView .= '<tr><td class="config" colspan="3" style="text-align: center">
+                    <input type="submit" name="action" value="Delete"/>
+                    <input type="button" value="New user"
+                        onclick="document.location.href=\'newuser.php\'" />
+                    <input type="submit" name="action" value="Reset password" />
+                </td></tr></table></p></form>';
+}
+
+print "<div id=\"ui\">$pageView</div>";
+
+include TOP_FOLDER.'/include/footer.inc.php';
+
+?>
This page took 1.473662 seconds and 5 git commands to generate.