]> err.no Git - scalable-opengroupware.org/commitdiff
git-svn-id: http://svn.opengroupware.org/SOGo/inverse/trunk@1013 d1b88da0-ebda-0310...
authorwolfgang <wolfgang@d1b88da0-ebda-0310-925b-ed51d893ca5b>
Wed, 31 Jan 2007 17:26:13 +0000 (17:26 +0000)
committerwolfgang <wolfgang@d1b88da0-ebda-0310-925b-ed51d893ca5b>
Wed, 31 Jan 2007 17:26:13 +0000 (17:26 +0000)
152 files changed:
ChangeLog
GNUmakefile
Main/GNUmakefile.preamble
OGoContentStore/GNUmakefile
OGoContentStore/GNUmakefile.preamble
OGoContentStore/sql/folderinfo-create.psql
OGoContentStore/sql/generate-folderinfo-sql-for-users.sh
Protocols/common.make
SOPE/NGCards/COPYING [new file with mode: 0644]
SOPE/NGCards/COPYRIGHT [new file with mode: 0644]
SOPE/NGCards/CardElement.h [new file with mode: 0644]
SOPE/NGCards/CardElement.m [new file with mode: 0644]
SOPE/NGCards/CardGroup+iCal.h [new file with mode: 0644]
SOPE/NGCards/CardGroup+iCal.m [new file with mode: 0644]
SOPE/NGCards/CardGroup.h [new file with mode: 0644]
SOPE/NGCards/CardGroup.m [new file with mode: 0644]
SOPE/NGCards/CardVersitRenderer.h [new file with mode: 0644]
SOPE/NGCards/CardVersitRenderer.m [new file with mode: 0644]
SOPE/NGCards/ChangeLog [new file with mode: 0644]
SOPE/NGCards/GNUmakefile [new file with mode: 0644]
SOPE/NGCards/GNUmakefile.postamble [new file with mode: 0644]
SOPE/NGCards/GNUmakefile.preamble [new file with mode: 0644]
SOPE/NGCards/IcalElements.m [new file with mode: 0644]
SOPE/NGCards/IcalResponse.h [new file with mode: 0644]
SOPE/NGCards/IcalResponse.m [new file with mode: 0644]
SOPE/NGCards/NGCards-Info.plist [new file with mode: 0644]
SOPE/NGCards/NGCards.h [new file with mode: 0644]
SOPE/NGCards/NGCards.xcodeproj/project.pbxproj [new file with mode: 0644]
SOPE/NGCards/NGCards.xmap [new file with mode: 0644]
SOPE/NGCards/NGCardsSaxHandler.h [new file with mode: 0644]
SOPE/NGCards/NGCardsSaxHandler.m [new file with mode: 0644]
SOPE/NGCards/NGVCard.h [new file with mode: 0644]
SOPE/NGCards/NGVCard.m [new file with mode: 0644]
SOPE/NGCards/NSArray+NGCards.h [new file with mode: 0644]
SOPE/NGCards/NSArray+NGCards.m [new file with mode: 0644]
SOPE/NGCards/NSCalendarDate+ICal.h [new file with mode: 0644]
SOPE/NGCards/NSCalendarDate+ICal.m [new file with mode: 0644]
SOPE/NGCards/NSCalendarDate+NGCards.h [new file with mode: 0644]
SOPE/NGCards/NSCalendarDate+NGCards.m [new file with mode: 0644]
SOPE/NGCards/NSDictionary+NGCards.h [new file with mode: 0644]
SOPE/NGCards/NSDictionary+NGCards.m [new file with mode: 0644]
SOPE/NGCards/NSString+NGCards.h [new file with mode: 0644]
SOPE/NGCards/NSString+NGCards.m [new file with mode: 0644]
SOPE/NGCards/README [new file with mode: 0644]
SOPE/NGCards/Version [new file with mode: 0644]
SOPE/NGCards/common.h [new file with mode: 0644]
SOPE/NGCards/iCalAlarm.h [new file with mode: 0644]
SOPE/NGCards/iCalAlarm.m [new file with mode: 0644]
SOPE/NGCards/iCalAttachment.h [new file with mode: 0644]
SOPE/NGCards/iCalAttachment.m [new file with mode: 0644]
SOPE/NGCards/iCalCalendar.h [new file with mode: 0644]
SOPE/NGCards/iCalCalendar.m [new file with mode: 0644]
SOPE/NGCards/iCalDailyRecurrenceCalculator.m [new file with mode: 0644]
SOPE/NGCards/iCalDataSource.h [new file with mode: 0644]
SOPE/NGCards/iCalDataSource.m [new file with mode: 0644]
SOPE/NGCards/iCalDateHolder.h [new file with mode: 0644]
SOPE/NGCards/iCalDateHolder.m [new file with mode: 0644]
SOPE/NGCards/iCalDateTime.h [new file with mode: 0644]
SOPE/NGCards/iCalDateTime.m [new file with mode: 0644]
SOPE/NGCards/iCalEntityObject.h [new file with mode: 0644]
SOPE/NGCards/iCalEntityObject.m [new file with mode: 0644]
SOPE/NGCards/iCalEvent.h [new file with mode: 0644]
SOPE/NGCards/iCalEvent.m [new file with mode: 0644]
SOPE/NGCards/iCalEventChanges.h [new file with mode: 0644]
SOPE/NGCards/iCalEventChanges.m [new file with mode: 0644]
SOPE/NGCards/iCalFreeBusy.h [new file with mode: 0644]
SOPE/NGCards/iCalFreeBusy.m [new file with mode: 0644]
SOPE/NGCards/iCalJournal.h [new file with mode: 0644]
SOPE/NGCards/iCalJournal.m [new file with mode: 0644]
SOPE/NGCards/iCalMonthlyRecurrenceCalculator.m [new file with mode: 0644]
SOPE/NGCards/iCalObject.h [new file with mode: 0644]
SOPE/NGCards/iCalObject.m [new file with mode: 0644]
SOPE/NGCards/iCalPerson.h [new file with mode: 0644]
SOPE/NGCards/iCalPerson.m [new file with mode: 0644]
SOPE/NGCards/iCalRecurrenceCalculator.h [new file with mode: 0644]
SOPE/NGCards/iCalRecurrenceCalculator.m [new file with mode: 0644]
SOPE/NGCards/iCalRecurrenceRule.h [new file with mode: 0644]
SOPE/NGCards/iCalRecurrenceRule.m [new file with mode: 0644]
SOPE/NGCards/iCalRepeatableEntityObject.h [new file with mode: 0644]
SOPE/NGCards/iCalRepeatableEntityObject.m [new file with mode: 0644]
SOPE/NGCards/iCalTimeZone.h [new file with mode: 0644]
SOPE/NGCards/iCalTimeZone.m [new file with mode: 0644]
SOPE/NGCards/iCalTimeZonePeriod.h [new file with mode: 0644]
SOPE/NGCards/iCalTimeZonePeriod.m [new file with mode: 0644]
SOPE/NGCards/iCalToDo.h [new file with mode: 0644]
SOPE/NGCards/iCalToDo.m [new file with mode: 0644]
SOPE/NGCards/iCalTrigger.h [new file with mode: 0644]
SOPE/NGCards/iCalTrigger.m [new file with mode: 0644]
SOPE/NGCards/iCalWeeklyRecurrenceCalculator.m [new file with mode: 0644]
SOPE/NGCards/iCalYearlyRecurrenceCalculator.m [new file with mode: 0644]
SOPE/NGCards/samples/COPYING [new file with mode: 0644]
SOPE/NGCards/samples/ChangeLog [new file with mode: 0644]
SOPE/NGCards/samples/GNUmakefile [new file with mode: 0644]
SOPE/NGCards/samples/GNUmakefile.preamble [new file with mode: 0644]
SOPE/NGCards/samples/README [new file with mode: 0644]
SOPE/NGCards/samples/common.h [new file with mode: 0644]
SOPE/NGCards/samples/fhs.make [new file with mode: 0644]
SOPE/NGCards/samples/icalds.m [new file with mode: 0644]
SOPE/NGCards/samples/icalparsetest.m [new file with mode: 0644]
SOPE/NGCards/samples/ievalrrule.m [new file with mode: 0644]
SOPE/NGCards/samples/unittest.h [new file with mode: 0644]
SOPE/NGCards/samples/unittest.m [new file with mode: 0644]
SOPE/NGCards/samples/vcardtest.m [new file with mode: 0644]
SOPE/NGCards/samples/vcf2xml.m [new file with mode: 0644]
SOPE/NGCards/samples/vcfparsetest.m [new file with mode: 0644]
SOPE/NGCards/samples/versittest.m [new file with mode: 0644]
SOPE/NGCards/tests/NGiCalTests-Info.plist [new file with mode: 0644]
SOPE/NGCards/tests/README [new file with mode: 0644]
SOPE/NGCards/tests/common.h [new file with mode: 0644]
SOPE/NGCards/tests/iCalRecurrenceCalculatorTests.m [new file with mode: 0644]
SOPE/NGCards/versitCardsSaxDriver/AUTHORS [new file with mode: 0644]
SOPE/NGCards/versitCardsSaxDriver/COPYING [new file with mode: 0644]
SOPE/NGCards/versitCardsSaxDriver/COPYRIGHT [new file with mode: 0644]
SOPE/NGCards/versitCardsSaxDriver/ChangeLog [new file with mode: 0644]
SOPE/NGCards/versitCardsSaxDriver/GNUmakefile [new file with mode: 0644]
SOPE/NGCards/versitCardsSaxDriver/GNUmakefile.postamble [new file with mode: 0644]
SOPE/NGCards/versitCardsSaxDriver/GNUmakefile.preamble [new file with mode: 0644]
SOPE/NGCards/versitCardsSaxDriver/README [new file with mode: 0644]
SOPE/NGCards/versitCardsSaxDriver/VSCardSaxDriver.h [new file with mode: 0644]
SOPE/NGCards/versitCardsSaxDriver/VSCardSaxDriver.m [new file with mode: 0644]
SOPE/NGCards/versitCardsSaxDriver/VSSaxDriver.h [new file with mode: 0644]
SOPE/NGCards/versitCardsSaxDriver/VSSaxDriver.m [new file with mode: 0644]
SOPE/NGCards/versitCardsSaxDriver/VSStringFormatter.h [new file with mode: 0644]
SOPE/NGCards/versitCardsSaxDriver/VSStringFormatter.m [new file with mode: 0644]
SOPE/NGCards/versitCardsSaxDriver/Version [new file with mode: 0644]
SOPE/NGCards/versitCardsSaxDriver/bundle-info.plist [new file with mode: 0644]
SOPE/NGCards/versitCardsSaxDriver/common.h [new file with mode: 0644]
SOPE/NGCards/versitCardsSaxDriver/fhs.make [new file with mode: 0644]
SOPE/NGCards/versitCardsSaxDriver/versitCardsSaxDriver-Info.plist [new file with mode: 0644]
SOPE/NGCards/versitCardsSaxDriver/versitCardsSaxDriver.xcodeproj/project.pbxproj [new file with mode: 0644]
SoObjects/Appointments/SOGoAppointmentObject.h
SoObjects/Appointments/SOGoAppointmentObject.m
SoObjects/Appointments/SOGoCalendarComponent.h
SoObjects/Appointments/SOGoCalendarComponent.m
SoObjects/Appointments/SOGoTaskObject.h
SoObjects/GNUmakefile
SoObjects/SOGo/NSString+Utilities.h
SoObjects/SOGo/NSString+Utilities.m
SoObjects/SOGo/SOGoAuthenticator.m
SoObjects/common.make
UI/MailerUI/UIxMailEditorAction.m
UI/MailerUI/UIxMailListView.m
UI/SOGoUI/UIxComponent.m
UI/Scheduler/UIxCalCalendarsListView.m
UI/Templates/MailerUI/UIxMailEditor.wox
UI/WebServerResources/MailerUI.js
UI/WebServerResources/SOGoDragAndDrop.js
UI/WebServerResources/SchedulerUI.css
UI/WebServerResources/SchedulerUI.js
UI/WebServerResources/generic.css
UI/WebServerResources/generic.js
UI/common.make

index 10d2f7a77af977bddca0565b3bbefcb4c4ecf10b..e236403f15bc01e8467c8846b4751c0840bcc643 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,18 @@
+2007-01-30  Wolfgang Sourdeau  <wsourdeau@inverse.ca>
+
+       * UI/MailerUI/UIxMailListView.m ([UIxMailListView
+       -defaultAction]): invoke "flushMailCaches" on [self clientObject]
+       so that the mailbox cache is refreshed everytime the user presses
+       on "get mail". I doubt a mailbox cache really is useful in general
+       anyway.
+
+2007-01-26  Wolfgang Sourdeau  <wsourdeau@inverse.ca>
+
+       * SoObjects/SOGo/SOGoAuthenticator.m ([SOGoAuthenticator
+       -userInContext:]): if the lookup is on "freebusy.ifb" and the
+       username is "anonymous" the user is automatically set to
+       "freebusy".
+
 2007-01-12  Wolfgang Sourdeau  <wsourdeau@inverse.ca>
 
        * SoObjects/SOGo/SOGoAuthenticator.m ([SOGoAuthenticator
index def68eaa8c23ba6fb818d29abddb63d56f5a0456..75961d3065c5194854582c73034085879f0e1150 100644 (file)
@@ -4,6 +4,7 @@
 include $(GNUSTEP_MAKEFILES)/common.make
 
 SUBPROJECTS = \
+       SOPE/NGCards \
        OGoContentStore \
        SoObjects       \
        Main            \
@@ -11,4 +12,3 @@ SUBPROJECTS = \
        Protocols       \
 
 include $(GNUSTEP_MAKEFILES)/aggregate.make
-
index 99bdcff367e5d321db5e9faf7b7db96acca8e85c..9938d4af2d4870433f8bf8be9c0dac865f9b2047 100644 (file)
@@ -11,7 +11,8 @@ ADDITIONAL_INCLUDE_DIRS += \
 
 ADDITIONAL_LIB_DIRS += \
        -L../SoObjects/SOGo/$(GNUSTEP_OBJ_DIR)/ \
-       -L../OGoContentStore/$(GNUSTEP_OBJ_DIR)/
+       -L../OGoContentStore/$(GNUSTEP_OBJ_DIR)/ \
+       -L../SOPE/NGCards/$(GNUSTEP_OBJ_DIR)/
 
 SYSTEM_LIB_DIR += -L/usr/local/lib -L/usr/lib
 
index 1b09057cf6beed51fbeab04a804dd240f23ed265..5807b690d3e488dc1a0bc3c4949d32290e78f5f1 100644 (file)
@@ -7,7 +7,7 @@ include ./Version
 LIBRARY_NAME = libOGoContentStore
 TOOL_NAME    = test_quick_extract
 
-TYPEMODELS_DIR = $(GNUSTEP_USER_ROOT)/Library/OCSTypeModels/
+TYPEMODELS_DIR = $(GNUSTEP_SYSTEM_ROOT)/Library/OCSTypeModels/
 
 libOGoContentStore_HEADER_FILES_DIR         = .
 libOGoContentStore_HEADER_FILES_INSTALL_DIR = /OGoContentStore
index faf4e378bde64ea8a969904ad81f09e75088192d..e39140e3f58e565fce0169173fb65a0a53c54183 100644 (file)
@@ -8,9 +8,9 @@ libOGoContentStore_LIBRARIES_DEPEND_UPON += \
        -lEOControl     \
        -lSaxObjC
 
-ADDITIONAL_INCLUDE_DIRS += -I. -I..
+ADDITIONAL_INCLUDE_DIRS += -I. -I.. -I../SOPE
 
-ADDITIONAL_LIB_DIRS += -L./$(GNUSTEP_OBJ_DIR)
+ADDITIONAL_LIB_DIRS += -L./$(GNUSTEP_OBJ_DIR) -L../SOPE/NGCards/$(GNUSTEP_OBJ_DIR)/
 
 SYSTEM_LIB_DIR += -L/usr/local/lib -L/usr/lib
 
index e4a499eaadae6ef09944f7d28a60fdf2edf99aba..30559779737eac96c1667f1b4a0d8e5dc71bd98f 100644 (file)
@@ -4,6 +4,8 @@
 -- TODO:
 --   add a unique constraints on path
 
+DROP SEQUENCE SOGo_folder_info_seq;
+
 CREATE SEQUENCE SOGo_folder_info_seq;
 
 DROP TABLE SOGo_folder_info;
@@ -24,18 +26,3 @@ CREATE TABLE SOGo_folder_info (
   c_acl_location VARCHAR(2048) NULL,     -- URL to quicktable of folder
   c_folder_type    VARCHAR(255)  NOT NULL  -- the folder type ...
 );
-
-INSERT INTO SOGo_folder_info 
-  ( c_path, c_path1, c_path2, c_path3, c_path4, c_foldername,
-    c_location, c_quick_location, c_folder_type ) 
-VALUES 
-  ( '/Users', 
-    'Users',
-    NULL,
-    NULL,
-    NULL,
-    'Users', 
-    'http://OGo:OGo@localhost:5432/OGo/SOGo_user_folder', 
-    'http://OGo:OGo@localhost:5432/OGo/SOGo_user_folder_quick', 
-    'http://OGo:OGo@localhost:5432/OGo/SOGo_user_folder_acl', 
-    'Container' );
index ed2e01833ec83a786c0d903e918e61d5185d1274..2cff4355bd919e91fa5859a99ae78d0ea9f438fe 100755 (executable)
@@ -5,7 +5,7 @@
 
 DB_USER="sogo"
 DB_PASS="sogo"
-DB_HOST="192.168.0.4"
+DB_HOST="127.0.0.1"
 DB_PORT="5432"
 DB_NAME="sogo"
 TIMEZONE="Canada/Eastern"
index 0cc98888b3fb15db999e654143919e5cbe09f171..8d43914d605705427c485ff4f94e1102b93bb695 100644 (file)
@@ -12,14 +12,18 @@ ADDITIONAL_INCLUDE_DIRS += \
        -I..            \
        -I../..         \
        -I../../..      \
-       -I../../SoObjects
+       -I../../SoObjects \
+       -I../../SOPE
 
+RELBUILD_DIR_libNGCards = \
+       $(GNUSTEP_BUILD_DIR)/../../SOPE/NGCards/$(GNUSTEP_OBJ_DIR_NAME)
 RELBUILD_DIR_libSOGo = \
        $(GNUSTEP_BUILD_DIR)/../../SoObjects/SOGo/$(GNUSTEP_OBJ_DIR_NAME)
 RELBUILD_DIR_libOGoContentStore = \
        $(GNUSTEP_BUILD_DIR)/../../OGoContentStore/$(GNUSTEP_OBJ_DIR_NAME)
 
 ADDITIONAL_LIB_DIRS +=                                 \
+       -L$(RELBUILD_DIR_libNGCards)            \
        -L$(RELBUILD_DIR_libSOGo)               \
        -L$(RELBUILD_DIR_libOGoContentStore)
 
diff --git a/SOPE/NGCards/COPYING b/SOPE/NGCards/COPYING
new file mode 100644 (file)
index 0000000..161a3d1
--- /dev/null
@@ -0,0 +1,482 @@
+                 GNU LIBRARY GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1991 Free Software Foundation, Inc.
+ 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the library GPL.  It is
+ numbered 2 because it goes with version 2 of the ordinary GPL.]
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Library General Public License, applies to some
+specially designated Free Software Foundation software, and to any
+other libraries whose authors decide to use it.  You can use it for
+your libraries, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if
+you distribute copies of the library, or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link a program with the library, you must provide
+complete object files to the recipients so that they can relink them
+with the library, after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  Our method of protecting your rights has two steps: (1) copyright
+the library, and (2) offer you this license which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  Also, for each distributor's protection, we want to make certain
+that everyone understands that there is no warranty for this free
+library.  If the library is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original
+version, so that any problems introduced by others will not reflect on
+the original authors' reputations.
+\f
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that companies distributing free
+software will individually obtain patent licenses, thus in effect
+transforming the program into proprietary software.  To prevent this,
+we have made it clear that any patent must be licensed for everyone's
+free use or not licensed at all.
+
+  Most GNU software, including some libraries, is covered by the ordinary
+GNU General Public License, which was designed for utility programs.  This
+license, the GNU Library General Public License, applies to certain
+designated libraries.  This license is quite different from the ordinary
+one; be sure to read it in full, and don't assume that anything in it is
+the same as in the ordinary license.
+
+  The reason we have a separate public license for some libraries is that
+they blur the distinction we usually make between modifying or adding to a
+program and simply using it.  Linking a program with a library, without
+changing the library, is in some sense simply using the library, and is
+analogous to running a utility program or application program.  However, in
+a textual and legal sense, the linked executable is a combined work, a
+derivative of the original library, and the ordinary General Public License
+treats it as such.
+
+  Because of this blurred distinction, using the ordinary General
+Public License for libraries did not effectively promote software
+sharing, because most developers did not use the libraries.  We
+concluded that weaker conditions might promote sharing better.
+
+  However, unrestricted linking of non-free programs would deprive the
+users of those programs of all benefit from the free status of the
+libraries themselves.  This Library General Public License is intended to
+permit developers of non-free programs to use free libraries, while
+preserving your freedom as a user of such programs to change the free
+libraries that are incorporated in them.  (We have not seen how to achieve
+this as regards changes in header files, but we have achieved it as regards
+changes in the actual functions of the Library.)  The hope is that this
+will lead to faster development of free libraries.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, while the latter only
+works together with the library.
+
+  Note that it is possible for a library to be covered by the ordinary
+General Public License rather than by this special one.
+\f
+                 GNU LIBRARY GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library which
+contains a notice placed by the copyright holder or other authorized
+party saying it may be distributed under the terms of this Library
+General Public License (also called "this License").  Each licensee is
+addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+  
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+\f
+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+\f
+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+\f
+  6. As an exception to the Sections above, you may also compile or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Accompany the work with a written offer, valid for at
+    least three years, to give the same user the materials
+    specified in Subsection 6a, above, for a charge no more
+    than the cost of performing this distribution.
+
+    c) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    d) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the source code distributed need not include anything that is normally
+distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+\f
+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+\f
+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded.  In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Library General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+\f
+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+                           NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+\f
+     Appendix: How to Apply These Terms to Your New Libraries
+
+  If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change.  You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+  To apply these terms, attach the following notices to the library.  It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the library's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library 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
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public
+    License along with this library; if not, write to the Free
+    Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+    MA 02111-1307, USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the
+  library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+  <signature of Ty Coon>, 1 April 1990
+  Ty Coon, President of Vice
+
+That's all there is to it!
diff --git a/SOPE/NGCards/COPYRIGHT b/SOPE/NGCards/COPYRIGHT
new file mode 100644 (file)
index 0000000..70205ec
--- /dev/null
@@ -0,0 +1,4 @@
+Copyright (C) 2000-2005 SKYRIX Software AG
+
+
+Contact: info@skyrix.com
diff --git a/SOPE/NGCards/CardElement.h b/SOPE/NGCards/CardElement.h
new file mode 100644 (file)
index 0000000..d4fd031
--- /dev/null
@@ -0,0 +1,109 @@
+/* CardElement.h - this file is part of SOPE
+ *
+ * Copyright (C) 2006 Inverse groupe conseil
+ *
+ * Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This file 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; see the file COPYING.  If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef CARDELEMENT_H
+#define CARDELEMENT_H
+
+#import <Foundation/NSObject.h>
+
+@class NSArray;
+@class NSDictionary;
+@class NSMutableArray;
+@class NSMutableDictionary;
+@class NSString;
+
+@class CardGroup;
+
+@interface CardElement : NSObject <NSCopying>
+{
+  NSString *tag;
+  NSMutableArray *values;
+  NSMutableDictionary *attributes;
+  NSString *group;
+  CardGroup *parent;
+}
+
++ (id) elementWithTag: (NSString *) aTag;
+
++ (id) simpleElementWithTag: (NSString *) aTag
+                      value: (NSString *) aValue;
+
++ (id) simpleElementWithTag: (NSString *) aTag
+                 singleType: (NSString *) aType
+                      value: (NSString *) aValue;
+
++ (id) elementWithTag: (NSString *) aTag
+           attributes: (NSDictionary *) someAttributes
+               values: (NSArray *) someValues;
+
+- (void) setParent: (CardGroup *) aParent;
+- (CardGroup *) parent;
+
+- (void) setTag: (NSString *) aTag;
+
+- (void) setGroup: (NSString *) aGroup;
+- (NSString *) group;
+
+- (void) addValue: (NSString *) aValue;
+- (void) addValues: (NSArray *) someValues;
+
+- (void) setValue: (unsigned int) anInt
+               to: (NSString *) aValue;
+- (NSString *) value: (unsigned int) anInt;
+
+- (void) setNamedValue: (NSString *) aValueName
+                    to: (NSString *) aValue;
+- (NSString *) namedValue: (NSString *) aValueName;
+
+- (void) setValue: (unsigned int) anInt
+      ofAttribute: (NSString *) anAttribute
+               to: (NSString *) aValue;
+- (NSString *) value: (unsigned int) anInt
+         ofAttribute: (NSString *) anAttribute;
+
+- (void) addAttribute: (NSString *) anAttribute
+                value: (NSString *) aValue;
+- (void) addAttributes: (NSDictionary *) someAttributes;
+- (void) removeValue: (NSString *) aValue
+       fromAttribute: (NSString *) anAttribute;
+
+- (void) addType: (NSString *) aType;
+
+- (NSString *) tag;
+- (NSArray *) values;
+- (NSDictionary *) attributes;
+- (BOOL) hasAttribute: (NSString *) aType
+          havingValue: (NSString *) aValue;
+
+- (BOOL) isVoid;
+
+- (NSString *) versitString;
+
+- (CardGroup *) searchParentOfClass: (Class) parentClass;
+
+- (CardElement *) elementWithClass: (Class) elementClass;
+- (void) setValuesAsCopy: (NSMutableArray *) someValues;
+- (void) setAttributesAsCopy: (NSMutableDictionary *) someAttributes;
+
+@end
+
+#endif /* CARDELEMENT_H */
diff --git a/SOPE/NGCards/CardElement.m b/SOPE/NGCards/CardElement.m
new file mode 100644 (file)
index 0000000..4e85a8e
--- /dev/null
@@ -0,0 +1,500 @@
+/* CardElement.m - this file is part of SOPE
+ *
+ * Copyright (C) 2006 Inverse groupe conseil
+ *
+ * Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This file 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; see the file COPYING.  If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#import <Foundation/NSArray.h>
+#import <Foundation/NSDictionary.h>
+#import <Foundation/NSString.h>
+
+#import "NSArray+NGCards.h"
+#import "NSDictionary+NGCards.h"
+#import "CardVersitRenderer.h"
+
+#import "CardElement.h"
+#import "CardGroup.h"
+
+@implementation CardElement
+
++ (id) elementWithTag: (NSString *) aTag
+{
+  id newElement;
+
+  newElement = [self new];
+  [newElement autorelease];
+  [newElement setTag: aTag];
+
+  return newElement;
+}
+
++ (id) simpleElementWithTag: (NSString *) aTag
+                      value: (NSString *) aValue
+{
+  id newElement;
+
+  newElement = [self elementWithTag: aTag];
+  [newElement addValue: aValue];
+
+  return newElement;
+}
+
++ (id) simpleElementWithTag: (NSString *) aTag
+                 singleType: (NSString *) aType
+                      value: (NSString *) aValue
+{
+  id newElement;
+
+  newElement = [self simpleElementWithTag: aTag
+                     value: aValue];
+  [newElement addType: aType];
+
+  return newElement;
+}
+
++ (id) elementWithTag: (NSString *) aTag
+           attributes: (NSDictionary *) someAttributes
+               values: (NSArray *) someValues
+{
+  id newElement;
+
+  newElement = [self new];
+  [newElement autorelease];
+  [newElement setTag: aTag];
+  [newElement addAttributes: someAttributes];
+  [newElement addValues: someValues];
+
+  return newElement;
+}
+
+- (id) init
+{
+  if ((self = [super init]))
+    {
+      parent = nil;
+      tag = nil;
+      group = nil;
+      values = [NSMutableArray new];
+      attributes = [NSMutableDictionary new];
+    }
+
+  return self;
+}
+
+- (void) dealloc
+{
+  if (tag)
+    [tag release];
+  [values release];
+  [attributes release];
+  [super dealloc];
+}
+
+- (void) setParent: (CardGroup *) aParent
+{
+  parent = aParent;
+}
+
+- (CardGroup *) parent
+{
+  return parent;
+}
+
+- (void) setTag: (NSString *) aTag
+{
+  if (tag)
+    [tag release];
+  tag = aTag;
+  if (tag)
+    [tag retain];
+}
+
+- (void) setGroup: (NSString *) aGroup
+{
+  if (group)
+    [group release];
+  group = aGroup;
+  if (group)
+    [group retain];
+}
+
+- (NSString *) group
+{
+  return group;
+}
+
+- (void) addValue: (NSString *) aValue
+{
+  [values addObject: aValue];
+}
+
+- (void) addValues: (NSArray *) someValues
+{
+  [values addObjectsFromArray: someValues];
+}
+
+- (void) addAttribute: (NSString *) anAttribute
+                value: (NSString *) aType
+{
+  NSMutableArray *attrValues;
+
+  attrValues = [attributes objectForCaseInsensitiveKey: anAttribute];
+  if (!attrValues)
+    {
+      attrValues = [NSMutableArray new];
+      [attrValues autorelease];
+      [attributes setObject: attrValues forKey: anAttribute];
+    }
+
+  [attrValues addObject: aType];
+}
+
+- (void) removeValue: (NSString *) aValue
+       fromAttribute: (NSString *) anAttribute
+{
+  NSMutableArray *attrValues;
+  NSString *currentValue;
+
+  attrValues = [attributes objectForCaseInsensitiveKey: anAttribute];
+  if (attrValues)
+    {
+      currentValue = [attrValues valueForCaseInsensitiveString: aValue];
+      while (currentValue)
+        {
+          [attrValues removeObject: currentValue];
+          currentValue = [attrValues valueForCaseInsensitiveString: aValue];
+        }
+    }
+}
+
+- (void) addAttributes: (NSDictionary *) someAttributes
+{
+  NSEnumerator *keys;
+  NSString *currentKey;
+  NSMutableArray *oldValues;
+  NSArray *newValues;
+
+  keys = [[someAttributes allKeys] objectEnumerator];
+  currentKey = [keys nextObject];
+  while (currentKey)
+    {
+      oldValues = [attributes objectForCaseInsensitiveKey: currentKey];
+      newValues = [someAttributes objectForKey: currentKey];
+      if (oldValues)
+        [oldValues addObjectsFromArray: newValues];
+      else
+        [attributes setObject: newValues forKey: currentKey];
+      currentKey = [keys nextObject];
+    }
+}
+
+- (void) addType: (NSString *) aType
+{
+  [self addAttribute: @"type" value: aType];
+}
+
+- (NSString *) tag
+{
+  return tag;
+}
+
+- (NSArray *) values
+{
+  return values;
+}
+
+- (NSDictionary *) attributes
+{
+  return attributes;
+}
+
+- (BOOL) hasAttribute: (NSString *) anAttribute
+          havingValue: (NSString *) aValue
+{
+  NSArray *attribute;
+
+  attribute = [attributes objectForCaseInsensitiveKey: anAttribute];
+
+  return (attribute && [attribute hasCaseInsensitiveString: aValue]);
+}
+
+- (void) setValue: (unsigned int) anInt
+               to: (NSString *) aValue
+{
+  while ([values count] <= anInt)
+    [self addValue: @""];
+  [values replaceObjectAtIndex: anInt withObject: aValue];
+}
+
+- (NSString *) value: (unsigned int) anInt
+{
+  NSString *value;
+
+  if ([values count] <= anInt)
+    value = @"";
+  else
+    value = [values objectAtIndex: anInt];
+
+  return value;
+}
+
+- (unsigned int) _namedValue: (NSString *) aValueName
+{
+  NSString *prefix, *value;
+  unsigned int count, max, result;
+
+  result = NSNotFound;
+
+  prefix = [NSString stringWithFormat: @"%@=", [aValueName uppercaseString]];
+  max = [values count];
+  count = 0;
+
+  while (result == NSNotFound && count < max)
+    {
+      value = [[values objectAtIndex: count] uppercaseString];
+      if ([value hasPrefix: prefix])
+        result = count;
+      else
+        count++;
+    }
+
+  return result;
+}
+
+- (void) setNamedValue: (NSString *) aValueName
+                    to: (NSString *) aValue
+{
+  NSString *newValue;
+  unsigned int index;
+
+  newValue = [NSString stringWithFormat: @"%@=%@",
+                       [aValueName uppercaseString],
+                       aValue];
+  index = [self _namedValue: aValueName];
+  if (index == NSNotFound)
+    [self addValue: newValue];
+  else
+    {
+      if (aValue)
+        [self setValue: index to: newValue];
+      else
+        [values removeObjectAtIndex: index];
+    }
+}
+
+- (NSString *) namedValue: (NSString *) aValueName
+{
+  unsigned int index, equalSign;
+  NSString *value;
+
+  index = [self _namedValue: aValueName];
+  if (index == NSNotFound)
+    value = @"";
+  else
+    {
+      value = [values objectAtIndex: index];
+      equalSign = [value indexOfString: @"="];
+      if (equalSign != NSNotFound)
+        value = [value substringFromIndex: equalSign + 1];
+    }
+
+  return value;
+}
+
+- (void) setValue: (unsigned int) anInt
+      ofAttribute: (NSString *) anAttribute
+               to: (NSString *) aValue
+{
+  NSMutableArray *attrValues;
+
+  attrValues = [attributes objectForCaseInsensitiveKey: anAttribute];
+  if (!attrValues)
+    {
+      attrValues = [NSMutableArray new];
+      [attrValues autorelease];
+      [attributes setObject: attrValues forKey: anAttribute];
+    }
+
+  while ([attrValues count] <= anInt)
+    [attrValues addObject: @""];
+  [attrValues replaceObjectAtIndex: anInt
+              withObject: aValue];
+}
+
+- (NSString *) value: (unsigned int) anInt
+         ofAttribute: (NSString *) anAttribute
+{
+  NSArray *attrValues;
+  NSString *value;
+
+  attrValues = [attributes objectForCaseInsensitiveKey: anAttribute];
+  if (attrValues && [attrValues count] > anInt)
+    value = [attrValues objectAtIndex: anInt];
+  else
+    value = @"";
+
+  return value;
+}
+
+- (NSString *) description
+{
+  NSArray *attrs;
+  NSMutableString *str;
+  unsigned int count, max;
+  NSString *attr;
+
+  str = [NSMutableString stringWithCapacity:64];
+  [str appendFormat:@"<%p[%@]:", self, NSStringFromClass([self class])];
+  if (group)
+    [str appendFormat: @"%@ (group: %@)\n", tag, group];
+  else
+    [str appendFormat: @"%@\n", tag, group];
+
+  attrs = [attributes allKeys];
+  max = [attrs count];
+  if (max > 0)
+    {
+      [str appendFormat: @"\n  %d attributes: {\n", [attrs count]];
+      for (count = 0; count < max; count++)
+        {
+          attr = [attrs objectAtIndex: count];
+          [str appendFormat: @"  %@: %@\n",
+               attr, [attributes objectForKey: attr]];
+        }
+      [str appendFormat: @"}"];
+    }
+
+  max = [values count];
+  if (max > 0)
+    {
+      [str appendFormat: @"\n  %d values: {\n", [values count]];
+      for (count = 0; count < max; count++)
+        [str appendFormat: @"  %@\n", [values objectAtIndex: count]];
+      [str appendFormat: @"}"];
+    }
+
+  [str appendString:@">"];
+
+  return str;
+}
+
+- (BOOL) isVoid
+{
+  BOOL result;
+  NSString *value;
+  NSEnumerator *enu;
+
+  result = YES;
+
+  enu = [values objectEnumerator];
+  value = [enu nextObject];
+  while (value && result)
+    {
+      result = ([value length] == 0);
+      value = [enu nextObject];
+    }
+
+  return result;
+}
+
+- (NSString *) versitString
+{
+  CardVersitRenderer *renderer;
+  NSString *string;
+
+  renderer = [CardVersitRenderer new];
+  string = [renderer render: self];
+  [renderer release];
+
+  return string;
+}
+
+- (CardElement *) elementWithClass: (Class) elementClass
+{
+  CardElement *newElement;
+
+  if ([self isKindOfClass: elementClass])
+    newElement = self;
+  else
+    {
+      newElement = [elementClass new];
+      [newElement autorelease];
+      [newElement setTag: tag];
+      [newElement setValuesAsCopy: values];
+      [newElement setAttributesAsCopy: attributes];
+      if (group)
+        [newElement setGroup: group];
+      if (parent)
+        {
+          [newElement setParent: parent];
+          [parent replaceThisElement: self
+                  withThisOne: newElement];
+        }
+    }
+
+  return newElement;
+}
+
+- (void) setValuesAsCopy: (NSMutableArray *) someValues
+{
+  [values release];
+  values = someValues;
+  [values retain];
+}
+
+- (void) setAttributesAsCopy: (NSMutableDictionary *) someAttributes
+{
+  [attributes release];
+  attributes = someAttributes;
+  [attributes retain];
+}
+
+- (CardGroup *) searchParentOfClass: (Class) parentClass
+{
+  CardGroup *current;
+  CardGroup *found;
+
+  found = nil;
+  current = parent;
+  while (current && !found)
+    if ([current isKindOfClass: parentClass])
+      found = current;
+    else
+      current = [current parent];
+
+  return found;
+}
+
+/* NSCopying */
+
+- (id) copyWithZone: (NSZone *) aZone
+{
+  CardElement *new;
+
+  new = [[self class] new];
+  [new setTag: [tag copyWithZone: aZone]];
+  [new setGroup: [group copyWithZone: aZone]];
+  [new setParent: parent];
+  [new setValuesAsCopy: [values copyWithZone: aZone]];
+  [new setAttributesAsCopy: [attributes copyWithZone: aZone]];
+
+  return new;
+}
+
+@end
diff --git a/SOPE/NGCards/CardGroup+iCal.h b/SOPE/NGCards/CardGroup+iCal.h
new file mode 100644 (file)
index 0000000..ece4cf1
--- /dev/null
@@ -0,0 +1,39 @@
+/* CardGroup+iCal.h - this file is part of $PROJECT_NAME_HERE$
+ *
+ * Copyright (C) 2006 Inverse groupe conseil
+ *
+ * Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This file 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; see the file COPYING.  If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef CARDGROUP_ICAL_H
+#define CARDGROUP_ICAL_H
+
+@class NSString;
+@class NSCalendarDate;
+
+#import "CardGroup.h"
+
+@interface CardGroup (iCalExtensions)
+
+- (void)   setDate: (NSCalendarDate *) _value
+  forDateTimeValue: (NSString *) valueName;
+- (NSCalendarDate *) dateForDateTimeValue: (NSString *) valueName;
+
+@end
+
+#endif /* CARDGROUP_ICAL_H */
diff --git a/SOPE/NGCards/CardGroup+iCal.m b/SOPE/NGCards/CardGroup+iCal.m
new file mode 100644 (file)
index 0000000..a7fb4e5
--- /dev/null
@@ -0,0 +1,48 @@
+/* CardGroup+iCal.m - this file is part of SOPE
+ *
+ * Copyright (C) 2006 Inverse groupe conseil
+ *
+ * Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This file 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; see the file COPYING.  If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#import "iCalDateTime.h"
+
+#import "CardGroup+iCal.h"
+
+@implementation CardGroup (iCalExtensions)
+
+- (void)   setDate: (NSCalendarDate *) _value
+  forDateTimeValue: (NSString *) valueName
+{
+  iCalDateTime *dateValue;
+
+  dateValue = (iCalDateTime *) [self uniqueChildWithTag: valueName];
+
+  [dateValue setDateTime: _value];
+}
+
+- (NSCalendarDate *) dateForDateTimeValue: (NSString *) valueName
+{
+  iCalDateTime *dateValue;
+
+  dateValue = (iCalDateTime *) [self uniqueChildWithTag: valueName];
+
+  return [dateValue dateTime];
+}
+
+@end
diff --git a/SOPE/NGCards/CardGroup.h b/SOPE/NGCards/CardGroup.h
new file mode 100644 (file)
index 0000000..3108f8b
--- /dev/null
@@ -0,0 +1,76 @@
+/* CardGroup.h - this file is part of SOPE
+ *
+ * Copyright (C) 2006 Inverse groupe conseil
+ *
+ * Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This file 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; see the file COPYING.  If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef CARDGROUP_H
+#define CARDGROUP_H
+
+#import "CardElement.h"
+
+@class NSArray;
+@class NSMutableArray;
+@class NSString;
+
+@interface CardGroup : CardElement <NSCopying>
+{
+  NSMutableArray *children;
+}
+
++ (id) parseSingleFromSource: (id) source;
++ (NSArray *) parseFromSource: (id) source;
+
++ (id) groupWithTag: (NSString *) aTag;
++ (id) groupWithTag: (NSString *) aTag
+           children: (NSArray *) someChildren;
+
+- (Class) classForTag: (NSString *) tagClass;
+
+- (CardElement *) uniqueChildWithTag: (NSString *) aTag;
+- (void) setUniqueChild: (CardElement *) aChild;
+
+- (void) addChild: (CardElement *) aChild;
+- (void) addChildren: (NSArray *) someChildren;
+
+- (NSArray *) children;
+- (NSArray *) childrenWithTag: (NSString *) aTag;
+- (NSArray *) childrenWithAttribute: (NSString *) anAttribute
+                        havingValue: (NSString *) aValue;
+- (NSArray *) childrenWithTag: (NSString *) aTag
+                 andAttribute: (NSString *) anAttribute
+                  havingValue: (NSString *) aValue;
+- (NSArray *) childrenGroupWithTag: (NSString *) aTag
+                         withChild: (NSString *) aChild
+                 havingSimpleValue: (NSString *) aValue;
+
+- (void) addChildWithTag: (NSString *) aTag
+                   types: (NSArray *) someTypes
+             singleValue: (NSString *) aValue;
+
+
+- (CardGroup *) groupWithClass: (Class) groupClass;
+- (void) setChildrenAsCopy: (NSMutableArray *) someChildren;
+
+- (void) replaceThisElement: (CardElement *) oldElement
+                withThisOne: (CardElement *) newElement;
+
+@end
+
+#endif /* CARDGROUP_H */
diff --git a/SOPE/NGCards/CardGroup.m b/SOPE/NGCards/CardGroup.m
new file mode 100644 (file)
index 0000000..90700c8
--- /dev/null
@@ -0,0 +1,386 @@
+/* CardGroup.m - this file is part of SOPE
+ *
+ * Copyright (C) 2006 Inverse groupe conseil
+ *
+ * Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This file 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; see the file COPYING.  If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#import <Foundation/NSArray.h>
+#import <Foundation/NSDictionary.h>
+#import <Foundation/NSString.h>
+#import <SaxObjC/SaxXMLReader.h>
+#import <SaxObjC/SaxXMLReaderFactory.h>
+
+#import "NGCardsSaxHandler.h"
+#import "NSArray+NGCards.h"
+
+#import "CardGroup.h"
+
+static id<NSObject,SaxXMLReader> parser = nil;
+static NGCardsSaxHandler *sax = nil;
+
+@implementation CardGroup
+
++ (id<NSObject,SaxXMLReader>) cardParser
+{
+  if (!sax)
+    sax = [NGCardsSaxHandler new];
+  
+  if (!parser)
+    {
+      parser =
+        [[[SaxXMLReaderFactory standardXMLReaderFactory] 
+           createXMLReaderForMimeType:@"text/x-vcard"]
+          retain];
+      if (parser)
+        {
+          [parser setContentHandler:sax];
+          [parser setErrorHandler:sax];
+        }
+      else
+        NSLog(@"ERROR(%s): did not find a parser for text/x-vcard!",
+              __PRETTY_FUNCTION__);
+    }
+  
+  return parser;
+}
+
++ (NSArray *) parseFromSource: (id) source
+{
+  static id <NSObject,SaxXMLReader> cardParser;
+  NSMutableArray *cardGroups;
+  NSEnumerator *cards;
+  CardGroup *currentCard;
+
+  cardParser = [self cardParser];
+  [sax setTopElementClass: [self class]];
+
+  if (parser)
+    {
+      cardGroups = [NSMutableArray new];
+      [cardGroups autorelease];
+      
+      [parser parseFromSource: source];
+      cards = [[sax cards] objectEnumerator];
+
+      currentCard = [cards nextObject];
+      while (currentCard)
+        {
+          [cardGroups addObject: currentCard];
+          currentCard = [cards nextObject];
+        }
+    }
+  else
+    cardGroups = nil;
+
+  return cardGroups;
+}
+
++ (id) parseSingleFromSource: (id) source
+{
+  NSArray *cards;
+  CardGroup *card;
+
+  cards = [self parseFromSource: source];
+  if (cards && [cards count])
+    card = [cards objectAtIndex: 0];
+  else
+    card = nil;
+
+  return card;
+}
+
++ (id) groupWithTag: (NSString *) aTag
+{
+  id newGroup;
+
+  newGroup = [self new];
+  [newGroup autorelease];
+  [newGroup setTag: aTag];
+
+  return newGroup;
+}
+
++ (id) groupWithTag: (NSString *) aTag
+           children: (NSArray *) someChildren
+{
+  id newGroup;
+
+  newGroup = [self new];
+  [newGroup autorelease];
+  [newGroup setTag: aTag];
+  [newGroup addChildren: someChildren];
+
+  return newGroup;
+}
+
+- (id) init
+{
+  if ((self = [super init]))
+    {
+      children = [NSMutableArray new];
+    }
+
+  return self;
+}
+
+- (void) dealloc
+{
+  [children release];
+  [super dealloc];
+}
+
+- (Class) classForTag: (NSString *) tagClass
+{
+  NSLog (@"class '%@': '%@'", NSStringFromClass([self class]),
+         tagClass);
+
+  return nil;
+}
+
+- (void) addChild: (CardElement *) aChild
+{
+  Class mappedClass;
+  NSString *childTag;
+  CardElement *newChild;
+
+  childTag = [aChild tag];
+  newChild = nil;
+  mappedClass = [self classForTag: [childTag uppercaseString]];
+  if (mappedClass)
+    {
+      if (![aChild isKindOfClass: mappedClass])
+        {
+          NSLog (@"warning: new child to entity '%@': '%@' converted to '%@'",
+                 tag, childTag, NSStringFromClass(mappedClass));
+          if ([aChild isKindOfClass: [CardGroup class]])
+            newChild = [(CardGroup *) aChild groupWithClass: mappedClass];
+          else
+            newChild = [aChild elementWithClass: mappedClass];
+        }
+    }
+  else
+    NSLog (@"warning: no mapped class for tag '%@'",
+           childTag);
+
+  if (!newChild)
+    newChild = aChild;
+  [children addObject: newChild];
+  [newChild setParent: self];
+}
+
+- (CardElement *) uniqueChildWithTag: (NSString *) aTag
+{
+  NSArray *existing;
+  Class elementClass;
+  CardElement *uniqueChild;
+
+  existing = [self childrenWithTag: aTag];
+  if ([existing count] > 0)
+    uniqueChild = [existing objectAtIndex: 0];
+  else
+    {
+      elementClass = [self classForTag: [aTag uppercaseString]];
+      if (!elementClass)
+        elementClass = [CardElement class];
+
+      uniqueChild = [elementClass new];
+      [uniqueChild autorelease];
+      [uniqueChild setTag: aTag];
+      [self addChild: uniqueChild];
+    }
+
+  return uniqueChild;
+}
+
+- (void) setUniqueChild: (CardElement *) aChild
+{
+  CardElement *currentChild;
+  NSString *childTag;
+  NSEnumerator *existing;
+
+  childTag = [aChild tag];
+  existing = [[self childrenWithTag: childTag] objectEnumerator];
+
+  currentChild = [existing nextObject];
+  while (currentChild)
+    {
+      [children removeObject: currentChild];
+      currentChild = [existing nextObject];
+    }
+
+  [self addChild: aChild];
+}
+
+- (void) addChildren: (NSArray *) someChildren
+{
+  CardElement *currentChild;
+  NSEnumerator *newChildren;
+
+  newChildren = [someChildren objectEnumerator];
+  currentChild = [newChildren nextObject];
+  while (currentChild)
+    {
+      [self addChild: currentChild];
+      currentChild = [newChildren nextObject];
+    }
+}
+
+- (NSArray *) children
+{
+  return children;
+}
+
+- (NSArray *) childrenWithTag: (NSString *) aTag
+{
+  return [children cardElementsWithTag: aTag];
+}
+
+- (NSArray *) childrenWithAttribute: (NSString *) anAttribute
+                        havingValue: (NSString *) aValue
+{
+  return [children cardElementsWithAttribute: anAttribute
+                   havingValue: aValue];
+}
+
+- (NSArray *) childrenWithTag: (NSString *) aTag
+                 andAttribute: (NSString *) anAttribute
+                  havingValue: (NSString *) aValue
+{
+  NSArray *elements;
+
+  elements = [self childrenWithTag: aTag];
+
+  return [elements cardElementsWithAttribute: anAttribute
+                   havingValue: aValue];
+}
+
+- (NSArray *) childrenGroupWithTag: (NSString *) aTag
+                         withChild: (NSString *) aChild
+                 havingSimpleValue: (NSString *) aValue
+{
+  NSEnumerator *allElements;
+  NSMutableArray *elements;
+  CardGroup *element;
+  NSString *value;
+
+  elements = [NSMutableArray new];
+  [elements autorelease];
+
+  allElements = [[self childrenWithTag: aTag] objectEnumerator];
+  element = [allElements nextObject];
+  while (element)
+    {
+      if ([element isKindOfClass: [CardGroup class]])
+        {
+          value = [[element uniqueChildWithTag: aChild] value: 0];
+          if ([value isEqualToString: aValue])
+            [elements addObject: element];
+        }
+      element = [allElements nextObject];
+    }
+
+  return elements;
+}
+
+#warning should be renamed to elementWithClass...
+- (CardGroup *) groupWithClass: (Class) groupClass
+{
+  CardGroup *newGroup;
+
+  if ([self isKindOfClass: groupClass])
+    newGroup = self;
+  else
+    {
+      newGroup = (CardGroup *) [self elementWithClass: groupClass];
+      [newGroup setChildrenAsCopy: children];
+    }
+
+  return newGroup;
+}
+
+- (void) setChildrenAsCopy: (NSMutableArray *) someChildren
+{
+  [children release];
+  children = someChildren;
+  [children retain];
+}
+
+- (void) addChildWithTag: (NSString *) aTag
+                   types: (NSArray *) someTypes
+             singleValue: (NSString *) aValue
+{
+  CardElement *newChild;
+  NSEnumerator *types;
+  NSString *type;
+
+  newChild = [CardElement simpleElementWithTag: aTag value: aValue];
+  types = [someTypes objectEnumerator];
+  type = [types nextObject];
+  while (type)
+    {
+      [newChild addType: type];
+      type = [types nextObject];
+    }
+
+  [self addChild: newChild];
+}
+
+- (NSString *) description
+{
+  NSMutableString *str;
+  unsigned int count, max;
+
+  str = [NSMutableString stringWithCapacity:64];
+  [str appendFormat:@"<%p[%@]:%@",
+       self, NSStringFromClass([self class]), [self tag]];
+  max = [children count];
+  if (max > 0)
+    {
+      [str appendFormat: @"\n  %d children: {\n", [children count]];
+      for (count = 0; count < max; count++)
+        [str appendFormat: @"  %@\n",
+             [[children objectAtIndex: count] description]];
+      [str appendFormat: @"}"];
+    }
+  [str appendString:@">"];
+
+  return str;
+}
+
+- (void) replaceThisElement: (CardElement *) oldElement
+                withThisOne: (CardElement *) newElement
+{
+  unsigned int index;
+
+  index = [children indexOfObject: oldElement];
+  if (index != NSNotFound)
+    [children replaceObjectAtIndex: index withObject: newElement];
+}
+
+- (id) copyWithZone: (NSZone *) aZone
+{
+  CardGroup *new;
+
+  new = [super copyWithZone: aZone];
+  [new setChildrenAsCopy: [children copyWithZone: aZone]];
+
+  return new;
+}
+
+@end
diff --git a/SOPE/NGCards/CardVersitRenderer.h b/SOPE/NGCards/CardVersitRenderer.h
new file mode 100644 (file)
index 0000000..70ee867
--- /dev/null
@@ -0,0 +1,37 @@
+/* CardVersitRenderer.h - this file is part of SOPE
+ *
+ * Copyright (C) 2006 Inverse groupe conseil
+ *
+ * Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This file 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; see the file COPYING.  If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef CARDCARDRENDERER_H
+#define CARDCARDRENDERER_H
+
+#import <Foundation/NSObject.h>
+
+@class CardGroup;
+@class NSString;
+
+@interface CardVersitRenderer : NSObject
+
+- (NSString *) render: (id) anElement;
+
+@end
+
+#endif /* CARDCARDRENDERER_H */
diff --git a/SOPE/NGCards/CardVersitRenderer.m b/SOPE/NGCards/CardVersitRenderer.m
new file mode 100644 (file)
index 0000000..0643bbd
--- /dev/null
@@ -0,0 +1,136 @@
+/* CardVersitRenderer.m - this file is part of SOPE
+ *
+ * Copyright (C) 2006 Inverse groupe conseil
+ *
+ * Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This file 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; see the file COPYING.  If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#import <Foundation/NSArray.h>
+#import <Foundation/NSDictionary.h>
+#import <Foundation/NSString.h>
+
+#import <NGExtensions/NSObject+Logs.h>
+
+#import "CardElement.h"
+#import "CardGroup.h"
+
+#import "NSString+NGCards.h"
+#import "NSArray+NGCards.h"
+
+#import "CardVersitRenderer.h"
+
+@interface CardVersitRenderer (PrivateAPI)
+
+- (NSString *) renderElement: (CardElement *) anElement;
+- (NSString *) renderGroup: (CardGroup *) aGroup;
+
+@end
+
+@implementation CardVersitRenderer
+
+- (NSString *) render: (id) anElement
+{
+  return (([anElement isKindOfClass: [CardGroup class]])
+          ? [self renderGroup: anElement]
+          : [self renderElement: anElement]);
+}
+
+- (NSString *) renderElement: (CardElement *) anElement
+{
+  NSMutableString *rendering;
+  NSDictionary *attributes;
+  NSEnumerator *keys;
+  NSArray *values, *renderedAttrs;
+  NSString *key, *finalRendering, *tag;
+
+  if (![anElement isVoid])
+    {
+      rendering = [NSMutableString new];
+      [rendering autorelease];
+      if ([anElement group])
+        [rendering appendFormat: @"%@.", [anElement group]];
+      tag = [anElement tag];
+      if (!(tag && [tag length]))
+        {
+          tag = @"<no-tag>";
+          [self warnWithFormat: @"card element of class '%@' has an empty tag",
+                NSStringFromClass([anElement class])];
+        }
+
+      [rendering appendString: [tag uppercaseString]];
+      attributes = [anElement attributes];
+      keys = [[attributes allKeys] objectEnumerator];
+      key = [keys nextObject];
+      while (key)
+        {
+          renderedAttrs = [[attributes objectForKey: key] renderedForCards];
+          [rendering appendFormat: @";%@=%@",
+                     [key uppercaseString],
+                     [renderedAttrs componentsJoinedByString: @","]];
+          key = [keys nextObject];
+        }
+
+      values = [anElement values];
+      if ([values count] > 0)
+        [rendering appendFormat: @":%@",
+                   [[values renderedForCards] componentsJoinedByString: @";"]];
+
+      if ([rendering length] > 0)
+        [rendering appendString: @"\r\n"];
+
+      finalRendering = [rendering foldedForVersitCards];
+    }
+  else
+    finalRendering = @"";
+
+  return finalRendering;
+}
+
+- (NSString *) renderGroup: (CardGroup *) aGroup
+{
+  NSEnumerator *children;
+  CardElement *currentChild;
+  NSMutableString *rendering;
+  NSString *groupTag;
+
+  rendering = [NSMutableString new];
+  [rendering autorelease];
+
+  groupTag = [aGroup tag];
+  if (!(groupTag && [groupTag length]))
+    {
+      groupTag = @"<no-tag>";
+      [self warnWithFormat: @"card group of class '%@' has an empty tag",
+            NSStringFromClass([aGroup class])];
+    }
+
+  groupTag = [groupTag uppercaseString];
+  [rendering appendFormat: @"BEGIN:%@\r\n", groupTag];
+  children = [[aGroup children] objectEnumerator];
+  currentChild = [children nextObject];
+  while (currentChild)
+    {
+      [rendering appendString: [self render: currentChild]];
+      currentChild = [children nextObject];
+    }
+  [rendering appendFormat: @"END:%@\r\n", groupTag];
+
+  return rendering;
+}
+
+@end
diff --git a/SOPE/NGCards/ChangeLog b/SOPE/NGCards/ChangeLog
new file mode 100644 (file)
index 0000000..9a88926
--- /dev/null
@@ -0,0 +1,600 @@
+2006-08-03  Helge Hess  <helge.hess@opengroupware.org>
+
+       * NGVCardSaxHandler.m: fixed a bug with returning parsing results.
+         Properly make a copy of the vCard array so that -clear doesn't
+         destroy references. Thanks go to Wolfgang Sourdeau for documenting
+         the issue! (v4.5.76)
+
+2006-07-04  Helge Hess  <helge.hess@opengroupware.org>
+
+       * use %p for pointer formats, fixed gcc 4.1 warnings (v4.5.75)
+
+2006-05-16  Marcus Mueller  <znek@mulle-kybernetik.com>
+
+       * iCalDataSource.h, common.h: changed EOControl related includes into
+         imports to enable compilation against MulleEOF (v4.5.74)
+
+2006-04-07  Marcus Mueller  <znek@mulle-kybernetik.com>
+
+       * iCalEntityObject.h: added missing forward declaration for
+         gstep-base compile (v4.5.73)
+
+2006-04-06  Marcus Mueller  <znek@mulle-kybernetik.com>
+
+       * v4.5.72
+
+       * iCalEntityObject.[hm]: added "url" property - unfortunately
+         this was missing before. Increased class version to "1".
+
+       * iCalRepeatableEntityObject.m, iCalEvent.m, iCalToDo.m: adjusted
+         super class version check.
+
+2006-01-16  Helge Hess  <helge.hess@opengroupware.org>
+
+       * iCalRecurrenceRule.m: properly include NSString+Ext.h to avoid a
+         warning during Cocoa compilation (v4.5.71)
+
+2005-11-17  Helge Hess  <helge.hess@opengroupware.org>
+
+       * include string.h where required (v4.5.70)
+
+2005-10-05  Helge Hess  <helge.hess@opengroupware.org>
+
+       * iCalMonthlyRecurrenceCalculator.m: implemented calculation of
+         negative byday occurrences (eg -1TH) (v4.5.69)
+
+2005-09-22  Helge Hess  <helge.hess@skyrix.com>
+
+       * iCalRecurrenceRule.m: added direct parser support for 'interval',
+         allow 'until' values w/o Z marker for date-only values (v4.5.68)
+
+2005-09-22  Helge Hess  <helge.hess@opengroupware.org>
+
+       * v4.5.67
+
+       * iCalMonthlyRecurrenceCalculator.m: finished 'byday' calculation
+
+       * iCalRecurrenceRule.m: added support for 'bymonthday', fixed handling
+         of occurrence1
+
+2005-09-21  Helge Hess  <helge.hess@skyrix.com>
+
+       * v4.5.66
+
+       * iCalRecurrenceRule.m: temporarily expose byDayOccurence1 until the
+         API is fixed
+
+       * iCalMonthlyRecurrenceCalculator.m: prepared for byday/bymonthday
+         rule specs
+
+       * v4.5.65
+
+       * iCalMonthlyRecurrenceCalculator.m: fixed calculation of 'count' field
+
+       * iCalRecurrenceCalculator.m: minor code cleanups
+
+       * iCalRecurrenceRule.m: improved rrule parser
+
+2005-09-20  Helge Hess  <helge.hess@opengroupware.org>
+
+       * iCalMonthlyRecurrenceCalculator.m: stop calculation if a byday part
+         was detected in the rule (v4.5.64)
+
+       * iCalRecurrenceCalculator.m: moved cluster subclasses to own source
+         files (v4.5.63)
+
+       * iCalRecurrenceRule.m: added some parsing/gen support for BYDAY
+         (v4.5.62)
+
+2005-09-19  Helge Hess  <helge.hess@opengroupware.org>
+
+       * iCalRecurrenceRule.m: minor code improvements, more tolerant on
+         invalid iCal rrule input (v4.5.61)
+
+2005-08-16  Helge Hess  <helge.hess@opengroupware.org>
+
+       * GNUmakefile, GNUmakefile.preamble: added OSX framework compilation
+         (v4.5.60)
+
+2005-08-06  Helge Hess  <helge.hess@opengroupware.org>
+
+       * iCalCalendar.m, NGVCardName.m: fixed gcc 4.0 warnings (v4.5.59)
+
+2005-08-05  Helge Hess  <helge.hess@opengroupware.org>
+
+       * NGVCardSaxHandler.h: fixed a gcc 4.0 warning (v4.5.58)
+
+2005-07-18  Helge Hess  <helge.hess@opengroupware.org>
+
+       * iCalPerson.m: added -partStatWithDefault method to retrieve the
+         partstat and return NEEDS-ACTION in case none is set (v4.5.57)
+
+2005-07-15  Marcus Mueller  <znek@mulle-kybernetik.com>
+
+       * iCalObject.[hm], iCalEntityObject.m, iCalToDo.m, iCalAlarm.m,
+         iCalPerson.m, iCalCalendar.m, iCalEvent.m, iCalTrigger.m,
+         iCalObject.m, iCalFreeBusy.m, iCalRepeatableEntityObject.m:
+         fixed NSCopying (v4.5.56)
+
+2005-07-15  Helge Hess  <helge.hess@opengroupware.org>
+
+       * added fragile base class version checks (v4.5.55)
+
+2005-07-15  Marcus Mueller  <znek@mulle-kybernetik.com>
+
+       * iCalObject.[hm], iCalEntityObject.m, iCalToDo.m, iCalAlarm.m,
+         iCalPerson.m, iCalCalendar.m, iCalEvent.m, iCalTrigger.m,
+         iCalObject.m, iCalFreeBusy.m, iCalRepeatableEntityObject.m:
+         added NSCopying (v4.5.54)
+
+2005-07-15  Helge Hess  <helge.hess@opengroupware.org>
+
+       * v4.5.53
+
+       * iCalCalendar.m: added +parseCalendarFromSource: convenience method
+
+       * NGVCard.m: properly reset sax driver after parsing
+
+2005-07-15  Marcus Mueller  <znek@mulle-kybernetik.com>
+
+       * iCalEntityObject.[hm]: added -increaseSequence (v4.5.52)
+
+       * iCalPerson.m: fixed -hasSameEmailAddress: to lowercase email
+         addresses before attempting comparison (v4.5.51)
+
+2005-07-15  Helge Hess  <helge.hess@opengroupware.org>
+
+       * v4.5.50
+       
+       * NGiCal.xmap: fixed mapping of 'method' property (is an attribute of
+         the calendar)
+         
+       * iCalCalendar.m: use ASSIGNCOPY in accessors, added 'method' to
+         -description
+         
+2005-07-14  Marcus Mueller  <znek@mulle-kybernetik.com>
+
+       * v4.5.49
+
+       * iCalCalendar.[hm]: added 'method'
+
+       * iCalEntityObject.[hm]: added 'userComment', changed all email
+         related comparisons to lowercase all strings before comparison
+
+       * iCalEvent.h: pretty printed iVars
+
+       * NGiCal.xmap: added mapping for 'comment' -> 'userComment' and
+         'method' -> 'method'
+
+2005-07-13  Helge Hess  <helge.hess@opengroupware.org>
+
+       * v4.5.48
+
+       * NGVCard.m: added some convenience methods to access the preferred
+         email, tel and adr
+
+       * NGVCardOrg.m: added a convenience method to access the first orgunit
+
+2005-07-05  Marcus Mueller  <znek@mulle-kybernetik.com>
+
+       * NSCalendarDate+ICal.m: fixed 'gmtcalfmt' which removed seconds from
+         format for no obvious reason (v4.5.47)
+
+2005-05-31  Helge Hess  <helge.hess@skyrix.com>
+
+       * NGVCardSaxHandler.m: ensure that types are always uppercase, improved
+         check for DUPs (v4.5.46)
+
+2005-05-16  Marcus Mueller  <znek@mulle-kybernetik.com>
+
+       * NGiCal.xcode: reorganized ordering of headers/classes
+
+2005-05-15  Marcus Mueller  <znek@mulle-kybernetik.com>
+
+       * NGiCal.xcode: added vCard related stuff
+
+2005-05-10  Helge Hess  <helge.hess@opengroupware.org>
+
+       * NGVCard.m: added support for profile, source, name (v4.5.45)
+
+2005-05-09  Helge Hess  <helge.hess@opengroupware.org>
+
+       * first version of working vCard SAX handler (v4.5.54)
+
+2005-05-08  Helge Hess  <helge.hess@opengroupware.org>
+
+       * more work on vCard parsing (v4.5.53)
+
+       * work on vCard objects (incomplete) (v4.5.52)
+
+2005-04-25  Helge Hess  <helge.hess@opengroupware.org>
+
+       * iCalDateHolder.m, iCalDataSource.m: fixed gcc 4.0 warnings (v4.5.51)
+
+2005-03-23  Marcus Mueller  <znek@mulle-kybernetik.com>
+
+       * v4.5.50
+
+       * iCalRenderer.h: added missing paranthesis for iVar declarations
+
+       * iCalTrigger.m, iCalEntityObject.m, iCalToDo.m,
+         iCalRecurrenceCalculator.m, iCalDateHolder.m, iCalDataSource.m,
+         iCalPerson.m, iCalCalendar.m: numerous dealloc bugs/leaks fixed
+
+2005-03-02  Marcus Mueller  <znek@mulle-kybernetik.com>
+
+       * iCalRecurrenceCalculator.m: bugfix for monthly and yearly recurrences
+         (v4.5.49)
+
+       * iCalRecurrenceCalculator.m: optimized exception date handling in
+         complex calculation method quite a bit. Fixed bugs in all calculation
+         methods by introducing checks on the desired range. (v4.5.48)
+
+2005-02-28  Marcus Mueller  <znek@mulle-kybernetik.com>
+
+       * v4.5.47
+
+       * iCalRepeatableEntityObject.m:  shifted code to
+         iCalRecurrenceCalculator
+
+       * iCalRecurrenceCalculator.[hm]: new class method to calculate
+         complex recurrences. In SOGo this information is stored in quick
+         fields to reduce the complexity of lookups, hence we need to offer
+         a proper API to calculate date ranges from this information.
+         Fixed all calculations to explicitly set timeZone for all newly
+         created startDates - it seems the timeZone information is not
+         properly retained by the hour:minute:second: method from NGExtensions
+         which lead to improper DST related shifts.
+
+       * iCalDateHolder.h: exposed the API
+
+       * NSCalendarDate+ICal.[hm]: new convenience constructor for calendar
+         dates from iCal representations (uses iCalDateHolder internally)
+
+       * iCalRecurrenceRule.m: changed setUntil: to utilize new public
+         NSCalendarDate+ICal category
+
+       * NGiCal.h: added NSCalendarDate+ICal.h to the public headers
+
+       * GNUmakefile: NSCalendarDate+ICal.h is public now
+
+2005-02-20  Helge Hess  <helge.hess@opengroupware.org>
+
+       * NGiCal.xmap: fixed a missing semicolon (did not load on MacOSX)
+         (v.4.5.46)
+
+2005-02-17  Helge Hess  <helge.hess@skyrix.com>
+
+       * GNUmakefile.preamble: fixed linking locations for dependencies
+         (v4.5.45)
+
+2005-02-17  Marcus Mueller  <znek@mulle-kybernetik.com>
+
+       * v4.5.44
+
+       * iCalEntityObject.[hm]: added convenience API
+
+       * iCalRepeatableEntityObject.[hm]: added convenience API
+
+       * iCalRecurrenceRule.m: bugfixes in -byDayList and
+         -iCalRepresentationForWeekDay:
+
+       * iCalRecurrenceCalculator.m: implemented 'BYDAY' calculations for
+         weekly frequency. Note that 'COUNT' is still broken for this case.
+
+       * iCalRenderer.m: updated rendering, now can render recurrence rules
+         and accompanied stuff properly.
+
+2005-02-15  Marcus Mueller  <znek@mulle-kybernetik.com>
+
+       * v4.5.43
+
+       * iCalRecurrenceRule.[hm]: exposed some more of the API
+
+       * iCalRecurrenceCalculator.m: some bugfixes. Split the code and gave
+         iCalWeeklyRecurrenceCalculator its own calculation - there are a
+         number of foreseeable differences to daily calculation that make
+         this necessary in the near future anyways.
+
+       * v4.5.42
+
+       * iCalRepeatableEntityObject.[hm]: added ability to properly calculate
+         the recurrence ranges within a specific calendar date range, taking
+         all possible exceptions into account.
+
+       * iCalEvent.[hm]: convenience wrapper for the new method found in
+         iCalRepeatableEntityObject.
+
+2005-02-14  Helge Hess  <helge.hess@opengroupware.org>
+
+       * v4.5.41
+
+       * GNUmakefile.preamble: added missing dependency on libNGExtensions
+         (which also adds the dependency on libDOM)
+
+       * iCalRecurrenceCalculator.h: fixed missing forward declaration in
+         header
+
+2005-02-12  Marcus Mueller  <znek@mulle-kybernetik.com>
+
+       * v4.5.40
+
+       * iCalRecurrenceCalculator.[hm]: implemented all required (and simple)
+         calculations. Added some convenience API to query some of the more
+         obvious ranges suitable as limits for fetching/comparison. 
+
+       * iCalRepeatableEntityObject.[hm]: new method for calculating the
+         last possible recurrence start date. This can be used for fetches
+         as well.
+
+       * iCalEvent.[hm]: more convenient wrapper for the new method found in
+         iCalRepeatableEntityObject.
+
+       * NSCalendarDate+ICal.[hm]: convenience methods for calculating
+         "distances" between dates.
+
+       * tests/*: updated
+
+2005-02-11  Marcus Mueller  <znek@mulle-kybernetik.com>
+
+       * v4.5.39
+
+       * README: updated
+
+       * iCalRepeatableEntityObject.[hm]: new base class for all other
+         repeatable entity objects. Offers a convenience API for generating
+         recurrence ranges and tests, taking all exceptions into account.
+
+       * iCalRecurrenceRule.[hm]: an iCal recurrence rule, modeled as closely
+         as possible to RFC2445. Please note that this is work in progress
+         and far from being complete, yet.
+
+       * iCalRecurrenceCalculator.[hm]: a controller implementing RFC2445
+         to properly generate recurrence ranges and accompanied functionality.
+
+       * iCalEvent.[hm], iCalToDo.[hm]: now subclasses from
+         iCalRepeatableEntityObject, thus removed code dealing with
+         recurrences
+
+       * NGiCal.h: added new headers
+
+       * NGiCal.xmap: changed recurrenceRule mappings due to model change
+
+       * tests/*: contains unit tests for stuff dealing with recurrences. See
+         accompanied README for details
+
+2004-12-17  Marcus Mueller  <znek@mulle-kybernetik.com>
+
+       * iCalPerson.[hm]: formalized participationStatus according to RFC2445.
+         Provided convenience API to set status without concrete knowledge
+         of string values involved. (v4.5.38)
+
+2004-12-16  Marcus Mueller  <znek@mulle-kybernetik.com>
+
+       * v4.5.37
+
+       * iCalEvent.[hm]: Added transparency 'TRANSP'. Also multiple
+         convenience methods provided.
+
+       * NGiCal.xmap: added proper mapping for 'transp'.
+
+2004-12-14  Marcus Mueller  <znek@mulle-kybernetik.com>
+
+       * NGiCal.xcode: minor changes and updated
+
+2004-12-13  Marcus Mueller  <znek@mulle-kybernetik.com>
+
+       * iCalPerson.[hm]: added -cnWithoutQuotes and -rfc822Email convenience
+         methods to simplify client code dealing with these properties
+         (v4.5.36)
+
+2004-11-07  Marcus Mueller  <znek@mulle-kybernetik.com>
+
+       * NGiCal.xcode: provide SOPE_{MAJOR,MINOR}_VERSION to the build
+
+2004-11-06  Helge Hess  <helge.hess@opengroupware.org>
+
+       * iCalRenderer.m: use SOPE version defines for iCalendar product id
+         (v4.5.35)
+
+2004-11-04  Helge Hess  <helge.hess@skyrix.com>
+
+       * use Version file for install directory location of sax mapping
+
+2004-10-31  Marcus Mueller  <znek@mulle-kybernetik.com>
+
+       * iCalAlarm.[hm], iCalEvent.[hm], iCalToDo.[hm],
+         NGiCal.xmap: added recurrenceRule (v4.3.34)
+
+2004-10-20  Marcus Mueller  <znek@mulle-kybernetik.com>
+
+       * NGiCal.xcode: added iCalRenderer.[hm], bumped framework version
+
+2004-10-20  Helge Hess  <helge.hess@opengroupware.org>
+
+       * iCalRenderer.m: fixed some issue with Cocoa Foundation (v4.3.33)
+
+       * v4.3.32
+
+       * iCalEvent.m: added -vEvent method to produce an iCalendar
+         representation of an event
+
+       * added iCalRenderer class based on the SOGo render by ZNeK
+
+2004-10-14  Marcus Mueller  <znek@mulle-kybernetik.com>
+
+       * NGiCal.xmap: added "categories" mapping (v4.3.31)
+
+2004-10-05  Marcus Mueller  <znek@mulle-kybernetik.com>
+
+       * iCalEntityObject.[hm]: added categories property (v4.3.30)
+
+2004-09-22  Marcus Mueller  <znek@mulle-kybernetik.com>
+
+       * NGiCal.xcode: fixed several build parameters
+
+2004-09-01  Marcus Mueller  <znek@mulle-kybernetik.com>
+
+       * v4.3.29
+
+       * iCalEventChanges.[hm]: new class for tracking changes between
+         two given events
+
+       * iCalEvent.[hm]: new API to generate iCalEventChanges objects.
+
+       * common.h: inline function and macro for "safe" comparison of
+         object values
+
+2004-09-01  Helge Hess  <helge.hess@opengroupware.org>
+
+       * GNUmakefile.postamble: copy sax-model to FHS_INSTALL_ROOT + 
+         /share/sope-4.3/saxmappings/ when compiling for FHS (v4.3.28)
+
+2004-08-29  Helge Hess  <helge.hess@opengroupware.org>
+
+       * added hack to install the project in FHS locations - the library,
+         its headers, the tools and the resources will be installed in
+         FHS_INSTALL_ROOT if specified (eg make FHS_INSTALL_ROOT=/usr/local)
+         (v4.3.27)
+
+2004-08-26  Marcus Mueller  <znek@mulle-kybernetik.com>
+
+       * NGiCal.xcode: new Xcode project
+
+2004-08-20  Helge Hess  <helge.hess@opengroupware.org>
+
+       * fixed for SOPE 3.3 directory layout (v4.3.26)
+
+       * moved to sope-ical
+
+       * moved to SOPE 4.3 (v4.3.25)
+
+2004-08-14  Helge Hess  <helge.hess@opengroupware.org>
+
+       * iCalEntityObject.m: changed "sequence" attribute from NSString to
+         NSNumber (this might break some code, though some care was taken to
+         accept NSString parameters) (v4.2.24)
+
+2004-07-14  Helge Hess  <helge.hess@opengroupware.org>
+
+       * iCalEvent.h: moved 'status' field to iCalEntityObject, because it is
+         also available in todo objects (v4.2.23)
+
+2004-06-30  Marcus Mueller  <znek@mulle-kybernetik.com>
+
+       * iCalEntityObject.m: added -removeAllAttendees and -removeAllAlarms.
+         These are necessary in case you want to remake the contents of these
+         collections, but keep all other attributes otherwise. (v4.2.22)
+
+2004-06-09  Helge Hess  <helge.hess@opengroupware.org>
+
+       * v4.2.21
+
+       * GNUmakefile.preamble: added prebinding
+
+       * GNUmakefile: create GNUmakefile.preamble, GNUmakefile.postamble
+
+2004-05-05  Marcus Mueller  <znek@mulle-kybernetik.com>
+
+       * GNUmakefile: added support for building with
+         GNUSTEP_BUILD_DIR environment variable set for recent
+         gnustep-make package. (v4.2.20)
+
+2004-01-23  Helge Hess  <helge.hess@opengroupware.org>
+
+       * iCalDataSource.m: added some sanity checks, improved to work better
+         with Mozilla generated iCal files (v4.2.19)
+
+2003-12-22  Helge Hess  <helge.hess@skyrix.com>
+
+       * iCalDateHolder.m: small fix not to print a warning for "YYYYMMDD"
+         style dates (which are supported) (v4.2.18)
+
+2003-12-21  Helge Hess  <helge.hess@opengroupware.org>
+
+       * v4.2.17
+
+       * iCalEvent.m: added parsing of duration values and calculation of 
+         end-dates based on that (eg required for Panther iCal.app)
+       
+       * iCalEvent.m: added support for 'status' and 'duration' fields 
+
+Thu Nov 20 16:31:15 2003  Martin Hoerning  <martin@opengroupware.org>
+
+       * iCalDateHolder.m: added timeless date format: YYYYMMDD
+         (solves ogo bug 424) (v4.2.16)
+
+Wed Oct 29 22:04:32 2003  Martin Hoerning  <martin@opengroupware.org>
+
+       * iCalDateHolder.m, iCalObject.[m|h]: added iCalDefaultTimeZone to 
+         create date from iCalDates without timeZone. (solves OGoo bug 257) 
+         (v4.2.15)
+
+2003-10-12  Helge Hess  <helge@opengroupware.org>
+
+       * GNUmakefile (libNGiCal_LIBRARIES_DEPEND_UPON): link against
+         libSaxObjC on MacOSX (v4.2.14)
+
+Fri Jul 18 17:04:55 2003  Martin Hoerning  <mh@skyrix.com>
+
+       * iCalToDo.h, iCalPerson.h, iCalEvent.h, iCalEntityObject.h: added 
+         accessor-methods to interface (v4.2.13)
+
+2003-07-18  Helge Hess  <helge.hess@skyrix.com>
+
+       * iCalDateHolder.m: replaces and indexOfString with rangeOfString,
+         thanks to Filip Van Raemdonck for pointing that out (v4.2.12)
+
+2003-03-21  Helge Hess  <helge.hess@skyrix.com>
+
+       * added some method prototypes
+
+2003-03-13  Helge Hess  <helge.hess@skyrix.com>
+
+       * moved iCalEntityObject to a separate file (v4.2.11)
+
+Mon Mar 10 18:41:10 2003  Bjoern Stierand  <bjoern@skyrix.com>
+
+       * NGiCal.xmap: added missing attendee attributes (as dictated by
+         mh on the phone)
+
+2003-03-10  Helge Hess  <helge.hess@skyrix.com>
+
+       * iCalPerson.h: added some method prototypes
+
+2003-02-24  Helge Hess  <helge.hess@skyrix.com>
+
+       * iCalObject.h: added some accessors (v4.2.10)
+
+2003-02-12  Helge Hess  <helge.hess@skyrix.com>
+
+       * moved to skyrix-core (v4.2.9)
+
+2003-01-13  Helge Hess  <helge.hess@skyrix.com>
+
+       * added some support for timezones to be able to parse Evolution
+         apt creation requests (v4.2.7)
+
+2002-10-14  Helge Hess  <helge.hess@skyrix.com>
+
+       * added iCalDateHolder for decoding xCal date values with timezone
+         attributes to a NSCalendarDate
+
+       * iCalObject.m: ignore X- keys
+
+       * removed all the old stuff (v4.2.4)
+
+       * ICalVEvent.m: renamed -class to -eventClass since -class conflicts
+         with the NSObject method
+
+       * started XML based iCal (v4.2.3)
+
+2002-10-10  Helge Hess  <helge.hess@skyrix.com>
+
+       * ICalParser.m: small cleanups (v4.2.2)
+
+2002-10-04  Helge Hess  <helge.hess@skyrix.com>
+
+       * created ChangeLog, created NGiCal library into SkyCore
+
+
diff --git a/SOPE/NGCards/GNUmakefile b/SOPE/NGCards/GNUmakefile
new file mode 100644 (file)
index 0000000..efc491c
--- /dev/null
@@ -0,0 +1,130 @@
+# GNUstep makefile
+
+-include ../../config.make
+include $(GNUSTEP_MAKEFILES)/common.make
+include ./Version
+
+SUBPROJECTS += versitCardsSaxDriver
+
+ifneq ($(frameworks),yes)
+LIBRARY_NAME = libNGCards
+else
+FRAMEWORK_NAME = NGCards
+endif
+
+FHS_HEADER_DIRS = NGCards
+
+libNGCards_PCH_FILE = common.h
+libNGCards_HEADER_FILES_DIR         = .
+libNGCards_HEADER_FILES_INSTALL_DIR = /NGCards
+libNGCards_SOVERSION=$(MAJOR_VERSION).$(MINOR_VERSION)
+libNGCards_VERSION=$(MAJOR_VERSION).$(MINOR_VERSION).$(SUBMINOR_VERSION)
+
+libNGCards_HEADER_FILES =              \
+       NSArray+NGCards.h               \
+       NSCalendarDate+NGCards.h        \
+       NSDictionary+NGCards.h          \
+       NSString+NGCards.h              \
+       NSCalendarDate+ICal.h           \
+       \
+       CardElement.h                   \
+       CardGroup.h                     \
+       CardGroup+iCal.h                \
+       CardVersitRenderer.h            \
+       \
+       NGCards.h                       \
+       iCalAlarm.h                     \
+       iCalAttachment.h                \
+       iCalCalendar.h                  \
+       iCalDataSource.h                \
+       iCalDateTime.m                  \
+       iCalEntityObject.h              \
+       iCalEvent.h                     \
+       iCalEventChanges.h              \
+       iCalFreeBusy.h                  \
+       iCalJournal.h                   \
+       iCalObject.h                    \
+       iCalPerson.h                    \
+       iCalRecurrenceRule.h            \
+       iCalRecurrenceCalculator.h      \
+       iCalRepeatableEntityObject.h    \
+       iCalTimeZone.h                  \
+       iCalTimeZonePeriod.h            \
+       iCalToDo.h                      \
+       iCalTrigger.h                   \
+                                       \
+       NSCalendarDate+ICal.h           \
+       \
+       NGVCard.h                       \
+#      NGVCardAddress.h                \
+#      NGVCardStrArrayValue.h          \
+#      NGVCardName.h                   \
+#      NGVCardOrg.h                    \
+#      NGVCardPhone.h                  \
+       NGCardsSaxHandler.h             \
+#      NGVCardSimpleValue.h            \
+#      NGVCardValue.h                  \
+
+#      IcalResponse.h                  \
+
+libNGCards_OBJC_FILES =                \
+       NSArray+NGCards.m               \
+       NSCalendarDate+NGCards.m        \
+       NSDictionary+NGCards.m          \
+       NSString+NGCards.m              \
+       NSCalendarDate+ICal.m           \
+       iCalDateHolder.m                \
+       \
+       CardElement.m                   \
+       CardGroup.m                     \
+       CardGroup+iCal.m                \
+       CardVersitRenderer.m            \
+       \
+       iCalAlarm.m                     \
+       iCalAttachment.m                \
+       iCalCalendar.m                  \
+       iCalDailyRecurrenceCalculator.m \
+       iCalDateTime.m                  \
+       iCalDataSource.m                \
+       iCalEntityObject.m              \
+       iCalEvent.m                     \
+       iCalEventChanges.m              \
+       iCalFreeBusy.m                  \
+       iCalJournal.m                   \
+       iCalMonthlyRecurrenceCalculator.m \
+       iCalObject.m                    \
+       iCalPerson.m                    \
+       iCalRecurrenceRule.m            \
+       iCalRecurrenceCalculator.m      \
+       iCalRepeatableEntityObject.m    \
+       iCalTimeZone.m                  \
+       iCalTimeZonePeriod.m            \
+       iCalToDo.m                      \
+       iCalTrigger.m                   \
+       iCalWeeklyRecurrenceCalculator.m\
+       iCalYearlyRecurrenceCalculator.m\
+       \
+       NGVCard.m                       \
+       NGCardsSaxHandler.m             \
+#      IcalElements.m
+#      IcalResponse.m
+
+
+# framework support
+
+NGCards_PCH_FILE     = $(libNGCards_PCH_FILE)
+NGCards_HEADER_FILES = $(libNGCards_HEADER_FILES)
+NGCards_OBJC_FILES   = $(libNGCards_OBJC_FILES)
+NGCards_SUBPROJECTS  = $(libNGCards_SUBPROJECTS)
+
+# building
+
+-include GNUmakefile.preamble
+ifneq ($(frameworks),yes)
+include $(GNUSTEP_MAKEFILES)/library.make
+else
+include $(GNUSTEP_MAKEFILES)/framework.make
+endif
+include $(GNUSTEP_MAKEFILES)/aggregate.make
+-include GNUmakefile.postamble
+-include ../../fhslib.make
diff --git a/SOPE/NGCards/GNUmakefile.postamble b/SOPE/NGCards/GNUmakefile.postamble
new file mode 100644 (file)
index 0000000..8074f12
--- /dev/null
@@ -0,0 +1,13 @@
+# compilation settings
+
+ifeq ($(FHS_INSTALL_ROOT),)
+MAPDIR="$(GNUSTEP_INSTALLATION_DIR)/Library/SaxMappings/"
+else
+MAPDIR="$(FHS_INSTALL_ROOT)/share/sope-$(MAJOR_VERSION).$(MINOR_VERSION)/saxmappings/"
+endif
+
+mappings-dir ::
+       $(MKDIRS) $(MAPDIR)
+
+after-install :: mappings-dir
+       cp NGCards.xmap $(MAPDIR)
diff --git a/SOPE/NGCards/GNUmakefile.preamble b/SOPE/NGCards/GNUmakefile.preamble
new file mode 100644 (file)
index 0000000..0f6c0a4
--- /dev/null
@@ -0,0 +1,61 @@
+# compilation settings
+
+SOPE_ROOT=../..
+
+ADDITIONAL_CPPFLAGS += \
+        -Wall -DCOMPILE_FOR_GSTEP_MAKE=1        \
+        -DSOPE_MAJOR_VERSION=$(MAJOR_VERSION)   \
+        -DSOPE_MINOR_VERSION=$(MINOR_VERSION)   \
+        -DSOPE_SUBMINOR_VERSION=$(SUBMINOR_VERSION)
+
+ADDITIONAL_INCLUDE_DIRS += \
+       -I. -I..                        \
+       -I$(SOPE_ROOT)/sope-core/NGExtensions/  \
+       -I$(SOPE_ROOT)/sope-core                \
+       -I$(SOPE_ROOT)/sope-xml
+
+
+# dependencies
+
+libNGCards_LIBRARIES_DEPEND_UPON += \
+       -lNGExtensions  \
+       -lEOControl     \
+       -lDOM           \
+       -lSaxObjC
+
+NGCards_LIBRARIES_DEPEND_UPON += \
+       -framework NGExtensions -framework EOControl \
+       -framework DOM -framework SaxObjC
+
+
+# library/framework search pathes
+
+DEP_DIRS = \
+       $(SOPE_ROOT)/sope-core/NGExtensions     \
+       $(SOPE_ROOT)/sope-core/EOControl        \
+       $(SOPE_ROOT)/sope-xml/DOM               \
+       $(SOPE_ROOT)/sope-xml/SaxObjC
+
+ifneq ($(frameworks),yes)
+ADDITIONAL_LIB_DIRS += \
+       $(foreach dir,$(DEP_DIRS),\
+         -L$(GNUSTEP_BUILD_DIR)/$(dir)/$(GNUSTEP_OBJ_DIR_NAME))
+else
+ADDITIONAL_LIB_DIRS += \
+       $(foreach dir,$(DEP_DIRS),-F$(GNUSTEP_BUILD_DIR)/$(dir))
+endif
+
+ifeq ($(findstring _64, $(GNUSTEP_TARGET_CPU)), _64)
+SYSTEM_LIB_DIR += -L/usr/local/lib64 -L/usr/lib64
+else
+SYSTEM_LIB_DIR += -L/usr/local/lib -L/usr/lib
+endif
+
+
+# Apple
+
+ifeq ($(FOUNDATION_LIB),apple)
+libNGCards_PREBIND_ADDR="0xC1E00000"
+libNGCards_LDFLAGS += -seg1addr $(libNGCards_PREBIND_ADDR)
+NGCards_LDFLAGS += -seg1addr $(libNGCards_PREBIND_ADDR)
+endif
diff --git a/SOPE/NGCards/IcalElements.m b/SOPE/NGCards/IcalElements.m
new file mode 100644 (file)
index 0000000..c5002dc
--- /dev/null
@@ -0,0 +1,210 @@
+/*
+  Copyright (C) 2000-2005 SKYRIX Software AG
+
+  This file is part of SOPE.
+
+  SOPE is free software; you can redistribute it and/or modify it under
+  the terms of the GNU Lesser General Public License as published by the
+  Free Software Foundation; either version 2, or (at your option) any
+  later version.
+
+  SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+  License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with SOPE; see the file COPYING.  If not, write to the
+  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+  02111-1307, USA.
+*/
+
+#include <NGObjWeb/WODynamicElement.h>
+
+@interface IcalComponent : WODynamicElement
+{
+  WOAssociation *cname;
+  WOElement     *template;
+}
+@end
+
+@interface IcalProperty : WODynamicElement
+{
+  WOAssociation *pname;
+  WOElement     *template;
+  NSDictionary  *parameters;
+  WOAssociation *value;
+  WOAssociation *valueType;
+}
+@end
+
+#include "common.h"
+
+static inline NSDictionary *ExtractParameters(NSDictionary *_set) {
+  /* extracts ? parameters */
+  NSMutableDictionary *paras = nil;
+  NSMutableArray      *paraKeys = nil;
+  NSEnumerator        *keys;
+  NSString            *key;
+  
+  // locate query parameters
+  keys = [_set keyEnumerator];
+  while ((key = [keys nextObject])) {
+    if ([key hasPrefix:@"?"]) {
+      WOAssociation *value;
+
+      if ([key isEqualToString:@"?wosid"])
+        continue;
+
+      value = [_set objectForKey:key];
+          
+      if (paraKeys == nil)
+        paraKeys = [NSMutableArray arrayWithCapacity:8];
+      if (paras == nil)
+        paras = [NSMutableDictionary dictionaryWithCapacity:8];
+          
+      [paraKeys addObject:key];
+      [paras setObject:value forKey:[key substringFromIndex:1]];
+    }
+  }
+
+  // remove query parameters
+  if (paraKeys) {
+    unsigned cnt, count;
+    for (cnt = 0, count = [paraKeys count]; cnt < count; cnt++) {
+      [(NSMutableDictionary *)_set removeObjectForKey:
+                                     [paraKeys objectAtIndex:cnt]];
+    }
+  }
+
+  // assign parameters
+  return [paras copy];
+}
+
+static inline id GetProperty(NSDictionary *_set, NSString *_name) {
+  id propValue = [_set objectForKey:_name];
+
+  if (propValue) {
+    propValue = RETAIN(propValue);
+    [(id)_set removeObjectForKey:_name];
+  }
+  return propValue;
+}
+
+@implementation IcalComponent
+
+- (id)initWithName:(NSString *)_name
+  associations:(NSDictionary *)_config
+  template:(WOElement *)_t
+{
+  if ((self = [super initWithName:_name associations:_config template:_t])) {
+    self->cname = GetProperty(_config, @"name");
+    self->template = RETAIN(_t);
+  }
+  return self;
+}
+
+- (void)dealloc {
+  RELEASE(self->template);
+  RELEASE(self->cname);
+  [super dealloc];
+}
+
+- (void)appendToResponse:(WOResponse *)_response inContext:(WOContext *)_ctx {
+  NSString *n;
+  
+  n = [self->cname stringValueInComponent:[_ctx component]];
+  
+  [_response appendContentString:@"BEGIN:"];
+  [_response appendContentString:n];
+  [self->template appendToResponse:_response inContext:_ctx];
+  [_response appendContentString:@"END:"];
+  [_response appendContentString:n];
+}
+
+@end /* IcalComponent */
+
+@implementation IcalProperty
+
+- (id)initWithName:(NSString *)_name
+  associations:(NSDictionary *)_config
+  template:(WOElement *)_t
+{
+  if ((self = [super initWithName:_name associations:_config template:_t])) {
+    self->pname      = GetProperty(_config, @"name");
+    self->value      = GetProperty(_config, @"value");
+    self->valueType  = GetProperty(_config, @"valueType");
+    self->template   = RETAIN(_t);
+    self->parameters = ExtractParameters(_config);
+  }
+  return self;
+}
+
+- (void)dealloc {
+  RELEASE(self->value);
+  RELEASE(self->valueType);
+  RELEASE(self->parameters);
+  RELEASE(self->template);
+  RELEASE(self->pname);
+  [super dealloc];
+}
+
+- (void)appendToResponse:(WOResponse *)_response inContext:(WOContext *)_ctx {
+  WOComponent  *sComponent;
+  NSString     *n;
+  NSEnumerator *keys;
+  NSString     *key;
+  id           val;
+  NSString     *valType;
+
+  sComponent = [_ctx component];
+  n       = [self->pname     stringValueInComponent:sComponent];
+  val     = [self->value     valueInComponent:sComponent];
+  valType = [self->valueType stringValueInComponent:sComponent];
+
+  /* add name */
+  [_response appendContentString:n];
+
+  /* add parameters */
+  keys = [self->parameters keyEnumerator];
+  while ((key = [keys nextObject])) {
+    WOAssociation *val;
+    NSString *s;
+    
+    val = [self->parameters objectForKey:key];
+    s   = [val stringValueInComponent:sComponent];
+    
+    if ([s length] > 0) {
+      [_response appendContentString:@";"];
+      [_response appendContentString:key];
+      [_response appendContentString:@"="];
+      [_response appendContentString:s];
+    }
+  }
+  
+  /* add value */
+  [_response appendContentString:@":"];
+
+  if ([valType length] == 0) {
+    val = [val stringValue];
+  }
+  else if ([valType isEqualToString:@"datetime"]) {
+    static NSString *calfmt = @"%Y%m%dT%H%M00Z";
+    
+    if ([val respondsToSelector:@selector(descriptionWithCalendarFormat:)]) {
+      static NSTimeZone *gmt = nil;
+      if (gmt == nil) gmt = [NSTimeZone timeZoneWithAbbreviation:@"GMT"];
+      [val setTimeZone:gmt];
+      val = [val descriptionWithCalendarFormat:calfmt];
+    }
+    else
+      val = [val stringValue];
+  }
+  else
+    val = [val stringValue];
+  
+  [_response appendContentString:val];
+  [self->template appendToResponse:_response inContext:_ctx];
+}
+
+@end /* IcalProperty */
diff --git a/SOPE/NGCards/IcalResponse.h b/SOPE/NGCards/IcalResponse.h
new file mode 100644 (file)
index 0000000..1b2e258
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+  Copyright (C) 2000-2005 SKYRIX Software AG
+
+  This file is part of SOPE.
+
+  SOPE is free software; you can redistribute it and/or modify it under
+  the terms of the GNU Lesser General Public License as published by the
+  Free Software Foundation; either version 2, or (at your option) any
+  later version.
+
+  SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+  License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with SOPE; see the file COPYING.  If not, write to the
+  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+  02111-1307, USA.
+*/
+
+#ifndef __SkyDaemon_skyaptd_IcalResponse_H__
+#define __SkyDaemon_skyaptd_IcalResponse_H__
+
+#import <Foundation/NSObject.h>
+
+@class NSMutableString, NSData;
+
+@interface IcalResponse : NSObject
+{
+  NSMutableString *content;
+  BOOL            isFinished;
+}
+
+- (id)initWithCapacity:(unsigned)_capacity;
+
+- (void)finish;
+- (NSString *)asString;
+- (BOOL)appendLine:(NSString *)_line;
+
+// _attr:_line
+- (BOOL)appendLine:(NSString *)_line forAttribute:(NSString *)_attr;
+
+@end /* IcalResponse */
+
+#endif /* __SkyDaemon_skyaptd_IcalResponse_H__ */
diff --git a/SOPE/NGCards/IcalResponse.m b/SOPE/NGCards/IcalResponse.m
new file mode 100644 (file)
index 0000000..1e90c2b
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+  Copyright (C) 2000-2005 SKYRIX Software AG
+
+  This file is part of SOPE.
+
+  SOPE is free software; you can redistribute it and/or modify it under
+  the terms of the GNU Lesser General Public License as published by the
+  Free Software Foundation; either version 2, or (at your option) any
+  later version.
+
+  SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+  License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with SOPE; see the file COPYING.  If not, write to the
+  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+  02111-1307, USA.
+*/
+
+#include "IcalResponse.h"
+#import <Foundation/Foundation.h>
+
+@implementation IcalResponse
+
+- (void)_initContent {
+  [self appendLine:@"BEGIN:VCALENDAR"];
+  [self appendLine:@"VERSION:2.0"];
+  [self appendLine:@"PRODID:-//skyrix42/scheduler/skyaptd v.1.0//"];
+}
+
+- (id)init {
+  if ((self = [super init])) {
+    self->content    = [[NSMutableString alloc] initWithCapacity:0xFFFF];
+    self->isFinished = NO;
+    [self _initContent];
+  }
+  return self;
+}
+- (id)initWithCapacity:(unsigned)_capacity {
+  if ((self = [super init])) {
+    self->content    = [[NSMutableString alloc] initWithCapacity:_capacity];
+    self->isFinished = NO;
+    [self _initContent];
+  }
+  return self;
+}
+
+- (void)dealloc {
+  [self->content release];
+  [super dealloc];
+}
+
+
+- (BOOL)appendLine:(NSString *)_line {
+  if (self->isFinished) {
+    NSLog(@"WARNING[%s]: already finished!", __PRETTY_FUNCTION__);
+    return NO;
+  }
+  // limit length to 75 chars
+  while ([_line length] > 75) {
+    [self appendLine:[_line substringToIndex:75]];
+    _line = [@" " stringByAppendingString:[_line substringFromIndex:75]];
+  }
+
+  [self->content appendString:_line];
+  [self->content appendString:@"\r\n"];
+  
+  return YES;
+}
+
+- (BOOL)appendLine:(NSString *)_line forAttribute:(NSString *)_attr {
+  _line = [NSString stringWithFormat:@"%@:%@", _attr, _line];
+  return [self appendLine:_line];
+}
+
+
+- (void)finish {
+  if (self->isFinished) return;
+  [self appendLine:@"END:VCALENDAR"];
+  self->isFinished = YES;
+}
+
+- (NSString *)asString {
+  [self finish];
+  return [[self->content copy] autorelease];
+}
+
+@end /* IcalResponse */
diff --git a/SOPE/NGCards/NGCards-Info.plist b/SOPE/NGCards/NGCards-Info.plist
new file mode 100644 (file)
index 0000000..0080a1c
--- /dev/null
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+       <key>CFBundleDevelopmentRegion</key>
+       <string>English</string>
+       <key>CFBundleExecutable</key>
+       <string>NGiCal</string>
+       <key>CFBundleGetInfoString</key>
+       <string></string>
+       <key>CFBundleIdentifier</key>
+       <string>org.OpenGroupware.SOPE.ical.NGiCal</string>
+       <key>CFBundleInfoDictionaryVersion</key>
+       <string>6.0</string>
+       <key>CFBundlePackageType</key>
+       <string>FMWK</string>
+       <key>CFBundleShortVersionString</key>
+       <string></string>
+       <key>CFBundleSignature</key>
+       <string>????</string>
+       <key>CFBundleVersion</key>
+       <string>4.5</string>
+</dict>
+</plist>
diff --git a/SOPE/NGCards/NGCards.h b/SOPE/NGCards/NGCards.h
new file mode 100644 (file)
index 0000000..f58f9a8
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+  Copyright (C) 2000-2005 SKYRIX Software AG
+
+  This file is part of SOPE.
+
+  SOPE is free software; you can redistribute it and/or modify it under
+  the terms of the GNU Lesser General Public License as published by the
+  Free Software Foundation; either version 2, or (at your option) any
+  later version.
+
+  SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+  License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with SOPE; see the file COPYING.  If not, write to the
+  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+  02111-1307, USA.
+*/
+
+#ifndef __NGCards_H__
+#define __NGCards_H__
+
+#import <NGCards/iCalAttachment.h>
+#import <NGCards/iCalObject.h>
+#import <NGCards/iCalEntityObject.h>
+#import <NGCards/iCalRepeatableEntityObject.h>
+#import <NGCards/iCalCalendar.h>
+#import <NGCards/iCalToDo.h>
+#import <NGCards/iCalJournal.h>
+#import <NGCards/iCalEvent.h>
+#import <NGCards/iCalFreeBusy.h>
+#import <NGCards/iCalPerson.h>
+#import <NGCards/iCalAlarm.h>
+#import <NGCards/iCalTrigger.h>
+
+#import <NGCards/iCalEventChanges.h>
+
+#import <NGCards/iCalRecurrenceRule.h>
+#import <NGCards/iCalRecurrenceCalculator.h>
+
+#import <NGCards/NSCalendarDate+ICal.h>
+
+#import <NGCards/NSArray+NGCards.h>
+#import <NGCards/NSCalendarDate+ICal.h>
+#import <NGCards/NSCalendarDate+NGCards.h>
+#import <NGCards/NSDictionary+NGCards.h>
+#import <NGCards/NSString+NGCards.h>
+
+#endif /* __NGCards_H__ */
diff --git a/SOPE/NGCards/NGCards.xcodeproj/project.pbxproj b/SOPE/NGCards/NGCards.xcodeproj/project.pbxproj
new file mode 100644 (file)
index 0000000..43e1ec5
--- /dev/null
@@ -0,0 +1,1093 @@
+// !$*UTF8*$!
+{
+       archiveVersion = 1;
+       classes = {
+       };
+       objectVersion = 42;
+       objects = {
+
+/* Begin PBXBuildFile section */
+               AD2EC1C408E2E630006B7836 /* iCalDailyRecurrenceCalculator.m in Sources */ = {isa = PBXBuildFile; fileRef = AD2EC1C008E2E630006B7836 /* iCalDailyRecurrenceCalculator.m */; };
+               AD2EC1C508E2E630006B7836 /* iCalMonthlyRecurrenceCalculator.m in Sources */ = {isa = PBXBuildFile; fileRef = AD2EC1C108E2E630006B7836 /* iCalMonthlyRecurrenceCalculator.m */; };
+               AD2EC1C608E2E630006B7836 /* iCalWeeklyRecurrenceCalculator.m in Sources */ = {isa = PBXBuildFile; fileRef = AD2EC1C208E2E630006B7836 /* iCalWeeklyRecurrenceCalculator.m */; };
+               AD2EC1C708E2E630006B7836 /* iCalYearlyRecurrenceCalculator.m in Sources */ = {isa = PBXBuildFile; fileRef = AD2EC1C308E2E630006B7836 /* iCalYearlyRecurrenceCalculator.m */; };
+               AD596D9E083769C500C4D81D /* NGICalSaxHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = AD596D8A083769C500C4D81D /* NGICalSaxHandler.h */; };
+               AD596D9F083769C500C4D81D /* NGICalSaxHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = AD596D8B083769C500C4D81D /* NGICalSaxHandler.m */; };
+               AD596DA0083769C500C4D81D /* NGVCard.h in Headers */ = {isa = PBXBuildFile; fileRef = AD596D8C083769C500C4D81D /* NGVCard.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               AD596DA1083769C500C4D81D /* NGVCard.m in Sources */ = {isa = PBXBuildFile; fileRef = AD596D8D083769C500C4D81D /* NGVCard.m */; };
+               AD596DA2083769C500C4D81D /* NGVCardAddress.h in Headers */ = {isa = PBXBuildFile; fileRef = AD596D8E083769C500C4D81D /* NGVCardAddress.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               AD596DA3083769C500C4D81D /* NGVCardAddress.m in Sources */ = {isa = PBXBuildFile; fileRef = AD596D8F083769C500C4D81D /* NGVCardAddress.m */; };
+               AD596DA4083769C500C4D81D /* NGVCardName.h in Headers */ = {isa = PBXBuildFile; fileRef = AD596D90083769C500C4D81D /* NGVCardName.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               AD596DA5083769C500C4D81D /* NGVCardName.m in Sources */ = {isa = PBXBuildFile; fileRef = AD596D91083769C500C4D81D /* NGVCardName.m */; };
+               AD596DA6083769C500C4D81D /* NGVCardOrg.h in Headers */ = {isa = PBXBuildFile; fileRef = AD596D92083769C500C4D81D /* NGVCardOrg.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               AD596DA7083769C500C4D81D /* NGVCardOrg.m in Sources */ = {isa = PBXBuildFile; fileRef = AD596D93083769C500C4D81D /* NGVCardOrg.m */; };
+               AD596DA8083769C500C4D81D /* NGVCardPhone.h in Headers */ = {isa = PBXBuildFile; fileRef = AD596D94083769C500C4D81D /* NGVCardPhone.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               AD596DA9083769C500C4D81D /* NGVCardPhone.m in Sources */ = {isa = PBXBuildFile; fileRef = AD596D95083769C500C4D81D /* NGVCardPhone.m */; };
+               AD596DAA083769C500C4D81D /* NGVCardSaxHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = AD596D96083769C500C4D81D /* NGVCardSaxHandler.h */; };
+               AD596DAB083769C500C4D81D /* NGVCardSaxHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = AD596D97083769C500C4D81D /* NGVCardSaxHandler.m */; };
+               AD596DAC083769C500C4D81D /* NGVCardSimpleValue.h in Headers */ = {isa = PBXBuildFile; fileRef = AD596D98083769C500C4D81D /* NGVCardSimpleValue.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               AD596DAD083769C500C4D81D /* NGVCardSimpleValue.m in Sources */ = {isa = PBXBuildFile; fileRef = AD596D99083769C500C4D81D /* NGVCardSimpleValue.m */; };
+               AD596DAE083769C500C4D81D /* NGVCardStrArrayValue.h in Headers */ = {isa = PBXBuildFile; fileRef = AD596D9A083769C500C4D81D /* NGVCardStrArrayValue.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               AD596DAF083769C500C4D81D /* NGVCardStrArrayValue.m in Sources */ = {isa = PBXBuildFile; fileRef = AD596D9B083769C500C4D81D /* NGVCardStrArrayValue.m */; };
+               AD596DB0083769C500C4D81D /* NGVCardValue.h in Headers */ = {isa = PBXBuildFile; fileRef = AD596D9C083769C500C4D81D /* NGVCardValue.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               AD596DB1083769C500C4D81D /* NGVCardValue.m in Sources */ = {isa = PBXBuildFile; fileRef = AD596D9D083769C500C4D81D /* NGVCardValue.m */; };
+               AD770E6907AE627500F5C7A1 /* iCalRecurrenceRule.h in Headers */ = {isa = PBXBuildFile; fileRef = AD770E6707AE627500F5C7A1 /* iCalRecurrenceRule.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               AD770E6A07AE627500F5C7A1 /* iCalRecurrenceRule.m in Sources */ = {isa = PBXBuildFile; fileRef = AD770E6807AE627500F5C7A1 /* iCalRecurrenceRule.m */; };
+               AD77103E07AE8F8500F5C7A1 /* iCalRepeatableEntityObject.h in Headers */ = {isa = PBXBuildFile; fileRef = AD77103C07AE8F8500F5C7A1 /* iCalRepeatableEntityObject.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               AD77103F07AE8F8500F5C7A1 /* iCalRepeatableEntityObject.m in Sources */ = {isa = PBXBuildFile; fileRef = AD77103D07AE8F8500F5C7A1 /* iCalRepeatableEntityObject.m */; };
+               AD8BF0F80701902800EC239A /* XmlRpc.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AD8BF0F70701902800EC239A /* XmlRpc.framework */; };
+               ADAACE6807B3973900FC48D6 /* iCalRecurrenceCalculator.h in Headers */ = {isa = PBXBuildFile; fileRef = ADAACE6607B3973900FC48D6 /* iCalRecurrenceCalculator.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               ADAACE6907B3973900FC48D6 /* iCalRecurrenceCalculator.m in Sources */ = {isa = PBXBuildFile; fileRef = ADAACE6707B3973900FC48D6 /* iCalRecurrenceCalculator.m */; };
+               ADAAD00707B6AF7700FC48D6 /* SenTestingKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ADAAD00607B6AF7700FC48D6 /* SenTestingKit.framework */; };
+               ADAAD02307B6AFD700FC48D6 /* iCalRecurrenceCalculatorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = ADAAD02107B6AFD700FC48D6 /* iCalRecurrenceCalculatorTests.m */; };
+               ADAAD04207B6B05000FC48D6 /* common.h in Headers */ = {isa = PBXBuildFile; fileRef = ADAAD04107B6B05000FC48D6 /* common.h */; };
+               ADAAD0A307B6B74F00FC48D6 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ADDF503B06DE528200C4E7F8 /* Foundation.framework */; };
+               ADAAD11107B6B75300FC48D6 /* NGExtensions.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ADDF503F06DE52F600C4E7F8 /* NGExtensions.framework */; };
+               ADAAD11207B6B7A100FC48D6 /* NGCards.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ADDF4E6006DE4FF200C4E7F8 /* NGCards.framework */; };
+               ADBE3DF8072713AF000FEA6A /* iCalRenderer.h in Headers */ = {isa = PBXBuildFile; fileRef = ADBE3DF7072713AF000FEA6A /* iCalRenderer.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               ADBE3DFA072713C2000FEA6A /* iCalRenderer.m in Sources */ = {isa = PBXBuildFile; fileRef = ADBE3DF9072713C2000FEA6A /* iCalRenderer.m */; };
+               ADD1FC9B06E4D6D400E387F0 /* iCalEventChanges.h in Headers */ = {isa = PBXBuildFile; fileRef = ADD1FC9906E4D6D400E387F0 /* iCalEventChanges.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               ADD1FC9C06E4D6D400E387F0 /* iCalEventChanges.m in Sources */ = {isa = PBXBuildFile; fileRef = ADD1FC9A06E4D6D400E387F0 /* iCalEventChanges.m */; };
+               ADDF4F4106DE513D00C4E7F8 /* common.h in Headers */ = {isa = PBXBuildFile; fileRef = ADDF4F1306DE513D00C4E7F8 /* common.h */; };
+               ADDF4F4206DE513D00C4E7F8 /* COPYING in Resources */ = {isa = PBXBuildFile; fileRef = ADDF4F1406DE513D00C4E7F8 /* COPYING */; };
+               ADDF4F4306DE513D00C4E7F8 /* COPYRIGHT in Resources */ = {isa = PBXBuildFile; fileRef = ADDF4F1506DE513D00C4E7F8 /* COPYRIGHT */; };
+               ADDF4F4706DE513D00C4E7F8 /* iCalAlarm.h in Headers */ = {isa = PBXBuildFile; fileRef = ADDF4F1906DE513D00C4E7F8 /* iCalAlarm.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               ADDF4F4806DE513D00C4E7F8 /* iCalAlarm.m in Sources */ = {isa = PBXBuildFile; fileRef = ADDF4F1A06DE513D00C4E7F8 /* iCalAlarm.m */; };
+               ADDF4F4906DE513D00C4E7F8 /* iCalAttachment.h in Headers */ = {isa = PBXBuildFile; fileRef = ADDF4F1B06DE513D00C4E7F8 /* iCalAttachment.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               ADDF4F4A06DE513D00C4E7F8 /* iCalAttachment.m in Sources */ = {isa = PBXBuildFile; fileRef = ADDF4F1C06DE513D00C4E7F8 /* iCalAttachment.m */; };
+               ADDF4F4B06DE513D00C4E7F8 /* iCalCalendar.h in Headers */ = {isa = PBXBuildFile; fileRef = ADDF4F1D06DE513D00C4E7F8 /* iCalCalendar.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               ADDF4F4C06DE513D00C4E7F8 /* iCalCalendar.m in Sources */ = {isa = PBXBuildFile; fileRef = ADDF4F1E06DE513D00C4E7F8 /* iCalCalendar.m */; };
+               ADDF4F4D06DE513D00C4E7F8 /* iCalDataSource.h in Headers */ = {isa = PBXBuildFile; fileRef = ADDF4F1F06DE513D00C4E7F8 /* iCalDataSource.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               ADDF4F4E06DE513D00C4E7F8 /* iCalDataSource.m in Sources */ = {isa = PBXBuildFile; fileRef = ADDF4F2006DE513D00C4E7F8 /* iCalDataSource.m */; };
+               ADDF4F4F06DE513D00C4E7F8 /* iCalDateHolder.h in Headers */ = {isa = PBXBuildFile; fileRef = ADDF4F2106DE513D00C4E7F8 /* iCalDateHolder.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               ADDF4F5006DE513D00C4E7F8 /* iCalDateHolder.m in Sources */ = {isa = PBXBuildFile; fileRef = ADDF4F2206DE513D00C4E7F8 /* iCalDateHolder.m */; };
+               ADDF4F5106DE513D00C4E7F8 /* iCalDuration.h in Headers */ = {isa = PBXBuildFile; fileRef = ADDF4F2306DE513D00C4E7F8 /* iCalDuration.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               ADDF4F5206DE513D00C4E7F8 /* iCalDuration.m in Sources */ = {isa = PBXBuildFile; fileRef = ADDF4F2406DE513D00C4E7F8 /* iCalDuration.m */; };
+               ADDF4F5406DE513D00C4E7F8 /* iCalEntityObject.h in Headers */ = {isa = PBXBuildFile; fileRef = ADDF4F2606DE513D00C4E7F8 /* iCalEntityObject.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               ADDF4F5506DE513D00C4E7F8 /* iCalEntityObject.m in Sources */ = {isa = PBXBuildFile; fileRef = ADDF4F2706DE513D00C4E7F8 /* iCalEntityObject.m */; };
+               ADDF4F5606DE513D00C4E7F8 /* iCalEvent.h in Headers */ = {isa = PBXBuildFile; fileRef = ADDF4F2806DE513D00C4E7F8 /* iCalEvent.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               ADDF4F5706DE513D00C4E7F8 /* iCalEvent.m in Sources */ = {isa = PBXBuildFile; fileRef = ADDF4F2906DE513D00C4E7F8 /* iCalEvent.m */; };
+               ADDF4F5806DE513D00C4E7F8 /* iCalFreeBusy.h in Headers */ = {isa = PBXBuildFile; fileRef = ADDF4F2A06DE513D00C4E7F8 /* iCalFreeBusy.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               ADDF4F5906DE513D00C4E7F8 /* iCalFreeBusy.m in Sources */ = {isa = PBXBuildFile; fileRef = ADDF4F2B06DE513D00C4E7F8 /* iCalFreeBusy.m */; };
+               ADDF4F5A06DE513D00C4E7F8 /* iCalJournal.h in Headers */ = {isa = PBXBuildFile; fileRef = ADDF4F2C06DE513D00C4E7F8 /* iCalJournal.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               ADDF4F5B06DE513D00C4E7F8 /* iCalJournal.m in Sources */ = {isa = PBXBuildFile; fileRef = ADDF4F2D06DE513D00C4E7F8 /* iCalJournal.m */; };
+               ADDF4F5C06DE513D00C4E7F8 /* iCalObject.h in Headers */ = {isa = PBXBuildFile; fileRef = ADDF4F2E06DE513D00C4E7F8 /* iCalObject.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               ADDF4F5D06DE513D00C4E7F8 /* iCalObject.m in Sources */ = {isa = PBXBuildFile; fileRef = ADDF4F2F06DE513D00C4E7F8 /* iCalObject.m */; };
+               ADDF4F5E06DE513D00C4E7F8 /* iCalPerson.h in Headers */ = {isa = PBXBuildFile; fileRef = ADDF4F3006DE513D00C4E7F8 /* iCalPerson.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               ADDF4F5F06DE513D00C4E7F8 /* iCalPerson.m in Sources */ = {isa = PBXBuildFile; fileRef = ADDF4F3106DE513D00C4E7F8 /* iCalPerson.m */; };
+               ADDF4F6206DE513D00C4E7F8 /* iCalToDo.h in Headers */ = {isa = PBXBuildFile; fileRef = ADDF4F3406DE513D00C4E7F8 /* iCalToDo.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               ADDF4F6306DE513D00C4E7F8 /* iCalToDo.m in Sources */ = {isa = PBXBuildFile; fileRef = ADDF4F3506DE513D00C4E7F8 /* iCalToDo.m */; };
+               ADDF4F6406DE513D00C4E7F8 /* iCalTrigger.h in Headers */ = {isa = PBXBuildFile; fileRef = ADDF4F3606DE513D00C4E7F8 /* iCalTrigger.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               ADDF4F6506DE513D00C4E7F8 /* iCalTrigger.m in Sources */ = {isa = PBXBuildFile; fileRef = ADDF4F3706DE513D00C4E7F8 /* iCalTrigger.m */; };
+               ADDF4F6606DE513D00C4E7F8 /* NGCards.h in Headers */ = {isa = PBXBuildFile; fileRef = ADDF4F3806DE513D00C4E7F8 /* NGCards.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               ADDF4F6806DE513D00C4E7F8 /* NSCalendarDate+ICal.h in Headers */ = {isa = PBXBuildFile; fileRef = ADDF4F3A06DE513D00C4E7F8 /* NSCalendarDate+ICal.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               ADDF4F6906DE513D00C4E7F8 /* NSCalendarDate+ICal.m in Sources */ = {isa = PBXBuildFile; fileRef = ADDF4F3B06DE513D00C4E7F8 /* NSCalendarDate+ICal.m */; };
+               ADDF4F6C06DE513D00C4E7F8 /* README in Resources */ = {isa = PBXBuildFile; fileRef = ADDF4F3E06DE513D00C4E7F8 /* README */; };
+               ADDF503C06DE528200C4E7F8 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ADDF503B06DE528200C4E7F8 /* Foundation.framework */; };
+               ADDF504106DE52F600C4E7F8 /* DOM.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ADDF503D06DE52F600C4E7F8 /* DOM.framework */; };
+               ADDF504206DE52F600C4E7F8 /* EOControl.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ADDF503E06DE52F600C4E7F8 /* EOControl.framework */; };
+               ADDF504306DE52F600C4E7F8 /* NGExtensions.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ADDF503F06DE52F600C4E7F8 /* NGExtensions.framework */; };
+               ADDF504406DE52F600C4E7F8 /* SaxObjC.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ADDF504006DE52F600C4E7F8 /* SaxObjC.framework */; };
+               ADDF51AC06DE56E100C4E7F8 /* NGCards.xmap in CopyFiles */ = {isa = PBXBuildFile; fileRef = ADDF4F3906DE513D00C4E7F8 /* NGCards.xmap */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXBuildStyle section */
+               ADDF4E5306DE4FC600C4E7F8 /* Development */ = {
+                       isa = PBXBuildStyle;
+                       buildSettings = {
+                               COPY_PHASE_STRIP = NO;
+                               GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
+                               GCC_OPTIMIZATION_LEVEL = 0;
+                               GCC_PREPROCESSOR_DEFINITIONS = (
+                                       "APPLE_RUNTIME=1",
+                                       "NeXT_Foundation_LIBRARY=1",
+                                       "COCOA_Foundation_LIBRARY=1",
+                                       "NeXT_RUNTIME=1",
+                                       "COMPILE_AS_FRAMEWORK=1",
+                                       "DEBUG=1",
+                               );
+                       };
+                       name = Development;
+               };
+               ADDF4E5406DE4FC800C4E7F8 /* Wrapper */ = {
+                       isa = PBXBuildStyle;
+                       buildSettings = {
+                               COPY_PHASE_STRIP = YES;
+                               DEPLOYMENT_LOCATION = NO;
+                               DEPLOYMENT_POSTPROCESSING = YES;
+                               DSTROOT = /;
+                               DYLIB_INSTALL_NAME_BASE = "@executable_path/../Frameworks/";
+                               FRAMEWORK_SEARCH_PATHS = "$(USER_LIBRARY_DIR)/EmbeddedFrameworks";
+                               GCC_ENABLE_FIX_AND_CONTINUE = NO;
+                               GCC_GENERATE_DEBUGGING_SYMBOLS = NO;
+                               GCC_OPTIMIZATION_LEVEL = 3;
+                               GCC_PREPROCESSOR_DEFINITIONS = (
+                                       "APPLE_RUNTIME=1",
+                                       "NeXT_Foundation_LIBRARY=1",
+                                       "COCOA_Foundation_LIBRARY=1",
+                                       "NeXT_RUNTIME=1",
+                                       "COMPILE_AS_FRAMEWORK=1",
+                               );
+                               SKIP_INSTALL = YES;
+                               SYMROOT = "$(USER_LIBRARY_DIR)/EmbeddedFrameworks";
+                               TEMP_DIR = "$(SYMROOT)/$(PROJECT_NAME).build";
+                               UNSTRIPPED_PRODUCT = NO;
+                               ZERO_LINK = NO;
+                       };
+                       name = Wrapper;
+               };
+/* End PBXBuildStyle section */
+
+/* Begin PBXContainerItemProxy section */
+               ADAACFED07B6AEBF00FC48D6 /* PBXContainerItemProxy */ = {
+                       isa = PBXContainerItemProxy;
+                       containerPortal = ADDF4E5506DE4FC800C4E7F8 /* Project object */;
+                       proxyType = 1;
+                       remoteGlobalIDString = ADDF4E5F06DE4FF200C4E7F8;
+                       remoteInfo = NGCards;
+               };
+/* End PBXContainerItemProxy section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+               ADDF51AB06DE56C500C4E7F8 /* CopyFiles */ = {
+                       isa = PBXCopyFilesBuildPhase;
+                       buildActionMask = 8;
+                       dstPath = /Library/SaxMappings;
+                       dstSubfolderSpec = 0;
+                       files = (
+                               ADDF51AC06DE56E100C4E7F8 /* NGCards.xmap in CopyFiles */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 1;
+               };
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+               AD2EC1C008E2E630006B7836 /* iCalDailyRecurrenceCalculator.m */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.objc; path = iCalDailyRecurrenceCalculator.m; sourceTree = "<group>"; };
+               AD2EC1C108E2E630006B7836 /* iCalMonthlyRecurrenceCalculator.m */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.objc; path = iCalMonthlyRecurrenceCalculator.m; sourceTree = "<group>"; };
+               AD2EC1C208E2E630006B7836 /* iCalWeeklyRecurrenceCalculator.m */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.objc; path = iCalWeeklyRecurrenceCalculator.m; sourceTree = "<group>"; };
+               AD2EC1C308E2E630006B7836 /* iCalYearlyRecurrenceCalculator.m */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.objc; path = iCalYearlyRecurrenceCalculator.m; sourceTree = "<group>"; };
+               AD596D8A083769C500C4D81D /* NGICalSaxHandler.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = NGICalSaxHandler.h; sourceTree = "<group>"; };
+               AD596D8B083769C500C4D81D /* NGICalSaxHandler.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = NGICalSaxHandler.m; sourceTree = "<group>"; };
+               AD596D8C083769C500C4D81D /* NGVCard.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = NGVCard.h; sourceTree = "<group>"; };
+               AD596D8D083769C500C4D81D /* NGVCard.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = NGVCard.m; sourceTree = "<group>"; };
+               AD596D8E083769C500C4D81D /* NGVCardAddress.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = NGVCardAddress.h; sourceTree = "<group>"; };
+               AD596D8F083769C500C4D81D /* NGVCardAddress.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = NGVCardAddress.m; sourceTree = "<group>"; };
+               AD596D90083769C500C4D81D /* NGVCardName.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = NGVCardName.h; sourceTree = "<group>"; };
+               AD596D91083769C500C4D81D /* NGVCardName.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = NGVCardName.m; sourceTree = "<group>"; };
+               AD596D92083769C500C4D81D /* NGVCardOrg.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = NGVCardOrg.h; sourceTree = "<group>"; };
+               AD596D93083769C500C4D81D /* NGVCardOrg.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = NGVCardOrg.m; sourceTree = "<group>"; };
+               AD596D94083769C500C4D81D /* NGVCardPhone.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = NGVCardPhone.h; sourceTree = "<group>"; };
+               AD596D95083769C500C4D81D /* NGVCardPhone.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = NGVCardPhone.m; sourceTree = "<group>"; };
+               AD596D96083769C500C4D81D /* NGVCardSaxHandler.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = NGVCardSaxHandler.h; sourceTree = "<group>"; };
+               AD596D97083769C500C4D81D /* NGVCardSaxHandler.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = NGVCardSaxHandler.m; sourceTree = "<group>"; };
+               AD596D98083769C500C4D81D /* NGVCardSimpleValue.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = NGVCardSimpleValue.h; sourceTree = "<group>"; };
+               AD596D99083769C500C4D81D /* NGVCardSimpleValue.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = NGVCardSimpleValue.m; sourceTree = "<group>"; };
+               AD596D9A083769C500C4D81D /* NGVCardStrArrayValue.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = NGVCardStrArrayValue.h; sourceTree = "<group>"; };
+               AD596D9B083769C500C4D81D /* NGVCardStrArrayValue.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = NGVCardStrArrayValue.m; sourceTree = "<group>"; };
+               AD596D9C083769C500C4D81D /* NGVCardValue.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = NGVCardValue.h; sourceTree = "<group>"; };
+               AD596D9D083769C500C4D81D /* NGVCardValue.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = NGVCardValue.m; sourceTree = "<group>"; };
+               AD770E6707AE627500F5C7A1 /* iCalRecurrenceRule.h */ = {isa = PBXFileReference; fileEncoding = 5; indentWidth = 2; lastKnownFileType = sourcecode.c.h; path = iCalRecurrenceRule.h; sourceTree = "<group>"; };
+               AD770E6807AE627500F5C7A1 /* iCalRecurrenceRule.m */ = {isa = PBXFileReference; fileEncoding = 5; indentWidth = 2; lastKnownFileType = sourcecode.c.objc; path = iCalRecurrenceRule.m; sourceTree = "<group>"; };
+               AD77103C07AE8F8500F5C7A1 /* iCalRepeatableEntityObject.h */ = {isa = PBXFileReference; fileEncoding = 5; indentWidth = 2; lastKnownFileType = sourcecode.c.h; path = iCalRepeatableEntityObject.h; sourceTree = "<group>"; };
+               AD77103D07AE8F8500F5C7A1 /* iCalRepeatableEntityObject.m */ = {isa = PBXFileReference; fileEncoding = 5; indentWidth = 2; lastKnownFileType = sourcecode.c.objc; path = iCalRepeatableEntityObject.m; sourceTree = "<group>"; };
+               AD8BF0F70701902800EC239A /* XmlRpc.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XmlRpc.framework; path = "$(USER_LIBRARY_DIR)/EmbeddedFrameworks/Wrapper/XmlRpc.framework"; sourceTree = "<absolute>"; };
+               ADAACE6607B3973900FC48D6 /* iCalRecurrenceCalculator.h */ = {isa = PBXFileReference; fileEncoding = 5; indentWidth = 2; lastKnownFileType = sourcecode.c.h; path = iCalRecurrenceCalculator.h; sourceTree = "<group>"; };
+               ADAACE6707B3973900FC48D6 /* iCalRecurrenceCalculator.m */ = {isa = PBXFileReference; fileEncoding = 5; indentWidth = 2; lastKnownFileType = sourcecode.c.objc; path = iCalRecurrenceCalculator.m; sourceTree = "<group>"; };
+               ADAACFEB07B6AEB500FC48D6 /* NGCardsTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = NGCardsTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+               ADAACFEC07B6AEB500FC48D6 /* NGCardsTests-Info.plist */ = {isa = PBXFileReference; indentWidth = 2; lastKnownFileType = text.xml; path = "NGCardsTests-Info.plist"; sourceTree = "<group>"; };
+               ADAAD00607B6AF7700FC48D6 /* SenTestingKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SenTestingKit.framework; path = /Library/Frameworks/SenTestingKit.framework; sourceTree = "<absolute>"; };
+               ADAAD02107B6AFD700FC48D6 /* iCalRecurrenceCalculatorTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = iCalRecurrenceCalculatorTests.m; sourceTree = "<group>"; };
+               ADAAD04007B6B02400FC48D6 /* README */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = README; sourceTree = "<group>"; };
+               ADAAD04107B6B05000FC48D6 /* common.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = common.h; sourceTree = "<group>"; };
+               ADBE3DF7072713AF000FEA6A /* iCalRenderer.h */ = {isa = PBXFileReference; fileEncoding = 5; indentWidth = 2; lastKnownFileType = sourcecode.c.h; path = iCalRenderer.h; sourceTree = "<group>"; };
+               ADBE3DF9072713C2000FEA6A /* iCalRenderer.m */ = {isa = PBXFileReference; fileEncoding = 5; indentWidth = 2; lastKnownFileType = sourcecode.c.objc; path = iCalRenderer.m; sourceTree = "<group>"; };
+               ADBE3DFE072713DB000FEA6A /* fhs.make */ = {isa = PBXFileReference; explicitFileType = sourcecode.make; fileEncoding = 5; indentWidth = 8; path = fhs.make; sourceTree = "<group>"; tabWidth = 8; };
+               ADD1FC9906E4D6D400E387F0 /* iCalEventChanges.h */ = {isa = PBXFileReference; fileEncoding = 5; indentWidth = 2; lastKnownFileType = sourcecode.c.h; path = iCalEventChanges.h; sourceTree = "<group>"; };
+               ADD1FC9A06E4D6D400E387F0 /* iCalEventChanges.m */ = {isa = PBXFileReference; fileEncoding = 5; indentWidth = 2; lastKnownFileType = sourcecode.c.objc; path = iCalEventChanges.m; sourceTree = "<group>"; };
+               ADDF4E6006DE4FF200C4E7F8 /* NGCards.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = NGCards.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+               ADDF4E6206DE4FF200C4E7F8 /* NGCards-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = "NGCards-Info.plist"; sourceTree = "<group>"; };
+               ADDF4F1206DE513D00C4E7F8 /* ChangeLog */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 8; lastKnownFileType = text; path = ChangeLog; sourceTree = "<group>"; tabWidth = 8; usesTabs = 1; };
+               ADDF4F1306DE513D00C4E7F8 /* common.h */ = {isa = PBXFileReference; fileEncoding = 5; indentWidth = 2; lastKnownFileType = sourcecode.c.h; path = common.h; sourceTree = "<group>"; };
+               ADDF4F1406DE513D00C4E7F8 /* COPYING */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = COPYING; sourceTree = "<group>"; };
+               ADDF4F1506DE513D00C4E7F8 /* COPYRIGHT */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = COPYRIGHT; sourceTree = "<group>"; };
+               ADDF4F1606DE513D00C4E7F8 /* GNUmakefile */ = {isa = PBXFileReference; explicitFileType = sourcecode.make; fileEncoding = 4; indentWidth = 8; path = GNUmakefile; sourceTree = "<group>"; tabWidth = 8; usesTabs = 1; };
+               ADDF4F1706DE513D00C4E7F8 /* GNUmakefile.postamble */ = {isa = PBXFileReference; explicitFileType = sourcecode.make; fileEncoding = 4; indentWidth = 8; path = GNUmakefile.postamble; sourceTree = "<group>"; tabWidth = 8; usesTabs = 1; };
+               ADDF4F1806DE513D00C4E7F8 /* GNUmakefile.preamble */ = {isa = PBXFileReference; explicitFileType = sourcecode.make; fileEncoding = 4; indentWidth = 8; path = GNUmakefile.preamble; sourceTree = "<group>"; tabWidth = 8; usesTabs = 1; };
+               ADDF4F1906DE513D00C4E7F8 /* iCalAlarm.h */ = {isa = PBXFileReference; fileEncoding = 5; indentWidth = 2; lastKnownFileType = sourcecode.c.h; path = iCalAlarm.h; sourceTree = "<group>"; };
+               ADDF4F1A06DE513D00C4E7F8 /* iCalAlarm.m */ = {isa = PBXFileReference; fileEncoding = 5; indentWidth = 2; lastKnownFileType = sourcecode.c.objc; path = iCalAlarm.m; sourceTree = "<group>"; };
+               ADDF4F1B06DE513D00C4E7F8 /* iCalAttachment.h */ = {isa = PBXFileReference; fileEncoding = 5; indentWidth = 2; lastKnownFileType = sourcecode.c.h; path = iCalAttachment.h; sourceTree = "<group>"; };
+               ADDF4F1C06DE513D00C4E7F8 /* iCalAttachment.m */ = {isa = PBXFileReference; fileEncoding = 5; indentWidth = 2; lastKnownFileType = sourcecode.c.objc; path = iCalAttachment.m; sourceTree = "<group>"; };
+               ADDF4F1D06DE513D00C4E7F8 /* iCalCalendar.h */ = {isa = PBXFileReference; fileEncoding = 5; indentWidth = 2; lastKnownFileType = sourcecode.c.h; path = iCalCalendar.h; sourceTree = "<group>"; };
+               ADDF4F1E06DE513D00C4E7F8 /* iCalCalendar.m */ = {isa = PBXFileReference; fileEncoding = 5; indentWidth = 2; lastKnownFileType = sourcecode.c.objc; path = iCalCalendar.m; sourceTree = "<group>"; };
+               ADDF4F1F06DE513D00C4E7F8 /* iCalDataSource.h */ = {isa = PBXFileReference; fileEncoding = 5; indentWidth = 2; lastKnownFileType = sourcecode.c.h; path = iCalDataSource.h; sourceTree = "<group>"; };
+               ADDF4F2006DE513D00C4E7F8 /* iCalDataSource.m */ = {isa = PBXFileReference; fileEncoding = 5; indentWidth = 2; lastKnownFileType = sourcecode.c.objc; path = iCalDataSource.m; sourceTree = "<group>"; };
+               ADDF4F2106DE513D00C4E7F8 /* iCalDateHolder.h */ = {isa = PBXFileReference; fileEncoding = 5; indentWidth = 2; lastKnownFileType = sourcecode.c.h; path = iCalDateHolder.h; sourceTree = "<group>"; };
+               ADDF4F2206DE513D00C4E7F8 /* iCalDateHolder.m */ = {isa = PBXFileReference; fileEncoding = 5; indentWidth = 2; lastKnownFileType = sourcecode.c.objc; path = iCalDateHolder.m; sourceTree = "<group>"; };
+               ADDF4F2306DE513D00C4E7F8 /* iCalDuration.h */ = {isa = PBXFileReference; fileEncoding = 5; indentWidth = 2; lastKnownFileType = sourcecode.c.h; path = iCalDuration.h; sourceTree = "<group>"; };
+               ADDF4F2406DE513D00C4E7F8 /* iCalDuration.m */ = {isa = PBXFileReference; fileEncoding = 5; indentWidth = 2; lastKnownFileType = sourcecode.c.objc; path = iCalDuration.m; sourceTree = "<group>"; };
+               ADDF4F2506DE513D00C4E7F8 /* IcalElements.m */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.c.objc; path = IcalElements.m; sourceTree = "<group>"; };
+               ADDF4F2606DE513D00C4E7F8 /* iCalEntityObject.h */ = {isa = PBXFileReference; fileEncoding = 5; indentWidth = 2; lastKnownFileType = sourcecode.c.h; path = iCalEntityObject.h; sourceTree = "<group>"; };
+               ADDF4F2706DE513D00C4E7F8 /* iCalEntityObject.m */ = {isa = PBXFileReference; fileEncoding = 5; indentWidth = 2; lastKnownFileType = sourcecode.c.objc; path = iCalEntityObject.m; sourceTree = "<group>"; };
+               ADDF4F2806DE513D00C4E7F8 /* iCalEvent.h */ = {isa = PBXFileReference; fileEncoding = 5; indentWidth = 2; lastKnownFileType = sourcecode.c.h; path = iCalEvent.h; sourceTree = "<group>"; };
+               ADDF4F2906DE513D00C4E7F8 /* iCalEvent.m */ = {isa = PBXFileReference; fileEncoding = 5; indentWidth = 2; lastKnownFileType = sourcecode.c.objc; path = iCalEvent.m; sourceTree = "<group>"; };
+               ADDF4F2A06DE513D00C4E7F8 /* iCalFreeBusy.h */ = {isa = PBXFileReference; fileEncoding = 5; indentWidth = 2; lastKnownFileType = sourcecode.c.h; path = iCalFreeBusy.h; sourceTree = "<group>"; };
+               ADDF4F2B06DE513D00C4E7F8 /* iCalFreeBusy.m */ = {isa = PBXFileReference; fileEncoding = 5; indentWidth = 2; lastKnownFileType = sourcecode.c.objc; path = iCalFreeBusy.m; sourceTree = "<group>"; };
+               ADDF4F2C06DE513D00C4E7F8 /* iCalJournal.h */ = {isa = PBXFileReference; fileEncoding = 5; indentWidth = 2; lastKnownFileType = sourcecode.c.h; path = iCalJournal.h; sourceTree = "<group>"; };
+               ADDF4F2D06DE513D00C4E7F8 /* iCalJournal.m */ = {isa = PBXFileReference; fileEncoding = 5; indentWidth = 2; lastKnownFileType = sourcecode.c.objc; path = iCalJournal.m; sourceTree = "<group>"; };
+               ADDF4F2E06DE513D00C4E7F8 /* iCalObject.h */ = {isa = PBXFileReference; fileEncoding = 5; indentWidth = 2; lastKnownFileType = sourcecode.c.h; path = iCalObject.h; sourceTree = "<group>"; };
+               ADDF4F2F06DE513D00C4E7F8 /* iCalObject.m */ = {isa = PBXFileReference; fileEncoding = 5; indentWidth = 2; lastKnownFileType = sourcecode.c.objc; path = iCalObject.m; sourceTree = "<group>"; };
+               ADDF4F3006DE513D00C4E7F8 /* iCalPerson.h */ = {isa = PBXFileReference; fileEncoding = 5; indentWidth = 2; lastKnownFileType = sourcecode.c.h; path = iCalPerson.h; sourceTree = "<group>"; };
+               ADDF4F3106DE513D00C4E7F8 /* iCalPerson.m */ = {isa = PBXFileReference; fileEncoding = 5; indentWidth = 2; lastKnownFileType = sourcecode.c.objc; path = iCalPerson.m; sourceTree = "<group>"; };
+               ADDF4F3206DE513D00C4E7F8 /* IcalResponse.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.c.h; path = IcalResponse.h; sourceTree = "<group>"; };
+               ADDF4F3306DE513D00C4E7F8 /* IcalResponse.m */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.c.objc; path = IcalResponse.m; sourceTree = "<group>"; };
+               ADDF4F3406DE513D00C4E7F8 /* iCalToDo.h */ = {isa = PBXFileReference; fileEncoding = 5; indentWidth = 2; lastKnownFileType = sourcecode.c.h; path = iCalToDo.h; sourceTree = "<group>"; };
+               ADDF4F3506DE513D00C4E7F8 /* iCalToDo.m */ = {isa = PBXFileReference; fileEncoding = 5; indentWidth = 2; lastKnownFileType = sourcecode.c.objc; path = iCalToDo.m; sourceTree = "<group>"; };
+               ADDF4F3606DE513D00C4E7F8 /* iCalTrigger.h */ = {isa = PBXFileReference; fileEncoding = 5; indentWidth = 2; lastKnownFileType = sourcecode.c.h; path = iCalTrigger.h; sourceTree = "<group>"; };
+               ADDF4F3706DE513D00C4E7F8 /* iCalTrigger.m */ = {isa = PBXFileReference; fileEncoding = 5; indentWidth = 2; lastKnownFileType = sourcecode.c.objc; path = iCalTrigger.m; sourceTree = "<group>"; };
+               ADDF4F3806DE513D00C4E7F8 /* NGCards.h */ = {isa = PBXFileReference; fileEncoding = 5; indentWidth = 2; lastKnownFileType = sourcecode.c.h; path = NGCards.h; sourceTree = "<group>"; };
+               ADDF4F3906DE513D00C4E7F8 /* NGCards.xmap */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = text.xml; path = NGCards.xmap; sourceTree = "<group>"; };
+               ADDF4F3A06DE513D00C4E7F8 /* NSCalendarDate+ICal.h */ = {isa = PBXFileReference; fileEncoding = 5; indentWidth = 2; lastKnownFileType = sourcecode.c.h; path = "NSCalendarDate+ICal.h"; sourceTree = "<group>"; };
+               ADDF4F3B06DE513D00C4E7F8 /* NSCalendarDate+ICal.m */ = {isa = PBXFileReference; fileEncoding = 5; indentWidth = 2; lastKnownFileType = sourcecode.c.objc; path = "NSCalendarDate+ICal.m"; sourceTree = "<group>"; };
+               ADDF4F3C06DE513D00C4E7F8 /* NSString+ICal.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.c.h; path = "NSString+ICal.h"; sourceTree = "<group>"; };
+               ADDF4F3D06DE513D00C4E7F8 /* NSString+ICal.m */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.c.objc; path = "NSString+ICal.m"; sourceTree = "<group>"; };
+               ADDF4F3E06DE513D00C4E7F8 /* README */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = README; sourceTree = "<group>"; };
+               ADDF4F3F06DE513D00C4E7F8 /* Version */ = {isa = PBXFileReference; explicitFileType = sourcecode.make; fileEncoding = 4; indentWidth = 8; path = Version; sourceTree = "<group>"; tabWidth = 8; usesTabs = 1; };
+               ADDF503B06DE528200C4E7F8 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = "<absolute>"; };
+               ADDF503D06DE52F600C4E7F8 /* DOM.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = DOM.framework; path = "$(USER_LIBRARY_DIR)/EmbeddedFrameworks/Wrapper/DOM.framework"; sourceTree = "<absolute>"; };
+               ADDF503E06DE52F600C4E7F8 /* EOControl.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = EOControl.framework; path = "$(USER_LIBRARY_DIR)/EmbeddedFrameworks/Wrapper/EOControl.framework"; sourceTree = "<absolute>"; };
+               ADDF503F06DE52F600C4E7F8 /* NGExtensions.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = NGExtensions.framework; path = "$(USER_LIBRARY_DIR)/EmbeddedFrameworks/Wrapper/NGExtensions.framework"; sourceTree = "<absolute>"; };
+               ADDF504006DE52F600C4E7F8 /* SaxObjC.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SaxObjC.framework; path = "$(USER_LIBRARY_DIR)/EmbeddedFrameworks/Wrapper/SaxObjC.framework"; sourceTree = "<absolute>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+               ADAACFE807B6AEB500FC48D6 /* Frameworks */ = {
+                       isa = PBXFrameworksBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               ADAAD0A307B6B74F00FC48D6 /* Foundation.framework in Frameworks */,
+                               ADAAD11107B6B75300FC48D6 /* NGExtensions.framework in Frameworks */,
+                               ADAAD00707B6AF7700FC48D6 /* SenTestingKit.framework in Frameworks */,
+                               ADAAD11207B6B7A100FC48D6 /* NGCards.framework in Frameworks */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               ADDF4E5E06DE4FF200C4E7F8 /* Frameworks */ = {
+                       isa = PBXFrameworksBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               ADDF503C06DE528200C4E7F8 /* Foundation.framework in Frameworks */,
+                               ADDF504206DE52F600C4E7F8 /* EOControl.framework in Frameworks */,
+                               ADDF504306DE52F600C4E7F8 /* NGExtensions.framework in Frameworks */,
+                               ADDF504406DE52F600C4E7F8 /* SaxObjC.framework in Frameworks */,
+                               ADDF504106DE52F600C4E7F8 /* DOM.framework in Frameworks */,
+                               AD8BF0F80701902800EC239A /* XmlRpc.framework in Frameworks */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+               AD596D730837697900C4D81D /* iCal */ = {
+                       isa = PBXGroup;
+                       children = (
+                               ADDF4F7306DE516A00C4E7F8 /* Headers */,
+                               ADDF4FD406DE517900C4E7F8 /* Classes */,
+                       );
+                       name = iCal;
+                       sourceTree = "<group>";
+               };
+               AD596D830837698200C4D81D /* vCard */ = {
+                       isa = PBXGroup;
+                       children = (
+                               AD596D860837699600C4D81D /* Headers */,
+                               AD596D890837699A00C4D81D /* Classes */,
+                       );
+                       name = vCard;
+                       sourceTree = "<group>";
+               };
+               AD596D860837699600C4D81D /* Headers */ = {
+                       isa = PBXGroup;
+                       children = (
+                               AD596D96083769C500C4D81D /* NGVCardSaxHandler.h */,
+                               AD596D8C083769C500C4D81D /* NGVCard.h */,
+                               AD596D9C083769C500C4D81D /* NGVCardValue.h */,
+                               AD596D98083769C500C4D81D /* NGVCardSimpleValue.h */,
+                               AD596D9A083769C500C4D81D /* NGVCardStrArrayValue.h */,
+                               AD596D8E083769C500C4D81D /* NGVCardAddress.h */,
+                               AD596D90083769C500C4D81D /* NGVCardName.h */,
+                               AD596D92083769C500C4D81D /* NGVCardOrg.h */,
+                               AD596D94083769C500C4D81D /* NGVCardPhone.h */,
+                       );
+                       name = Headers;
+                       sourceTree = "<group>";
+               };
+               AD596D890837699A00C4D81D /* Classes */ = {
+                       isa = PBXGroup;
+                       children = (
+                               AD596D97083769C500C4D81D /* NGVCardSaxHandler.m */,
+                               AD596D8D083769C500C4D81D /* NGVCard.m */,
+                               AD596D9D083769C500C4D81D /* NGVCardValue.m */,
+                               AD596D99083769C500C4D81D /* NGVCardSimpleValue.m */,
+                               AD596D9B083769C500C4D81D /* NGVCardStrArrayValue.m */,
+                               AD596D8F083769C500C4D81D /* NGVCardAddress.m */,
+                               AD596D91083769C500C4D81D /* NGVCardName.m */,
+                               AD596D93083769C500C4D81D /* NGVCardOrg.m */,
+                               AD596D95083769C500C4D81D /* NGVCardPhone.m */,
+                       );
+                       name = Classes;
+                       sourceTree = "<group>";
+               };
+               ADAACFFF07B6AF0E00FC48D6 /* Unit Tests */ = {
+                       isa = PBXGroup;
+                       children = (
+                               ADAAD04007B6B02400FC48D6 /* README */,
+                               ADAAD01F07B6AFA600FC48D6 /* Classes */,
+                               ADAAD05C07B6B0CB00FC48D6 /* Resources */,
+                       );
+                       fileEncoding = 5;
+                       indentWidth = 2;
+                       name = "Unit Tests";
+                       path = tests;
+                       sourceTree = "<group>";
+               };
+               ADAAD01F07B6AFA600FC48D6 /* Classes */ = {
+                       isa = PBXGroup;
+                       children = (
+                               ADAAD04107B6B05000FC48D6 /* common.h */,
+                               ADAAD02107B6AFD700FC48D6 /* iCalRecurrenceCalculatorTests.m */,
+                       );
+                       name = Classes;
+                       sourceTree = "<group>";
+               };
+               ADAAD05C07B6B0CB00FC48D6 /* Resources */ = {
+                       isa = PBXGroup;
+                       children = (
+                               ADAACFEC07B6AEB500FC48D6 /* NGCardsTests-Info.plist */,
+                       );
+                       name = Resources;
+                       sourceTree = "<group>";
+               };
+               ADDF4E5106DE4FC600C4E7F8 = {
+                       isa = PBXGroup;
+                       children = (
+                               ADDF4F3E06DE513D00C4E7F8 /* README */,
+                               ADDF4F1406DE513D00C4E7F8 /* COPYING */,
+                               ADDF4F1506DE513D00C4E7F8 /* COPYRIGHT */,
+                               ADDF4F1206DE513D00C4E7F8 /* ChangeLog */,
+                               ADDF4F3F06DE513D00C4E7F8 /* Version */,
+                               ADDF4F7006DE514B00C4E7F8 /* Makefiles */,
+                               AD596D730837697900C4D81D /* iCal */,
+                               AD596D830837698200C4D81D /* vCard */,
+                               ADDF503706DE520000C4E7F8 /* Unused */,
+                               ADAACFFF07B6AF0E00FC48D6 /* Unit Tests */,
+                               ADDF4E6506DE500200C4E7F8 /* Resources */,
+                               ADDF4E6106DE4FF200C4E7F8 /* Products */,
+                               ADDF503A06DE524200C4E7F8 /* Linked Frameworks */,
+                       );
+                       sourceTree = "<group>";
+               };
+               ADDF4E6106DE4FF200C4E7F8 /* Products */ = {
+                       isa = PBXGroup;
+                       children = (
+                               ADDF4E6006DE4FF200C4E7F8 /* NGCards.framework */,
+                               ADAACFEB07B6AEB500FC48D6 /* NGCardsTests.framework */,
+                       );
+                       name = Products;
+                       sourceTree = "<group>";
+               };
+               ADDF4E6506DE500200C4E7F8 /* Resources */ = {
+                       isa = PBXGroup;
+                       children = (
+                               ADDF4E6206DE4FF200C4E7F8 /* NGCards-Info.plist */,
+                               ADDF4F3906DE513D00C4E7F8 /* NGCards.xmap */,
+                       );
+                       name = Resources;
+                       sourceTree = "<group>";
+               };
+               ADDF4F7006DE514B00C4E7F8 /* Makefiles */ = {
+                       isa = PBXGroup;
+                       children = (
+                               ADDF4F1606DE513D00C4E7F8 /* GNUmakefile */,
+                               ADDF4F1806DE513D00C4E7F8 /* GNUmakefile.preamble */,
+                               ADDF4F1706DE513D00C4E7F8 /* GNUmakefile.postamble */,
+                               ADBE3DFE072713DB000FEA6A /* fhs.make */,
+                       );
+                       name = Makefiles;
+                       sourceTree = "<group>";
+               };
+               ADDF4F7306DE516A00C4E7F8 /* Headers */ = {
+                       isa = PBXGroup;
+                       children = (
+                               ADDF4F3806DE513D00C4E7F8 /* NGCards.h */,
+                               AD596D8A083769C500C4D81D /* NGICalSaxHandler.h */,
+                               ADDF4F1906DE513D00C4E7F8 /* iCalAlarm.h */,
+                               ADDF4F1B06DE513D00C4E7F8 /* iCalAttachment.h */,
+                               ADDF4F1D06DE513D00C4E7F8 /* iCalCalendar.h */,
+                               ADDF4F1F06DE513D00C4E7F8 /* iCalDataSource.h */,
+                               ADDF4F2106DE513D00C4E7F8 /* iCalDateHolder.h */,
+                               ADDF4F2306DE513D00C4E7F8 /* iCalDuration.h */,
+                               ADDF4F2606DE513D00C4E7F8 /* iCalEntityObject.h */,
+                               AD77103C07AE8F8500F5C7A1 /* iCalRepeatableEntityObject.h */,
+                               ADDF4F2806DE513D00C4E7F8 /* iCalEvent.h */,
+                               ADDF4F2A06DE513D00C4E7F8 /* iCalFreeBusy.h */,
+                               ADDF4F2C06DE513D00C4E7F8 /* iCalJournal.h */,
+                               ADDF4F2E06DE513D00C4E7F8 /* iCalObject.h */,
+                               ADDF4F3006DE513D00C4E7F8 /* iCalPerson.h */,
+                               ADDF4F3406DE513D00C4E7F8 /* iCalToDo.h */,
+                               ADDF4F3606DE513D00C4E7F8 /* iCalTrigger.h */,
+                               ADD1FC9906E4D6D400E387F0 /* iCalEventChanges.h */,
+                               ADBE3DF7072713AF000FEA6A /* iCalRenderer.h */,
+                               AD770E6707AE627500F5C7A1 /* iCalRecurrenceRule.h */,
+                               ADAACE6607B3973900FC48D6 /* iCalRecurrenceCalculator.h */,
+                               ADDF4F3A06DE513D00C4E7F8 /* NSCalendarDate+ICal.h */,
+                       );
+                       fileEncoding = 5;
+                       indentWidth = 2;
+                       name = Headers;
+                       sourceTree = "<group>";
+               };
+               ADDF4FD406DE517900C4E7F8 /* Classes */ = {
+                       isa = PBXGroup;
+                       children = (
+                               ADDF4F1306DE513D00C4E7F8 /* common.h */,
+                               AD596D8B083769C500C4D81D /* NGICalSaxHandler.m */,
+                               ADDF4F1A06DE513D00C4E7F8 /* iCalAlarm.m */,
+                               ADDF4F1C06DE513D00C4E7F8 /* iCalAttachment.m */,
+                               ADDF4F1E06DE513D00C4E7F8 /* iCalCalendar.m */,
+                               ADDF4F2006DE513D00C4E7F8 /* iCalDataSource.m */,
+                               ADDF4F2206DE513D00C4E7F8 /* iCalDateHolder.m */,
+                               ADDF4F2406DE513D00C4E7F8 /* iCalDuration.m */,
+                               ADDF4F2706DE513D00C4E7F8 /* iCalEntityObject.m */,
+                               AD77103D07AE8F8500F5C7A1 /* iCalRepeatableEntityObject.m */,
+                               ADDF4F2906DE513D00C4E7F8 /* iCalEvent.m */,
+                               ADDF4F2B06DE513D00C4E7F8 /* iCalFreeBusy.m */,
+                               ADDF4F2D06DE513D00C4E7F8 /* iCalJournal.m */,
+                               ADDF4F2F06DE513D00C4E7F8 /* iCalObject.m */,
+                               ADDF4F3106DE513D00C4E7F8 /* iCalPerson.m */,
+                               ADDF4F3506DE513D00C4E7F8 /* iCalToDo.m */,
+                               ADDF4F3706DE513D00C4E7F8 /* iCalTrigger.m */,
+                               ADD1FC9A06E4D6D400E387F0 /* iCalEventChanges.m */,
+                               ADBE3DF9072713C2000FEA6A /* iCalRenderer.m */,
+                               AD770E6807AE627500F5C7A1 /* iCalRecurrenceRule.m */,
+                               ADAACE6707B3973900FC48D6 /* iCalRecurrenceCalculator.m */,
+                               AD2EC1C008E2E630006B7836 /* iCalDailyRecurrenceCalculator.m */,
+                               AD2EC1C208E2E630006B7836 /* iCalWeeklyRecurrenceCalculator.m */,
+                               AD2EC1C108E2E630006B7836 /* iCalMonthlyRecurrenceCalculator.m */,
+                               AD2EC1C308E2E630006B7836 /* iCalYearlyRecurrenceCalculator.m */,
+                               ADDF4F3B06DE513D00C4E7F8 /* NSCalendarDate+ICal.m */,
+                       );
+                       fileEncoding = 5;
+                       indentWidth = 2;
+                       name = Classes;
+                       sourceTree = "<group>";
+               };
+               ADDF503706DE520000C4E7F8 /* Unused */ = {
+                       isa = PBXGroup;
+                       children = (
+                               ADDF4F3206DE513D00C4E7F8 /* IcalResponse.h */,
+                               ADDF4F3306DE513D00C4E7F8 /* IcalResponse.m */,
+                               ADDF4F2506DE513D00C4E7F8 /* IcalElements.m */,
+                               ADDF4F3C06DE513D00C4E7F8 /* NSString+ICal.h */,
+                               ADDF4F3D06DE513D00C4E7F8 /* NSString+ICal.m */,
+                       );
+                       name = Unused;
+                       sourceTree = "<group>";
+               };
+               ADDF503A06DE524200C4E7F8 /* Linked Frameworks */ = {
+                       isa = PBXGroup;
+                       children = (
+                               ADDF503B06DE528200C4E7F8 /* Foundation.framework */,
+                               ADDF503E06DE52F600C4E7F8 /* EOControl.framework */,
+                               ADDF503F06DE52F600C4E7F8 /* NGExtensions.framework */,
+                               ADAAD00607B6AF7700FC48D6 /* SenTestingKit.framework */,
+                               ADDF510206DE54BE00C4E7F8 /* sope-xml */,
+                       );
+                       name = "Linked Frameworks";
+                       sourceTree = "<group>";
+               };
+               ADDF510206DE54BE00C4E7F8 /* sope-xml */ = {
+                       isa = PBXGroup;
+                       children = (
+                               ADDF504006DE52F600C4E7F8 /* SaxObjC.framework */,
+                               ADDF503D06DE52F600C4E7F8 /* DOM.framework */,
+                               AD8BF0F70701902800EC239A /* XmlRpc.framework */,
+                       );
+                       name = "sope-xml";
+                       sourceTree = "<group>";
+               };
+/* End PBXGroup section */
+
+/* Begin PBXHeadersBuildPhase section */
+               ADAACFE507B6AEB500FC48D6 /* Headers */ = {
+                       isa = PBXHeadersBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               ADAAD04207B6B05000FC48D6 /* common.h in Headers */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               ADDF4E5B06DE4FF200C4E7F8 /* Headers */ = {
+                       isa = PBXHeadersBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               ADDF4F4106DE513D00C4E7F8 /* common.h in Headers */,
+                               ADDF4F4706DE513D00C4E7F8 /* iCalAlarm.h in Headers */,
+                               ADDF4F4906DE513D00C4E7F8 /* iCalAttachment.h in Headers */,
+                               ADDF4F4B06DE513D00C4E7F8 /* iCalCalendar.h in Headers */,
+                               ADDF4F4D06DE513D00C4E7F8 /* iCalDataSource.h in Headers */,
+                               ADDF4F4F06DE513D00C4E7F8 /* iCalDateHolder.h in Headers */,
+                               ADDF4F5106DE513D00C4E7F8 /* iCalDuration.h in Headers */,
+                               ADDF4F5406DE513D00C4E7F8 /* iCalEntityObject.h in Headers */,
+                               ADDF4F5606DE513D00C4E7F8 /* iCalEvent.h in Headers */,
+                               ADDF4F5806DE513D00C4E7F8 /* iCalFreeBusy.h in Headers */,
+                               ADDF4F5A06DE513D00C4E7F8 /* iCalJournal.h in Headers */,
+                               ADDF4F5C06DE513D00C4E7F8 /* iCalObject.h in Headers */,
+                               ADDF4F5E06DE513D00C4E7F8 /* iCalPerson.h in Headers */,
+                               ADDF4F6206DE513D00C4E7F8 /* iCalToDo.h in Headers */,
+                               ADDF4F6406DE513D00C4E7F8 /* iCalTrigger.h in Headers */,
+                               ADDF4F6606DE513D00C4E7F8 /* NGCards.h in Headers */,
+                               ADDF4F6806DE513D00C4E7F8 /* NSCalendarDate+ICal.h in Headers */,
+                               ADD1FC9B06E4D6D400E387F0 /* iCalEventChanges.h in Headers */,
+                               ADBE3DF8072713AF000FEA6A /* iCalRenderer.h in Headers */,
+                               AD770E6907AE627500F5C7A1 /* iCalRecurrenceRule.h in Headers */,
+                               AD77103E07AE8F8500F5C7A1 /* iCalRepeatableEntityObject.h in Headers */,
+                               ADAACE6807B3973900FC48D6 /* iCalRecurrenceCalculator.h in Headers */,
+                               AD596D9E083769C500C4D81D /* NGICalSaxHandler.h in Headers */,
+                               AD596DA0083769C500C4D81D /* NGVCard.h in Headers */,
+                               AD596DA2083769C500C4D81D /* NGVCardAddress.h in Headers */,
+                               AD596DA4083769C500C4D81D /* NGVCardName.h in Headers */,
+                               AD596DA6083769C500C4D81D /* NGVCardOrg.h in Headers */,
+                               AD596DA8083769C500C4D81D /* NGVCardPhone.h in Headers */,
+                               AD596DAA083769C500C4D81D /* NGVCardSaxHandler.h in Headers */,
+                               AD596DAC083769C500C4D81D /* NGVCardSimpleValue.h in Headers */,
+                               AD596DAE083769C500C4D81D /* NGVCardStrArrayValue.h in Headers */,
+                               AD596DB0083769C500C4D81D /* NGVCardValue.h in Headers */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+/* End PBXHeadersBuildPhase section */
+
+/* Begin PBXNativeTarget section */
+               ADAACFEA07B6AEB500FC48D6 /* NGCardsTests */ = {
+                       isa = PBXNativeTarget;
+                       buildConfigurationList = ADA07A4D0857394900993825 /* Build configuration list for PBXNativeTarget "NGCardsTests" */;
+                       buildPhases = (
+                               ADAACFE507B6AEB500FC48D6 /* Headers */,
+                               ADAACFE607B6AEB500FC48D6 /* Resources */,
+                               ADAACFE707B6AEB500FC48D6 /* Sources */,
+                               ADAACFE807B6AEB500FC48D6 /* Frameworks */,
+                               ADAACFE907B6AEB500FC48D6 /* ShellScript */,
+                       );
+                       buildRules = (
+                       );
+                       buildSettings = {
+                               DYLIB_COMPATIBILITY_VERSION = 1;
+                               DYLIB_CURRENT_VERSION = 4.5.74;
+                               FRAMEWORK_SEARCH_PATHS = "\"$(USER_LIBRARY_DIR)/EmbeddedFrameworks\"";
+                               FRAMEWORK_VERSION = A;
+                               GCC_PRECOMPILE_PREFIX_HEADER = NO;
+                               GCC_WARN_FOUR_CHARACTER_CONSTANTS = NO;
+                               GCC_WARN_UNKNOWN_PRAGMAS = NO;
+                               INFOPLIST_FILE = "tests/NGCardsTests-Info.plist";
+                               INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+                               OTHER_CFLAGS = "-fobjc-exceptions";
+                               OTHER_LDFLAGS = "";
+                               OTHER_REZFLAGS = "";
+                               PRODUCT_NAME = NGCardsTests;
+                               SECTORDER_FLAGS = "";
+                               TEST_AFTER_BUILD = YES;
+                               WARNING_CFLAGS = "-Wmost";
+                       };
+                       dependencies = (
+                               ADAACFEE07B6AEBF00FC48D6 /* PBXTargetDependency */,
+                       );
+                       name = NGCardsTests;
+                       productName = NGCardsTests;
+                       productReference = ADAACFEB07B6AEB500FC48D6 /* NGCardsTests.framework */;
+                       productSettingsXML = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
+<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">
+<plist version=\"1.0\">
+<dict>
+       <key>CFBundleDevelopmentRegion</key>
+       <string>English</string>
+       <key>CFBundleExecutable</key>
+       <string>NGCardsTests</string>
+       <key>CFBundleGetInfoString</key>
+       <string></string>
+       <key>CFBundleIdentifier</key>
+       <string>com.MySoftwareCompany.NGCardsTests</string>
+       <key>CFBundleInfoDictionaryVersion</key>
+       <string>6.0</string>
+       <key>CFBundlePackageType</key>
+       <string>FMWK</string>
+       <key>CFBundleShortVersionString</key>
+       <string></string>
+       <key>CFBundleSignature</key>
+       <string>????</string>
+       <key>CFBundleVersion</key>
+       <string>1.0.0d1</string>
+</dict>
+</plist>
+";
+                       productType = "com.apple.product-type.framework";
+               };
+               ADDF4E5F06DE4FF200C4E7F8 /* NGCards */ = {
+                       isa = PBXNativeTarget;
+                       buildConfigurationList = ADA07A490857394900993825 /* Build configuration list for PBXNativeTarget "NGCards" */;
+                       buildPhases = (
+                               ADDF4E5B06DE4FF200C4E7F8 /* Headers */,
+                               ADDF4E5C06DE4FF200C4E7F8 /* Resources */,
+                               ADDF4E5D06DE4FF200C4E7F8 /* Sources */,
+                               ADDF4E5E06DE4FF200C4E7F8 /* Frameworks */,
+                               ADDF51AB06DE56C500C4E7F8 /* CopyFiles */,
+                       );
+                       buildRules = (
+                       );
+                       buildSettings = {
+                               DYLIB_COMPATIBILITY_VERSION = 1;
+                               DYLIB_CURRENT_VERSION = 4.5.74;
+                               FRAMEWORK_SEARCH_PATHS = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+                               FRAMEWORK_VERSION = A;
+                               GCC_PRECOMPILE_PREFIX_HEADER = YES;
+                               GCC_PREFIX_HEADER = common.h;
+                               GCC_WARN_FOUR_CHARACTER_CONSTANTS = NO;
+                               GCC_WARN_UNKNOWN_PRAGMAS = NO;
+                               INFOPLIST_FILE = "NGCards-Info.plist";
+                               INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+                               OTHER_CFLAGS = (
+                                       "-DSOPE_MAJOR_VERSION=4",
+                                       "-DSOPE_MINOR_VERSION=5",
+                               );
+                               OTHER_LDFLAGS = (
+                                       "-seg1addr",
+                                       0xC1E00000,
+                                       "-headerpad_max_install_names",
+                               );
+                               OTHER_REZFLAGS = "";
+                               PRODUCT_NAME = NGCards;
+                               SECTORDER_FLAGS = "";
+                               WARNING_CFLAGS = "-Wmost";
+                       };
+                       dependencies = (
+                       );
+                       name = NGCards;
+                       productName = NGCards;
+                       productReference = ADDF4E6006DE4FF200C4E7F8 /* NGCards.framework */;
+                       productSettingsXML = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
+<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">
+<plist version=\"1.0\">
+<dict>
+       <key>CFBundleDevelopmentRegion</key>
+       <string>English</string>
+       <key>CFBundleExecutable</key>
+       <string>NGCards</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.yourcompany.NGCards</string>
+       <key>CFBundleInfoDictionaryVersion</key>
+       <string>6.0</string>
+       <key>CFBundlePackageType</key>
+       <string>FMWK</string>
+       <key>CFBundleSignature</key>
+       <string>????</string>
+       <key>CFBundleVersion</key>
+       <string>1.0</string>
+</dict>
+</plist>
+";
+                       productType = "com.apple.product-type.framework";
+               };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+               ADDF4E5506DE4FC800C4E7F8 /* Project object */ = {
+                       isa = PBXProject;
+                       buildConfigurationList = ADA07A510857394900993825 /* Build configuration list for PBXProject "NGCards" */;
+                       buildSettings = {
+                       };
+                       buildStyles = (
+                               ADDF4E5306DE4FC600C4E7F8 /* Development */,
+                               ADDF4E5406DE4FC800C4E7F8 /* Wrapper */,
+                       );
+                       hasScannedForEncodings = 0;
+                       mainGroup = ADDF4E5106DE4FC600C4E7F8;
+                       productRefGroup = ADDF4E6106DE4FF200C4E7F8 /* Products */;
+                       projectDirPath = "";
+                       targets = (
+                               ADDF4E5F06DE4FF200C4E7F8 /* NGCards */,
+                               ADAACFEA07B6AEB500FC48D6 /* NGCardsTests */,
+                       );
+               };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+               ADAACFE607B6AEB500FC48D6 /* Resources */ = {
+                       isa = PBXResourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               ADDF4E5C06DE4FF200C4E7F8 /* Resources */ = {
+                       isa = PBXResourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               ADDF4F4206DE513D00C4E7F8 /* COPYING in Resources */,
+                               ADDF4F4306DE513D00C4E7F8 /* COPYRIGHT in Resources */,
+                               ADDF4F6C06DE513D00C4E7F8 /* README in Resources */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXShellScriptBuildPhase section */
+               ADAACFE907B6AEB500FC48D6 /* ShellScript */ = {
+                       isa = PBXShellScriptBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                       );
+                       inputPaths = (
+                       );
+                       outputPaths = (
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+                       shellPath = /bin/sh;
+                       shellScript = /Developer/Tools/RunTargetUnitTests;
+               };
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+               ADAACFE707B6AEB500FC48D6 /* Sources */ = {
+                       isa = PBXSourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               ADAAD02307B6AFD700FC48D6 /* iCalRecurrenceCalculatorTests.m in Sources */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               ADDF4E5D06DE4FF200C4E7F8 /* Sources */ = {
+                       isa = PBXSourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               ADDF4F4806DE513D00C4E7F8 /* iCalAlarm.m in Sources */,
+                               ADDF4F4A06DE513D00C4E7F8 /* iCalAttachment.m in Sources */,
+                               ADDF4F4C06DE513D00C4E7F8 /* iCalCalendar.m in Sources */,
+                               ADDF4F4E06DE513D00C4E7F8 /* iCalDataSource.m in Sources */,
+                               ADDF4F5006DE513D00C4E7F8 /* iCalDateHolder.m in Sources */,
+                               ADDF4F5206DE513D00C4E7F8 /* iCalDuration.m in Sources */,
+                               ADDF4F5506DE513D00C4E7F8 /* iCalEntityObject.m in Sources */,
+                               ADDF4F5706DE513D00C4E7F8 /* iCalEvent.m in Sources */,
+                               ADDF4F5906DE513D00C4E7F8 /* iCalFreeBusy.m in Sources */,
+                               ADDF4F5B06DE513D00C4E7F8 /* iCalJournal.m in Sources */,
+                               ADDF4F5D06DE513D00C4E7F8 /* iCalObject.m in Sources */,
+                               ADDF4F5F06DE513D00C4E7F8 /* iCalPerson.m in Sources */,
+                               ADDF4F6306DE513D00C4E7F8 /* iCalToDo.m in Sources */,
+                               ADDF4F6506DE513D00C4E7F8 /* iCalTrigger.m in Sources */,
+                               ADDF4F6906DE513D00C4E7F8 /* NSCalendarDate+ICal.m in Sources */,
+                               ADD1FC9C06E4D6D400E387F0 /* iCalEventChanges.m in Sources */,
+                               ADBE3DFA072713C2000FEA6A /* iCalRenderer.m in Sources */,
+                               AD770E6A07AE627500F5C7A1 /* iCalRecurrenceRule.m in Sources */,
+                               AD77103F07AE8F8500F5C7A1 /* iCalRepeatableEntityObject.m in Sources */,
+                               ADAACE6907B3973900FC48D6 /* iCalRecurrenceCalculator.m in Sources */,
+                               AD596D9F083769C500C4D81D /* NGICalSaxHandler.m in Sources */,
+                               AD596DA1083769C500C4D81D /* NGVCard.m in Sources */,
+                               AD596DA3083769C500C4D81D /* NGVCardAddress.m in Sources */,
+                               AD596DA5083769C500C4D81D /* NGVCardName.m in Sources */,
+                               AD596DA7083769C500C4D81D /* NGVCardOrg.m in Sources */,
+                               AD596DA9083769C500C4D81D /* NGVCardPhone.m in Sources */,
+                               AD596DAB083769C500C4D81D /* NGVCardSaxHandler.m in Sources */,
+                               AD596DAD083769C500C4D81D /* NGVCardSimpleValue.m in Sources */,
+                               AD596DAF083769C500C4D81D /* NGVCardStrArrayValue.m in Sources */,
+                               AD596DB1083769C500C4D81D /* NGVCardValue.m in Sources */,
+                               AD2EC1C408E2E630006B7836 /* iCalDailyRecurrenceCalculator.m in Sources */,
+                               AD2EC1C508E2E630006B7836 /* iCalMonthlyRecurrenceCalculator.m in Sources */,
+                               AD2EC1C608E2E630006B7836 /* iCalWeeklyRecurrenceCalculator.m in Sources */,
+                               AD2EC1C708E2E630006B7836 /* iCalYearlyRecurrenceCalculator.m in Sources */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXTargetDependency section */
+               ADAACFEE07B6AEBF00FC48D6 /* PBXTargetDependency */ = {
+                       isa = PBXTargetDependency;
+                       target = ADDF4E5F06DE4FF200C4E7F8 /* NGCards */;
+                       targetProxy = ADAACFED07B6AEBF00FC48D6 /* PBXContainerItemProxy */;
+               };
+/* End PBXTargetDependency section */
+
+/* Begin XCBuildConfiguration section */
+               ADA07A4A0857394900993825 /* Development */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               COPY_PHASE_STRIP = NO;
+                               DYLIB_COMPATIBILITY_VERSION = 1;
+                               DYLIB_CURRENT_VERSION = 4.5.74;
+                               FRAMEWORK_SEARCH_PATHS = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+                               FRAMEWORK_VERSION = A;
+                               GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
+                               GCC_OPTIMIZATION_LEVEL = 0;
+                               GCC_PRECOMPILE_PREFIX_HEADER = YES;
+                               GCC_PREFIX_HEADER = common.h;
+                               GCC_PREPROCESSOR_DEFINITIONS = (
+                                       "APPLE_RUNTIME=1",
+                                       "NeXT_Foundation_LIBRARY=1",
+                                       "COCOA_Foundation_LIBRARY=1",
+                                       "NeXT_RUNTIME=1",
+                                       "COMPILE_AS_FRAMEWORK=1",
+                                       "DEBUG=1",
+                               );
+                               GCC_WARN_FOUR_CHARACTER_CONSTANTS = NO;
+                               GCC_WARN_UNKNOWN_PRAGMAS = NO;
+                               INFOPLIST_FILE = "NGCards-Info.plist";
+                               INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+                               OTHER_CFLAGS = (
+                                       "-DSOPE_MAJOR_VERSION=4",
+                                       "-DSOPE_MINOR_VERSION=5",
+                               );
+                               OTHER_LDFLAGS = (
+                                       "-seg1addr",
+                                       0xC1E00000,
+                                       "-headerpad_max_install_names",
+                               );
+                               OTHER_REZFLAGS = "";
+                               PRODUCT_NAME = NGCards;
+                               SECTORDER_FLAGS = "";
+                               WARNING_CFLAGS = "-Wmost";
+                       };
+                       name = Development;
+               };
+               ADA07A4B0857394900993825 /* Wrapper */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               COPY_PHASE_STRIP = YES;
+                               DEPLOYMENT_LOCATION = NO;
+                               DEPLOYMENT_POSTPROCESSING = YES;
+                               DSTROOT = /;
+                               DYLIB_COMPATIBILITY_VERSION = 1;
+                               DYLIB_CURRENT_VERSION = 4.5.74;
+                               DYLIB_INSTALL_NAME_BASE = "@executable_path/../Frameworks/";
+                               FRAMEWORK_SEARCH_PATHS = "$(USER_LIBRARY_DIR)/EmbeddedFrameworks";
+                               FRAMEWORK_VERSION = A;
+                               GCC_ENABLE_FIX_AND_CONTINUE = NO;
+                               GCC_GENERATE_DEBUGGING_SYMBOLS = NO;
+                               GCC_OPTIMIZATION_LEVEL = 3;
+                               GCC_PRECOMPILE_PREFIX_HEADER = YES;
+                               GCC_PREFIX_HEADER = common.h;
+                               GCC_PREPROCESSOR_DEFINITIONS = (
+                                       "APPLE_RUNTIME=1",
+                                       "NeXT_Foundation_LIBRARY=1",
+                                       "COCOA_Foundation_LIBRARY=1",
+                                       "NeXT_RUNTIME=1",
+                                       "COMPILE_AS_FRAMEWORK=1",
+                               );
+                               GCC_WARN_FOUR_CHARACTER_CONSTANTS = NO;
+                               GCC_WARN_UNKNOWN_PRAGMAS = NO;
+                               INFOPLIST_FILE = "NGCards-Info.plist";
+                               INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+                               OTHER_CFLAGS = (
+                                       "-DSOPE_MAJOR_VERSION=4",
+                                       "-DSOPE_MINOR_VERSION=5",
+                               );
+                               OTHER_LDFLAGS = (
+                                       "-seg1addr",
+                                       0xC1E00000,
+                                       "-headerpad_max_install_names",
+                               );
+                               OTHER_REZFLAGS = "";
+                               PRODUCT_NAME = NGCards;
+                               SECTORDER_FLAGS = "";
+                               SKIP_INSTALL = YES;
+                               SYMROOT = "$(USER_LIBRARY_DIR)/EmbeddedFrameworks";
+                               TEMP_DIR = "$(SYMROOT)/$(PROJECT_NAME).build";
+                               UNSTRIPPED_PRODUCT = NO;
+                               WARNING_CFLAGS = "-Wmost";
+                               ZERO_LINK = NO;
+                       };
+                       name = Wrapper;
+               };
+               ADA07A4C0857394900993825 /* Default */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               DYLIB_COMPATIBILITY_VERSION = 1;
+                               DYLIB_CURRENT_VERSION = 4.5.74;
+                               FRAMEWORK_SEARCH_PATHS = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+                               FRAMEWORK_VERSION = A;
+                               GCC_PRECOMPILE_PREFIX_HEADER = YES;
+                               GCC_PREFIX_HEADER = common.h;
+                               GCC_WARN_FOUR_CHARACTER_CONSTANTS = NO;
+                               GCC_WARN_UNKNOWN_PRAGMAS = NO;
+                               INFOPLIST_FILE = "NGCards-Info.plist";
+                               INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+                               OTHER_CFLAGS = (
+                                       "-DSOPE_MAJOR_VERSION=4",
+                                       "-DSOPE_MINOR_VERSION=5",
+                               );
+                               OTHER_LDFLAGS = (
+                                       "-seg1addr",
+                                       0xC1E00000,
+                                       "-headerpad_max_install_names",
+                               );
+                               OTHER_REZFLAGS = "";
+                               PRODUCT_NAME = NGCards;
+                               SECTORDER_FLAGS = "";
+                               WARNING_CFLAGS = "-Wmost";
+                       };
+                       name = Default;
+               };
+               ADA07A4E0857394900993825 /* Development */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               COPY_PHASE_STRIP = NO;
+                               DYLIB_COMPATIBILITY_VERSION = 1;
+                               DYLIB_CURRENT_VERSION = 4.5.74;
+                               FRAMEWORK_SEARCH_PATHS = "\"$(USER_LIBRARY_DIR)/EmbeddedFrameworks\"";
+                               FRAMEWORK_VERSION = A;
+                               GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
+                               GCC_OPTIMIZATION_LEVEL = 0;
+                               GCC_PRECOMPILE_PREFIX_HEADER = NO;
+                               GCC_PREPROCESSOR_DEFINITIONS = (
+                                       "APPLE_RUNTIME=1",
+                                       "NeXT_Foundation_LIBRARY=1",
+                                       "COCOA_Foundation_LIBRARY=1",
+                                       "NeXT_RUNTIME=1",
+                                       "COMPILE_AS_FRAMEWORK=1",
+                                       "DEBUG=1",
+                               );
+                               GCC_WARN_FOUR_CHARACTER_CONSTANTS = NO;
+                               GCC_WARN_UNKNOWN_PRAGMAS = NO;
+                               INFOPLIST_FILE = "tests/NGCardsTests-Info.plist";
+                               INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+                               OTHER_CFLAGS = "-fobjc-exceptions";
+                               OTHER_LDFLAGS = "";
+                               OTHER_REZFLAGS = "";
+                               PRODUCT_NAME = NGCardsTests;
+                               SECTORDER_FLAGS = "";
+                               TEST_AFTER_BUILD = YES;
+                               WARNING_CFLAGS = "-Wmost";
+                       };
+                       name = Development;
+               };
+               ADA07A4F0857394900993825 /* Wrapper */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               COPY_PHASE_STRIP = YES;
+                               DEPLOYMENT_LOCATION = NO;
+                               DEPLOYMENT_POSTPROCESSING = YES;
+                               DSTROOT = /;
+                               DYLIB_COMPATIBILITY_VERSION = 1;
+                               DYLIB_CURRENT_VERSION = 4.5.74;
+                               DYLIB_INSTALL_NAME_BASE = "@executable_path/../Frameworks/";
+                               FRAMEWORK_SEARCH_PATHS = "$(USER_LIBRARY_DIR)/EmbeddedFrameworks";
+                               FRAMEWORK_VERSION = A;
+                               GCC_ENABLE_FIX_AND_CONTINUE = NO;
+                               GCC_GENERATE_DEBUGGING_SYMBOLS = NO;
+                               GCC_OPTIMIZATION_LEVEL = 3;
+                               GCC_PRECOMPILE_PREFIX_HEADER = NO;
+                               GCC_PREPROCESSOR_DEFINITIONS = (
+                                       "APPLE_RUNTIME=1",
+                                       "NeXT_Foundation_LIBRARY=1",
+                                       "COCOA_Foundation_LIBRARY=1",
+                                       "NeXT_RUNTIME=1",
+                                       "COMPILE_AS_FRAMEWORK=1",
+                               );
+                               GCC_WARN_FOUR_CHARACTER_CONSTANTS = NO;
+                               GCC_WARN_UNKNOWN_PRAGMAS = NO;
+                               INFOPLIST_FILE = "tests/NGCardsTests-Info.plist";
+                               INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+                               OTHER_CFLAGS = "-fobjc-exceptions";
+                               OTHER_LDFLAGS = "";
+                               OTHER_REZFLAGS = "";
+                               PRODUCT_NAME = NGCardsTests;
+                               SECTORDER_FLAGS = "";
+                               SKIP_INSTALL = YES;
+                               SYMROOT = "$(USER_LIBRARY_DIR)/EmbeddedFrameworks";
+                               TEMP_DIR = "$(SYMROOT)/$(PROJECT_NAME).build";
+                               TEST_AFTER_BUILD = YES;
+                               UNSTRIPPED_PRODUCT = NO;
+                               WARNING_CFLAGS = "-Wmost";
+                               ZERO_LINK = NO;
+                       };
+                       name = Wrapper;
+               };
+               ADA07A500857394900993825 /* Default */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               DYLIB_COMPATIBILITY_VERSION = 1;
+                               DYLIB_CURRENT_VERSION = 4.5.74;
+                               FRAMEWORK_SEARCH_PATHS = "\"$(USER_LIBRARY_DIR)/EmbeddedFrameworks\"";
+                               FRAMEWORK_VERSION = A;
+                               GCC_PRECOMPILE_PREFIX_HEADER = NO;
+                               GCC_WARN_FOUR_CHARACTER_CONSTANTS = NO;
+                               GCC_WARN_UNKNOWN_PRAGMAS = NO;
+                               INFOPLIST_FILE = "tests/NGCardsTests-Info.plist";
+                               INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+                               OTHER_CFLAGS = "-fobjc-exceptions";
+                               OTHER_LDFLAGS = "";
+                               OTHER_REZFLAGS = "";
+                               PRODUCT_NAME = NGCardsTests;
+                               SECTORDER_FLAGS = "";
+                               TEST_AFTER_BUILD = YES;
+                               WARNING_CFLAGS = "-Wmost";
+                       };
+                       name = Default;
+               };
+               ADA07A520857394900993825 /* Development */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                       };
+                       name = Development;
+               };
+               ADA07A530857394900993825 /* Wrapper */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               ARCHS = (
+                                       ppc,
+                                       i386,
+                               );
+                               SDKROOT = /Developer/SDKs/MacOSX10.4u.sdk;
+                       };
+                       name = Wrapper;
+               };
+               ADA07A540857394900993825 /* Default */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                       };
+                       name = Default;
+               };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+               ADA07A490857394900993825 /* Build configuration list for PBXNativeTarget "NGCards" */ = {
+                       isa = XCConfigurationList;
+                       buildConfigurations = (
+                               ADA07A4A0857394900993825 /* Development */,
+                               ADA07A4B0857394900993825 /* Wrapper */,
+                               ADA07A4C0857394900993825 /* Default */,
+                       );
+                       defaultConfigurationIsVisible = 0;
+                       defaultConfigurationName = Default;
+               };
+               ADA07A4D0857394900993825 /* Build configuration list for PBXNativeTarget "NGCardsTests" */ = {
+                       isa = XCConfigurationList;
+                       buildConfigurations = (
+                               ADA07A4E0857394900993825 /* Development */,
+                               ADA07A4F0857394900993825 /* Wrapper */,
+                               ADA07A500857394900993825 /* Default */,
+                       );
+                       defaultConfigurationIsVisible = 0;
+                       defaultConfigurationName = Default;
+               };
+               ADA07A510857394900993825 /* Build configuration list for PBXProject "NGCards" */ = {
+                       isa = XCConfigurationList;
+                       buildConfigurations = (
+                               ADA07A520857394900993825 /* Development */,
+                               ADA07A530857394900993825 /* Wrapper */,
+                               ADA07A540857394900993825 /* Default */,
+                       );
+                       defaultConfigurationIsVisible = 0;
+                       defaultConfigurationName = Default;
+               };
+/* End XCConfigurationList section */
+       };
+       rootObject = ADDF4E5506DE4FC800C4E7F8 /* Project object */;
+}
diff --git a/SOPE/NGCards/NGCards.xmap b/SOPE/NGCards/NGCards.xmap
new file mode 100644 (file)
index 0000000..182db0b
--- /dev/null
@@ -0,0 +1,313 @@
+{
+  "http://www.ietf.org/internet-drafts/draft-ietf-calsch-many-xcal-01.txt" = {
+
+    // components
+    
+    iCalendar = {
+      class  = NSMutableDictionary; // "ICalXRoot";
+      tagKey = "tag";
+      
+      ToManyRelationships = {
+        "subcomponents" = ( vcalendar );
+      };
+    };
+    
+    vcalendar = {
+      class  = iCalCalendar; // ICalVCalendar;
+
+      attributes = {
+        prodid   = prodId;
+        version  = version;
+        calscale = calscale;
+       method   = method;
+      };
+      
+      ToManyRelationships = {
+        events    = ( vevent    );
+        todos     = ( vtodo     );
+        journals  = ( journals  );
+        freeBusys = ( vfreebusy );
+        timezones = ( vtimezone );
+      };
+    };
+    
+    vevent = {
+      class  = iCalEvent;
+
+      attributes = {
+      };
+
+      ToManyRelationships = {
+        "alarms"          = ( valarm   );
+        "attendees"       = ( attendee );
+        "recurrenceRules" = ( rrule    );
+        "exceptionRules"  = ( exrule   );
+        "exceptionDates"  = ( exdate   );
+      };
+    };
+    
+    vtodo = {
+      class = "iCalToDo";
+
+      attributes = {
+      };
+
+      ToManyRelationships = {
+        "alarms"    = ( valarm   );
+        "attendees" = ( attendee );
+      };
+    };
+    
+    valarm = {
+      class = "iCalAlarm";
+
+      attributes = {
+        rrule = recurrenceRule;
+      };
+    };
+    
+    vfreebusy = {
+      class = "iCalFreeBusy";
+      
+      ToManyRelationships = {
+        "entries" = ( freebusy );
+      };
+    };
+    
+    vtimezone = {
+      class = NSMutableDictionary;
+      
+      attributes = {
+        tzid             = timeZoneID;
+        "X-LIC-LOCATION" = location;
+        daylight         = daylightInfo;
+        standard         = standardInfo;
+      };
+    };
+    daylight = {
+      class = NSMutableDictionary;
+      
+      attributes = {
+        tzoffsetfrom = tzOffsetFrom;
+        tzoffsetto   = tzOffsetTo;
+        tzname       = tzName;
+        dtstart      = startDate;
+        rrule        = recurrenceRule;
+      };
+    };
+    standard = {
+      class = NSMutableDictionary;
+      
+      attributes = {
+        tzoffsetfrom = tzOffsetFrom;
+        tzoffsetto   = tzOffsetTo;
+        tzname       = tzName;
+        dtstart      = startDate;
+        rrule        = recurrenceRule;
+      };
+    };
+    
+    // attributes
+    
+    dtstamp = {
+      class      = iCalDateHolder;
+      key        = "timeStampAsDate";
+      tagKey     = "tag";
+      contentKey = "string";
+    };
+    created = {
+      class      = iCalDateHolder;
+      key        = "created";
+      tagKey     = "tag";
+      contentKey = "string";
+    };
+    "last-modified" = {
+      class      = iCalDateHolder;
+      key        = "lastModified";
+      tagKey     = "tag";
+      contentKey = "string";
+    };
+    
+    dtstart = {
+      class = iCalDateHolder;
+      key   = "startDate";
+      attributes = {
+        tzid = tzid;
+      };
+      tagKey     = "tag";
+      contentKey = "string";
+    };
+    dtend = {
+      class = iCalDateHolder;
+      key   = "endDate";
+      attributes = {
+        tzid = tzid;
+      };
+      tagKey     = "tag";
+      contentKey = "string";
+    };
+    exdate = {
+      class = iCalDateHolder;
+      key   = "recurrenceRuleExceptionDate";
+      attributes = {
+        tzid = tzid;
+      };
+      tagKey     = "tag";
+      contentKey = "string";
+    };
+    due = {
+      class = iCalDateHolder;
+      attributes = {
+        tzid = tzid;
+      };
+      tagKey     = "tag";
+      contentKey = "string";
+    };
+    completed = {
+      class = iCalDateHolder;
+      attributes = {
+        tzid = tzid;
+      };
+      tagKey     = "tag";
+      contentKey = "string";
+    };
+    
+    duration = {
+      class = NSString;
+    };
+    
+    summary     = { class = NSString; };
+    description = { class = NSString; key = comment; };
+    comment     = { class = NSString; key = userComment; };
+    uid         = { class = NSString; };
+    action      = { class = NSString; };
+    priority    = { class = NSString; };
+    status      = { class = NSString; };
+    transp      = { class = NSString; key = transparency; };
+    sequence    = { class = NSString; };
+    categories  = { class = NSString; };
+    class       = { class = NSString; key = accessClass; };
+    percent-complete = { class = NSString; key = "percentComplete"; };
+    
+    attendee = {
+      class = iCalPerson;
+      attributes = {
+        cn = cn;
+        rsvp = rsvp;
+        role = role;
+        partstat = partStat;
+      };
+      contentKey = "email";
+    };
+    organizer = {
+      class      = iCalPerson;
+      attributes = {
+        cn = cn;
+      };
+      contentKey = "email";
+    };
+    
+    freebusy = {
+      class = NSString;
+    };
+    url = {
+      class = NSString;
+    };
+    
+    trigger = {
+      class = iCalTrigger;
+      attributes = {
+        value = valueType;
+      };
+      contentKey = "value";
+    };
+    attach = {
+      class = iCalAttachment;
+      attributes = {
+        value = valueType;
+      };
+      contentKey = "value";
+    };
+
+    tzid = {
+      class = NSString;
+    };
+    tzname = {
+      class = NSString;
+    };
+    tzoffsetfrom = {
+      class = NSString;
+    };
+    tzoffsetto = {
+      class = NSString;
+    };
+    rrule = {
+      class = iCalRecurrenceRule;
+      /*
+      attributes = {
+        freq       = rrFreq;
+        until      = rrUntil;
+        count      = rrCount;
+        interval   = rrInterval;
+        bysecond   = rrBySecondList;
+        byminute   = rrByMinuteList;
+        byhour     = rrByHourList;
+        byday      = rrByDayList;
+        bymonthday = rrByMonthDayList;
+        byyearday  = rrByYearDayList;
+        byweekno   = rrByWeekNumberList;
+        bymonth    = rrByMonthList;
+        bysetpos   = rrBySetPosList;
+        wkst       = rrWeekStart;
+      };
+      */
+      contentKey = "rrule";
+      key        = "recurrenceRule";
+    };
+    location = {
+      class = NSString;
+    };
+    
+    // extra tags
+
+    "X-LIC-LOCATION" = {
+      class = NSString;
+    };
+    
+    "X-WR-TIMEZONE" = {
+      rejectWithContent = YES;
+    };
+    "X-WR-CALNAME" = {
+      rejectWithContent = YES;
+    };
+    "X-WR-RELCALID" = {
+      rejectWithContent = YES;
+    };
+
+    "X-MICROSOFT-CDO-TZID" = {
+      rejectWithContent = YES;
+    };
+    "X-MICROSOFT-CDO-BUSYSTATUS" = {
+      rejectWithContent = YES;
+    };
+    "X-MICROSOFT-CDO-INSTTYPE" = {
+      rejectWithContent = YES;
+    };
+    "X-MICROSOFT-CDO-INTENDEDSTATUS" = {
+      rejectWithContent = YES;
+    };
+    "X-MICROSOFT-CDO-ALLDAYEVENT" = {
+      rejectWithContent = YES;
+    };
+    "X-MICROSOFT-CDO-IMPORTANCE" = {
+      rejectWithContent = YES;
+    };
+
+    "X-PILOTID" = {
+      rejectWithContent = YES;
+    };
+    "X-PILOTSTAT" = {
+      rejectWithContent = YES;
+    };
+  };
+}
diff --git a/SOPE/NGCards/NGCardsSaxHandler.h b/SOPE/NGCards/NGCardsSaxHandler.h
new file mode 100644 (file)
index 0000000..5472fe5
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+  Copyright (C) 2005 Helge Hess
+
+  This file is part of SOPE.
+
+  SOPE is free software; you can redistribute it and/or modify it under
+  the terms of the GNU Lesser General Public License as published by the
+  Free Software Foundation; either version 2, or (at your option) any
+  later version.
+
+  SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+  License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with SOPE; see the file COPYING.  If not, write to the
+  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+  02111-1307, USA.
+*/
+
+#ifndef __NGiCal_NGCardsSaxHandler_H__
+#define __NGiCal_NGCardsSaxHandler_H__
+
+#include <SaxObjC/SaxDefaultHandler.h>
+
+/*
+  NGCardsSaxHandler
+  
+  This SAX handler is supposed to create the NGiCal object model from SAX
+  events.
+*/
+
+@class NSString, NSMutableString, NSMutableArray;
+@class NSDictionary, NSMutableDictionary;
+
+@class CardElement;
+@class CardGroup;
+
+@interface NGCardsSaxHandler : SaxDefaultHandler
+{
+  NSMutableArray *cards;
+  CardElement *currentElement;
+  CardGroup *currentCardGroup;
+
+  NSString *currentGroup;
+  unichar  *content;
+  unsigned contentLength;
+
+  NSMutableDictionary *xtags;
+  NSMutableDictionary *subvalues;
+  NSMutableArray      *types;
+  NSMutableDictionary *args;
+  NSMutableArray      *tel;
+  NSMutableArray      *adr;
+  NSMutableArray      *email;
+  NSMutableArray      *label;
+  NSMutableArray      *url;
+  NSMutableArray      *fburl;
+  NSMutableArray      *caluri;
+  
+  struct {
+    int isInVCardSet:1;
+    int isInVCard:1;
+    int isInN:1;
+    int isInAdr:1;
+    int isInOrg:1;
+    int isInGroup:1;
+    int isInGeo:1;
+    int collectContent:1;
+    int reserved:24;
+  } vcs;
+
+  Class topGroupClass;
+}
+
+/* results */
+
+- (NSArray *) cards;
+- (void)reset;
+
+/* content */
+
+- (void) startCollectingContent;
+- (NSArray *) finishCollectingContent;
+
+- (void) startGroupElement: (NSString *) _localName;
+- (void) endGroupElement;
+
+- (void) setTopElementClass: (Class) aGroupClass;
+
+@end
+
+#endif /* __NGiCal_NGCardsSaxHandler_H__ */
diff --git a/SOPE/NGCards/NGCardsSaxHandler.m b/SOPE/NGCards/NGCardsSaxHandler.m
new file mode 100644 (file)
index 0000000..8c77b55
--- /dev/null
@@ -0,0 +1,326 @@
+/*
+  Copyright (C) 2005 Helge Hess
+
+  This file is part of SOPE.
+
+  SOPE is free software; you can redistribute it and/or modify it under
+  the terms of the GNU Lesser General Public License as published by the
+  Free Software Foundation; either version 2, or (at your option) any
+  later version.
+
+  SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+  License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with SOPE; see the file COPYING.  If not, write to the
+  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+  02111-1307, USA.
+*/
+
+#import "NGCardsSaxHandler.h"
+#import "common.h"
+#import <string.h>
+
+#import "CardGroup.h"
+
+#ifndef XMLNS_VCARD_XML_03
+#  define XMLNS_VCARD_XML_03 \
+     @"http://www.ietf.org/internet-drafts/draft-dawson-vcard-xml-dtd-03.txt"
+#endif
+
+@implementation NGCardsSaxHandler
+
+- (id) init
+{
+  if ((self = [super init]))
+    topGroupClass = nil;
+
+  return self;
+}
+
+- (void) dealloc
+{
+  if (content)
+    free(content);
+  [cards release];
+  if (currentGroup)
+    [currentGroup release];
+  [super dealloc];
+}
+
+/* results */
+
+- (NSArray *) cards
+{
+  return [[cards copy] autorelease];;
+}
+
+/* state */
+
+- (void)resetExceptResult
+{
+  if (content)
+    {
+      free(content);
+      content = NULL;
+    }
+
+  vcs.isInVCardSet   = 0;
+  vcs.collectContent = 0;
+}
+
+- (void) reset
+{
+  [self resetExceptResult];
+  [cards removeAllObjects];
+}
+
+/* document events */
+
+- (void) startDocument
+{
+  if (!cards)
+    cards = [[NSMutableArray alloc] initWithCapacity:16];
+  [self reset];
+}
+
+- (void) endDocument
+{
+  [self resetExceptResult];
+}
+
+/* common tags */
+
+- (void) startValueTag: (NSString *) _tag
+            attributes: (id<SaxAttributes>) _attrs
+{
+  /* a tag with types and attributes */
+  unsigned i, count;
+  
+  [types removeAllObjects];
+  [args  removeAllObjects];
+  
+  for (i = 0, count = [_attrs count]; i < count; i++) {
+    NSString *n, *v;
+    
+    n = [_attrs nameAtIndex:i];
+    v = [_attrs valueAtIndex:i];
+    
+    if ([n hasSuffix:@".type"] || [n isEqualToString:@"TYPE"]) {
+      /*
+        Note: types cannot be separated by comma! Its indeed always a space,eg
+                "work pref voice"
+              If you find commas, usually the vCard is broken.
+      */
+      NSEnumerator *e;
+      NSString *k;
+      
+      e = [[v componentsSeparatedByString:@" "] objectEnumerator];
+      while ((k = [e nextObject]) != nil) {
+        k = [k uppercaseString];
+        if ([types containsObject:k]) continue;
+       [types addObject:k];
+      }
+    }
+    else
+      [args setObject:v forKey:n];
+  }
+}
+
+- (void)endValueTag
+{
+  [types removeAllObjects];
+  [args  removeAllObjects];
+}
+
+/* handle elements */
+
+- (void) startGroup: (NSString *)_name
+{
+  vcs.isInGroup = 1;
+  ASSIGNCOPY(currentGroup, _name);
+}
+
+- (void) endGroup
+{
+  vcs.isInGroup = 0;
+  [currentGroup release];
+  currentGroup = nil;
+}
+
+- (void)startVCardSet
+{
+  currentCardGroup = nil;
+  currentGroup = nil;
+  vcs.isInVCardSet = 1;
+}
+
+- (void)endVCardSet
+{
+  vcs.isInVCardSet = 0;
+}
+
+/* element events */
+
+- (void) startElement:(NSString *)_localName
+            namespace:(NSString *)_ns
+              rawName:(NSString *)_rawName
+           attributes:(id<SaxAttributes>)_attrs
+{
+  unsigned int count, max;
+  Class elementClass;
+
+//   NSLog (@"startElement localName: '%@'", _localName);
+
+  if ([_localName isEqualToString: @"vCardSet"])
+    [self startVCardSet];
+  else if ([_localName isEqualToString: @"group"])
+    [self startGroup: [_attrs valueAtIndex: 0]];
+  else if ([_localName isEqualToString: @"container"])
+    [self startGroupElement: [_attrs valueAtIndex: 0]];
+  else
+    {
+      if (currentCardGroup)
+        elementClass
+          = [currentCardGroup classForTag: [_localName uppercaseString]];
+      else
+        elementClass = topGroupClass;
+      
+      if (!elementClass)
+        elementClass = [CardElement class];
+
+      currentElement = [elementClass elementWithTag: _localName];
+      [currentElement setTag: _localName];
+      if (currentGroup)
+        [currentElement setGroup: currentGroup];
+
+      max = [_attrs count];
+      for (count = 0; count < max; count++)
+        [currentElement addAttribute: [_attrs nameAtIndex: count]
+                        value: [_attrs valueAtIndex: count]];
+
+      [currentCardGroup addChild: currentElement];
+      [self startCollectingContent];
+    }
+}
+
+- (void) endElement: (NSString *)_localName
+          namespace: (NSString *)_ns
+            rawName: (NSString *)_rawName
+{
+//   NSLog (@"endElement localName: '%@'", _localName);
+  if ([_localName isEqualToString: @"vCardSet"])
+    [self endVCardSet];
+  else if ([_localName isEqualToString: @"group"])
+    [self endGroup];
+  else if ([_localName isEqualToString: @"container"])
+    [self endGroupElement];
+  else
+    [currentElement addValues: [self finishCollectingContent]];
+}
+
+/* content */
+
+- (void) startCollectingContent
+{
+  if (content) {
+    free(content);
+    content = NULL;
+  }
+  vcs.collectContent = 1;
+}
+
+- (NSArray *) finishCollectingContent
+{
+  NSArray *contentValues;
+  NSString *s;
+
+  vcs.collectContent = 0;
+
+  if (content && contentLength)
+    {
+      s = [NSString stringWithCharacters: content
+                    length: contentLength];
+      free (content);
+      content = NULL;
+//       NSLog (@"content: '%@'", s);
+      contentValues = [s componentsSeparatedByString: @";"];
+    }
+  else
+    contentValues = nil;
+
+  return contentValues;
+}
+
+- (void) characters:(unichar *)_chars
+             length:(int)_len
+{
+  if (_len && _chars)
+    {
+      if (!content)
+        {
+          /* first content */
+          contentLength = _len;
+          content = calloc (_len + 1, sizeof(unichar));
+          memcpy (content, _chars, (_len * sizeof(unichar)));
+        }
+      else
+        {
+          /* increase content */
+          content = 
+            realloc (content, (contentLength + _len+2) * sizeof(unichar));
+          memcpy (&(content[contentLength]), _chars, 
+                  (_len * sizeof(unichar)));
+          contentLength += _len;
+        }
+      content[contentLength] = 0;
+    }
+}
+
+- (void) startGroupElement: (NSString *) _localName
+{
+  CardGroup *newGroup;
+  Class groupClass;
+
+//   NSLog (@"startGroupElement localName: '%@'", _localName);
+
+  if (currentCardGroup)
+    {
+      groupClass
+        = [currentCardGroup classForTag: [_localName uppercaseString]];
+      if (!groupClass)
+        groupClass = [CardGroup class];
+
+      newGroup = [groupClass groupWithTag: _localName];
+      [currentCardGroup addChild: newGroup];
+    }
+  else
+    {
+      if (topGroupClass)
+        groupClass = topGroupClass;
+      else
+        groupClass = [CardGroup class];
+
+      newGroup = [groupClass groupWithTag: _localName];
+      [cards addObject: newGroup];
+    }
+
+  currentCardGroup = newGroup;
+}
+
+- (void) endGroupElement
+{
+//   NSLog (@"endGroupElement localName: '%@'", _localName);
+
+  if (currentCardGroup)
+    currentCardGroup = [currentCardGroup parent];
+}
+
+- (void) setTopElementClass: (Class) aGroupClass
+{
+  topGroupClass = aGroupClass;
+}
+
+@end /* NGCardsSaxHandler */
diff --git a/SOPE/NGCards/NGVCard.h b/SOPE/NGCards/NGVCard.h
new file mode 100644 (file)
index 0000000..7b9f69e
--- /dev/null
@@ -0,0 +1,155 @@
+/*
+  Copyright (C) 2005 Helge Hess
+
+  This file is part of SOPE.
+
+  SOPE is free software; you can redistribute it and/or modify it under
+  the terms of the GNU Lesser General Public License as published by the
+  Free Software Foundation; either version 2, or (at your option) any
+  later version.
+
+  SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+  License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with SOPE; see the file COPYING.  If not, write to the
+  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+  02111-1307, USA.
+*/
+
+#ifndef __NGiCal_NGVCard_H__
+#define __NGiCal_NGVCard_H__
+
+#import "CardGroup.h"
+
+/*
+  NGVCard
+  
+  Represents a vCard object.
+
+  XML DTD in Dawson-03 Draft:
+    <!ELEMENT vCard      (%prop.man;, (%prop.opt;)*)>
+    
+    <!ATTLIST vCard
+            %attr.lang;
+            xmlns     CDATA #FIXED 
+        'http://www.ietf.org/internet-drafts/draft-dawson-vcard-xml-dtd-02.txt'
+            xmlns:vcf CDATA #FIXED 
+        'http://www.ietf.org/internet-drafts/draft-dawson-vcard-xml-dtd-02.txt'
+            version   CDATA #REQUIRED
+            rev       CDATA #IMPLIED
+            uid       CDATA #IMPLIED
+            prodid    CDATA #IMPLIED
+            class (PUBLIC | PRIVATE | CONFIDENTIAL) "PUBLIC"
+            value NOTATION (VCARD) #IMPLIED>
+    <!-- version - Must be "3.0" if document conforms to this spec -->
+    <!-- rev - ISO 8601 formatted date or date/time string -->
+    <!-- uid - UID associated with the object described by the vCard -->
+    <!-- prodid - ISO 9070 FPI for product that generated vCard -->
+    <!-- class - Security classification for vCard information -->
+  
+  Mandatory elements:
+    n
+    fn
+*/
+
+@class NSArray;
+@class NSDictionary;
+@class NSString;
+
+@interface NGVCard : CardGroup
+
++ (id) cardWithUid: (NSString *) _uid;
+- (id) initWithUid: (NSString *) _uid;
+
+/* accessors */
+
+- (void) setVersion: (NSString *) _version;
+- (NSString *) version;
+
+- (void) setUid: (NSString *) _uid;
+- (NSString *) uid;
+
+- (void) setPreferred: (CardElement *) aChild;
+
+- (void) setVClass: (NSString *) _s;
+- (NSString *) vClass;
+- (void) setVName: (NSString *) _s;
+- (NSString *) vName;
+- (void) setProdID: (NSString *) _s;
+- (NSString *) prodID;
+- (void) setProfile: (NSString *) _s;
+- (NSString *) profile;
+- (void) setSource: (NSString *) _s;
+- (NSString *) source;
+
+- (void) setFn: (NSString *) _fn;
+- (NSString *) fn;
+- (void) setNickname: (NSString *) aValue;
+- (NSString *) nickname;
+
+- (void) setRole: (NSString *) _s;
+- (NSString *) role;
+- (void) setTitle: (NSString *) _title;
+- (NSString *) title;
+- (void) setBday: (NSString *) _bday;
+- (NSString *) bday;
+- (void) setNote: (NSString *) _note;
+- (NSString *) note;
+- (void) setTz: (NSString *) _tz;
+- (NSString *) tz;
+
+- (void) addTel: (NSString *) phoneNumber
+          types: (NSArray *) types;
+- (void) addEmail: (NSString *) emailAddress
+            types: (NSArray *) types;
+
+- (void) setNWithFamily: (NSString *) family
+                  given: (NSString *) given
+             additional: (NSString *) additional
+               prefixes: (NSString *) prefixes
+               suffixes: (NSString *) suffixes;
+- (NSArray *) n;
+
+- (void) setOrg: (NSString *) anOrg
+          units: (NSArray *) someUnits;
+- (NSArray *) org;
+
+// - (void) setN: (NGVCardName *) _v;
+// - (NGVCardName *) n;
+// - (void) setOrg: (NGVCardOrg *) _v;
+// - (NGVCardOrg *) org;
+
+// - (void) setCategories: (id) _v;
+// - (NGVCardStrArrayValue *) categories;
+
+// - (void) setTel: (NSArray *) _tel;
+// - (NSArray *) tel;
+// - (void) setAdr: (NSArray *) _adr;
+// - (NSArray *) adr;
+// - (void) setEmail: (NSArray *) _array;
+// - (NSArray *) email;
+// - (void) setLabel: (NSArray *) _array;
+// - (NSArray *) label;
+// - (void) setUrl: (NSArray *) _url;
+// - (NSArray *) url;
+
+// - (void) setFreeBusyURL: (NSArray *) _v;
+// - (NSArray *) freeBusyURL;
+// - (void) setCalURI: (NSArray *) _calURI;
+// - (NSArray *) calURI;
+
+// - (void) setX: (NSDictionary *) _dict;
+// - (NSDictionary *) x;
+
+/* convenience */
+
+- (NSString *) preferredEMail;
+- (NSString *) preferredTel;
+- (CardElement *) preferredAdr;
+
+@end
+
+#endif /* __NGiCal_NGVCard_H__ */
diff --git a/SOPE/NGCards/NGVCard.m b/SOPE/NGCards/NGVCard.m
new file mode 100644 (file)
index 0000000..21add8f
--- /dev/null
@@ -0,0 +1,521 @@
+/*
+  Copyright (C) 2005 Helge Hess
+
+  This file is part of SOPE.
+
+  SOPE is free software; you can redistribute it and/or modify it under
+  the terms of the GNU Lesser General Public License as published by the
+  Free Software Foundation; either version 2, or (at your option) any
+  later version.
+
+  SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+  License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with SOPE; see the file COPYING.  If not, write to the
+  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+  02111-1307, USA.
+*/
+
+#import <Foundation/NSArray.h>
+#import <Foundation/NSEnumerator.h>
+#import <Foundation/NSString.h>
+
+#import "NSArray+NGCards.h"
+
+#import "NGVCard.h"
+
+@implementation NGVCard
+
++ (id) parseSingleFromSource: (id) source
+{
+  NGVCard *newCard;
+
+  newCard = [super parseSingleFromSource: source];
+  [newCard setVersion: @"3.0"];
+
+  return newCard;
+}
+
++ (id) cardWithUid: (NSString *) _uid
+{
+  NGVCard *newCard;
+
+  newCard = [[self alloc] initWithUid: _uid];
+  [newCard autorelease];
+
+  return newCard;
+}
+
+- (id) initWithUid: (NSString *) _uid
+{
+  if ((self = [self init]))
+    {
+      [self setTag: @"vcard"];
+      [self setUid: _uid];
+      [self setVersion: @"3.0"];
+    }
+
+  return self;
+}
+
+/* class mapping */
+- (Class) classForTag: (NSString *) classTag
+{
+  Class tagClass;
+
+  tagClass = Nil;
+  if ([classTag isEqualToString: @"PRODID"]
+      || [classTag isEqualToString: @"PRODID"]
+      || [classTag isEqualToString: @"PROFILE"]
+      || [classTag isEqualToString: @"UID"]
+      || [classTag isEqualToString: @"CLASS"]
+      || [classTag isEqualToString: @"N"]
+      || [classTag isEqualToString: @"FN"]
+      || [classTag isEqualToString: @"ORG"]
+      || [classTag isEqualToString: @"ADR"]
+      || [classTag isEqualToString: @"TEL"]
+      || [classTag isEqualToString: @"TZ"]
+      || [classTag isEqualToString: @"URL"]
+      || [classTag isEqualToString: @"LABEL"]
+      || [classTag isEqualToString: @"EMAIL"]
+      || [classTag isEqualToString: @"NICKNAME"]
+      || [classTag isEqualToString: @"NOTE"]
+      || [classTag isEqualToString: @"BDAY"]
+      || [classTag isEqualToString: @"TITLE"]
+      || [classTag isEqualToString: @"VERSION"])
+    tagClass = [CardElement class];
+  else
+    tagClass = [super classForTag: classTag];
+
+  return tagClass;
+}
+
+- (void) setPreferred: (CardElement *) aChild
+{
+  NSEnumerator *elements;
+  CardElement *element;
+
+  if (![aChild hasAttribute: @"type" havingValue: @"pref"])
+    {
+      elements = [[children cardElementsWithTag: tag] objectEnumerator];
+      element = [elements nextObject];
+      while (element)
+        {
+          [element removeValue: @"pref" fromAttribute: @"type"];
+          element = [elements nextObject];
+        }
+      [aChild addAttribute: @"type" value: @"pref"];
+    }
+}
+
+/* helpers */
+- (NSArray *) childrenWithType: (NSString *) aType
+{
+  return [self childrenWithAttribute: @"type" havingValue: aType];
+}
+
+/* accessors */
+
+- (void) setVersion: (NSString *) _version
+{
+  [[self uniqueChildWithTag: @"version"] setValue: 0 to: _version];
+}
+
+- (NSString *) version
+{
+  return [[self uniqueChildWithTag: @"version"] value: 0];
+}
+
+- (void) setUid: (NSString *) _uid
+{
+  [[self uniqueChildWithTag: @"uid"] setValue: 0 to: _uid];
+}
+
+- (NSString *) uid
+{
+  return [[self uniqueChildWithTag: @"uid"] value: 0];
+}
+
+- (void) setVClass: (NSString *) _class
+{
+  [[self uniqueChildWithTag: @"class"] setValue: 0 to: _class];
+}
+
+- (NSString *) vClass
+{
+  return [[self uniqueChildWithTag: @"class"] value: 0];
+}
+
+- (void) setVName: (NSString *) _vname
+{
+  [[self uniqueChildWithTag: @"vname"] setValue: 0 to: _vname];
+}
+
+- (NSString *) vName
+{
+  return [[self uniqueChildWithTag: @"vname"] value: 0];
+}
+
+- (void) setProdID: (NSString *) _value
+{
+  [[self uniqueChildWithTag: @"prodid"] setValue: 0 to: _value];
+}
+
+- (NSString *) prodID
+{
+  return [[self uniqueChildWithTag: @"prodid"] value: 0];
+}
+
+- (void) setProfile: (NSString *) _value
+{
+  [[self uniqueChildWithTag: @"profile"] setValue: 0 to: _value];
+}
+
+- (NSString *) profile
+{
+  return [[self uniqueChildWithTag: @"profile"] value: 0];
+}
+
+- (void) setSource: (NSString *) _value
+{
+  [[self uniqueChildWithTag: @"source"] setValue: 0 to: _value];
+}
+
+- (NSString *) source
+{
+  return [[self uniqueChildWithTag: @"source"] value: 0];
+}
+
+- (void) setFn: (NSString *) _value
+{
+  [[self uniqueChildWithTag: @"fn"] setValue: 0 to: _value];
+}
+
+- (NSString *) fn
+{
+  return [[self uniqueChildWithTag: @"fn"] value: 0];
+}
+
+- (void) setRole: (NSString *) _value
+{
+  [[self uniqueChildWithTag: @"role"] setValue: 0 to: _value];
+}
+
+- (NSString *) role
+{
+  return [[self uniqueChildWithTag: @"role"] value: 0];
+}
+
+- (void) setTitle: (NSString *) _title
+{
+  [[self uniqueChildWithTag: @"title"] setValue: 0 to: _title];
+}
+
+- (NSString *) title
+{
+  return [[self uniqueChildWithTag: @"title"] value: 0];
+}
+
+- (void) setBday: (NSString *) _value
+{
+  [[self uniqueChildWithTag: @"bday"] setValue: 0 to: _value];
+}
+
+- (NSString *) bday
+{
+  return [[self uniqueChildWithTag: @"bday"] value: 0];
+}
+
+- (void) setNote: (NSString *) _value
+{
+  [[self uniqueChildWithTag: @"note"] setValue: 0 to: _value];
+}
+
+- (NSString *) note
+{
+  return [[self uniqueChildWithTag: @"note"] value: 0];
+}
+
+- (void) setTz: (NSString *) _value
+{
+  [[self uniqueChildWithTag: @"tz"] setValue: 0 to: _value];
+}
+
+- (NSString *) tz
+{
+  return [[self uniqueChildWithTag: @"tz"] value: 0];
+}
+
+- (void) setNickname: (NSString *) _value
+{
+  [[self uniqueChildWithTag: @"nickname"] setValue: 0 to: _value];
+}
+
+- (NSString *) nickname
+{
+  return [[self uniqueChildWithTag: @"nickname"] value: 0];
+}
+
+- (void) addTel: (NSString *) phoneNumber
+          types: (NSArray *) types
+{
+  [self addChildWithTag: @"tel" types: types singleValue: phoneNumber];
+}
+
+- (void) addEmail: (NSString *) emailAddress
+            types: (NSArray *) types
+{
+  [self addChildWithTag: @"email"
+        types: types
+        singleValue: emailAddress];
+}
+
+- (void) setNWithFamily: (NSString *) family
+                  given: (NSString *) given
+             additional: (NSString *) additional
+               prefixes: (NSString *) prefixes
+               suffixes: (NSString *) suffixes
+{
+  CardElement *n;
+
+  n = [self uniqueChildWithTag: @"n"];
+  if (family)
+    [n setValue: 0 to: family];
+  if (given)
+    [n setValue: 1 to: given];
+  if (additional)
+    [n setValue: 2 to: additional];
+  if (prefixes)
+    [n setValue: 3 to: prefixes];
+  if (suffixes)
+    [n setValue: 4 to: suffixes];
+}
+
+- (NSArray *) n
+{
+  NSArray *elements, *n;
+
+  elements = [self childrenWithTag: @"n"];
+  if ([elements count] > 0)
+    n = [[elements objectAtIndex: 0] values];
+  else
+    n = nil;
+
+  return n;
+}
+
+- (void) setOrg: (NSString *) anOrg
+          units: (NSArray *) someUnits
+{
+  CardElement *org;
+  unsigned int count, max;
+
+  org = [self uniqueChildWithTag: @"org"];
+  if (anOrg)
+    [org setValue: 0 to: anOrg];
+  if (someUnits)
+    {
+      max = [someUnits count];
+      for (count = 0; count < max; count++)
+        [org setValue: count + 1 to: [someUnits objectAtIndex: count]];
+    }
+}
+
+- (NSArray *) org
+{
+  NSArray *elements, *org;
+
+  elements = [self childrenWithTag: @"org"];
+  if ([elements count] > 0)
+    org = [[elements objectAtIndex: 0] values];
+  else
+    org = nil;
+
+  return org;
+}
+
+// - (void) setOrg: (NGVCardOrg *) _v
+// {
+//   ASSIGNCOPY(self->org, _v);
+// }
+
+// - (NGVCardOrg *)org {
+//   return self->org;
+// }
+
+// - (void)setNickname: (id) _v
+// {
+//   if (![_v isKindOfClass:[NGVCardStrArrayValue class]] && [_v isNotNull])
+//     _v = [[[NGVCardStrArrayValue alloc] initWithPropertyList:_v] autorelease];
+  
+//   ASSIGNCOPY(self->nickname, _v);
+// }
+
+// - (NGVCardStrArrayValue *) nickname
+// {
+//   return self->nickname;
+// }
+
+// - (void) setCategories: (id) _v
+// {
+//   if (![_v isKindOfClass:[NGVCardStrArrayValue class]] && [_v isNotNull])
+//     _v = [[[NGVCardStrArrayValue alloc] initWithPropertyList:_v] autorelease];
+  
+//   ASSIGNCOPY(self->categories, _v);
+// }
+
+// - (NGVCardStrArrayValue *) categories
+// {
+//   return self->categories;
+// }
+
+// - (void) setTel: (NSArray *) _tel
+// {
+//   ASSIGNCOPY(self->tel, _tel);
+// }
+
+// - (NSArray *) tel
+// {
+//   return [self childrenWithTag: @"tel"];
+// }
+
+// - (void) setAdr: (NSArray *) _adr
+// {
+//   ASSIGNCOPY(self->adr, _adr);
+// }
+
+// - (NSArray *) adr
+// {
+//   return self->adr;
+// }
+
+// - (void) setEmail: (NSArray *) _email
+// {
+//   ASSIGNCOPY(self->email, _email);
+// }
+
+// - (NSArray *) email
+// {
+//   return [self childrenWithTag: @"email"];
+// }
+
+// - (void) setLabel: (NSArray *) _label
+// {
+//   ASSIGNCOPY(self->label, _label);
+// }
+
+// - (NSArray *) label
+// {
+//   return [self childrenWithTag: @"email"];
+// }
+
+// - (void) setUrl: (NSArray *) _url
+// {
+//   ASSIGNCOPY(self->url, _url);
+// }
+
+// - (NSArray *) url
+// {
+//   return self->url;
+// }
+
+// - (void) setFreeBusyURL: (NSArray *) _v
+// {
+//   ASSIGNCOPY(self->fburl, _v);
+// }
+
+// - (NSArray *) freeBusyURL
+// {
+//   return self->fburl;
+// }
+
+// - (void) setCalURI: (NSArray *) _v
+// {
+//   ASSIGNCOPY(self->caluri, _v);
+// }
+
+// - (NSArray *) calURI
+// {
+//   return self->caluri;
+// }
+
+// - (void) setX: (NSDictionary *) _dict
+// {
+//   ASSIGNCOPY(self->x, _dict);
+// }
+
+// - (NSDictionary *) x
+// {
+//   return self->x;
+// }
+
+/* convenience */
+
+- (CardElement *) _preferredElementWithTag: (NSString *) aTag
+{
+  NSArray *elements, *prefElements;
+  CardElement *element;
+
+  elements = [self childrenWithTag: aTag];
+  if (elements && [elements count] > 0)
+    {
+      prefElements = [elements cardElementsWithAttribute: @"type"
+                               havingValue: @"pref"];
+      if (prefElements && [prefElements count] > 0)
+        element = [prefElements objectAtIndex: 0];
+      else
+        element = [elements objectAtIndex: 0];
+    }
+  else
+    element = nil;
+
+  return element;
+}
+
+- (NSString *) preferredEMail
+{
+  return [[self _preferredElementWithTag: @"email"] value: 0];
+}
+
+- (NSString *) preferredTel
+{
+  return [[self _preferredElementWithTag: @"tel"] value: 0];
+}
+
+- (CardElement *) preferredAdr
+{
+  return [self _preferredElementWithTag: @"adr"];
+}
+
+/* description */
+
+- (void) appendAttributesToDescription: (NSMutableString *) _ms
+{
+  if ([self uid]) [_ms appendFormat:@" uid='%@'", [self uid]];
+  
+//   if ([[self tel] count] > 0) [_ms appendFormat:@" tel=%@", [self tel];
+//   if ([[self adr] count])
+//     [_ms appendFormat:@" adr=%@", [self adr]];
+//   if ([[self email] count])
+//     [_ms appendFormat:@" email=%@", [self email]];
+//   if ([[self label] count])
+//     [_ms appendFormat:@" label=%@", [self label]];
+//   if ([[self x] count])
+//     [_ms appendFormat:@" x=%@", [self x]];
+}
+
+- (NSString *) description
+{
+  NSMutableString *str = nil;
+  
+  str = [NSMutableString stringWithCapacity:64];
+  [str appendFormat:@"<0x%p[%@]:", self, NSStringFromClass([self class])];
+  [self appendAttributesToDescription:str];
+  [str appendString:@">"];
+  return str;
+}
+
+@end /* NGVCard */
diff --git a/SOPE/NGCards/NSArray+NGCards.h b/SOPE/NGCards/NSArray+NGCards.h
new file mode 100644 (file)
index 0000000..0d2ba14
--- /dev/null
@@ -0,0 +1,42 @@
+/* NSArray+NGCards.h - this file is part of SOPE
+ *
+ * Copyright (C) 2006 Inverse groupe conseil
+ *
+ * Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This file 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; see the file COPYING.  If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef NSARRAY_NGCARDS_H
+#define NSARRAY_NGCARDS_H
+
+#import <Foundation/NSArray.h>
+
+@interface NSArray (NGCardsExtensions)
+
+- (NSString *) valueForCaseInsensitiveString: (NSString *) aString;
+
+- (BOOL) hasCaseInsensitiveString: (NSString *) aString;
+
+- (NSArray *) cardElementsWithTag: (NSString *) aTag;
+- (NSArray *) cardElementsWithAttribute: (NSString *) anAttribute
+                            havingValue: (NSString *) aValue;
+
+- (NSArray *) renderedForCards;
+
+@end
+
+#endif /* NSARRAY_NGCARDS_H */
diff --git a/SOPE/NGCards/NSArray+NGCards.m b/SOPE/NGCards/NSArray+NGCards.m
new file mode 100644 (file)
index 0000000..9c87ab3
--- /dev/null
@@ -0,0 +1,133 @@
+/* NSArray+NGCards.m - this file is part of SOPE
+ *
+ * Copyright (C) 2006 Inverse groupe conseil
+ *
+ * Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This file 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; see the file COPYING.  If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#import <Foundation/NSString.h>
+
+#import "CardElement.h"
+#import "NSString+NGCards.h"
+
+#import "NSArray+NGCards.h"
+
+@implementation NSArray (NGCardsExtensions)
+
+- (NSString *) valueForCaseInsensitiveString: (NSString *) aString
+{
+  NSString *currentString, *resultString, *cmpString;
+  unsigned int count, max;
+  BOOL found;
+
+  found = NO;
+
+  cmpString = [aString uppercaseString];
+  max = [self count];
+  count = 0;
+
+  currentString = nil;
+  while (!found && count < max)
+    {
+      currentString = [self objectAtIndex: count];
+      if ([[currentString uppercaseString] isEqualToString: cmpString])
+        found = YES;
+      else
+        count++;
+    }
+
+  if (found)
+    resultString = currentString;
+  else
+    resultString = nil;
+
+  return resultString;
+}
+
+- (BOOL) hasCaseInsensitiveString: (NSString *) aString
+{
+  return ([self valueForCaseInsensitiveString: aString] != nil);
+}
+
+- (NSArray *) cardElementsWithTag: (NSString *) aTag
+{
+  NSMutableArray *matchingElements;
+  NSEnumerator *allElements;
+  CardElement *currentElement;
+  NSString *cmpTag, *currentTag;
+
+  cmpTag = [aTag uppercaseString];
+
+  matchingElements = [NSMutableArray new];
+  [matchingElements autorelease];
+
+  allElements = [self objectEnumerator];
+  currentElement = [allElements nextObject];
+  while (currentElement)
+    {
+      currentTag = [[currentElement tag] uppercaseString];
+      if ([currentTag isEqualToString: cmpTag])
+        [matchingElements addObject: currentElement];
+      currentElement = [allElements nextObject];
+    }
+
+  return matchingElements;
+}
+
+- (NSArray *) cardElementsWithAttribute: (NSString *) anAttribute
+                            havingValue: (NSString *) aValue
+{
+  NSMutableArray *matchingElements;
+  NSEnumerator *allElements;
+  CardElement *currentElement;
+
+  allElements = [self objectEnumerator];
+
+  matchingElements = [NSMutableArray new];
+  [matchingElements autorelease];
+  currentElement = [allElements nextObject];
+  while (currentElement)
+    {
+      if ([currentElement hasAttribute: anAttribute
+                          havingValue: aValue])
+        [matchingElements addObject: currentElement];
+      currentElement = [allElements nextObject];
+    }
+
+  return matchingElements;
+}
+
+- (NSArray *) renderedForCards
+{
+  NSMutableArray *purified;
+  NSString *string;
+  NSEnumerator *strings;
+
+  purified = [NSMutableArray arrayWithCapacity: [self count]];
+  strings = [self objectEnumerator];
+  string = [strings nextObject];
+  while (string)
+    {
+      [purified addObject: [string escapedForCards]];
+      string = [strings nextObject];
+    }
+
+  return purified;
+}
+
+@end
diff --git a/SOPE/NGCards/NSCalendarDate+ICal.h b/SOPE/NGCards/NSCalendarDate+ICal.h
new file mode 100644 (file)
index 0000000..1f10eac
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+  Copyright (C) 2000-2005 SKYRIX Software AG
+
+  This file is part of SOPE.
+
+  SOPE is free software; you can redistribute it and/or modify it under
+  the terms of the GNU Lesser General Public License as published by the
+  Free Software Foundation; either version 2, or (at your option) any
+  later version.
+
+  SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+  License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with SOPE; see the file COPYING.  If not, write to the
+  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+  02111-1307, USA.
+*/
+
+#ifndef __NGiCal_NSCalendarDate_ICal_H__
+#define __NGiCal_NSCalendarDate_ICal_H__
+
+#import <Foundation/NSCalendarDate.h>
+
+@class NSTimeZone;
+
+@interface NSCalendarDate(iCalRepresentation)
+
++ (id)calendarDateWithICalRepresentation:(NSString *)_iCalRep;
+
+/* represention */
+
+- (NSString *)icalStringWithTimeZone:(NSTimeZone *)_tz;
+- (NSString *)icalString;
+
+@end
+
+@interface NSCalendarDate (iCalRecurrenceCalculatorExtensions)
+- (unsigned)yearsBetweenDate:(NSCalendarDate *)_date;
+- (unsigned)monthsBetweenDate:(NSCalendarDate *)_date;
+- (unsigned)daysBetweenDate:(NSCalendarDate *)_date;
+@end
+
+#endif /* __NGiCal_NSCalendarDate_ICal_H__ */
diff --git a/SOPE/NGCards/NSCalendarDate+ICal.m b/SOPE/NGCards/NSCalendarDate+ICal.m
new file mode 100644 (file)
index 0000000..7e42019
--- /dev/null
@@ -0,0 +1,145 @@
+/*
+  Copyright (C) 2000-2005 SKYRIX Software AG
+
+  This file is part of SOPE.
+
+  SOPE is free software; you can redistribute it and/or modify it under
+  the terms of the GNU Lesser General Public License as published by the
+  Free Software Foundation; either version 2, or (at your option) any
+  later version.
+
+  SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+  License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with SOPE; see the file COPYING.  If not, write to the
+  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+  02111-1307, USA.
+*/
+
+#include "NSCalendarDate+ICal.h"
+#include "iCalDateHolder.h"
+#include "common.h"
+
+static NSTimeZone *gmt = nil;
+static inline void _setupGMT(void) {
+  if (gmt == nil)
+    gmt = [[NSTimeZone timeZoneWithAbbreviation:@"GMT"] retain];
+}
+
+@interface iCalDateHolder (PrivateAPI)
+- (id)awakeAfterUsingSaxDecoder:(id)_decoder;
+@end
+
+@implementation NSCalendarDate(iCalRepresentation)
+
+/* represention */
+
+static NSString *gmtcalfmt = @"%Y%m%dT%H%M%SZ";
+
++ (id)calendarDateWithICalRepresentation:(NSString *)_iCalRep {
+  iCalDateHolder *dh;
+  NSCalendarDate *date;
+  
+  dh = [[iCalDateHolder alloc] init];
+  [dh setString:_iCalRep];
+  date = [dh awakeAfterUsingSaxDecoder:nil];
+  [dh release];
+  return date;
+}
+
+- (NSString *)icalStringInGMT {
+  NSTimeZone *oldtz;
+  NSString   *s;
+  _setupGMT();
+  
+  /* set GMT as timezone */
+  oldtz = [[self timeZone] retain];
+  if (oldtz == gmt) {
+    [oldtz release];
+    oldtz = nil;
+  }
+  else {
+    [self setTimeZone:gmt];
+  }
+  
+  /* calc string */
+  s = [self descriptionWithCalendarFormat:gmtcalfmt];
+  
+  /* restore old timezone */
+  if (oldtz) {
+    [self setTimeZone:oldtz];
+    [oldtz release];
+  }
+  
+  return s;
+}
+
+- (NSString *)icalStringWithTimeZone:(NSTimeZone *)_tz {
+  _setupGMT();
+  
+  if (_tz == gmt || _tz == nil)
+    return [self icalStringInGMT];
+  else if ([_tz isEqual:gmt])
+    return [self icalStringInGMT];
+  else {
+    /* not in GMT */
+    NSLog(@"WARNING(%s): arbitary timezones not supported yet: %@",
+          __PRETTY_FUNCTION__, _tz);
+    return [self icalStringInGMT];
+  }
+}
+
+- (NSString *)icalString {
+  _setupGMT();
+  return [self icalStringWithTimeZone:gmt];
+}
+
+@end /* NSDate(ICalValue) */
+
+
+#ifndef ABS
+#define ABS(a) ((a) < 0 ? -(a) : (a))
+#endif
+
+@implementation NSCalendarDate (iCalRecurrenceCalculatorExtensions)
+
+- (unsigned)yearsBetweenDate:(NSCalendarDate *)_date {
+  return ABS([self yearOfCommonEra] - [_date yearOfCommonEra]);
+}
+
+- (unsigned)monthsBetweenDate:(NSCalendarDate *)_date {
+  NSCalendarDate     *start, *end;
+  NSComparisonResult order;
+  int                yDiff;
+  
+  order = [self compare:_date];
+  if (order == NSOrderedSame)
+    return 0;
+  else if (order == NSOrderedAscending) {
+    start = self;
+    end   = _date;
+  }
+  else {
+    start = _date;
+    end   = self;
+  }
+  yDiff = [end yearOfCommonEra] - [start yearOfCommonEra];
+  if (yDiff > 0) {
+    unsigned monthsRemaining, monthsToGo;
+    
+    monthsRemaining = 12 - [start monthOfYear];
+    monthsToGo      = [end monthOfYear];
+    yDiff          -= 1;
+    return monthsRemaining + monthsToGo + (12 * yDiff);
+  }
+  /* start and end in same year, calculate plain diff */
+  return [end monthOfYear] - [start monthOfYear];
+}
+
+- (unsigned)daysBetweenDate:(NSCalendarDate *)_date {
+  return ABS([self julianNumber] - [_date julianNumber]);
+}
+@end /* NSCalendarDate (iCalRecurrenceCalculatorExtensions) */
diff --git a/SOPE/NGCards/NSCalendarDate+NGCards.h b/SOPE/NGCards/NSCalendarDate+NGCards.h
new file mode 100644 (file)
index 0000000..bbdc9ad
--- /dev/null
@@ -0,0 +1,38 @@
+/* NSDate+NGCards.h - this file is part of SOPE
+ *
+ * Copyright (C) 2006 Inverse groupe conseil
+ *
+ * Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This file 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; see the file COPYING.  If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef NSDATE_NGCARDS_H
+#define NSDATE_NGCARDS_H
+
+#import <Foundation/NSDate.h>
+#import <Foundation/NSCalendarDate.h>
+
+#import "CardElement.h"
+
+@interface NSDate (NGCardsExtensions)
+
+- (NSString *) iCalFormattedDateTimeString;
+- (NSString *) iCalFormattedDateString;
+
+@end
+
+#endif /* NSDATE_NGCARDS_H */
diff --git a/SOPE/NGCards/NSCalendarDate+NGCards.m b/SOPE/NGCards/NSCalendarDate+NGCards.m
new file mode 100644 (file)
index 0000000..d4620e4
--- /dev/null
@@ -0,0 +1,47 @@
+/* NSDate+NGCards.m - this file is part of SOGo
+ *
+ * Copyright (C) 2006 Inverse groupe conseil
+ *
+ * Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This file 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; see the file COPYING.  If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#import <Foundation/NSString.h>
+
+#import "CardElement.h"
+
+#import "NSCalendarDate+NGCards.h"
+#import "NSString+NGCards.h"
+
+@implementation NSCalendarDate (NGCardsExtensions)
+
+- (NSString *) iCalFormattedDateTimeString
+{
+  return [NSString stringWithFormat: @"%.4d%.2d%.2dT%.2d%.2d%.2d",
+                   [self yearOfCommonEra], [self monthOfYear],
+                   [self dayOfMonth], [self hourOfDay],
+                   [self minuteOfHour], [self secondOfMinute]];
+}
+
+- (NSString *) iCalFormattedDateString
+{
+  return [NSString stringWithFormat: @"%.4d%.2d%.2d",
+                   [self yearOfCommonEra], [self monthOfYear],
+                   [self dayOfMonth]];
+}
+
+@end
diff --git a/SOPE/NGCards/NSDictionary+NGCards.h b/SOPE/NGCards/NSDictionary+NGCards.h
new file mode 100644 (file)
index 0000000..97d464d
--- /dev/null
@@ -0,0 +1,34 @@
+/* NSDictionary+NGCards.h - this file is part of SOPE
+ *
+ * Copyright (C) 2006 Inverse groupe conseil
+ *
+ * Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This file 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; see the file COPYING.  If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef NSDICTIONARY_NGCARDS_H
+#define NSDICTIONARY_NGCARDS_H
+
+#import <Foundation/NSDictionary.h>
+
+@interface NSDictionary (NGCardsExtensions)
+
+- (id) objectForCaseInsensitiveKey: (NSString *) aKey;
+
+@end
+
+#endif /* NSDICTIONARY_NGCARDS_H */
diff --git a/SOPE/NGCards/NSDictionary+NGCards.m b/SOPE/NGCards/NSDictionary+NGCards.m
new file mode 100644 (file)
index 0000000..2e77546
--- /dev/null
@@ -0,0 +1,40 @@
+/* NSDictionary+NGCards.m - this file is part of SOPE
+ *
+ * Copyright (C) 2006 Inverse groupe conseil
+ *
+ * Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This file 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; see the file COPYING.  If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#import "NSArray+NGCards.h"
+
+#import "NSDictionary+NGCards.h"
+
+@implementation NSDictionary (NGCardsExtension)
+
+- (id) objectForCaseInsensitiveKey: (NSString *) aKey
+{
+  NSString *realKey;
+  NSArray *keys;
+
+  keys = [self allKeys];
+  realKey = [keys valueForCaseInsensitiveString: aKey];
+
+  return ((realKey) ? [self objectForKey: realKey] : nil);
+}
+
+@end
diff --git a/SOPE/NGCards/NSString+NGCards.h b/SOPE/NGCards/NSString+NGCards.h
new file mode 100644 (file)
index 0000000..2b57059
--- /dev/null
@@ -0,0 +1,45 @@
+/* NSString+NGCards.h - this file is part of SOPE
+ *
+ * Copyright (C) 2006 Inverse groupe conseil
+ *
+ * Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This file 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; see the file COPYING.  If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef NSSTRING_NGCARDS_H
+#define NSSTRING_NGCARDS_H
+
+#import <Foundation/NSString.h>
+
+@class NSCalendarDate;
+@class NSTimeZone;
+
+@interface NSString (NGCardsExtensions)
+
+- (NSString *) foldedForVersitCards;
+- (NSArray *) asCardAttributeValues;
+- (NSString *) escapedForCards;
+- (NSString *) unescapedFromCard;
+
+- (NSTimeInterval) durationAsTimeInterval;
+- (NSCalendarDate *) asCalendarDate;
+
+- (NSArray *) commaSeparatedValues;
+
+@end
+
+#endif /* NSSTRING_NGCARDS_H */
diff --git a/SOPE/NGCards/NSString+NGCards.m b/SOPE/NGCards/NSString+NGCards.m
new file mode 100644 (file)
index 0000000..768009d
--- /dev/null
@@ -0,0 +1,272 @@
+/* NSString+NGCards.m - this file is part of SOPE
+ *
+ * Copyright (C) 2006 Inverse groupe conseil
+ *
+ * Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This file 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; see the file COPYING.  If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#import <Foundation/NSArray.h>
+#import <Foundation/NSRange.h>
+#import <Foundation/NSTimeZone.h>
+#import <NGExtensions/NSObject+Logs.h>
+
+#import <ctype.h>
+
+#import "NSString+NGCards.h"
+
+static NSString *commaSeparator = nil;
+
+@implementation NSString (NGCardsExtensions)
+
+- (void) _initCommaSeparator
+{
+  commaSeparator = [NSMutableString stringWithFormat: @"%c", 255];
+  [commaSeparator retain];
+}
+
+- (NSString *) foldedForVersitCards
+{
+  NSString *newString;
+  NSMutableString *foldedString;
+  unsigned int length;
+  NSRange subStringRange;
+
+  length = [self length];
+  if (length < 76)
+    newString = self;
+  else
+    {
+      foldedString = [NSMutableString new];
+      [foldedString autorelease];
+
+      subStringRange = NSMakeRange (0, 75);
+      [foldedString appendFormat: @"%@\r\n",
+                    [self substringWithRange: subStringRange]];
+      subStringRange = NSMakeRange (75, 74);
+      while ((length - subStringRange.location) > 74)
+        {
+          [foldedString appendFormat: @" %@\r\n",
+                        [self substringWithRange: subStringRange]];
+          subStringRange.location += 74;
+        }
+      subStringRange.length = length - subStringRange.location;
+      [foldedString appendFormat: @" %@",
+                    [self substringWithRange: subStringRange]];
+
+      newString = foldedString;
+    }
+
+  return newString;
+}
+
+- (NSArray *) asCardAttributeValues
+{
+  NSMutableArray *values;
+  NSEnumerator *rawValues;
+  NSString *tmpString, *rawValue, *newString;
+
+  values = [NSMutableArray new];
+  [values autorelease];
+
+  if (!commaSeparator)
+    [self _initCommaSeparator];
+
+  tmpString = [self stringByReplacingString: @"\\,"
+                    withString: commaSeparator];
+  rawValues = [[tmpString componentsSeparatedByString: @","]
+                objectEnumerator];
+  rawValue = [rawValues nextObject];
+  while (rawValue)
+    {
+      newString = [rawValue stringByReplacingString: commaSeparator
+                            withString: @","];
+      [values addObject: [newString stringByTrimmingSpaces]];
+      rawValue = [rawValues nextObject];
+    }
+
+  return values;
+}
+
+- (NSString *) escapedForCards
+{
+  NSString *string;
+
+  string = [self stringByReplacingString: @"\\"
+                 withString: @"\\\\"];
+  string = [string stringByReplacingString: @","
+                   withString: @"\\,"];
+//   string = [string stringByReplacingString: @":"
+//                    withString: @"\\:"];
+  string = [string stringByReplacingString: @";"
+                   withString: @"\\;"];
+  string = [string stringByReplacingString: @"\n"
+                   withString: @"\\n"];
+  string = [string stringByReplacingString: @"\r"
+                   withString: @"\\r"];
+
+  return string;
+}
+
+- (NSString *) unescapedFromCard
+{
+  NSString *string;
+
+  string = [self stringByReplacingString: @"\\,"
+                 withString: @","];
+  string = [string stringByReplacingString: @"\\:"
+                   withString: @":"];
+  string = [string stringByReplacingString: @"\\;"
+                   withString: @";"];
+  string = [string stringByReplacingString: @"\\n"
+                   withString: @"\n"];
+  string = [string stringByReplacingString: @"\\r"
+                   withString: @"\r"];
+  string = [string stringByReplacingString: @"\\\\"
+                   withString: @"\\"];
+
+  return string;
+}
+
+- (NSTimeInterval) durationAsTimeInterval
+{
+  /*
+    eg: DURATION:PT1H
+    P      - "period"
+    P2H30M - "2 hours 30 minutes"
+
+     dur-value  = (["+"] / "-") "P" (dur-date / dur-time / dur-week)
+
+     dur-date   = dur-day [dur-time]
+     dur-time   = "T" (dur-hour / dur-minute / dur-second)
+     dur-week   = 1*DIGIT "W"
+     dur-hour   = 1*DIGIT "H" [dur-minute]
+     dur-minute = 1*DIGIT "M" [dur-second]
+     dur-second = 1*DIGIT "S"
+     dur-day    = 1*DIGIT "D"
+  */
+  unsigned       i, len;
+  NSTimeInterval ti;
+  BOOL           isTime;
+  int            val;
+    
+  if (![self hasPrefix:@"P"]) {
+    NSLog(@"Cannot parse iCal duration value: '%@'", self);
+    return 0.0;
+  }
+    
+  ti  = 0.0;
+  val = 0;
+  for (i = 1, len = [self length], isTime = NO; i < len; i++) {
+    unichar c;
+      
+    c = [self characterAtIndex:i];
+    if (c == 't' || c == 'T') {
+      isTime = YES;
+      val = 0;
+      continue;
+    }
+      
+    if (isdigit(c)) {
+      val = (val * 10) + (c - 48);
+      continue;
+    }
+      
+    switch (c) {
+    case 'W': /* week */
+      ti += (val * 7 * 24 * 60 * 60);
+      break;
+    case 'D': /* day  */
+      ti += (val * 24 * 60 * 60);
+      break;
+    case 'H': /* hour */
+      ti += (val * 60 * 60);
+      break;
+    case 'M': /* min  */
+      ti += (val * 60);
+      break;
+    case 'S': /* sec  */
+      ti += val;
+      break;
+    default:
+      [self logWithFormat: @"cannot process duration unit: '%c'", c];
+      break;
+    }
+    val = 0;
+  }
+  return ti;
+}
+
+- (NSCalendarDate *) asCalendarDate
+{
+  NSRange cursor;
+  NSCalendarDate *date;
+  NSTimeZone *utc;
+  int year, month, day, hour, minute, second;
+
+  if ([self length] > 14)
+    {
+      cursor = NSMakeRange(0, 4);
+      year = [[self substringWithRange: cursor] intValue];
+      cursor.location += cursor.length;
+      cursor.length = 2;
+      month = [[self substringWithRange: cursor] intValue];
+      cursor.location += cursor.length;
+      day = [[self substringWithRange: cursor] intValue];
+
+      cursor.location += cursor.length + 1;
+      hour = [[self substringWithRange: cursor] intValue];
+      cursor.location += cursor.length;
+      minute = [[self substringWithRange: cursor] intValue];
+      cursor.location += cursor.length;
+      second = [[self substringWithRange: cursor] intValue];
+
+      utc = [NSTimeZone timeZoneWithAbbreviation: @"GMT"];
+      date = [NSCalendarDate dateWithYear: year month: month 
+                             day: day hour: hour minute: minute 
+                             second: second
+                             timeZone: utc];
+    }
+  else
+    date = nil;
+
+  return date;
+}
+
+- (NSArray *) commaSeparatedValues
+{
+  NSEnumerator *rawValues;
+  NSMutableArray *values;
+  NSString *currentValue, *newValue;
+
+  values = [NSMutableArray new];
+  [values autorelease];
+
+  rawValues = [[self componentsSeparatedByString: @","] objectEnumerator];
+  currentValue = [rawValues nextObject];
+  while (currentValue)
+    {
+      newValue = [currentValue stringByTrimmingSpaces];
+      if ([newValue length])
+        [values addObject: newValue];
+      currentValue = [rawValues nextObject];
+    }
+
+  return values;
+}
+
+@end
diff --git a/SOPE/NGCards/README b/SOPE/NGCards/README
new file mode 100644 (file)
index 0000000..8497a5b
--- /dev/null
@@ -0,0 +1,19 @@
+TODO: improve text
+
+Objective-C classes for representing iCalendar entities as objects. To
+actually parse iCalendar entities the sope-xml versitSaxDriver is used.
+Note that this library doesn't make any use of the now deprecated libical but
+rather relies on the SAX interface (SaxObjectDecoder is used).
+
+Recurrences
+===========
+
+Recurrences are modeled via iCalRecurrenceRules and an iCalRecurrenceCalculator
+which contains all the necessary logic according to RFC2445 to interpret
+iCalRecurrenceRules. The calculator needs a referrence date for the first
+instance of a recurrence which is usually provided by any of the repeatable
+entity objects (i.e. iCalEvent).
+
+Please note that recurrences are work in progress and far from being
+complete/compliant with RFC2445. So far only the most simple cases are done
+properly.
diff --git a/SOPE/NGCards/Version b/SOPE/NGCards/Version
new file mode 100644 (file)
index 0000000..dbc6c2d
--- /dev/null
@@ -0,0 +1,8 @@
+# version file
+
+MAJOR_VERSION=4
+MINOR_VERSION=5
+SUBMINOR_VERSION:=76
+
+# v4.5.40 requires NGExtensions v4.5.145
+# v4.5.37 requires NGExtensions v4.5.140
diff --git a/SOPE/NGCards/common.h b/SOPE/NGCards/common.h
new file mode 100644 (file)
index 0000000..0ffdb57
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+  Copyright (C) 2000-2005 SKYRIX Software AG
+
+  This file is part of SOPE.
+
+  SOPE is free software; you can redistribute it and/or modify it under
+  the terms of the GNU Lesser General Public License as published by the
+  Free Software Foundation; either version 2, or (at your option) any
+  later version.
+
+  SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+  License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with SOPE; see the file COPYING.  If not, write to the
+  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+  02111-1307, USA.
+*/
+
+#ifndef __ICal_common_H__
+#define __ICal_common_H__
+
+#import <Foundation/Foundation.h>
+#import <EOControl/EOControl.h>
+#include <NGExtensions/NGExtensions.h>
+
+#define IS_EQUAL(a, b, sel) \
+  _iCalSafeCompareObjects(a, b, @selector(sel))
+
+static __inline__ BOOL _iCalSafeCompareObjects(id a, id b, SEL comparator) {
+  id va = a;
+  id vb = b;
+  BOOL (*compm)(id, SEL, id);
+
+  if((va == nil && vb != nil) || (va != nil && vb == nil))
+    return NO;
+  else if(va == vb)
+    return YES;
+  compm = (BOOL (*)( id, SEL, id))[va methodForSelector:comparator];
+  return compm(va, comparator, vb);
+}
+
+#endif /* __ICal_common_H__ */
diff --git a/SOPE/NGCards/iCalAlarm.h b/SOPE/NGCards/iCalAlarm.h
new file mode 100644 (file)
index 0000000..1f97547
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+  Copyright (C) 2000-2005 SKYRIX Software AG
+
+  This file is part of SOPE.
+
+  SOPE is free software; you can redistribute it and/or modify it under
+  the terms of the GNU Lesser General Public License as published by the
+  Free Software Foundation; either version 2, or (at your option) any
+  later version.
+
+  SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+  License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with SOPE; see the file COPYING.  If not, write to the
+  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+  02111-1307, USA.
+*/
+
+#ifndef __NGCards_iCalAlarm_H__
+#define __NGCards_iCalAlarm_H__
+
+#import "CardGroup.h"
+
+@class NSString;
+@class iCalTrigger;
+@class iCalAttachment;
+
+@interface iCalAlarm : CardGroup
+
+/* accessors */
+
+- (iCalTrigger *) trigger;
+- (iCalAttachment *) attach;
+- (NSString *) comment;
+- (NSString *) action;
+- (void) setRecurrenceRule: (NSString *) _recurrenceRule;
+- (NSString *) recurrenceRule;
+
+@end
+
+#endif /* __NGCards_iCalAlarm_H__ */
diff --git a/SOPE/NGCards/iCalAlarm.m b/SOPE/NGCards/iCalAlarm.m
new file mode 100644 (file)
index 0000000..2d6a95b
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+  Copyright (C) 2000-2005 SKYRIX Software AG
+
+  This file is part of SOPE.
+
+  SOPE is free software; you can redistribute it and/or modify it under
+  the terms of the GNU Lesser General Public License as published by the
+  Free Software Foundation; either version 2, or (at your option) any
+  later version.
+
+  SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+  License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with SOPE; see the file COPYING.  If not, write to the
+  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+  02111-1307, USA.
+*/
+
+#import "iCalAttachment.h"
+#import "iCalRecurrenceRule.h"
+#import "iCalTrigger.h"
+
+#import "iCalAlarm.h"
+#import "common.h"
+
+@implementation iCalAlarm
+
+- (Class) classForTag: (NSString *) classTag
+{
+  Class tagClass;
+
+  if ([classTag isEqualToString: @"TRIGGER"])
+    tagClass = [iCalTrigger class];
+  else if ([classTag isEqualToString: @"ATTACH"])
+    tagClass = [iCalAttachment class];
+  else if ([classTag isEqualToString: @"RRULE"])
+    tagClass = [iCalRecurrenceRule class];
+  else if ([classTag isEqualToString: @"ACTION"]
+           || [classTag isEqualToString: @"COMMENT"])
+    tagClass = [CardElement class];
+  else
+    tagClass = [super classForTag: classTag];
+
+  return tagClass;
+}
+
+/* accessors */
+
+- (void) setTrigger: (iCalTrigger *) _value
+{
+  [self setUniqueChild: _value];
+}
+
+- (iCalTrigger *) trigger
+{
+  return (iCalTrigger *) [self uniqueChildWithTag: @"trigger"];
+}
+
+- (void) setAttach: (iCalAttachment *) _value
+{
+  [self setUniqueChild: _value];
+}
+
+- (iCalAttachment *) attach
+{
+  return (iCalAttachment *) [self uniqueChildWithTag: @"attach"];
+}
+
+- (void) setComment: (NSString *) _value
+{
+  [[self uniqueChildWithTag: @"comment"] setValue: 0
+                                         to: _value];
+}
+
+- (NSString *) comment
+{
+  return [[self uniqueChildWithTag: @"comment"] value: 0];
+}
+
+- (void) setAction: (NSString *) _value
+{
+  [[self uniqueChildWithTag: @"action"] setValue: 0
+                                        to: _value];
+}
+
+- (NSString *) action
+{
+  return [[self uniqueChildWithTag: @"action"] value: 0];
+}
+
+- (void) setRecurrenceRule: (NSString *) _recurrenceRule
+{
+  [[self uniqueChildWithTag: @"rrule"] setValue: 0
+                                       to: _recurrenceRule];
+}
+
+- (NSString *) recurrenceRule
+{
+  return [[self uniqueChildWithTag: @"rrule"] value: 0];
+}
+
+@end /* iCalAlarm */
diff --git a/SOPE/NGCards/iCalAttachment.h b/SOPE/NGCards/iCalAttachment.h
new file mode 100644 (file)
index 0000000..0b326b7
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+  Copyright (C) 2000-2005 SKYRIX Software AG
+
+  This file is part of SOPE.
+
+  SOPE is free software; you can redistribute it and/or modify it under
+  the terms of the GNU Lesser General Public License as published by the
+  Free Software Foundation; either version 2, or (at your option) any
+  later version.
+
+  SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+  License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with SOPE; see the file COPYING.  If not, write to the
+  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+  02111-1307, USA.
+*/
+
+#ifndef __NGiCal_iCalAttachment_H__
+#define __NGiCal_iCalAttachment_H__
+
+#import "CardElement.h"
+
+@class NSString;
+
+@interface iCalAttachment : CardElement
+
+- (void) setValue: (NSString *) aValue;
+- (NSString *) value;
+
+- (void) setValueType: (NSString *) aType;
+- (NSString *) valueType;
+
+@end
+
+#endif /* __NGiCal_iCalAttachment_H__ */
diff --git a/SOPE/NGCards/iCalAttachment.m b/SOPE/NGCards/iCalAttachment.m
new file mode 100644 (file)
index 0000000..b115e09
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+  Copyright (C) 2000-2005 SKYRIX Software AG
+
+  This file is part of SOPE.
+
+  SOPE is free software; you can redistribute it and/or modify it under
+  the terms of the GNU Lesser General Public License as published by the
+  Free Software Foundation; either version 2, or (at your option) any
+  later version.
+
+  SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+  License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with SOPE; see the file COPYING.  If not, write to the
+  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+  02111-1307, USA.
+*/
+
+#import "iCalAttachment.h"
+#import "common.h"
+
+@implementation iCalAttachment
+
+/* accessors */
+
+- (void) setValue: (NSString *) _value
+{
+  [self setValue: 0 to: _value];
+}
+
+- (NSString *) value
+{
+  return [self value: 0];
+}
+
+- (void) setValueType: (NSString *) _value
+{
+  [self setValue: 0 ofAttribute: @"type" to: _value];
+}
+
+- (NSString *) valueType
+{
+  return [self value: 0 ofAttribute: @"type"];
+}
+
+@end /* iCalAttachment */
diff --git a/SOPE/NGCards/iCalCalendar.h b/SOPE/NGCards/iCalCalendar.h
new file mode 100644 (file)
index 0000000..074352e
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+  Copyright (C) 2000-2005 SKYRIX Software AG
+
+  This file is part of SOPE.
+
+  SOPE is free software; you can redistribute it and/or modify it under
+  the terms of the GNU Lesser General Public License as published by the
+  Free Software Foundation; either version 2, or (at your option) any
+  later version.
+
+  SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+  License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with SOPE; see the file COPYING.  If not, write to the
+  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+  02111-1307, USA.
+*/
+
+#ifndef __NGCards_iCalCalendar_H__
+#define __NGCards_iCalCalendar_H__
+
+#import "CardGroup.h"
+
+@class NSString;
+@class NSMutableArray;
+@class NSArray;
+@class NSEnumerator;
+@class NSMutableDictionary;
+
+@class iCalEvent;
+@class iCalToDo;
+@class iCalJournal;
+@class iCalFreeBusy;
+@class iCalEntityObject;
+@class iCalTimeZone;
+
+@interface iCalCalendar : CardGroup
+// {
+//   NSString *version;
+//   NSString *calscale;
+//   NSString *prodId;
+//   NSString *method;
+
+//   NSMutableArray *todos;
+//   NSMutableArray *events;
+//   NSMutableArray *journals;
+//   NSMutableArray *freeBusys;
+//   NSMutableDictionary *timezones;
+// }
+
+/* accessors */
+
+- (void) setMethod: (NSString *)_method;
+- (NSString *) method;
+
+- (void) setCalscale: (NSString *) _calscale;
+- (NSString *) calscale;
+
+- (void) setVersion: (NSString *) _version;
+- (NSString *) version;
+
+- (void) setProdID: (NSString *) _prodid;
+- (NSString *) prodId;
+
+- (void) setMethod: (NSString *) _method;
+- (NSString *) method;
+
+- (NSArray *) events;
+- (NSArray *) todos;
+- (NSArray *) journals;
+- (NSArray *) freeBusys;
+
+- (void) addTimeZone: (iCalTimeZone *) iTZ;
+- (iCalTimeZone *) timeZoneWithId: (NSString *) tzId;
+
+/* collection */
+
+- (NSArray *) allObjects;
+
+@end
+
+#endif /* __NGCards_iCalCalendar_H__ */
diff --git a/SOPE/NGCards/iCalCalendar.m b/SOPE/NGCards/iCalCalendar.m
new file mode 100644 (file)
index 0000000..060509e
--- /dev/null
@@ -0,0 +1,209 @@
+/*
+  Copyright (C) 2000-2005 SKYRIX Software AG
+
+  This file is part of SOPE.
+
+  SOPE is free software; you can redistribute it and/or modify it under
+  the terms of the GNU Lesser General Public License as published by the
+  Free Software Foundation; either version 2, or (at your option) any
+  later version.
+
+  SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+  License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with SOPE; see the file COPYING.  If not, write to the
+  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+  02111-1307, USA.
+*/
+
+#import <Foundation/NSArray.h>
+#import <Foundation/NSString.h>
+
+#import "iCalEvent.h"
+#import "iCalToDo.h"
+#import "iCalJournal.h"
+#import "iCalFreeBusy.h"
+#import "iCalEntityObject.h"
+#import "iCalTimeZone.h"
+
+#import "iCalCalendar.h"
+
+@interface iCalCalendar (Privates)
+- (void) addToEvents: (iCalEvent *) _event;
+- (void) addToTimezones: (iCalTimeZone *) _tz;
+- (void) addToTodos: (iCalToDo *) _todo;
+- (void) addToJournals: (iCalJournal *) _obj;
+- (void) addToFreeBusys: (iCalFreeBusy *) _obj;
+@end
+
+@implementation iCalCalendar
+
+/* class mapping */
+- (Class) classForTag: (NSString *) classTag
+{
+  Class tagClass;
+
+  tagClass = Nil;
+  if ([classTag isEqualToString: @"VEVENT"])
+    tagClass = [iCalEvent class];
+  else if ([classTag isEqualToString: @"VTODO"])
+    tagClass = [iCalToDo class];
+  else if ([classTag isEqualToString: @"VJOURNAL"])
+    tagClass = [iCalJournal class];
+  else if ([classTag isEqualToString: @"VFREEBUSY"])
+    tagClass = [iCalFreeBusy class];
+  else if ([classTag isEqualToString: @"VTIMEZONE"])
+    tagClass = [iCalTimeZone class];
+  else if ([classTag isEqualToString: @"METHOD"]
+           || [classTag isEqualToString: @"PRODID"]
+           || [classTag isEqualToString: @"VERSION"])
+    tagClass = [CardElement class];
+  else
+    tagClass = [super classForTag: classTag];
+
+  return tagClass;
+}
+
+/* accessors */
+
+- (void) setCalscale: (NSString *) _calscale
+{
+  [[self uniqueChildWithTag: @"calscale"] setValue: 0 to: _calscale];
+}
+
+- (NSString *) calscale
+{
+  return [[self uniqueChildWithTag: @"calscale"] value: 0];
+}
+
+- (void) setVersion: (NSString *) _version
+{
+  [[self uniqueChildWithTag: @"version"] setValue: 0 to: _version];
+}
+
+- (NSString *) version
+{
+  return [[self uniqueChildWithTag: @"version"] value: 0];
+}
+
+- (void) setProdID: (NSString *) _value
+{
+  [[self uniqueChildWithTag: @"prodid"] setValue: 0 to: _value];
+}
+
+- (NSString *) prodId
+{
+  return [[self uniqueChildWithTag: @"prodid"] value: 0];
+}
+
+- (void) setMethod: (NSString *) _value
+{
+  [[self uniqueChildWithTag: @"method"] setValue: 0 to: _value];
+}
+
+- (NSString *) method
+{
+  return [[self uniqueChildWithTag: @"method"] value: 0];
+}
+
+- (void) addToEvents: (iCalEvent *) _event
+{
+  [self addChild: _event];
+}
+
+- (NSArray *) events
+{
+  return [self childrenWithTag: @"vevent"];
+}
+
+- (void) addToTimezones: (iCalTimeZone *) _tz
+{
+  [self addChild: _tz];
+}
+
+- (NSArray *) timezones
+{
+  return [self childrenWithTag: @"vtimezone"];
+}
+
+- (void) addToTodos: (iCalToDo *) _todo
+{
+  [self addChild: _todo];
+}
+
+- (NSArray *) todos
+{
+  return [self childrenWithTag: @"vtodo"];
+}
+
+- (void) addToJournals: (iCalJournal *) _todo
+{
+  [self addChild: _todo];
+}
+
+- (NSArray *) journals
+{
+  return [self childrenWithTag: @"vjournal"];
+}
+
+- (void) addToFreeBusys: (iCalFreeBusy *) _fb
+{
+  [self addChild: _fb];
+}
+
+- (NSArray *) freeBusys
+{
+  return [self childrenWithTag: @"vfreebusy"];
+}
+
+- (void) addTimeZone: (iCalTimeZone *) iTZ
+{
+  iCalTimeZone *possibleTz;
+
+  possibleTz = [self timeZoneWithId: [iTZ tzId]];
+  if (!possibleTz)
+    [self addChild: iTZ];
+}
+
+- (iCalTimeZone *) timeZoneWithId: (NSString *) tzId
+{
+  NSArray *matchingTimeZones;
+  iCalTimeZone *timeZone;
+
+  matchingTimeZones = [self childrenGroupWithTag: @"vtimezone"
+                            withChild: @"tzid"
+                            havingSimpleValue: tzId];
+  if ([matchingTimeZones count])
+    timeZone = [matchingTimeZones objectAtIndex: 0];
+  else
+    timeZone = nil;
+
+  return timeZone;
+}
+
+/* collection */
+
+- (NSArray *) allObjects
+{
+  NSMutableArray *ma;
+  
+  ma = [NSMutableArray new];
+  [ma addObjectsFromArray: [self events]];
+  [ma addObjectsFromArray: [self todos]];
+  [ma addObjectsFromArray: [self journals]];
+  [ma addObjectsFromArray: [self freeBusys]];
+
+  return ma;
+}
+
+/* ical typing */
+
+- (NSString *) entityName
+{
+  return @"vcalendar";
+}
+
+@end /* iCalCalendar */
diff --git a/SOPE/NGCards/iCalDailyRecurrenceCalculator.m b/SOPE/NGCards/iCalDailyRecurrenceCalculator.m
new file mode 100644 (file)
index 0000000..6ba00f1
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+  Copyright (C) 2004-2005 SKYRIX Software AG
+  
+  This file is part of SOPE.
+  
+  SOPE is free software; you can redistribute it and/or modify it under
+  the terms of the GNU Lesser General Public License as published by the
+  Free Software Foundation; either version 2, or (at your option) any
+  later version.
+  
+  SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+  License for more details.
+  
+  You should have received a copy of the GNU Lesser General Public
+  License along with SOPE; see the file COPYING.  If not, write to the
+  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+  02111-1307, USA.
+*/
+
+#include "iCalRecurrenceCalculator.h"
+
+@interface iCalDailyRecurrenceCalculator : iCalRecurrenceCalculator
+@end
+
+#include <NGExtensions/NGCalendarDateRange.h>
+#include "iCalRecurrenceRule.h"
+#include "NSCalendarDate+ICal.h"
+#include "common.h"
+
+@interface iCalRecurrenceCalculator(PrivateAPI)
+- (NSCalendarDate *)lastInstanceStartDate;
+@end
+
+@implementation iCalDailyRecurrenceCalculator
+
+- (NSArray *)recurrenceRangesWithinCalendarDateRange:(NGCalendarDateRange *)_r{
+  NSMutableArray *ranges;
+  NSCalendarDate *firStart;
+  long           i, jnFirst, jnStart, jnEnd, startEndCount;
+  unsigned       interval;
+
+  firStart = [self->firstRange startDate];
+  jnFirst  = [firStart julianNumber];
+  jnEnd    = [[_r endDate] julianNumber];
+  
+  if (jnFirst > jnEnd)
+    return nil;
+  
+  jnStart  = [[_r startDate] julianNumber];
+  interval = [self->rrule repeatInterval];
+  
+  /* if rule is bound, check the bounds */
+  if (![self->rrule isInfinite]) {
+    NSCalendarDate *until;
+    long           jnRuleLast;
+    
+    until = [self->rrule untilDate];
+    if (until) {
+      if ([until compare:[_r startDate]] == NSOrderedAscending)
+        return nil;
+      jnRuleLast = [until julianNumber];
+    }
+    else {
+      jnRuleLast = (interval * [self->rrule repeatCount])
+      + jnFirst;
+      if (jnRuleLast < jnStart)
+        return nil;
+    }
+    /* jnStart < jnRuleLast < jnEnd ? */
+    if (jnEnd > jnRuleLast)
+      jnEnd = jnRuleLast;
+  }
+
+  startEndCount = (jnEnd - jnStart) + 1;
+  ranges        = [NSMutableArray arrayWithCapacity:startEndCount];
+  for (i = 0 ; i < startEndCount; i++) {
+    long jnCurrent;
+    
+    jnCurrent = jnStart + i;
+    if (jnCurrent >= jnFirst) {
+      long jnTest;
+      
+      jnTest = jnCurrent - jnFirst;
+      if ((jnTest % interval) == 0) {
+        NSCalendarDate      *start, *end;
+        NGCalendarDateRange *r;
+      
+        start = [NSCalendarDate dateForJulianNumber:jnCurrent];
+        [start setTimeZone:[firStart timeZone]];
+        start = [start hour:  [firStart hourOfDay]
+                       minute:[firStart minuteOfHour]
+                       second:[firStart secondOfMinute]];
+        end   = [start addTimeInterval:[self->firstRange duration]];
+        r     = [NGCalendarDateRange calendarDateRangeWithStartDate:start
+                                     endDate:end];
+        if ([_r containsDateRange:r])
+          [ranges addObject:r];
+      }
+    }
+  }
+  return ranges;
+}
+
+- (NSCalendarDate *)lastInstanceStartDate {
+  if ([self->rrule repeatCount] > 0) {
+    long           jnFirst, jnRuleLast;
+    NSCalendarDate *firStart, *until;
+
+    firStart   = [self->firstRange startDate];
+    jnFirst    = [firStart julianNumber];
+    jnRuleLast = ([self->rrule repeatInterval] *
+                  [self->rrule repeatCount]) +
+                  jnFirst;
+    until      = [NSCalendarDate dateForJulianNumber:jnRuleLast];
+    until      = [until hour:  [firStart hourOfDay]
+                        minute:[firStart minuteOfHour]
+                        second:[firStart secondOfMinute]];
+    return until;
+  }
+  return [super lastInstanceStartDate];
+}
+
+@end /* iCalDailyRecurrenceCalculator */
diff --git a/SOPE/NGCards/iCalDataSource.h b/SOPE/NGCards/iCalDataSource.h
new file mode 100644 (file)
index 0000000..770987b
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+  Copyright (C) 2000-2005 SKYRIX Software AG
+
+  This file is part of SOPE.
+
+  SOPE is free software; you can redistribute it and/or modify it under
+  the terms of the GNU Lesser General Public License as published by the
+  Free Software Foundation; either version 2, or (at your option) any
+  later version.
+
+  SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+  License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with SOPE; see the file COPYING.  If not, write to the
+  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+  02111-1307, USA.
+*/
+
+#ifndef __NGiCal_iCalDataSource_H__
+#define __NGiCal_iCalDataSource_H__
+
+#import <EOControl/EODataSource.h>
+
+@class NSString, NSURL;
+@class EOFetchSpecification;
+
+@interface iCalDataSource : EODataSource
+{
+  EOFetchSpecification *fetchSpecification;
+  NSURL    *url;
+  NSString *entityName;
+}
+
+- (id)initWithURL:(NSURL *)_url      entityName:(NSString *)_ename;
+- (id)initWithPath:(NSString *)_file entityName:(NSString *)_ename;
+- (id)initWithURL:(NSURL *)_url;
+- (id)initWithPath:(NSString *)_file;
+
+/* accessors */
+
+- (void)setFetchSpecification:(EOFetchSpecification *)_fspec;
+- (EOFetchSpecification *)fetchSpecification;
+
+/* fetching */
+
+- (NSArray *)fetchObjects;
+
+@end
+
+#endif /* __NGiCal_iCalDataSource_H__ */
diff --git a/SOPE/NGCards/iCalDataSource.m b/SOPE/NGCards/iCalDataSource.m
new file mode 100644 (file)
index 0000000..b84c394
--- /dev/null
@@ -0,0 +1,247 @@
+/*
+  Copyright (C) 2000-2005 SKYRIX Software AG
+
+  This file is part of SOPE.
+
+  SOPE is free software; you can redistribute it and/or modify it under
+  the terms of the GNU Lesser General Public License as published by the
+  Free Software Foundation; either version 2, or (at your option) any
+  later version.
+
+  SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+  License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with SOPE; see the file COPYING.  If not, write to the
+  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+  02111-1307, USA.
+*/
+
+#include "iCalDataSource.h"
+#include "iCalObject.h"
+#include "iCalCalendar.h"
+#include <SaxObjC/SaxObjC.h>
+#include "common.h"
+
+@interface NSObject(suppressCapitalizedKeyWarning)
++ (void)suppressCapitalizedKeyWarning;
+@end
+
+@implementation iCalDataSource
+
+// THREAD
+static id<NSObject,SaxXMLReader> parser = nil;
+static SaxObjectDecoder *sax = nil;
+
+- (void)_setupGlobals {
+  if (parser == nil ) {
+    SaxXMLReaderFactory *factory = 
+      [SaxXMLReaderFactory standardXMLReaderFactory];
+    parser = [[factory createXMLReaderForMimeType:@"text/calendar"] retain];
+    if (parser == nil)
+      [self logWithFormat:@"ERROR: found no SAX driver for 'text/calendar'!"];
+  }
+  if (sax == nil && parser != nil) {
+    NSBundle *bundle;
+    NSString *p;
+    
+#if COCOA_Foundation_LIBRARY
+    /* otherwise we get warning on "X-WR-TIMEZONE" etc */
+    [NSClassFromString(@"NSKeyBinding") suppressCapitalizedKeyWarning];
+#endif
+    
+    bundle = [NSBundle bundleForClass:[self class]];
+    if ((p = [bundle pathForResource:@"NGiCal" ofType:@"xmap"]))
+      sax = [[SaxObjectDecoder alloc] initWithMappingAtPath:p];
+    else
+      sax = [[SaxObjectDecoder alloc] initWithMappingNamed:@"NGiCal"];
+    
+    [parser setContentHandler:sax];
+    [parser setErrorHandler:sax];
+  }
+}
+
+
+- (id)initWithURL:(NSURL *)_url entityName:(NSString *)_ename {
+  [self _setupGlobals];
+  if ((self = [super init])) {
+    self->url        = [_url copy];
+    self->entityName = [_ename copy];
+  }
+  return self;
+}
+- (id)initWithPath:(NSString *)_path entityName:(NSString *)_ename {
+  NSURL *lurl;
+  
+  lurl = [[[NSURL alloc] initFileURLWithPath:_path] autorelease];
+  return [self initWithURL:lurl entityName:_ename];
+}
+
+- (id)initWithURL:(NSURL *)_url {
+  return [self initWithURL:_url entityName:nil];
+}
+- (id)initWithPath:(NSString *)_path {
+  return [self initWithPath:_path entityName:nil];
+}
+- (id)init {
+  return [self initWithURL:nil];
+}
+
+- (void)dealloc {
+  [self->fetchSpecification release];
+  [self->url                release];
+  [self->entityName         release];
+  [super dealloc];
+}
+
+/* accessors */
+
+- (void)setFetchSpecification:(EOFetchSpecification *)_fspec {
+  if ([self->fetchSpecification isEqual:_fspec])
+    return;
+  
+  ASSIGNCOPY(self->fetchSpecification, _fspec);
+
+  [self postDataSourceChangedNotification];
+}
+- (EOFetchSpecification *)fetchSpecification {
+  return self->fetchSpecification;
+}
+
+/* fetching */
+
+- (id)_parseCalendar {
+  id cal;
+  
+  if (parser == nil) {
+    [self logWithFormat:@"ERROR: missing iCalendar parser!"];
+    return nil;
+  }
+  if (sax == nil) {
+    [self logWithFormat:@"ERROR: missing SAX handler!"];
+    return nil;
+  }
+  
+  [parser parseFromSource:self->url];
+  cal = [sax rootObject];
+  
+  return cal;
+}
+
+- (NSArray *)objectsForEntityNamed:(NSString *)ename inCalendar:(id)_cal {
+  if ([ename isEqualToString:@"vevent"])
+    return [_cal events];
+  if ([ename isEqualToString:@"vtodo"])
+    return [_cal todos];
+  if ([ename isEqualToString:@"vjournal"])
+    return [_cal journals];
+  if ([ename isEqualToString:@"vfreebusy"])
+    return [_cal freeBusys];
+  
+  [self logWithFormat:@"unknown calendar entity '%@'", ename];
+  return nil;
+}
+
+- (NSArray *)objectsFromCalendar:(id)_cal {
+  NSString *ename;
+  
+  ename = [self->fetchSpecification entityName];
+  if ([ename length] == 0)
+    ename = self->entityName;
+
+  if ([ename length] == 0)
+    return [_cal allObjects];
+  
+  if ([_cal isKindOfClass:[NSDictionary class]]) {
+    /*
+      This happens with Mozilla Calendar which posts one vcalendar
+      entry (with method 'PUBLISH') per event.
+    */
+    NSMutableArray *ma;
+    NSArray  *calendars;
+    unsigned i, count;
+    
+    if (![[(NSDictionary *)_cal objectForKey:@"tag"] 
+          isEqualToString:@"iCalendar"]) {
+      [self logWithFormat:
+             @"ERROR: calendar (entity=%@) passed in as a dictionary: %@", 
+             _cal];
+    }
+    
+    if ((calendars=[(NSDictionary *)_cal objectForKey:@"subcomponents"])==nil)
+      return nil;
+
+    count = [calendars count];
+    ma = [NSMutableArray arrayWithCapacity:(count + 1)];
+    
+    for (i = 0; i < count; i++) {
+      NSArray *objects;
+      
+      objects = [self objectsForEntityNamed:ename 
+                     inCalendar:[calendars objectAtIndex:i]];
+      if ([objects count] == 0)
+       continue;
+      
+      [ma addObjectsFromArray:objects];
+    }
+    return ma;
+  }
+  
+  return [self objectsForEntityNamed:ename inCalendar:_cal];
+}
+
+- (NSArray *)fetchObjects {
+  NSAutoreleasePool *pool;
+  NSArray *result;
+  id calendar;
+
+  pool = [[NSAutoreleasePool alloc] init];
+  
+  if ((calendar = [self _parseCalendar]) == nil)
+    return nil;
+  
+  if (self->fetchSpecification == nil) {
+    result = [[self objectsFromCalendar:calendar] shallowCopy];
+  }
+  else {
+    NSMutableArray *ma;
+    NSEnumerator   *e;
+    EOQualifier *q;
+    NSArray     *sort;
+    NSArray     *objects;
+    iCalObject  *object;
+    
+    /* get objects */
+    
+    objects = [self objectsFromCalendar:calendar];
+    
+    /* first filter using qualifier */
+    
+    ma = [NSMutableArray arrayWithCapacity:[objects count]];
+    q  = [self->fetchSpecification qualifier];
+    e  = [objects objectEnumerator];
+    while ((object = [e nextObject])) {
+      if (q) {
+       if (![(id<EOQualifierEvaluation>)q evaluateWithObject:object])
+         continue;
+      }
+      
+      [ma addObject:object];
+    }
+    
+    /* now sort */
+    
+    if ((sort = [self->fetchSpecification sortOrderings]))
+      [ma sortUsingKeyOrderArray:sort];
+
+    result = [ma shallowCopy];
+  }
+  
+  [pool release];
+  
+  return [result autorelease];
+}
+
+@end /* iCalDataSource */
diff --git a/SOPE/NGCards/iCalDateHolder.h b/SOPE/NGCards/iCalDateHolder.h
new file mode 100644 (file)
index 0000000..fa62027
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+  Copyright (C) 2000-2005 SKYRIX Software AG
+
+  This file is part of SOPE.
+
+  SOPE is free software; you can redistribute it and/or modify it under
+  the terms of the GNU Lesser General Public License as published by the
+  Free Software Foundation; either version 2, or (at your option) any
+  later version.
+
+  SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+  License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with SOPE; see the file COPYING.  If not, write to the
+  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+  02111-1307, USA.
+*/
+
+#ifndef __NGiCal_iCalDateHolder_H__
+#define __NGiCal_iCalDateHolder_H__
+
+#import <Foundation/NSObject.h>
+
+@class NSString, NSTimeZone;
+
+@interface iCalDateHolder : NSObject
+{
+  NSString *tzid;
+  NSString *string;
+  NSString *tag;
+}
+
+- (void)setString:(NSString *)_value;
+- (NSString *)string;
+
+- (void)setTag:(NSString *)_value;
+- (NSString *)tag;
+
+- (void)setTzid:(NSString *)_value;
+- (NSString *)tzid;
+
+- (NSTimeZone *)timeZone;
+
+@end
+
+#endif /* __NGiCal_iCalDateHolder_H__ */
diff --git a/SOPE/NGCards/iCalDateHolder.m b/SOPE/NGCards/iCalDateHolder.m
new file mode 100644 (file)
index 0000000..91c4e0a
--- /dev/null
@@ -0,0 +1,232 @@
+/*
+  Copyright (C) 2000-2005 SKYRIX Software AG
+
+  This file is part of SOPE.
+
+  SOPE is free software; you can redistribute it and/or modify it under
+  the terms of the GNU Lesser General Public License as published by the
+  Free Software Foundation; either version 2, or (at your option) any
+  later version.
+
+  SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+  License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with SOPE; see the file COPYING.  If not, write to the
+  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+  02111-1307, USA.
+*/
+
+#include "iCalDateHolder.h"
+#include "iCalObject.h"
+#include "common.h"
+
+@interface NSTimeZone(iCalTimeZone)
+
++ (NSTimeZone *)timeZoneWithICalId:(NSString *)_tz;
+
+@end
+
+@implementation iCalDateHolder
+
+static NSTimeZone *gmt = nil;
+
++ (void)initialize {
+  if (gmt == nil)
+    gmt = [[NSTimeZone timeZoneWithName:@"GMT"] retain];
+}
+
+- (void)dealloc {
+  [self->tzid   release];
+  [self->string release];
+  [self->tag    release];
+  [super dealloc];
+}
+
+/* accessors */
+
+- (void)setString:(NSString *)_value {
+  ASSIGNCOPY(self->string, _value);
+}
+- (NSString *)string {
+  return self->string;
+}
+
+- (void)setTag:(NSString *)_value {
+  ASSIGNCOPY(self->tag, _value);
+}
+- (NSString *)tag {
+  return self->tag;
+}
+
+- (void)setTzid:(NSString *)_value {
+  ASSIGNCOPY(self->tzid, _value);
+}
+- (NSString *)tzid {
+  return self->tzid;
+}
+
+/* mapping to Foundation */
+
+- (NSTimeZone *)timeZone {
+  // TODO: lookup tzid in iCalCalendar !
+  NSString *s;
+
+  s = [self tzid];
+  
+  /* a hack */
+  if ([s hasPrefix:@"/softwarestudio.org"]) {
+    NSRange r;
+    
+    r = [s rangeOfString:@"Europe/"];
+    if (r.length > 0)
+      s = [s substringFromIndex:r.location];
+  }
+  return [NSTimeZone timeZoneWithICalId:s];
+}
+
+/* decoding */
+
+- (id)awakeAfterUsingSaxDecoder:(id)_decoder {
+  NSCalendarDate *date = nil;
+  NSString   *s;
+  NSTimeZone *tz;
+  
+  s = self->string;
+  if ([s length] < 5) {
+    [self logWithFormat:@"tag %@: got an weird date string '%@' ?!", 
+           self->tag, s];
+    return s;
+  }
+  
+  /* calculate timezone */
+  
+  if ([self->string hasSuffix:@"Z"]) {
+    /* zulu time, eg 20021009T094500Z */
+    tz = gmt;
+    s = [s substringToIndex:([s length] - 1)];
+  }
+  else
+    tz = [self timeZone];
+  
+  /* 
+     012345678901234
+     20021009T094500 - 15 chars 
+     20021009T0945   - 13 chars 
+     991009T0945     - 11 chars
+     
+     20031111        - 8 chars
+  */
+  if ([s rangeOfString:@"T"].length == 0 && [s length] == 8) {
+    /* hm, maybe a date without a time? like an allday event! */
+    int year, month, day;
+    char buf[16];
+    [s getCString:&(buf[0])];
+    
+    buf[9] = '\0';
+    day    = atoi(&(buf[6]));  buf[6] = '\0';
+    month  = atoi(&(buf[4]));  buf[4] = '\0';
+    year   = atoi(&(buf[0]));
+    
+    date = [NSCalendarDate dateWithYear:year month:month day:day
+                           hour:0 minute:0 second:0
+                           timeZone:tz];
+  }
+  else if ([s length] == 15) {
+    int year, month, day, hour, minute, second;
+    char buf[24];
+    [s getCString:&(buf[0])];
+      
+    second = atoi(&(buf[13])); buf[13] = '\0';
+    minute = atoi(&(buf[11])); buf[11] = '\0';
+    hour   = atoi(&(buf[9]));  buf[9] = '\0';
+    day    = atoi(&(buf[6]));  buf[6] = '\0';
+    month  = atoi(&(buf[4]));  buf[4] = '\0';
+    year   = atoi(&(buf[0]));
+      
+    date = [NSCalendarDate dateWithYear:year month:month day:day
+                           hour:hour minute:minute second:second
+                           timeZone:tz];
+  }
+  else
+    NSLog(@"%s: unknown date format (%@) ???", __PRETTY_FUNCTION__, s);
+    
+  if (date == nil)
+    NSLog(@"couldn't convert string '%@' to date (format '%@') ..", s);
+
+  return date;
+}
+
+/* description */
+
+- (void)appendAttributesToDescription:(NSMutableString *)ms {
+  if (self->tag)    [ms appendFormat:@" %@",  self->tag];
+  if (self->string) [ms appendFormat:@" '%@'",  self->string];
+  if (self->tzid)   [ms appendFormat:@" tz=%@", self->tzid];
+}
+
+- (NSString *)description {
+  NSMutableString *ms;
+
+  ms = [NSMutableString stringWithCapacity:128];
+  [ms appendFormat:@"<0x%p[%@]:", self, NSStringFromClass([self class])];
+  [self appendAttributesToDescription:ms];
+  [ms appendString:@">"];
+  return ms;
+}
+
+@end /* iCalDateHolder */
+
+@implementation NSTimeZone(iCalTimeZone)
+
+static NSMutableDictionary *idToTz = nil; // THREAD
+
++ (NSTimeZone *)timeZoneWithICalId:(NSString *)_tzid {
+  static NSString *iCalDefaultTZ = nil;
+  NSTimeZone *tz;
+  
+  if (idToTz == nil)
+    idToTz = [[NSMutableDictionary alloc] initWithCapacity:16];
+  
+  if ([_tzid length] == 0) {
+    
+    tz = [iCalObject iCalDefaultTimeZone];
+    if (tz != nil) return tz;
+
+    if (iCalDefaultTZ == nil) {
+      NSString *defTz;
+      NSUserDefaults *ud;
+      // TODO: take a default timeZone
+      ud = [NSUserDefaults standardUserDefaults];
+      defTz = [ud stringForKey:@"iCalTimeZoneName"];
+      if ([defTz length] == 0)
+        defTz = [ud stringForKey:@"TimeZoneName"];
+      if ([defTz length] == 0)
+        defTz = [ud stringForKey:@"TimeZone"];
+      if ([defTz length] == 0)
+        defTz = @"GMT";
+      iCalDefaultTZ = [defTz retain];
+    }
+    
+    _tzid = iCalDefaultTZ;
+    
+  }
+  
+  if ([_tzid length] == 0)
+    _tzid = @"GMT";
+  
+  tz = [idToTz objectForKey:_tzid];
+  if (tz == nil) tz = [NSTimeZone timeZoneWithName:_tzid];
+  if (tz == nil) tz = [NSTimeZone timeZoneWithAbbreviation:_tzid];
+  
+  if (tz == nil) {
+    NSLog(@"couldn't map timezone id %@", _tzid);
+  }
+  
+  if (tz) [idToTz setObject:tz forKey:_tzid];
+  return tz;
+}
+
+@end /* NSTimeZone(iCalTimeZone) */
diff --git a/SOPE/NGCards/iCalDateTime.h b/SOPE/NGCards/iCalDateTime.h
new file mode 100644 (file)
index 0000000..a3fa2c1
--- /dev/null
@@ -0,0 +1,41 @@
+/* iCalDateTime.h - this file is part of SOGo
+ *
+ * Copyright (C) 2006 Inverse groupe conseil
+ *
+ * Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This file 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; see the file COPYING.  If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef ICALDATETIMEELEMENT_H
+#define ICALDATETIMEELEMENT_H
+
+@class NSCalendarDate;
+@class iCalTimeZone;
+
+#import "CardElement.h"
+
+@interface iCalDateTime : CardElement
+
+- (void) setTimeZone: (iCalTimeZone *) iTZ;
+- (iCalTimeZone *) timeZone;
+
+- (void) setDateTime: (NSCalendarDate *) dateTime;
+- (NSCalendarDate *) dateTime;
+
+@end
+
+#endif /* ICALDATETIMEELEMENT_H */
diff --git a/SOPE/NGCards/iCalDateTime.m b/SOPE/NGCards/iCalDateTime.m
new file mode 100644 (file)
index 0000000..b0f1a19
--- /dev/null
@@ -0,0 +1,155 @@
+/* iCalDateTime.m - this file is part of SOPE
+ *
+ * Copyright (C) 2006 Inverse groupe conseil
+ *
+ * Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This file 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; see the file COPYING.  If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#import <Foundation/NSString.h>
+#import <Foundation/NSTimeZone.h>
+
+#import "NSCalendarDate+NGCards.h"
+#import "NSString+NGCards.h"
+
+#import "iCalCalendar.h"
+#import "iCalTimeZone.h"
+
+#import "iCalDateTime.h"
+
+// static NSTimeZone *localTimeZone = nil;
+
+@implementation iCalDateTime
+
+// + (void) initialize
+// {
+//   if (!localTimeZone)
+//     {
+//       localTimeZone = [NSTimeZone defaultTimeZone];
+//       [localTimeZone retain];
+//     }
+// }
+
+// + (void) setLocalTimeZone: (NSTimeZone *) aTimeZone
+// {
+//   [localTimeZone release];
+//   localTimeZone = aTimeZone;
+//   [localTimeZone retain];
+// }
+
+- (void) setTimeZone: (iCalTimeZone *) iTZ
+{
+  iCalCalendar *calendar;
+  NSCalendarDate *dateTime;
+  NSString *newTZId;
+
+  dateTime = [self dateTime];
+  if (iTZ)
+    {
+      calendar
+        = (iCalCalendar *) [self searchParentOfClass: [iCalCalendar class]];
+      if (calendar)
+        [calendar addTimeZone: iTZ];
+      newTZId = [iTZ tzId];
+    }
+  [self setValue: 0 ofAttribute: @"tzid" to: newTZId];
+  [self setDateTime: dateTime];
+}
+
+- (iCalTimeZone *) timeZone
+{
+  iCalCalendar *calendar;
+  NSString *tzId;
+  iCalTimeZone *timeZone;
+
+  tzId = [self value: 0 ofAttribute: @"tzid"];
+  calendar
+    = (iCalCalendar *) [self searchParentOfClass: [iCalCalendar class]];
+  if ([tzId length] && calendar)
+    timeZone = [calendar timeZoneWithId: tzId];
+  else
+    timeZone = nil;
+
+  return timeZone;
+}
+
+/* TODO: should implement the case where the TZ would be implicitly local
+   (no TZID and no UTC) */
+- (void) setDateTime: (NSCalendarDate *) dateTime
+{
+  NSCalendarDate *tmpTime;
+  NSTimeZone *utcTZ;
+  NSString *timeString;
+  iCalTimeZone *tz;
+
+  if (dateTime)
+    {
+      tz = [self timeZone];
+      if (tz)
+        timeString = [tz dateTimeStringForDate: dateTime];
+      else
+        {
+          utcTZ = [NSTimeZone timeZoneWithName: @"GMT"];
+
+          tmpTime = [dateTime copy];
+          [tmpTime setTimeZone: utcTZ];
+          timeString = [NSString stringWithFormat: @"%@Z",
+                                 [tmpTime iCalFormattedDateTimeString]];
+          [tmpTime release];
+        }
+    }
+  else
+    timeString = @"";
+
+  [self setValue: 0 to: timeString];
+}
+
+- (NSCalendarDate *) dateTime
+{
+  iCalTimeZone *iTZ;
+  NSString *date;
+  NSCalendarDate *initialDate, *dateTime;
+  NSTimeZone *tz;
+
+  date = [self value: 0];
+  iTZ = [self timeZone];
+  if (iTZ)
+    dateTime = [iTZ dateForDateTimeString: date];
+  else
+    {
+      initialDate = [date asCalendarDate];
+      if (initialDate)
+        {
+          if ([date hasSuffix: @"Z"] || [date hasSuffix: @"z"])
+            dateTime = initialDate;
+          else
+            {
+              /* same TODO as above */
+              tz = [NSTimeZone defaultTimeZone];
+              dateTime = [initialDate addYear: 0 month: 0 day: 0
+                                      hour: 0 minute: 0
+                                      second: -[tz secondsFromGMT]];
+            }
+        }
+      else
+        dateTime = nil;
+    }
+
+  return dateTime;
+}
+
+@end
diff --git a/SOPE/NGCards/iCalEntityObject.h b/SOPE/NGCards/iCalEntityObject.h
new file mode 100644 (file)
index 0000000..0f74893
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+  Copyright (C) 2000-2005 SKYRIX Software AG
+
+  This file is part of SOPE.
+
+  SOPE is free software; you can redistribute it and/or modify it under
+  the terms of the GNU Lesser General Public License as published by the
+  Free Software Foundation; either version 2, or (at your option) any
+  later version.
+
+  SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+  License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with SOPE; see the file COPYING.  If not, write to the
+  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+  02111-1307, USA.
+*/
+
+#ifndef __NGCards_iCalEntityObject_H__
+#define __NGCards_iCalEntityObject_H__
+
+#import "CardGroup.h"
+
+/*
+  iCalEntityObject
+  
+  This is a common base class for tasks and appointments which share a lot of
+  attributes.
+*/
+
+@class NSCalendarDate, NSMutableArray, NSString, NSArray, NSNumber;
+@class iCalPerson;
+@class NSURL;
+
+@interface iCalEntityObject : CardGroup
+
+/* accessors */
+
+- (void) setUid: (NSString *) _value;
+- (NSString *) uid;
+
+- (void) setSummary: (NSString *) _value;
+- (NSString *) summary;
+
+- (void) setLocation: (NSString *) _value;
+- (NSString *) location;
+
+- (void) setComment: (NSString *) _value;
+- (NSString *) comment;
+
+- (void) setAccessClass:(NSString *) _value;
+- (NSString *) accessClass;
+- (BOOL) isPublic;
+
+- (void) setPriority: (NSString *) _value;
+- (NSString *) priority;
+
+- (void) setCategories: (NSString *) _value;
+- (NSString *)categories;
+
+- (void) setUserComment: (NSString *) _userComment;
+- (NSString *) userComment;
+
+- (void) setTimeStampAsDate: (NSCalendarDate *)_date;
+- (NSCalendarDate *) timeStampAsDate;
+
+- (void)setStartDate:(NSCalendarDate *)_date;
+- (NSCalendarDate *)startDate;
+- (BOOL) hasStartDate;
+
+- (void)setLastModified:(NSCalendarDate *)_value;
+- (NSCalendarDate *)lastModified;
+
+- (void)setCreated:(NSCalendarDate *)_value;
+- (NSCalendarDate *)created;
+
+- (void)setSequence:(NSNumber *)_value; /* this is an int */
+- (NSNumber *)sequence;
+- (void)increaseSequence;
+
+/* url can either be set as NSString or NSURL */ 
+- (void)setUrl:(id)_value;
+- (NSURL *)url;
+  
+- (void)setOrganizer:(iCalPerson *)_organizer;
+- (iCalPerson *)organizer;
+- (BOOL)isOrganizer:(id)_email;
+
+- (void)setStatus:(NSString *)_value;
+- (NSString *)status;
+
+- (void)removeAllAttendees;
+- (void)addToAttendees:(iCalPerson *)_person;
+- (NSArray *)attendees;
+- (void) setAttendees: (NSArray *) attendees;
+
+/* categorize attendees into participants and resources */
+- (NSArray *)participants;
+- (NSArray *)resources;
+- (BOOL)isParticipant:(id)_email;
+- (iCalPerson *)findParticipantWithEmail:(id)_email;
+  
+- (void)removeAllAlarms;
+- (void)addToAlarms:(id)_alarm;
+- (NSArray *)alarms;
+- (BOOL)hasAlarms;
+
+@end
+
+#endif /* __NGCards_iCalEntityObject_H__ */
diff --git a/SOPE/NGCards/iCalEntityObject.m b/SOPE/NGCards/iCalEntityObject.m
new file mode 100644 (file)
index 0000000..d45de9d
--- /dev/null
@@ -0,0 +1,399 @@
+/*
+  Copyright (C) 2000-2005 SKYRIX Software AG
+
+  This file is part of SOPE.
+
+  SOPE is free software; you can redistribute it and/or modify it under
+  the terms of the GNU Lesser General Public License as published by the
+  Free Software Foundation; either version 2, or (at your option) any
+  later version.
+
+  SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+  License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with SOPE; see the file COPYING.  If not, write to the
+  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+  02111-1307, USA.
+*/
+
+#import "NSCalendarDate+NGCards.h"
+#import "CardGroup+iCal.h"
+
+#import "iCalAlarm.h"
+#import "iCalDateTime.h"
+#import "iCalEntityObject.h"
+#import "iCalPerson.h"
+#import "common.h"
+
+@interface iCalEntityObject (PrivateAPI)
+- (NSArray *)_filteredAttendeesThinkingOfPersons:(BOOL)_persons;
+@end
+
+@implementation iCalEntityObject
+
+- (Class) classForTag: (NSString *) classTag
+{
+  Class tagClass;
+
+  if ([classTag isEqualToString: @"ATTENDEE"]
+      || [classTag isEqualToString: @"ORGANIZER"])
+    tagClass = [iCalPerson class];
+  else if ([classTag isEqualToString: @"VALARM"])
+    tagClass = [iCalAlarm class];
+  else if ([classTag isEqualToString: @"SUMMARY"]
+           || [classTag isEqualToString: @"UID"]
+           || [classTag isEqualToString: @"COMMENT"]
+           || [classTag isEqualToString: @"DESCRIPTION"]
+           || [classTag isEqualToString: @"CLASS"]
+           || [classTag isEqualToString: @"STATUS"]
+           || [classTag isEqualToString: @"SEQUENCE"]
+           || [classTag isEqualToString: @"URL"]
+           || [classTag isEqualToString: @"PRIORITY"]
+           || [classTag isEqualToString: @"CATEGORIES"]
+           || [classTag isEqualToString: @"LOCATION"])
+    tagClass = [CardElement class];
+  else if ([classTag isEqualToString: @"DTSTAMP"]
+           || [classTag isEqualToString: @"DTSTART"]
+           || [classTag isEqualToString: @"CREATED"]
+           || [classTag isEqualToString: @"LAST-MODIFIED"])
+    tagClass = [iCalDateTime class];
+  else
+    tagClass = [super classForTag: classTag];
+
+  return tagClass;
+}
+
+/* accessors */
+
+- (void) setUid: (NSString *) _uid
+{
+  [[self uniqueChildWithTag: @"uid"] setValue: 0 to: _uid];
+}
+
+- (NSString *) uid
+{
+  return [[self uniqueChildWithTag: @"uid"] value: 0];
+}
+
+- (void) setSummary: (NSString *) _value
+{
+  [[self uniqueChildWithTag: @"summary"] setValue: 0 to: _value];
+}
+
+- (NSString *) summary
+{
+  return [[self uniqueChildWithTag: @"summary"] value: 0];
+}
+
+- (void) setLocation: (NSString *) _value
+{
+  [[self uniqueChildWithTag: @"location"] setValue: 0 to: _value];
+}
+
+- (NSString *) location
+{
+  return [[self uniqueChildWithTag: @"location"] value: 0];
+}
+
+- (void) setComment: (NSString *) _value
+{
+  [[self uniqueChildWithTag: @"description"] setValue: 0 to: _value];
+}
+
+- (NSString *) comment
+{
+  return [[self uniqueChildWithTag: @"description"] value: 0];
+}
+
+- (void) setAccessClass: (NSString *) _value
+{
+  [[self uniqueChildWithTag: @"class"] setValue: 0 to: _value];
+}
+
+- (NSString *) accessClass
+{
+  return [[self uniqueChildWithTag: @"class"] value: 0];
+}
+
+- (BOOL) isPublic
+{
+  return [[[self accessClass] uppercaseString] isEqualToString: @"PUBLIC"];
+}
+
+- (void) setPriority: (NSString *) _value
+{
+  [[self uniqueChildWithTag: @"priority"] setValue: 0 to: _value];
+}
+
+- (NSString *) priority
+{
+  return [[self uniqueChildWithTag: @"priority"] value: 0];
+}
+
+- (void) setCategories: (NSString *) _value
+{
+  [[self uniqueChildWithTag: @"categories"] setValue: 0 to: _value];
+}
+
+- (NSString *) categories
+{
+  return [[self uniqueChildWithTag: @"categories"] value: 0];
+}
+
+- (void) setUserComment: (NSString *) _value
+{
+  [[self uniqueChildWithTag: @"usercomment"] setValue: 0 to: _value];
+}
+
+- (NSString *) userComment
+{
+  return [[self uniqueChildWithTag: @"usercomment"] value: 0];
+}
+
+- (void) setStatus: (NSString *) _value
+{
+  [[self uniqueChildWithTag: @"status"] setValue: 0 to: _value];
+}
+
+- (NSString *) status
+{
+  return [[self uniqueChildWithTag: @"status"] value: 0];
+}
+
+- (void) setSequence: (NSNumber *)_value
+{
+  NSString *sequence;
+
+  sequence = [NSString stringWithFormat: @"%@", _value];
+  [[self uniqueChildWithTag: @"sequence"] setValue: 0
+                                          to: sequence];;
+}
+
+- (NSNumber *) sequence
+{
+  NSString *sequence;
+
+  sequence = [[self uniqueChildWithTag: @"sequence"] value: 0];
+
+  return [NSNumber numberWithInt: [sequence intValue]];
+}
+
+- (void) increaseSequence
+{
+  int seq;
+  
+  seq = [[self sequence] intValue];
+  seq += 1;
+  [self setSequence: [NSNumber numberWithInt: seq]];
+}
+
+- (void) setCreated: (NSCalendarDate *) _value
+{
+  [self setDate: _value forDateTimeValue: @"created"];
+}
+
+- (NSCalendarDate *) created
+{
+  return [self dateForDateTimeValue: @"created"];
+}
+
+- (void) setLastModified: (NSCalendarDate *) _value
+{
+  [self setDate: _value forDateTimeValue: @"last-modified"];
+}
+
+- (NSCalendarDate *) lastModified
+{
+  return [self dateForDateTimeValue: @"last-modified"];
+}
+
+- (void) setTimeStampAsDate: (NSCalendarDate *) _value
+{
+  [self setDate: _value forDateTimeValue: @"dtstamp"];
+}
+
+- (NSCalendarDate *) timeStampAsDate
+{
+  return [self dateForDateTimeValue: @"dtstamp"];
+}
+
+- (void) setStartDate: (NSCalendarDate *) _value
+{
+  [self setDate: _value forDateTimeValue: @"dtstart"];
+}
+
+- (NSCalendarDate *) startDate
+{
+  return [self dateForDateTimeValue: @"dtstart"];
+}
+
+- (BOOL) hasStartDate
+{
+  return ([[self childrenWithTag: @"dtstart"] count] > 0);
+}
+
+- (void) setOrganizer: (iCalPerson *) _organizer
+{
+  [_organizer setTag: @"organizer"];
+  [self setUniqueChild: _organizer];
+}
+
+- (iCalPerson *) organizer
+{
+  return (iCalPerson *) [self uniqueChildWithTag: @"organizer"];
+}
+
+- (void) removeAllAttendees
+{
+  [children removeObjectsInArray: [self attendees]];
+}
+
+- (void) addToAttendees: (iCalPerson *) _person
+{
+  [_person setTag: @"attendee"];
+  [self addChild: _person];
+}
+
+- (void) setAttendees: (NSArray *) attendees
+{
+  [self removeAllAttendees];
+  [self addChildren: attendees];
+}
+
+- (NSArray *) attendees
+{
+  return [self childrenWithTag: @"attendee"];
+}
+
+- (void) removeAllAlarms
+{
+  [children removeObjectsInArray: [self alarms]];
+}
+
+- (void) addToAlarms: (id) _alarm
+{
+  if (_alarm)
+    {
+      [_alarm setTag: @"valarm"];
+      [self addChild: _alarm];
+    }
+}
+
+- (BOOL) hasAlarms
+{
+  return ([[self childrenWithTag: @"valarm"] count] > 0);
+}
+
+- (NSArray *) alarms
+{
+  return [self childrenWithTag: @"valarm"];
+}
+
+- (void) setUrl: (id) _value
+{
+  NSString *asString;
+
+  if ([_value isKindOfClass: [NSString class]])
+    asString = _value;
+  else if ([_value isKindOfClass: [NSURL class]])
+    asString = [_value absoluteString];
+  else
+    asString = @"";
+
+  [[self uniqueChildWithTag: @"url"] setValue: 0 to: asString];
+}
+
+- (NSURL *) url
+{
+  NSString *stringUrl;
+
+  stringUrl = [[self uniqueChildWithTag: @"url"] value: 0];
+
+  return [NSURL URLWithString: stringUrl];
+}
+
+/* stuff */
+
+- (NSArray *) participants
+{
+  return [self _filteredAttendeesThinkingOfPersons: YES];
+}
+
+- (NSArray *) resources
+{
+  return [self _filteredAttendeesThinkingOfPersons: NO];
+}
+
+- (NSArray *) _filteredAttendeesThinkingOfPersons: (BOOL) _persons
+{
+  NSArray *list;
+  NSMutableArray *filtered;
+  unsigned count, max;
+  iCalPerson *person;
+  NSString *role;
+
+  if (_persons)
+    {
+      list = [self attendees];
+      max = [list count];
+      filtered = [NSMutableArray arrayWithCapacity: max];
+      for (count = 0; count < max; count++)
+        {
+          person = (iCalPerson *) [list objectAtIndex: count];
+          role = [[person role] uppercaseString];
+          if (![role hasPrefix: @"NON-PART"])
+            [filtered addObject: person];
+        }
+
+      list = filtered;
+    }
+  else
+    list = [self childrenWithTag: @"attendee"
+                 andAttribute: @"role"
+                 havingValue: @"non-part"];
+
+  return list;
+}
+
+- (BOOL) isOrganizer: (id)_email
+{
+  _email = [_email lowercaseString];
+
+  return [[[[self organizer] rfc822Email] lowercaseString]
+           isEqualToString:_email];
+}
+
+- (BOOL) isParticipant: (id) _email
+{
+  NSArray *partEmails;
+  
+  _email     = [_email lowercaseString];
+  partEmails = [[self participants] valueForKey:@"rfc822Email"];
+  partEmails = [partEmails valueForKey: @"lowercaseString"];
+  return [partEmails containsObject:_email];
+}
+
+- (iCalPerson *) findParticipantWithEmail: (id) _email
+{
+  NSArray  *ps;
+  unsigned i, count;
+  
+  _email = [_email lowercaseString];
+  ps     = [self participants];
+  count  = [ps count];
+
+  for (i = 0; i < count; i++) {
+    iCalPerson *p;
+    
+    p = [ps objectAtIndex:i];
+    if ([[[p rfc822Email] lowercaseString] isEqualToString:_email])
+      return p;
+  }
+
+  return nil; /* not found */
+}
+
+@end /* iCalEntityObject */
diff --git a/SOPE/NGCards/iCalEvent.h b/SOPE/NGCards/iCalEvent.h
new file mode 100644 (file)
index 0000000..608b058
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+  Copyright (C) 2000-2005 SKYRIX Software AG
+
+  This file is part of SOPE.
+
+  SOPE is free software; you can redistribute it and/or modify it under
+  the terms of the GNU Lesser General Public License as published by the
+  Free Software Foundation; either version 2, or (at your option) any
+  later version.
+
+  SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+  License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with SOPE; see the file COPYING.  If not, write to the
+  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+  02111-1307, USA.
+*/
+
+#ifndef __NGCards_iCalEvent_H__
+#define __NGCards_iCalEvent_H__
+
+#import "iCalRepeatableEntityObject.h"
+
+/*
+  iCalEvent
+  
+  This class keeps the attributes of an iCalendar event record, that is,
+  an appointment.
+*/
+
+@class NSCalendarDate;
+@class NSDate;
+@class NSString;
+@class NSMutableArray;
+
+@class NGCalendarDateRange;
+@class iCalEventChanges;
+
+@interface iCalEvent : iCalRepeatableEntityObject
+
+/* accessors */
+
+- (void) setEndDate: (NSCalendarDate *) _date;
+- (NSCalendarDate *) endDate;
+- (BOOL) hasEndDate;
+
+- (void) setDuration: (NSString *) _value;
+- (NSString *) duration;
+- (BOOL) hasDuration;
+- (NSTimeInterval) durationAsTimeInterval;
+
+- (void) setTransparency: (NSString *) _transparency;
+- (NSString *) transparency;
+
+/* convenience */
+
+- (BOOL) isOpaque;
+- (BOOL) isAllDay;
+
+- (BOOL) isWithinCalendarDateRange: (NGCalendarDateRange *) _range;
+- (NSArray *) recurrenceRangesWithinCalendarDateRange: (NGCalendarDateRange *)_r;
+
+- (NSCalendarDate *) lastPossibleRecurrenceStartDate;
+
+/* calculating changes */
+
+- (iCalEventChanges *) getChangesRelativeToEvent: (iCalEvent *) _event;
+
+@end 
+
+#endif /* __NGCards_iCalEvent_H__ */
diff --git a/SOPE/NGCards/iCalEvent.m b/SOPE/NGCards/iCalEvent.m
new file mode 100644 (file)
index 0000000..d237619
--- /dev/null
@@ -0,0 +1,260 @@
+/*
+  Copyright (C) 2000-2005 SKYRIX Software AG
+
+  This file is part of SOPE.
+
+  SOPE is free software; you can redistribute it and/or modify it under
+  the terms of the GNU Lesser General Public License as published by the
+  Free Software Foundation; either version 2, or (at your option) any
+  later version.
+
+  SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+  License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with SOPE; see the file COPYING.  If not, write to the
+  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+  02111-1307, USA.
+*/
+
+#import <NGExtensions/NGCalendarDateRange.h>
+
+#import "NSCalendarDate+NGCards.h"
+#import "CardGroup+iCal.h"
+#import "NSString+NGCards.h"
+
+#import "iCalEventChanges.h"
+#import "iCalDateTime.h"
+
+#import "iCalEvent.h"
+
+@implementation iCalEvent
+
+- (Class) classForTag: (NSString *) classTag
+{
+  Class tagClass;
+
+  if ([classTag isEqualToString: @"DURATION"]
+      || [classTag isEqualToString: @"TRANSP"])
+    tagClass = [CardElement class];
+  else if ([classTag isEqualToString: @"DTEND"])
+    tagClass = [iCalDateTime class];
+  else
+    tagClass = [super classForTag: classTag];
+
+  return tagClass;
+}
+
+/* accessors */
+
+- (void) setEndDate: (NSCalendarDate *)_date
+{
+  [self setDate: _date forDateTimeValue: @"dtend"];
+}
+
+- (NSCalendarDate *) endDate
+{
+  return [self dateForDateTimeValue: @"dtend"];
+}
+
+- (BOOL) hasEndDate
+{
+  return ([[self childrenWithTag: @"dtend"] count] > 0);
+}
+
+- (void) setDuration: (NSString *) _value
+{
+  [[self uniqueChildWithTag: @"duration"] setValue: 0
+                                          to: _value];
+}
+
+- (NSString *) duration
+{
+  return [[self uniqueChildWithTag: @"duration"] value: 0];
+}
+
+- (BOOL) hasDuration
+{
+  return ([[self childrenWithTag: @"duration"] count] > 0);
+}
+
+- (NSTimeInterval) durationAsTimeInterval
+{
+  /*
+    eg: DURATION:PT1H
+    P      - "period"
+    P2H30M - "2 hours 30 minutes"
+
+     dur-value  = (["+"] / "-") "P" (dur-date / dur-time / dur-week)
+
+     dur-date   = dur-day [dur-time]
+     dur-time   = "T" (dur-hour / dur-minute / dur-second)
+     dur-week   = 1*DIGIT "W"
+     dur-hour   = 1*DIGIT "H" [dur-minute]
+     dur-minute = 1*DIGIT "M" [dur-second]
+     dur-second = 1*DIGIT "S"
+     dur-day    = 1*DIGIT "D"
+  */
+  NSTimeInterval interval;
+
+  if ([self hasDuration])
+    interval = [[self duration] durationAsTimeInterval];
+  else if ([self hasEndDate] && [self hasStartDate])
+    /* calculate duration using enddate */
+    interval = [[self endDate] timeIntervalSinceDate: [self startDate]];
+  else
+    interval = 0.0;
+
+  return interval;
+}
+
+- (void) setTransparency: (NSString *) _transparency
+{
+  [[self uniqueChildWithTag: @"transp"] setValue: 0
+                                        to: _transparency];
+}
+
+- (NSString *) transparency
+{
+  return [[self uniqueChildWithTag: @"transp"] value: 0];
+}
+
+/* convenience */
+
+- (BOOL) isOpaque
+{
+  NSString *s;
+  
+  s = [[self transparency] uppercaseString];
+
+  return (![s isEqualToString: @"TRANSPARENT"]);
+}
+
+/* TODO: FIX THIS!
+   The problem is, that startDate/endDate are inappropriately modelled here.
+   We'd need to have a special iCalDate in order to fix all the mess.
+   For the time being, we chose allday to mean 00:00 - 23:59 in startDate's
+   timezone.
+*/
+- (BOOL) isAllDay
+{
+  NSCalendarDate *ed, *startDate;
+  BOOL allDay;
+
+  if ([self hasEndDate])
+    {
+      ed = [self endDate];
+      startDate = [self startDate];
+      [ed setTimeZone: [startDate timeZone]];
+      allDay = (([startDate hourOfDay] ==  0)
+                && ([startDate minuteOfHour] ==  0)
+                && ([ed hourOfDay] == 23)
+                && ([ed minuteOfHour] == 59));
+    }
+  else
+    allDay = NO;
+
+  return allDay;
+}
+
+- (BOOL) isWithinCalendarDateRange: (NGCalendarDateRange *) _range
+{
+  NGCalendarDateRange *r;
+  NSCalendarDate *startDate, *endDate;
+  NGCalendarDateRange *fir;
+
+  startDate = [self startDate];
+  endDate = [self endDate];
+
+  if (![self isRecurrent])
+    {
+      if ([self hasStartDate] && [self hasEndDate])
+        {
+          r = [NGCalendarDateRange calendarDateRangeWithStartDate: startDate
+                                   endDate: endDate];
+          return [_range containsDateRange: r];
+        }
+      else
+        return [_range containsDate: startDate];
+    }
+  else
+    {
+      fir = [NGCalendarDateRange calendarDateRangeWithStartDate:startDate
+                                 endDate: endDate];
+    
+      return [self isWithinCalendarDateRange: _range
+                   firstInstanceCalendarDateRange: fir];
+    }
+
+  return NO;
+}
+
+- (NSArray *) recurrenceRangesWithinCalendarDateRange: (NGCalendarDateRange *)_r
+{
+  NGCalendarDateRange *fir;
+  
+  if (![self isRecurrent])
+    return nil;
+
+  fir = [NGCalendarDateRange calendarDateRangeWithStartDate: [self startDate]
+                             endDate: [self endDate]];
+  return [self recurrenceRangesWithinCalendarDateRange:_r
+               firstInstanceCalendarDateRange:fir];
+}
+
+- (NSCalendarDate *) lastPossibleRecurrenceStartDate
+{
+  NGCalendarDateRange *fir;
+
+  if (![self isRecurrent])
+    return nil;
+
+  fir = [NGCalendarDateRange calendarDateRangeWithStartDate: [self startDate]
+                             endDate: [self endDate]];
+
+  return [self lastPossibleRecurrenceStartDateUsingFirstInstanceCalendarDateRange: fir];
+}
+
+/* ical typing */
+
+- (NSString *) entityName
+{
+  return @"vevent";
+}
+
+/* descriptions */
+
+// - (NSString *) description {
+//   NSMutableString *ms;
+
+//   ms = [NSMutableString stringWithCapacity:128];
+//   [ms appendFormat:@"<0x%p[%@]:", self, NSStringFromClass([self class])];
+
+//   if (uid)       [ms appendFormat:@" uid=%@", uid];
+//   if (startDate) [ms appendFormat:@" from=%@", startDate];
+//   if (endDate)   [ms appendFormat:@" to=%@", endDate];
+//   if (summary)   [ms appendFormat:@" summary=%@", summary];
+  
+//   if (organizer)
+//     [ms appendFormat:@" organizer=%@", organizer];
+//   if (attendees)
+//     [ms appendFormat:@" attendees=%@", attendees];
+  
+//   if ([self hasAlarms])
+//     [ms appendFormat:@" alarms=%@", alarms];
+  
+//   [ms appendString:@">"];
+//   return ms;
+// }
+
+/* changes */
+
+- (iCalEventChanges *) getChangesRelativeToEvent: (iCalEvent *) _event
+{
+  return [iCalEventChanges changesFromEvent: _event
+                           toEvent: self];
+}
+
+@end /* iCalEvent */
diff --git a/SOPE/NGCards/iCalEventChanges.h b/SOPE/NGCards/iCalEventChanges.h
new file mode 100644 (file)
index 0000000..b2fc90a
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ Copyright (C) 2004-2005 SKYRIX Software AG
+ This file is part of SOPE.
+ SOPE is free software; you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the
+ Free Software Foundation; either version 2, or (at your option) any
+ later version.
+
+ SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+ License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with SOPE; see the file COPYING.  If not, write to the
+ Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA.
+ */
+//  Created by znek on 31.08.04.
+
+#ifndef        __NGiCal_iCalEventChanges_H_
+#define        __NGiCal_iCalEventChanges_H_
+
+#include <Foundation/NSObject.h>
+
+@class iCalEvent, NSArray, NSMutableArray;
+
+@interface iCalEventChanges : NSObject
+{
+  NSMutableArray *insertedAttendees;
+  NSMutableArray *deletedAttendees;
+  NSMutableArray *updatedAttendees;
+  NSMutableArray *insertedAlarms;
+  NSMutableArray *deletedAlarms;
+  NSMutableArray *updatedAlarms;
+  NSMutableArray *updatedProperties;
+}
+
++ (id)changesFromEvent:(iCalEvent *)_old toEvent:(iCalEvent *)_to;
+- (id)initWithFromEvent:(iCalEvent *)_from toEvent:(iCalEvent *)_to;
+
+- (BOOL)hasChanges;
+
+- (BOOL)hasAttendeeChanges;
+- (NSArray *)insertedAttendees;
+- (NSArray *)deletedAttendees;
+- (NSArray *)updatedAttendees;
+
+- (BOOL)hasAlarmChanges;
+- (NSArray *)insertedAlarms;
+- (NSArray *)deletedAlarms;
+- (NSArray *)updatedAlarms;
+
+- (BOOL)hasPropertyChanges;
+- (NSArray *)updatedProperties;
+
+@end
+
+#endif /* __NGiCal_iCalEventChanges_H_ */
diff --git a/SOPE/NGCards/iCalEventChanges.m b/SOPE/NGCards/iCalEventChanges.m
new file mode 100644 (file)
index 0000000..6a7401c
--- /dev/null
@@ -0,0 +1,211 @@
+/*
+ Copyright (C) 2004-2005 SKYRIX Software AG
+
+ This file is part of SOPE.
+
+ SOPE is free software; you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the
+ Free Software Foundation; either version 2, or (at your option) any
+ later version.
+
+ SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+ License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with SOPE; see the file COPYING.  If not, write to the
+ Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA.
+ */
+
+
+#include "iCalEventChanges.h"
+#include "iCalEvent.h"
+#include "iCalPerson.h"
+#include "common.h"
+
+@interface iCalEventChanges (PrivateAPI)
+- (void)_trackAttendeeChanges:(iCalEvent *)_from :(iCalEvent *)_to;
+- (void)_trackAlarmChanges:(iCalEvent *)_from :(iCalEvent *)_to;
+- (void)_trackPropertyChanges:(iCalEvent *)_from :(iCalEvent *)_to;
+@end
+
+@implementation iCalEventChanges
+
++ (id)changesFromEvent:(iCalEvent *)_from toEvent:(iCalEvent *)_to {
+  return [[[self alloc] initWithFromEvent:_from toEvent:_to] autorelease];
+}
+
+- (id)initWithFromEvent:(iCalEvent *)_from toEvent:(iCalEvent *)_to {
+  self = [super init];
+  if(self) {
+    self->insertedAttendees = [[NSMutableArray alloc] init];
+    self->deletedAttendees  = [[NSMutableArray alloc] init];
+    self->updatedAttendees  = [[NSMutableArray alloc] init];
+    self->insertedAlarms    = [[NSMutableArray alloc] init];
+    self->deletedAlarms     = [[NSMutableArray alloc] init];
+    self->updatedAlarms     = [[NSMutableArray alloc] init];
+    self->updatedProperties = [[NSMutableArray alloc] init];
+    [self _trackAttendeeChanges:_from :_to];
+    [self _trackPropertyChanges:_from :_to];
+  }
+  return self;
+}
+
+- (void)dealloc {
+  [self->insertedAttendees release];
+  [self->deletedAttendees  release];
+  [self->updatedAttendees  release];
+  [self->insertedAlarms    release];
+  [self->deletedAlarms     release];
+  [self->updatedAlarms     release];
+  [self->updatedProperties release];
+  [super dealloc];
+}
+
+- (void)_trackAttendeeChanges:(iCalEvent *)_from :(iCalEvent *)_to {
+  unsigned f, t, fcount, tcount;
+  NSArray *fromAttendees, *toAttendees;
+
+  fromAttendees = [_from attendees];
+  fcount = [fromAttendees count];
+  toAttendees = [_to attendees];
+  tcount = [toAttendees count];
+  for(f = 0; f < fcount; f++) {
+    iCalPerson *fp;
+    BOOL found = NO;
+
+    fp = [fromAttendees objectAtIndex:f];
+
+    for(t = 0; t < tcount; t++) {
+      iCalPerson *tp;
+
+      tp = [toAttendees objectAtIndex:t];
+      if([fp hasSameEmailAddress:tp]) {
+        found = YES;
+        if(![fp isEqualToPerson:tp]) {
+          [self->updatedAttendees addObject:tp];
+        }
+        break;
+      }
+    }
+    if(!found) {
+      [self->deletedAttendees addObject:fp];
+    }
+  }
+  for(t = 0; t < tcount; t++) {
+    iCalPerson *tp;
+    BOOL found = NO;
+
+    tp = [toAttendees objectAtIndex:t];
+    for(f = 0; f < fcount; f++) {
+      iCalPerson *fp;
+
+      fp = [fromAttendees objectAtIndex:f];
+      if([tp hasSameEmailAddress:fp]) {
+        found = YES;
+        break;
+      }
+    }
+    if(!found)
+      [self->insertedAttendees addObject:tp];
+  }
+}
+
+- (void)_trackAlarmChanges:(iCalEvent *)_from :(iCalEvent *)_to {
+}
+
+- (void)_trackPropertyChanges:(iCalEvent *)_from :(iCalEvent *)_to {
+  if(!IS_EQUAL([_from startDate], [_to startDate], isEqualToDate:))
+    [self->updatedProperties addObject:@"startDate"];
+  if(!IS_EQUAL([_from endDate], [_to endDate], isEqualToDate:))
+    [self->updatedProperties addObject:@"endDate"];
+  if(!IS_EQUAL([_from created], [_to created], isEqualToDate:))
+    [self->updatedProperties addObject:@"created"];
+  if(!IS_EQUAL([_from lastModified], [_to lastModified], isEqualToDate:))
+    [self->updatedProperties addObject:@"lastModified"];
+  if(![_from durationAsTimeInterval] == [_to durationAsTimeInterval])
+    [self->updatedProperties addObject:@"duration"];
+  if(!IS_EQUAL([_from summary], [_to summary], isEqualToString:))
+    [self->updatedProperties addObject:@"summary"];
+  if(!IS_EQUAL([_from location], [_to location], isEqualToString:))
+    [self->updatedProperties addObject:@"location"];
+  if(!IS_EQUAL([_from comment], [_to comment], isEqualToString:))
+    [self->updatedProperties addObject:@"comment"];
+  if(!IS_EQUAL([_from priority], [_to priority], isEqualToString:))
+    [self->updatedProperties addObject:@"priority"];
+  if(!IS_EQUAL([_from status], [_to status], isEqualToString:))
+    [self->updatedProperties addObject:@"status"];
+  if(!IS_EQUAL([_from accessClass], [_to accessClass], isEqualToString:))
+    [self->updatedProperties addObject:@"accessClass"];
+  if(!IS_EQUAL([_from sequence], [_to sequence], isEqualToNumber:))
+    [self->updatedProperties addObject:@"sequence"];
+  if(!IS_EQUAL([_from organizer], [_to organizer], isEqual:))
+    [self->updatedProperties addObject:@"organizer"];
+}
+
+- (BOOL)hasChanges {
+  return [self hasAttendeeChanges]  ||
+         [self hasAlarmChanges]     ||
+         [self hasPropertyChanges];
+}
+
+- (BOOL)hasAttendeeChanges {
+  return [[self insertedAttendees] count] > 0 ||
+         [[self deletedAttendees]  count] > 0 ||
+         [[self updatedAttendees]  count] > 0;
+}
+
+- (BOOL)hasAlarmChanges {
+  return [[self insertedAlarms] count] > 0 ||
+         [[self deletedAlarms]  count] > 0 ||
+         [[self updatedAlarms]  count] > 0;
+}
+
+- (BOOL)hasPropertyChanges {
+  return [[self updatedProperties] count] > 0;
+}
+
+- (NSArray *)insertedAttendees {
+  return self->insertedAttendees;
+}
+- (NSArray *)deletedAttendees {
+  return self->deletedAttendees;
+}
+- (NSArray *)updatedAttendees {
+  return self->updatedAttendees;
+}
+
+- (NSArray *)insertedAlarms {
+  return self->insertedAlarms;
+}
+- (NSArray *)deletedAlarms {
+  return self->deletedAlarms;
+}
+- (NSArray *)updatedAlarms {
+  return self->updatedAlarms;
+}
+
+- (NSArray *)updatedProperties {
+  return self->updatedProperties;
+}
+
+/* descriptions */
+
+- (NSString *)description {
+  NSMutableString *ms;
+  
+  ms = [NSMutableString stringWithCapacity:128];
+  [ms appendFormat:@"<0x%p[%@]:", self, NSStringFromClass([self class])];
+  
+  [ms appendFormat:@" updatedProperties=%@", self->updatedProperties];
+  [ms appendFormat:@" insertedAttendees=%@", self->insertedAttendees];
+  [ms appendFormat:@" deletedAttendees=%@", self->deletedAttendees];
+  [ms appendFormat:@" updatedAttendees=%@", self->updatedAttendees];
+  
+  [ms appendString:@">"];
+  return ms;
+}
+
+@end
diff --git a/SOPE/NGCards/iCalFreeBusy.h b/SOPE/NGCards/iCalFreeBusy.h
new file mode 100644 (file)
index 0000000..8e74402
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+  Copyright (C) 2000-2005 SKYRIX Software AG
+
+  This file is part of SOPE.
+
+  SOPE is free software; you can redistribute it and/or modify it under
+  the terms of the GNU Lesser General Public License as published by the
+  Free Software Foundation; either version 2, or (at your option) any
+  later version.
+
+  SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+  License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with SOPE; see the file COPYING.  If not, write to the
+  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+  02111-1307, USA.
+*/
+
+#ifndef __NGCards_iCalFreeBusy_H__
+#define __NGCards_iCalFreeBusy_H__
+
+#import "iCalEntityObject.h"
+
+@class NSString;
+@class NSCalendarDate;
+@class NSURL;
+@class iCalPerson;
+
+typedef enum iCalFreeBusyType
+{
+  iCalFBBusy,
+  iCalFBFree,
+  iCalFBBusyUnavailable,
+  iCalFBBusyTentative
+} iCalFreeBusyType;
+
+@interface iCalFreeBusy : iCalEntityObject
+
+- (void) setEndDate: (NSCalendarDate *) _date;
+- (NSCalendarDate *) endDate;
+- (BOOL) hasEndDate;
+
+- (void) addFreeBusyFrom: (NSCalendarDate *) start
+                      to: (NSCalendarDate *) end
+                    type: (iCalFreeBusyType) type;
+
+@end
+
+#endif /* __NGCards_iCalFreeBusy_H__ */
diff --git a/SOPE/NGCards/iCalFreeBusy.m b/SOPE/NGCards/iCalFreeBusy.m
new file mode 100644 (file)
index 0000000..efae27e
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+  Copyright (C) 2000-2005 SKYRIX Software AG
+
+  This file is part of SOPE.
+
+  SOPE is free software; you can redistribute it and/or modify it under
+  the terms of the GNU Lesser General Public License as published by the
+  Free Software Foundation; either version 2, or (at your option) any
+  later version.
+
+  SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+  License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with SOPE; see the file COPYING.  If not, write to the
+  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+  02111-1307, USA.
+*/
+
+#import <Foundation/NSArray.h>
+#import <Foundation/NSString.h>
+#import <Foundation/NSTimeZone.h>
+
+#import "CardGroup+iCal.h"
+#import "iCalDateTime.h"
+#import "NSCalendarDate+NGCards.h"
+
+#import "iCalFreeBusy.h"
+
+@implementation iCalFreeBusy
+
+- (Class) classForTag: (NSString *) classTag
+{
+  Class tagClass;
+
+  if ([classTag isEqualToString: @"DTEND"])
+    tagClass = [iCalDateTime class];
+  else if ([classTag isEqualToString: @"FREEBUSY"])
+    tagClass = [CardElement class];
+  else
+    tagClass = [super classForTag: classTag];
+
+  return tagClass;
+}
+
+/* accessors */
+- (void) setEndDate: (NSCalendarDate *)_date
+{
+  [self setDate: _date forDateTimeValue: @"dtend"];
+}
+
+- (NSCalendarDate *) endDate
+{
+  return [self dateForDateTimeValue: @"dtend"];
+}
+
+- (BOOL) hasEndDate
+{
+  return ([[self childrenWithTag: @"dtend"] count] > 0);
+}
+
+- (NSString *) _freeBusyTypeString: (iCalFreeBusyType) type
+{
+  NSString *typeString;
+
+  switch (type)
+    {
+    case iCalFBBusy:
+      typeString = @"BUSY";
+      break;
+    case iCalFBFree:
+      typeString = @"FREE";
+      break;
+    case iCalFBBusyUnavailable:
+      typeString = @"BUSY-UNAVAILABLE";
+      break;
+    default:
+      typeString = @"BUSY-TENTATIVE";
+    }
+
+  return typeString;
+}
+
+- (void) addFreeBusyFrom: (NSCalendarDate *) start
+                      to: (NSCalendarDate *) end
+                    type: (iCalFreeBusyType) type
+{
+  CardElement *freeBusyElement;
+  NSString *value;
+  NSCalendarDate *utcStart, *utcEnd;
+  NSTimeZone *uTZ;
+
+  uTZ = [NSTimeZone timeZoneWithAbbreviation: @"GMT"];
+  utcStart = [start copy];
+  utcEnd = [end copy];
+  [utcStart setTimeZone: uTZ];
+  [utcEnd setTimeZone: uTZ];
+
+  value = [NSString stringWithFormat: @"%@Z/%@Z",
+                    [utcStart iCalFormattedDateTimeString],
+                    [utcEnd iCalFormattedDateTimeString]];
+  freeBusyElement = [CardElement simpleElementWithTag: @"freebusy"
+                                 value: value];
+  [freeBusyElement addAttribute: @"fbtype"
+                   value: [self _freeBusyTypeString: type]];
+  [self addChild: freeBusyElement];
+
+  [utcStart release];
+  [utcEnd release];
+}
+
+/* ical typing */
+
+- (NSString *) entityName
+{
+  return @"vfreebusy";
+}
+
+@end /* iCalFreeBusy */
diff --git a/SOPE/NGCards/iCalJournal.h b/SOPE/NGCards/iCalJournal.h
new file mode 100644 (file)
index 0000000..62a5ec4
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+  Copyright (C) 2000-2005 SKYRIX Software AG
+
+  This file is part of SOPE.
+
+  SOPE is free software; you can redistribute it and/or modify it under
+  the terms of the GNU Lesser General Public License as published by the
+  Free Software Foundation; either version 2, or (at your option) any
+  later version.
+
+  SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+  License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with SOPE; see the file COPYING.  If not, write to the
+  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+  02111-1307, USA.
+*/
+
+#ifndef __NGCards_iCalJournal_H__
+#define __NGCards_iCalJournal_H__
+
+#import "CardGroup.h"
+
+@interface iCalJournal : CardGroup
+{
+}
+
+@end
+
+#endif /* __NGCards_iCalJournal_H__ */
diff --git a/SOPE/NGCards/iCalJournal.m b/SOPE/NGCards/iCalJournal.m
new file mode 100644 (file)
index 0000000..3c15877
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+  Copyright (C) 2000-2005 SKYRIX Software AG
+
+  This file is part of SOPE.
+
+  SOPE is free software; you can redistribute it and/or modify it under
+  the terms of the GNU Lesser General Public License as published by the
+  Free Software Foundation; either version 2, or (at your option) any
+  later version.
+
+  SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+  License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with SOPE; see the file COPYING.  If not, write to the
+  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+  02111-1307, USA.
+*/
+
+#include "iCalJournal.h"
+#include "common.h"
+
+@implementation iCalJournal
+
+/* ical typing */
+
+- (NSString *) entityName
+{
+  return @"vjournal";
+}
+
+/* descriptions */
+
+@end /* iCalJournal */
diff --git a/SOPE/NGCards/iCalMonthlyRecurrenceCalculator.m b/SOPE/NGCards/iCalMonthlyRecurrenceCalculator.m
new file mode 100644 (file)
index 0000000..b2809ed
--- /dev/null
@@ -0,0 +1,406 @@
+/*
+  Copyright (C) 2004-2005 SKYRIX Software AG
+  
+  This file is part of SOPE.
+  
+  SOPE is free software; you can redistribute it and/or modify it under
+  the terms of the GNU Lesser General Public License as published by the
+  Free Software Foundation; either version 2, or (at your option) any
+  later version.
+  
+  SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+  License for more details.
+  
+  You should have received a copy of the GNU Lesser General Public
+  License along with SOPE; see the file COPYING.  If not, write to the
+  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+  02111-1307, USA.
+*/
+
+#include "iCalRecurrenceCalculator.h"
+
+@interface iCalMonthlyRecurrenceCalculator : iCalRecurrenceCalculator
+@end
+
+#include <NGExtensions/NGCalendarDateRange.h>
+#include "iCalRecurrenceRule.h"
+#include "NSCalendarDate+ICal.h"
+#include "common.h"
+#include <string.h>
+
+@interface iCalRecurrenceCalculator(PrivateAPI)
+- (NSCalendarDate *)lastInstanceStartDate;
+@end
+
+// #define HEAVY_DEBUG 1
+
+@implementation iCalMonthlyRecurrenceCalculator
+
+typedef BOOL NGMonthSet[12];
+typedef BOOL NGMonthDaySet[32]; // 0 is unused
+
+static void NGMonthDaySet_clear(NGMonthDaySet *daySet) {
+  register unsigned i;
+  
+  for (i = 1; i <= 31; i++)
+    (*daySet)[i] = NO;
+}
+
+static void NGMonthDaySet_copyOrUnion(NGMonthDaySet *base, NGMonthDaySet *new,
+                                      BOOL doCopy)
+{
+  register unsigned i;
+  
+  if (doCopy)
+    memcpy(base, new, sizeof(NGMonthDaySet));
+  else {
+    for (i = 1; i <= 31; i++) {
+      if (!(*new)[i])
+        (*base)[i] = NO;
+    }
+  }
+}
+
+static BOOL NGMonthDaySet_fillWithByMonthDay(NGMonthDaySet *daySet, 
+                                             NSArray *byMonthDay)
+{
+  /* list of days in the month */
+  unsigned i, count;
+  BOOL ok;
+  
+  NGMonthDaySet_clear(daySet);
+
+  for (i = 0, count = [byMonthDay count], ok = YES; i < count; i++) {
+    int dayInMonth; /* -31..-1 and 1..31 */
+        
+    if ((dayInMonth = [[byMonthDay objectAtIndex:i] intValue]) == 0) {
+      ok = NO;
+      continue; /* invalid value */
+    }
+    if (dayInMonth > 31) {
+      ok = NO;
+      continue; /* error, value to large */
+    }
+    if (dayInMonth < -31) {
+      ok = NO;
+      continue; /* error, value to large */
+    }
+    
+    /* adjust negative days */
+        
+    if (dayInMonth < 0) {
+      /* eg: -1 == last day in month, 30 days => 30 */
+      dayInMonth = 32 - dayInMonth /* because we count from 1 */;
+    }
+    
+    (*daySet)[dayInMonth] = YES;
+  }
+  return ok;
+}
+
+static inline unsigned iCalDoWForNSDoW(int dow) {
+  switch (dow) {
+  case 0: return iCalWeekDaySunday;
+  case 1: return iCalWeekDayMonday;
+  case 2: return iCalWeekDayTuesday;
+  case 3: return iCalWeekDayWednesday;
+  case 4: return iCalWeekDayThursday;
+  case 5: return iCalWeekDayFriday;
+  case 6: return iCalWeekDaySaturday;
+  case 7: return iCalWeekDaySunday;
+  default: return 0;
+  }
+}
+
+#if HEAVY_DEBUG
+static NSString *dowEN[8] = { 
+  @"SU", @"MO", @"TU", @"WE", @"TH", @"FR", @"SA", @"SU-"
+};
+#endif
+
+static void NGMonthDaySet_fillWithByDayX(NGMonthDaySet *daySet, 
+                                         unsigned dayMask,
+                                        unsigned firstDoWInMonth,
+                                        unsigned numberOfDaysInMonth,
+                                         int occurrence1)
+{
+  // TODO: this is called 'X' because the API doesn't allow for full iCalendar
+  //       functionality. The daymask must be a list of occurence+dow
+  register unsigned dayInMonth;
+  register int dow; /* current day of the week */
+  int occurrences[7] = { 0, 0, 0, 0, 0, 0, 0 } ;
+  
+  NGMonthDaySet_clear(daySet);
+  
+  if (occurrence1 >= 0) {
+    for (dayInMonth = 1, dow = firstDoWInMonth; dayInMonth<=31; dayInMonth++) {
+      // TODO: complete me
+      
+      if (dayMask & iCalDoWForNSDoW(dow)) {
+        if (occurrence1 == 0)
+         (*daySet)[dayInMonth] = YES;
+        else { /* occurrence1 > 0 */
+         occurrences[dow] = occurrences[dow] + 1;
+         
+         if (occurrences[dow] == occurrence1) 
+           (*daySet)[dayInMonth] = YES;
+        }
+      }
+      
+      dow = (dow == 6 /* Sat */) ? 0 /* Sun */ : (dow + 1);
+    }
+  }
+  else {
+    int lastDoWInMonthSet;
+    
+    /* get the last dow in the set (not necessarily the month!) */
+    for (dayInMonth = 1, dow = firstDoWInMonth; 
+        dayInMonth < numberOfDaysInMonth;dayInMonth++)
+      dow = (dow == 6 /* Sat */) ? 0 /* Sun */ : (dow + 1);
+    lastDoWInMonthSet = dow;
+    
+#if HEAVY_DEBUG
+    NSLog(@"LAST DOW IN SET: %i / %@", 
+         lastDoWInMonthSet, dowEN[lastDoWInMonthSet]);
+#endif
+    /* start at the end of the set */
+    for (dayInMonth = numberOfDaysInMonth, dow = lastDoWInMonthSet; 
+        dayInMonth >= 1; dayInMonth--) {
+      // TODO: complete me
+      
+#if HEAVY_DEBUG
+      NSLog(@"  CHECK day-of-month %02i, "
+           @" dow=%i/%@ (first=%i/%@, last=%i/%@)",
+           dayInMonth, 
+           dow, dowEN[dow],
+           firstDoWInMonth, dowEN[firstDoWInMonth],
+           lastDoWInMonthSet, dowEN[lastDoWInMonthSet]
+           );
+#endif
+      
+      if (dayMask & iCalDoWForNSDoW(dow)) {
+       occurrences[dow] = occurrences[dow] + 1;
+#if HEAVY_DEBUG
+       NSLog(@"    MATCH %i/%@ count: %i occurences=%i",
+             dow, dowEN[dow], occurrences[dow], occurrence1);
+#endif
+         
+       if (occurrences[dow] == -occurrence1) {
+#if HEAVY_DEBUG
+         NSLog(@"    COUNT MATCH");
+#endif
+         (*daySet)[dayInMonth] = YES;
+       }
+      }
+      
+      dow = (dow == 0 /* Sun */) ? 6 /* Sat */ : (dow - 1);
+    }
+  }
+}
+
+- (BOOL)_addInstanceWithStartDate:(NSCalendarDate *)_startDate
+  limitDate:(NSCalendarDate *)_until
+  limitRange:(NGCalendarDateRange *)_r
+  toArray:(NSMutableArray *)_ranges
+{
+  NGCalendarDateRange *r;
+  NSCalendarDate *end;
+  
+  /* check whether we are still in the limits */
+
+  // TODO: I think we should check in here whether we succeeded the
+  //       repeatCount. Currently we precalculate that info in the
+  //       -lastInstanceStartDate method.
+  if (_until != nil) {
+    /* Note: the 'until' in the rrule is inclusive as per spec */
+    if ([_until compare:_startDate] == NSOrderedAscending)
+      /* start after until */
+      return NO; /* Note: we assume that the algorithm is sequential */
+  }
+
+  /* create end date */
+
+  end = [_startDate addTimeInterval:[self->firstRange duration]];
+  [end setTimeZone:[_startDate timeZone]];
+    
+  /* create range and check whether its in the requested range */
+  
+  r = [[NGCalendarDateRange alloc] initWithStartDate:_startDate endDate:end];
+  if ([_r containsDateRange:r])
+    [_ranges addObject:r];
+  [r release]; r = nil;
+  
+  return YES;
+}
+
+- (NSArray *)recurrenceRangesWithinCalendarDateRange:(NGCalendarDateRange *)_r{
+  /* main entry */
+  // TODO: check whether this is OK for multiday-events!
+  NSMutableArray *ranges;
+  NSTimeZone     *timeZone;
+  NSCalendarDate *eventStartDate, *rStart, *rEnd, *until;
+  int            eventDayOfMonth;
+  unsigned       monthIdxInRange, numberOfMonthsInRange, interval;
+  int            diff;
+  NGMonthSet byMonthList = { // TODO: fill from rrule, this is the default
+    YES, YES, YES, YES, YES, YES, 
+    YES, YES, YES, YES, YES, YES
+  };
+  NSArray *byMonthDay = nil; // array of ints (-31..-1 and 1..31)
+  NGMonthDaySet byMonthDaySet;
+  
+  eventStartDate  = [self->firstRange startDate];
+  eventDayOfMonth = [eventStartDate dayOfMonth];
+  timeZone   = [eventStartDate timeZone];
+  rStart     = [_r startDate];
+  rEnd       = [_r endDate];
+  interval   = [self->rrule repeatInterval];
+  until      = [self lastInstanceStartDate]; // TODO: maybe replace
+  byMonthDay = [self->rrule byMonthDay];
+  
+
+  /* check whether the range to be processed is beyond the 'until' date */
+  
+  if (until != nil) {
+    if ([until compare:rStart] == NSOrderedAscending) /* until before start */
+      return nil;
+    if ([until compare:rEnd] == NSOrderedDescending) /* end before until */
+      rEnd = until; // TODO: why is that? end is _before_ until?
+  }
+
+  
+  /* precalculate month days (same for all instances) */
+
+  if (byMonthDay != nil)
+    NGMonthDaySet_fillWithByMonthDay(&byMonthDaySet, byMonthDay);
+  
+  
+  // TODO: I think the 'diff' is to skip recurrence which are before the
+  //       requested range. Not sure whether this is actually possible, eg
+  //       the repeatCount must be processed from the start.
+  diff = [eventStartDate monthsBetweenDate:rStart];
+  if ((diff != 0) && [rStart compare:eventStartDate] == NSOrderedAscending)
+    diff = -diff;
+  
+  numberOfMonthsInRange  = [rStart monthsBetweenDate:rEnd] + 1;
+  ranges = [NSMutableArray arrayWithCapacity:numberOfMonthsInRange];
+  
+  /* 
+     Note: we do not add 'eventStartDate', this is intentional, the event date
+           itself is _not_ necessarily part of the sequence, eg with monthly
+           byday recurrences.
+  */
+  
+  for (monthIdxInRange = 0; monthIdxInRange < numberOfMonthsInRange; 
+       monthIdxInRange++) {
+    NSCalendarDate *cursor;
+    unsigned       numDaysInMonth;
+    int            monthIdxInRecurrence, dom;
+    NGMonthDaySet  monthDays;
+    BOOL           didByFill, doCont;
+    
+    monthIdxInRecurrence = diff + monthIdxInRange;
+    
+    if (monthIdxInRecurrence < 0)
+      continue;
+    
+    /* first check whether we are in the interval */
+    
+    if ((monthIdxInRecurrence % interval) != 0)
+      continue;
+
+    /*
+      Then the sequence is:
+      - check whether the month is in the BYMONTH list
+    */
+    
+    cursor = [eventStartDate dateByAddingYears:0
+                             months:(diff + monthIdxInRange)
+                             days:0];
+    [cursor setTimeZone:timeZone];
+    numDaysInMonth = [cursor numberOfDaysInMonth];
+    
+
+    /* check whether we match the bymonth specification */
+    
+    if (!byMonthList[[cursor monthOfYear] - 1])
+      continue;
+    
+    
+    /* check 'day level' byXYZ rules */
+    
+    didByFill = NO;
+    
+    if (byMonthDay != nil) { /* list of days in the month */
+      NGMonthDaySet_copyOrUnion(&monthDays, &byMonthDaySet, !didByFill);
+      didByFill = YES;
+    }
+    
+    if ([self->rrule byDayMask] != 0) { // TODO: replace the mask with an array
+      NGMonthDaySet ruleset;
+      unsigned firstDoWInMonth;
+      
+      firstDoWInMonth = [[cursor firstDayOfMonth] dayOfWeek];
+      
+      NGMonthDaySet_fillWithByDayX(&ruleset, 
+                                   [self->rrule byDayMask],
+                                  firstDoWInMonth,
+                                  [cursor numberOfDaysInMonth],
+                                   [self->rrule byDayOccurence1]);
+      NGMonthDaySet_copyOrUnion(&monthDays, &ruleset, !didByFill);
+      didByFill = YES;
+    }
+    
+    if (!didByFill) {
+      /* no rules applied, take the dayOfMonth of the startDate */
+      NGMonthDaySet_clear(&monthDays);
+      monthDays[eventDayOfMonth] = YES;
+    }
+    
+    // TODO: add processing of byhour/byminute/bysecond etc
+    
+    for (dom = 1, doCont = YES; dom <= numDaysInMonth && doCont; dom++) {
+      NSCalendarDate *start;
+      
+      if (!monthDays[dom])
+       continue;
+      
+      if (eventDayOfMonth == dom)
+       start = cursor;
+      else {
+       start = [cursor dateByAddingYears:0 months:0
+                       days:(dom - eventDayOfMonth)];
+      }
+      
+      doCont = [self _addInstanceWithStartDate:start
+                    limitDate:until
+                    limitRange:_r
+                    toArray:ranges];
+    }
+    if (!doCont) break; /* reached some limit */
+  }
+  return ranges;
+}
+
+- (NSCalendarDate *)lastInstanceStartDate {
+  if ([self->rrule repeatCount] > 0) {
+    NSCalendarDate *until;
+    unsigned       months, interval;
+    
+    interval = [self->rrule repeatInterval];
+    months   = [self->rrule repeatCount] - 1 /* the first counts as one! */;
+    
+    if (interval > 0)
+      months *= interval;
+    
+    until = [[self->firstRange startDate] dateByAddingYears:0
+                                          months:months
+                                          days:0];
+    return until;
+  }
+  return [super lastInstanceStartDate];
+}
+
+@end /* iCalMonthlyRecurrenceCalculator */
diff --git a/SOPE/NGCards/iCalObject.h b/SOPE/NGCards/iCalObject.h
new file mode 100644 (file)
index 0000000..5369d0e
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+  Copyright (C) 2000-2005 SKYRIX Software AG
+
+  This file is part of SOPE.
+
+  SOPE is free software; you can redistribute it and/or modify it under
+  the terms of the GNU Lesser General Public License as published by the
+  Free Software Foundation; either version 2, or (at your option) any
+  later version.
+
+  SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+  License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with SOPE; see the file COPYING.  If not, write to the
+  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+  02111-1307, USA.
+*/
+
+/* WAS: class being obsoleted !! */
+
+#ifndef __NGiCal_iCalObject_H__
+#define __NGiCal_iCalObject_H__
+
+#import <Foundation/NSObject.h>
+#import <Foundation/NSDate.h>
+
+/*
+  iCalObject
+
+  A common base class for all iCal objects. Does some patches on key-value
+  coding.
+*/
+
+@class NSTimeZone;
+
+@interface iCalObject : NSObject <NSCopying>
+{
+}
+
+// what shall we take, if no timeZone is specified in dateValues
++ (void)setICalDefaultTimeZone:(NSTimeZone *)_timeZone;
++ (NSTimeZone *)iCalDefaultTimeZone;
+
+@end
+
+
+
+#endif /* __NGiCal_iCalObject_H__ */
diff --git a/SOPE/NGCards/iCalObject.m b/SOPE/NGCards/iCalObject.m
new file mode 100644 (file)
index 0000000..71f4597
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+  Copyright (C) 2000-2005 SKYRIX Software AG
+
+  This file is part of SOPE.
+
+  SOPE is free software; you can redistribute it and/or modify it under
+  the terms of the GNU Lesser General Public License as published by the
+  Free Software Foundation; either version 2, or (at your option) any
+  later version.
+
+  SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+  License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with SOPE; see the file COPYING.  If not, write to the
+  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+  02111-1307, USA.
+*/
+
+#include "iCalObject.h"
+#include "common.h"
+
+@implementation iCalObject
+
++ (int)version {
+  return 0;
+}
+
+// what shall we take, if no timeZone is specified in dateValues
+static NSTimeZone *defTZ = nil;
++ (void)setICalDefaultTimeZone:(NSTimeZone *)_timeZone {
+  ASSIGNCOPY(defTZ,_timeZone);
+}
++ (NSTimeZone *)iCalDefaultTimeZone {
+  return defTZ;
+}
+
+- (void)dealloc {
+  [super dealloc];
+}
+
+/* NSCopying */
+
+- (id)copyWithZone:(NSZone *)_zone {
+  iCalObject *new;
+  
+  new = [[[self class] allocWithZone:_zone] init];
+  return new;
+}
+
+/* KVC */
+
+- (void)takeValue:(id)_value forXKey:(id)_key {
+  /* extended keys are ignored by default */
+}
+
+- (void)handleTakeValue:(id)_value forUnboundKey:(NSString *)_key {
+  if ([_key hasPrefix:@"X-"]) {
+    [self takeValue:_value forXKey:_key];
+  }
+  else {
+    NSLog(@"0x%08x[%@]: ignoring attempt to set unbound key '%@'",
+         self, NSStringFromClass([self class]), _key);
+  }
+}
+
+@end /* iCalObject */
diff --git a/SOPE/NGCards/iCalPerson.h b/SOPE/NGCards/iCalPerson.h
new file mode 100644 (file)
index 0000000..222de9e
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+  Copyright (C) 2000-2005 SKYRIX Software AG
+
+  This file is part of SOPE.
+
+  SOPE is free software; you can redistribute it and/or modify it under
+  the terms of the GNU Lesser General Public License as published by the
+  Free Software Foundation; either version 2, or (at your option) any
+  later version.
+
+  SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+  License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with SOPE; see the file COPYING.  If not, write to the
+  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+  02111-1307, USA.
+*/
+
+#ifndef __NGCards_iCalPerson_H__
+#define __NGCards_iCalPerson_H__
+
+#import "CardElement.h"
+
+typedef enum {
+  iCalPersonPartStatNeedsAction  = 0, /* NEEDS-ACTION (DEFAULT) */
+  iCalPersonPartStatAccepted     = 1, /* ACCEPTED               */
+  iCalPersonPartStatDeclined     = 2, /* DECLINED               */
+  /* up to here defined for VJOURNAL                            */
+  iCalPersonPartStatTentative    = 3, /* TENTATIVE              */
+  iCalPersonPartStatDelegated    = 4, /* DELEGATED              */
+  /* up to here defined for VEVENT                              */
+  iCalPersonPartStatCompleted    = 5, /* COMPLETED              */
+  iCalPersonPartStatInProcess    = 6, /* IN-PROCESS             */
+  /* up to there defined for VTODO                              */
+  
+  /* these are also defined for VJOURNAL, VEVENT and VTODO      */
+  iCalPersonPartStatExperimental = 7, /* x-name                 */
+  iCalPersonPartStatOther        = 8  /* iana-token             */
+} iCalPersonPartStat;
+
+@interface iCalPerson : CardElement
+
+/* accessors */
+
+- (void)setCn:(NSString *)_s;
+- (NSString *)cn;
+- (NSString *)cnWithoutQuotes;
+
+- (void)setEmail:(NSString *)_s;
+- (NSString *)email;
+- (NSString *)rfc822Email; /* email without 'mailto:' prefix */
+
+- (void)setRsvp:(NSString *)_s;
+- (NSString *)rsvp;
+
+// - (void)setXuid:(NSString *)_s;
+// - (NSString *)xuid;
+
+- (void)setRole:(NSString *)_s;
+- (NSString *)role;
+
+- (void)setPartStat:(NSString *)_s;
+- (NSString *)partStat;
+- (NSString *)partStatWithDefault;
+
+- (void)setParticipationStatus:(iCalPersonPartStat)_status;
+- (iCalPersonPartStat)participationStatus;
+
+- (BOOL)isEqualToPerson:(iCalPerson *)_other;
+- (BOOL)hasSameEmailAddress:(iCalPerson *)_other;
+
+@end
+
+#endif /* __NGCards_iCalPerson_H__ */
diff --git a/SOPE/NGCards/iCalPerson.m b/SOPE/NGCards/iCalPerson.m
new file mode 100644 (file)
index 0000000..e22a81a
--- /dev/null
@@ -0,0 +1,230 @@
+/*
+  Copyright (C) 2000-2005 SKYRIX Software AG
+
+  This file is part of SOPE.
+
+  SOPE is free software; you can redistribute it and/or modify it under
+  the terms of the GNU Lesser General Public License as published by the
+  Free Software Foundation; either version 2, or (at your option) any
+  later version.
+
+  SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+  License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with SOPE; see the file COPYING.  If not, write to the
+  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+  02111-1307, USA.
+*/
+
+#include "iCalPerson.h"
+#include "common.h"
+
+@implementation iCalPerson
+
+/* accessors */
+
+- (void) setCn: (NSString *) _s
+{
+  [self setValue: 0 ofAttribute: @"cn" to: _s];
+}
+
+- (NSString *) cn
+{
+  return [self value: 0 ofAttribute: @"cn"];
+}
+
+- (NSString *) cnWithoutQuotes
+{
+  /* remove quotes around a CN */
+  NSString *_cn;
+  
+  _cn = [self cn];
+  if ([_cn length] <= 2)
+    return _cn;
+  if ([_cn characterAtIndex:0] != '"')
+    return _cn;
+  if (![_cn hasSuffix:@"\""])
+    return _cn;
+  
+  return [_cn substringWithRange:NSMakeRange(1, [_cn length] - 2)];
+}
+
+- (void) setEmail: (NSString *)_s
+{
+  [self setValue: 0
+        to: [NSString stringWithFormat: @"MAILTO:%@", _s]];
+}
+
+- (NSString *) email
+{
+  return [self value: 0];
+}
+
+- (NSString *) rfc822Email
+{
+  NSString *_email;
+  unsigned idx;
+
+  _email = [self email];
+  idx    = NSMaxRange([_email rangeOfString:@":"]);
+
+  if ((idx > 0) && ([_email length] > idx))
+    return [_email substringFromIndex:idx];
+
+  return _email;
+}
+
+- (void) setRsvp: (NSString *) _s
+{
+  [self setValue: 0 ofAttribute: @"rsvp" to: _s];
+}
+
+- (NSString *) rsvp
+{
+  return [self value: 0 ofAttribute: @"rsvp"];
+}
+
+// - (void)setXuid:(NSString *)_s {
+//   ASSIGNCOPY(self->xuid, _s);
+// }
+// - (NSString *)xuid {
+//   return self->xuid;
+// }
+
+- (void)setRole:(NSString *)_s
+{
+  [self setValue: 0 ofAttribute: @"role" to: _s];
+}
+
+- (NSString *) role
+{
+  return [self value: 0 ofAttribute: @"role"];
+}
+
+- (void)setPartStat:(NSString *)_s
+{
+  [self setValue: 0 ofAttribute: @"partstat" to: _s];
+}
+
+- (NSString *) partStat
+{
+  return [self value: 0 ofAttribute: @"partstat"];
+}
+
+- (NSString *) partStatWithDefault
+{
+  NSString *s;
+  
+  s = [self partStat];
+  if ([s length] > 0)
+    return s;
+  
+  return @"NEEDS-ACTION";
+}
+
+- (void) setParticipationStatus: (iCalPersonPartStat) _status
+{
+  NSString *stat;
+
+  switch (_status) {
+    case iCalPersonPartStatAccepted:
+      stat = @"ACCEPTED";
+      break;
+    case iCalPersonPartStatDeclined:
+      stat = @"DECLINED";
+      break;
+    case iCalPersonPartStatTentative:
+      stat = @"TENTATIVE";
+      break;
+    case iCalPersonPartStatDelegated:
+      stat = @"DELEGATED";
+      break;
+    case iCalPersonPartStatCompleted:
+      stat = @"COMPLETED";
+      break;
+    case iCalPersonPartStatInProcess:
+      stat = @"IN-PROCESS";
+      break;
+    case iCalPersonPartStatExperimental:
+    case iCalPersonPartStatOther:
+//       [NSException raise:NSInternalInconsistencyException
+//                    format:@"Attempt to set meaningless "
+//                           @"participationStatus (%d)!", _status];
+      stat = nil; /* keep compiler happy */
+      break;
+    default:
+      stat = @"NEEDS-ACTION";
+      break;
+  }
+  if (stat)
+    [self setPartStat:stat];
+}
+
+- (iCalPersonPartStat)participationStatus {
+  NSString *stat;
+  
+  stat = [[self partStat] uppercaseString];
+  if (![stat length] || [stat isEqualToString:@"NEEDS-ACTION"])
+    return iCalPersonPartStatNeedsAction;
+  else if ([stat isEqualToString:@"ACCEPTED"])
+    return iCalPersonPartStatAccepted;
+  else if ([stat isEqualToString:@"DECLINED"])
+    return iCalPersonPartStatDeclined;
+  else if ([stat isEqualToString:@"TENTATIVE"])
+    return iCalPersonPartStatTentative;
+  else if ([stat isEqualToString:@"DELEGATED"])
+    return iCalPersonPartStatDelegated;
+  else if ([stat isEqualToString:@"COMPLETED"])
+    return iCalPersonPartStatCompleted;
+  else if ([stat isEqualToString:@"IN-PROCESS"])
+    return iCalPersonPartStatInProcess;
+  else if ([stat hasPrefix:@"X-"])
+    return iCalPersonPartStatExperimental;
+  return iCalPersonPartStatOther;
+}
+
+
+/* comparison */
+
+- (unsigned)hash {
+  if([self email])
+    return [[self email] hash];
+  return [super hash];
+}
+
+- (BOOL)isEqual:(id)_other {
+  if(_other == nil)
+    return NO;
+  if([_other class] != self->isa)
+    return NO;
+  if([_other hash] != [self hash])
+    return NO;
+  return [self isEqualToPerson:_other];
+}
+
+- (BOOL)isEqualToPerson:(iCalPerson *)_other {
+  if(![self hasSameEmailAddress:_other])
+    return NO;
+  if(!IS_EQUAL([self cn], [_other cn], isEqualToString:))
+    return NO;
+  if(!IS_EQUAL([self rsvp], [_other rsvp], isEqualToString:))
+    return NO;
+  if(!IS_EQUAL([self partStat], [_other partStat], isEqualToString:))
+    return NO;
+  if(!IS_EQUAL([self role], [_other role], isEqualToString:))
+    return NO;
+//   if(!IS_EQUAL([self xuid], [_other xuid], isEqualToString:))
+//     return NO;
+  return YES;
+}
+
+- (BOOL)hasSameEmailAddress:(iCalPerson *)_other {
+  return IS_EQUAL([[self email] lowercaseString],
+                  [[_other email] lowercaseString],
+                  isEqualToString:);
+}
+
+@end /* iCalPerson */
diff --git a/SOPE/NGCards/iCalRecurrenceCalculator.h b/SOPE/NGCards/iCalRecurrenceCalculator.h
new file mode 100644 (file)
index 0000000..6dc92a8
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+  Copyright (C) 2004-2005 SKYRIX Software AG
+
+  This file is part of SOPE.
+
+  SOPE is free software; you can redistribute it and/or modify it under
+  the terms of the GNU Lesser General Public License as published by the
+  Free Software Foundation; either version 2, or (at your option) any
+  later version.
+
+  SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+  License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with SOPE; see the file COPYING.  If not, write to the
+  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+  02111-1307, USA.
+*/
+
+#ifndef        __NGiCal_iCalRecurrenceCalculator_H_
+#define        __NGiCal_iCalRecurrenceCalculator_H_
+
+#import <Foundation/NSObject.h>
+
+/*
+  iCalRecurrenceCalculator
+  Provides an API for performing common calculations performed in conjunction
+  with iCalRecurrenceRule objects.
+
+  TODO: rather move this functionality to iCalRecurrenceRule?
+*/
+
+@class NSArray;
+@class iCalRecurrenceRule, NGCalendarDateRange;
+
+@interface iCalRecurrenceCalculator : NSObject
+{
+  NGCalendarDateRange *firstRange;
+  iCalRecurrenceRule  *rrule;
+}
+
++ (NSArray *)
+ recurrenceRangesWithinCalendarDateRange: (NGCalendarDateRange *) _r
+          firstInstanceCalendarDateRange: (NGCalendarDateRange *) _fir
+                         recurrenceRules: (NSArray *) _rRules
+                          exceptionRules: (NSArray *) _exRules
+                          exceptionDates: (NSArray *) _exDates;
+
++ (id) recurrenceCalculatorForRecurrenceRule: (iCalRecurrenceRule *) _rrule
+          withFirstInstanceCalendarDateRange: (NGCalendarDateRange *) _range;
+
+- (id)    initWithRecurrenceRule: (iCalRecurrenceRule *) _rrule
+  firstInstanceCalendarDateRange: (NGCalendarDateRange *) _range;
+
+- (NSArray *)
+ recurrenceRangesWithinCalendarDateRange: (NGCalendarDateRange *)_r;
+- (BOOL) doesRecurrWithinCalendarDateRange: (NGCalendarDateRange *) _range;
+
+- (NGCalendarDateRange *) firstInstanceCalendarDateRange;
+- (NGCalendarDateRange *) lastInstanceCalendarDateRange; /* might be nil */
+  
+@end
+
+#endif /* __NGiCal_iCalRecurrenceCalculator_H_ */
diff --git a/SOPE/NGCards/iCalRecurrenceCalculator.m b/SOPE/NGCards/iCalRecurrenceCalculator.m
new file mode 100644 (file)
index 0000000..d144efe
--- /dev/null
@@ -0,0 +1,301 @@
+/*
+  Copyright (C) 2004-2005 SKYRIX Software AG
+  
+  This file is part of SOPE.
+  
+  SOPE is free software; you can redistribute it and/or modify it under
+  the terms of the GNU Lesser General Public License as published by the
+  Free Software Foundation; either version 2, or (at your option) any
+  later version.
+  
+  SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+  License for more details.
+  
+  You should have received a copy of the GNU Lesser General Public
+  License along with SOPE; see the file COPYING.  If not, write to the
+  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+  02111-1307, USA.
+*/
+
+#include "iCalRecurrenceCalculator.h"
+#include <NGExtensions/NGCalendarDateRange.h>
+#include "iCalRecurrenceRule.h"
+#include "NSCalendarDate+ICal.h"
+#include "common.h"
+
+/* class cluster */
+
+
+/* Private */
+
+@interface iCalRecurrenceCalculator (PrivateAPI)
+- (NSCalendarDate *)lastInstanceStartDate;
+
+- (unsigned)offsetFromSundayForJulianNumber:(long)_jn;
+- (unsigned)offsetFromSundayForWeekDay:(iCalWeekDay)_weekDay;
+- (unsigned)offsetFromSundayForCurrentWeekStart;
+  
+- (iCalWeekDay)weekDayForJulianNumber:(long)_jn;
+@end
+
+@implementation iCalRecurrenceCalculator
+
+static Class NSCalendarDateClass     = Nil;
+static Class iCalRecurrenceRuleClass = Nil;
+static Class dailyCalcClass   = Nil;
+static Class weeklyCalcClass  = Nil;
+static Class monthlyCalcClass = Nil;
+static Class yearlyCalcClass  = Nil;
+
++ (void)initialize {
+  static BOOL didInit = NO;
+  
+  if (didInit) return;
+  didInit = YES;
+
+  NSCalendarDateClass     = [NSCalendarDate class];
+  iCalRecurrenceRuleClass = [iCalRecurrenceRule class];
+
+  dailyCalcClass   = NSClassFromString(@"iCalDailyRecurrenceCalculator");
+  weeklyCalcClass  = NSClassFromString(@"iCalWeeklyRecurrenceCalculator");
+  monthlyCalcClass = NSClassFromString(@"iCalMonthlyRecurrenceCalculator");
+  yearlyCalcClass  = NSClassFromString(@"iCalYearlyRecurrenceCalculator");
+}
+
+/* factory */
+
++ (id)recurrenceCalculatorForRecurrenceRule:(iCalRecurrenceRule *)_rrule
+  withFirstInstanceCalendarDateRange:(NGCalendarDateRange *)_range
+{
+  return [[[self alloc] initWithRecurrenceRule:_rrule
+                        firstInstanceCalendarDateRange:_range] autorelease];
+}
+
+/* complex calculation convenience */
+
++ (NSArray *)recurrenceRangesWithinCalendarDateRange:(NGCalendarDateRange *)_r
+  firstInstanceCalendarDateRange:(NGCalendarDateRange *)_fir
+  recurrenceRules:(NSArray *)_rRules
+  exceptionRules:(NSArray *)_exRules
+  exceptionDates:(NSArray *)_exDates
+{
+  id                       rule;
+  iCalRecurrenceCalculator *calc;
+  NSMutableArray           *ranges;
+  NSMutableArray           *exDates;
+  unsigned                 i, count, rCount;
+  
+  ranges = [NSMutableArray arrayWithCapacity:64];
+  
+  for (i = 0, count  = [_rRules count]; i < count; i++) {
+    NSArray *rs;
+
+    rule = [_rRules objectAtIndex:i];
+    if (![rule isKindOfClass:iCalRecurrenceRuleClass])
+      rule = [iCalRecurrenceRule recurrenceRuleWithICalRepresentation:rule];
+  
+    calc = [self recurrenceCalculatorForRecurrenceRule:rule
+                 withFirstInstanceCalendarDateRange:_fir];
+    rs   = [calc recurrenceRangesWithinCalendarDateRange:_r];
+    [ranges addObjectsFromArray:rs];
+  }
+  
+  if ([ranges count] == 0)
+    return nil;
+  
+  /* test if any exceptions do match */
+  
+  for (i = 0, count = [_exRules count]; i < count; i++) {
+    NSArray *rs;
+
+    rule = [_exRules objectAtIndex:i];
+    if (![rule isKindOfClass:iCalRecurrenceRuleClass])
+      rule = [iCalRecurrenceRule recurrenceRuleWithICalRepresentation:rule];
+
+    calc = [self recurrenceCalculatorForRecurrenceRule:rule
+                 withFirstInstanceCalendarDateRange:_fir];
+    rs   = [calc recurrenceRangesWithinCalendarDateRange:_r];
+    [ranges removeObjectsInArray:rs];
+  }
+  
+  if (![ranges isNotEmpty])
+    return nil;
+  
+  /* exception dates */
+
+  if ((count = [_exDates count]) == 0)
+    return ranges;
+  
+  /* sort out exDates not within range */
+
+  exDates = [NSMutableArray arrayWithCapacity:count];
+  for (i = 0; i < count; i++) {
+    id exDate;
+
+    exDate = [_exDates objectAtIndex:i];
+    if (![exDate isKindOfClass:NSCalendarDateClass])
+      exDate = [NSCalendarDate calendarDateWithICalRepresentation:exDate];
+    
+    if ([_r containsDate:exDate])
+      [exDates addObject:exDate];
+  }
+
+  /* remove matching exDates from ranges */
+
+  if ((count = [exDates count]) == 0)
+    return ranges;
+  
+  for (i = 0, rCount = [ranges count]; i < count; i++) {
+    NSCalendarDate      *exDate;
+    NGCalendarDateRange *r;
+    unsigned            k;
+
+    exDate = [exDates objectAtIndex:i];
+    for (k = 0; k < rCount; k++) {
+      unsigned rIdx;
+      
+      rIdx = (rCount - k) - 1;
+      r    = [ranges objectAtIndex:rIdx];
+      if ([r containsDate:exDate]) {
+        [ranges removeObjectAtIndex:rIdx];
+        rCount--;
+        break; /* this is safe because we know that ranges don't overlap */
+      }
+    }
+  }
+  return ranges;
+}
+
+
+/* init */
+
+- (id)initWithRecurrenceRule:(iCalRecurrenceRule *)_rrule
+  firstInstanceCalendarDateRange:(NGCalendarDateRange *)_range
+{
+  iCalRecurrenceFrequency freq;
+  Class calcClass = Nil;
+
+  freq = [_rrule frequency];
+  if (freq == iCalRecurrenceFrequenceDaily)
+    calcClass = dailyCalcClass;
+  else if (freq == iCalRecurrenceFrequenceWeekly)
+    calcClass = weeklyCalcClass;
+  else if (freq == iCalRecurrenceFrequenceMonthly)
+    calcClass = monthlyCalcClass;
+  else if (freq == iCalRecurrenceFrequenceYearly)
+    calcClass = yearlyCalcClass;
+  else {
+    [self errorWithFormat:@"unsupported rrule frequency: %@", _rrule];
+    calcClass = Nil;
+    [self release];
+    return nil;
+  }
+  
+  [self autorelease]; // TODO: why autorelease?
+  if (calcClass == Nil)
+    return nil;
+  
+  if ((self = [[calcClass alloc] init]) != nil) {
+    self->rrule      = [_rrule retain];
+    self->firstRange = [_range retain];
+  }
+  return self;  
+}
+
+- (void)dealloc {
+  [self->firstRange release];
+  [self->rrule      release];
+  [super dealloc];
+}
+
+/* helpers */
+
+- (unsigned)offsetFromSundayForJulianNumber:(long)_jn {
+  return (unsigned)((int)(_jn + 1.5)) % 7;
+}
+
+- (unsigned)offsetFromSundayForWeekDay:(iCalWeekDay)_weekDay {
+  unsigned offset;
+  
+  switch (_weekDay) {
+    case iCalWeekDaySunday:    offset = 0; break;
+    case iCalWeekDayMonday:    offset = 1; break;
+    case iCalWeekDayTuesday:   offset = 2; break;
+    case iCalWeekDayWednesday: offset = 3; break;
+    case iCalWeekDayThursday:  offset = 4; break;
+    case iCalWeekDayFriday:    offset = 5; break;
+    case iCalWeekDaySaturday:  offset = 6; break;
+    default:                   offset = 0; break;
+  }
+  return offset;
+}
+
+- (unsigned)offsetFromSundayForCurrentWeekStart {
+  return [self offsetFromSundayForWeekDay:[self->rrule weekStart]];
+}
+
+- (iCalWeekDay)weekDayForJulianNumber:(long)_jn {
+  unsigned    day;
+  iCalWeekDay weekDay;
+
+  day = [self offsetFromSundayForJulianNumber:_jn];
+  switch (day) {
+    case 0:  weekDay = iCalWeekDaySunday;    break;
+    case 1:  weekDay = iCalWeekDayMonday;    break;
+    case 2:  weekDay = iCalWeekDayTuesday;   break;
+    case 3:  weekDay = iCalWeekDayWednesday; break;
+    case 4:  weekDay = iCalWeekDayThursday;  break;
+    case 5:  weekDay = iCalWeekDayFriday;    break;
+    case 6:  weekDay = iCalWeekDaySaturday;  break;
+    default: 
+      [self errorWithFormat:@"got unexpected weekday: %d", day];
+      weekDay = iCalWeekDaySunday;
+      break; /* keep compiler happy */
+  }
+  return weekDay;
+}
+
+/* calculation */
+
+- (NSArray *)recurrenceRangesWithinCalendarDateRange:(NGCalendarDateRange *)_r{
+  return nil; /* subclass responsibility */
+}
+- (BOOL)doesRecurrWithinCalendarDateRange:(NGCalendarDateRange *)_range {
+  NSArray *ranges;
+
+  ranges = [self recurrenceRangesWithinCalendarDateRange:_range];
+  return (ranges == nil || [ranges count] == 0) ? NO : YES;
+}
+
+- (NGCalendarDateRange *)firstInstanceCalendarDateRange {
+  return self->firstRange;
+}
+
+- (NGCalendarDateRange *)lastInstanceCalendarDateRange {
+  NSCalendarDate *start, *end;
+
+  if ((start = [self lastInstanceStartDate]) == nil)
+    return nil;
+  
+  end   = [start addTimeInterval:[self->firstRange duration]];
+  return [NGCalendarDateRange calendarDateRangeWithStartDate:start
+                              endDate:end];
+}
+
+- (NSCalendarDate *)lastInstanceStartDate {
+  NSCalendarDate *until;
+  
+  /* 
+     NOTE: this is horribly inaccurate and doesn't even consider the use
+           of repeatCount. It MUST be implemented by subclasses properly!
+          However, it does the trick for SOGo 1.0 - that's why it's left here.
+  */
+  if ((until = [self->rrule untilDate]) != nil)
+    return until;
+  
+  return nil;
+}
+
+@end /* iCalRecurrenceCalculator */
diff --git a/SOPE/NGCards/iCalRecurrenceRule.h b/SOPE/NGCards/iCalRecurrenceRule.h
new file mode 100644 (file)
index 0000000..53a254e
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+  Copyright (C) 2004-2005 SKYRIX Software AG
+
+  This file is part of SOPE.
+
+  SOPE is free software; you can redistribute it and/or modify it under
+  the terms of the GNU Lesser General Public License as published by the
+  Free Software Foundation; either version 2, or (at your option) any
+  later version.
+
+  SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+  License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with SOPE; see the file COPYING.  If not, write to the
+  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+  02111-1307, USA.
+*/
+
+#ifndef        __NGiCal_iCalRecurrenceRule_H_
+#define        __NGiCal_iCalRecurrenceRule_H_
+
+#import "CardElement.h"
+
+/*
+  iCalRecurrenceRule
+  Encapsulates a (probably complex) recurrence rule by offering
+  a high level API.
+  NOTE: as of now, only a very limited subset of RFC2445 is implemented.
+        Please see the unit tests for what is covered.
+*/
+
+// TODO: we could use string constants?
+typedef enum {
+  iCalRecurrenceFrequenceSecondly = 1,
+  iCalRecurrenceFrequenceMinutely = 2,
+  iCalRecurrenceFrequenceHourly   = 3,
+  iCalRecurrenceFrequenceDaily    = 4,
+  iCalRecurrenceFrequenceWeekly   = 5,
+  iCalRecurrenceFrequenceMonthly  = 6,
+  iCalRecurrenceFrequenceYearly   = 7,
+} iCalRecurrenceFrequency;
+
+typedef enum {
+  iCalWeekDayMonday    = 1,
+  iCalWeekDayTuesday   = 2,
+  iCalWeekDayWednesday = 4,
+  iCalWeekDayThursday  = 8,
+  iCalWeekDayFriday    = 16,
+  iCalWeekDaySaturday  = 32,
+  iCalWeekDaySunday    = 64,
+} iCalWeekDay;
+
+@class NSString, NSCalendarDate, NGCalendarDateRange, NSArray;
+
+@interface iCalRecurrenceRule : CardElement
+// {
+//   iCalRecurrenceFrequency frequency;
+//   int            interval;
+//   unsigned       repeatCount;
+//   NSCalendarDate *untilDate;
+//   struct {
+//     unsigned weekStart: 7;
+//     unsigned mask:      7;
+//     unsigned useOccurence:1;
+//     unsigned reserved:1;
+//   } byDay;
+//   int byDayOccurence1;
+//   NSArray        *byMonthDay;
+  
+//   NSString       *rrule;
+// }
+
++ (id) recurrenceRuleWithICalRepresentation: (NSString *) _iCalRep;
+- (id) initWithString: (NSString *) _str;
+
+/* accessors */
+
+- (void) setFrequency: (iCalRecurrenceFrequency) _frequency;
+- (iCalRecurrenceFrequency) frequency;
+
+- (void) setRepeatInterval: (int) _repeatInterval;
+- (int) repeatInterval;
+
+- (void) setWeekStart: (iCalWeekDay) _weekStart;
+- (iCalWeekDay) weekStart;
+
+- (void) setByDayMask: (unsigned int) _mask;
+- (unsigned int) byDayMask;
+- (int) byDayOccurence1;
+
+- (NSArray *) byMonthDay;
+  
+/* count and untilDate are mutually exclusive */
+
+- (void) setRepeatCount: (int) _repeatCount;
+- (int) repeatCount;
+
+- (void) setUntilDate: (NSCalendarDate *) _untilDate;
+- (NSCalendarDate *) untilDate;
+
+- (BOOL) isInfinite;
+
+/* parse complete iCal RRULE */
+
+- (void) setRrule: (NSString *) _rrule; // TODO: weird name? (better: RRule?)
+
+// - (NSString *)iCalRepresentation;
+
+@end
+
+#endif /* __NGiCal_iCalRecurrenceRule_H_ */
diff --git a/SOPE/NGCards/iCalRecurrenceRule.m b/SOPE/NGCards/iCalRecurrenceRule.m
new file mode 100644 (file)
index 0000000..4292d37
--- /dev/null
@@ -0,0 +1,631 @@
+/*
+  Copyright (C) 2004-2005 SKYRIX Software AG
+
+  This file is part of SOPE.
+
+  SOPE is free software; you can redistribute it and/or modify it under
+  the terms of the GNU Lesser General Public License as published by the
+  Free Software Foundation; either version 2, or (at your option) any
+  later version.
+
+  SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+  License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with SOPE; see the file COPYING.  If not, write to the
+  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+  02111-1307, USA.
+*/
+
+#import <Foundation/NSException.h>
+#import <NGExtensions/NSString+Ext.h>
+#import <NGExtensions/NSObject+Logs.h>
+
+#import <ctype.h>
+
+#import "NSCalendarDate+NGCards.h"
+#import "NSString+NGCards.h"
+
+#import "NSCalendarDate+ICal.h"
+
+#import "iCalRecurrenceRule.h"
+
+/*
+  freq       = rrFreq;
+  until      = rrUntil;
+  count      = rrCount;
+  interval   = rrInterval;
+  bysecond   = rrBySecondList;
+  byminute   = rrByMinuteList;
+  byhour     = rrByHourList;
+  byday      = rrByDayList;
+  bymonthday = rrByMonthDayList;
+  byyearday  = rrByYearDayList;
+  byweekno   = rrByWeekNumberList;
+  bymonth    = rrByMonthList;
+  bysetpos   = rrBySetPosList;
+  wkst       = rrWeekStart;
+*/
+
+// TODO: private API in the header file?!
+@interface iCalRecurrenceRule (PrivateAPI)
+
+- (iCalWeekDay) weekDayFromICalRepresentation: (NSString *) _day;
+- (NSString *) iCalRepresentationForWeekDay: (iCalWeekDay) _waeekDay;
+- (NSString *) freq;
+- (NSString *) wkst;
+- (NSString *) byDayList;
+
+// - (void)_parseRuleString:(NSString *)_rrule;
+
+/* currently used by parser, should be removed (replace with an -init..) */
+- (void)setByday:(NSString *)_byDayList;
+
+@end
+
+@implementation iCalRecurrenceRule
+
++ (id) recurrenceRuleWithICalRepresentation: (NSString *) _iCalRep
+{
+  return [self simpleElementWithTag: @"rrule" value: _iCalRep];
+}
+
+- (id) init
+{
+  if ((self = [super init]) != nil)
+    {
+      [self setTag: @"rrule"];
+    }
+
+  return self;
+}
+
+- (id) initWithString: (NSString *) _str
+{
+  if ((self = [self init]))
+    {
+      [self setRrule: _str];
+    }
+
+  return self;
+}
+
+- (void) setRrule: (NSString *) _rrule
+{
+  NSEnumerator *newValues;
+  NSString *newValue;
+
+  newValues = [[_rrule componentsSeparatedByString: @";"] objectEnumerator];
+  newValue = [newValues nextObject];
+  while (newValue)
+    {
+      [self addValue: newValue];
+      newValue = [newValues nextObject];
+    }
+}
+
+- (iCalRecurrenceFrequency) valueForFrequency: (NSString *) value
+{
+  NSString *frequency;
+  iCalRecurrenceFrequency freq;
+
+  frequency = [value uppercaseString];
+  if ([frequency isEqualToString:@"WEEKLY"])
+    freq = iCalRecurrenceFrequenceWeekly;
+  else if ([frequency isEqualToString:@"MONTHLY"])
+    freq = iCalRecurrenceFrequenceMonthly;
+  else if ([frequency isEqualToString:@"DAILY"])
+    freq = iCalRecurrenceFrequenceDaily;
+  else if ([frequency isEqualToString:@"YEARLY"])
+    freq = iCalRecurrenceFrequenceYearly;
+  else if ([frequency isEqualToString:@"HOURLY"])
+    freq = iCalRecurrenceFrequenceHourly;
+  else if ([frequency isEqualToString:@"MINUTELY"])
+    freq = iCalRecurrenceFrequenceMinutely;
+  else if ([frequency isEqualToString:@"SECONDLY"])
+    freq = iCalRecurrenceFrequenceSecondly;
+  else
+    freq = NSNotFound;
+
+  return freq;
+}
+
+- (NSString *) frequencyForValue: (iCalRecurrenceFrequency) freq
+{
+  NSString *frequency;
+
+  switch (freq)
+    {
+    case iCalRecurrenceFrequenceWeekly:
+      frequency = @"WEEKLY";
+      break;
+    case iCalRecurrenceFrequenceMonthly:
+      frequency = @"MONTHLY";
+      break;
+    case iCalRecurrenceFrequenceDaily:
+      frequency = @"DAILY";
+      break;
+    case iCalRecurrenceFrequenceYearly:
+      frequency = @"YEARLY";
+      break;
+    case iCalRecurrenceFrequenceHourly:
+      frequency = @"HOURLY";
+      break;
+    case iCalRecurrenceFrequenceMinutely:
+      frequency = @"MINUTELY";
+      break;
+    case iCalRecurrenceFrequenceSecondly:
+      frequency = @"SECONDLY";
+      break;
+    default:
+      frequency = nil;
+    }
+
+  return frequency;
+}
+
+/* accessors */
+
+- (void) setFrequency: (iCalRecurrenceFrequency) _frequency
+{
+  [self setNamedValue: @"freq" to: [self frequencyForValue: _frequency]];
+}
+
+- (iCalRecurrenceFrequency) frequency
+{
+  return [self valueForFrequency: [self namedValue: @"freq"]];
+}
+
+- (void) setRepeatCount: (int) _repeatCount
+{
+  [self setNamedValue: @"count"
+        to: [NSString stringWithFormat: @"%d", _repeatCount]];
+}
+
+- (int) repeatCount
+{
+  return [[self namedValue: @"count"] intValue];
+}
+
+- (void) setUntilDate: (NSCalendarDate *) _untilDate
+{
+  [self setNamedValue: @"until"
+        to: [_untilDate iCalFormattedDateTimeString]];
+}
+
+- (NSCalendarDate *) untilDate
+{
+#warning handling of default timezone needs to be implemented
+  return [[self namedValue: @"until"] asCalendarDate];
+}
+
+- (void) setInterval: (NSString *) _interval
+{
+  [self setNamedValue: @"interval" to: _interval];
+}
+
+- (void) setCount: (NSString *) _count
+{
+  [self setNamedValue: @"count" to: _count];
+}
+
+- (void) setUntil: (NSString *) _until
+{
+  [self setNamedValue: @"until" to: _until];
+}
+
+- (void) setRepeatInterval: (int) _repeatInterval
+{
+  [self setNamedValue: @"interval"
+        to: [NSString stringWithFormat: @"%d", _repeatInterval]];
+}
+
+- (int) repeatInterval
+{
+  return [[self namedValue: @"interval"] intValue];
+}
+
+- (void) setWkst: (NSString *) _weekStart
+{
+  [self setNamedValue: @"wkst" to: _weekStart];
+}
+
+- (NSString *) wkst
+{
+  return [self namedValue: @"wkst"];
+}
+
+- (void) setWeekStart: (iCalWeekDay) _weekStart
+{
+  [self setWkst: [self iCalRepresentationForWeekDay: _weekStart]];
+}
+
+- (iCalWeekDay) weekStart
+{
+  return [self weekDayFromICalRepresentation: [self wkst]];
+}
+
+- (void) setByDayMask: (unsigned) _mask
+{
+  NSMutableArray *days;
+  unsigned int count;
+  unsigned char maskDays[] = { iCalWeekDayMonday, iCalWeekDayTuesday,
+                               iCalWeekDayWednesday, iCalWeekDayThursday,
+                               iCalWeekDayFriday, iCalWeekDaySaturday,
+                               iCalWeekDaySunday };
+  days = [NSMutableArray arrayWithCapacity: 7];
+  if (_mask)
+    {
+      for (count = 0; count < 7; count++)
+        if (_mask & maskDays[count])
+          [days addObject:
+                  [self iCalRepresentationForWeekDay: maskDays[count]]];
+    }
+
+  [self setNamedValue: @"byday" to: [days componentsJoinedByString: @","]];
+}
+
+- (unsigned int) byDayMask
+{
+  NSArray *days;
+  unsigned int mask, count, max;
+  NSString *day;
+
+  mask = 0;
+
+  days = [[self namedValue: @"byday"] componentsSeparatedByString: @","];
+  max = [days count];
+  for (count = 0; count < max; count++)
+    {
+      day = [days objectAtIndex: count];
+      day = [day substringFromIndex: [day length] - 2];
+      mask |= [self weekDayFromICalRepresentation: day];
+    }
+
+  return mask;
+}
+
+#warning this is fucked up
+- (int) byDayOccurence1
+{
+  return 0;
+//   return byDayOccurence1;
+}
+
+- (NSArray *) byMonthDay
+{
+  return [[self namedValue: @"bymonthday"] componentsSeparatedByString: @","];
+}
+
+- (BOOL) isInfinite
+{
+  return !([self repeatCount] || [self untilDate]);
+}
+
+/* private */
+
+- (iCalWeekDay) weekDayFromICalRepresentation: (NSString *) _day
+{
+  if ([_day length] > 1) {
+    /* be tolerant */
+    unichar c0, c1;
+    
+    c0 = [_day characterAtIndex:0];
+    if (c0 == 'm' || c0 == 'M') return iCalWeekDayMonday;
+    if (c0 == 'w' || c0 == 'W') return iCalWeekDayWednesday;
+    if (c0 == 'f' || c0 == 'F') return iCalWeekDayFriday;
+
+    c1 = [_day characterAtIndex:1];
+    if (c0 == 't' || c0 == 'T') {
+      if (c1 == 'u' || c1 == 'U') return iCalWeekDayTuesday;
+      if (c1 == 'h' || c1 == 'H') return iCalWeekDayThursday;
+    }
+    if (c0 == 's' || c0 == 'S') {
+      if (c1 == 'a' || c1 == 'A') return iCalWeekDaySaturday;
+      if (c1 == 'u' || c1 == 'U') return iCalWeekDaySunday;
+    }
+  }
+  
+  // TODO: do not raise but rather return an error value?
+  [NSException raise:NSGenericException
+              format:@"Incorrect weekDay '%@' specified!", _day];
+  return iCalWeekDayMonday; /* keep compiler happy */
+}
+
+- (NSString *) iCalRepresentationForWeekDay: (iCalWeekDay) _weekDay
+{
+  switch (_weekDay)
+    {
+    case iCalWeekDayMonday: return @"MO";
+    case iCalWeekDayTuesday: return @"TU";
+    case iCalWeekDayWednesday: return @"WE";
+    case iCalWeekDayThursday: return @"TH";
+    case iCalWeekDayFriday: return @"FR";
+    case iCalWeekDaySaturday: return @"SA";
+    case iCalWeekDaySunday: return @"SU";
+    default:  return @"MO"; // TODO: return error?
+    }
+}
+
+// - (iCalWeekDay) weekDayForiCalRepre: (NSString *) _weekDay
+// {
+//   iCalWeekDay day;
+//   NSString *weekDay;
+
+//   weekDay = [_weekDay uppercaseString];
+//   if ([weekDay isEqualToString: @"TU"])
+//     day = iCalWeekDayTuesday;
+//   else if ([weekDay isEqualToString: @"WE"])
+//     day = iCalWeekDayWednesday;
+//   else if ([weekDay isEqualToString: @"TH"])
+//     day = iCalWeekDayThursday;
+//   else if ([weekDay isEqualToString: @"FR"])
+//     day = iCalWeekDayFriday;
+//   else if ([weekDay isEqualToString: @"SA"])
+//     day = iCalWeekDaySaturday;
+//   else if ([weekDay isEqualToString: @"SU"])
+//     day = iCalWeekDaySunday;
+//   else
+//     day = iCalWeekDayMonday;
+
+//   return day;
+// }
+
+// - (NSString *) wkst
+// {
+//   return [self iCalRepresentationForWeekDay:byDay.weekStart];
+// }
+
+/*
+  TODO:
+  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.
+*/
+// - (NSString *) byDayList
+// {
+//   NSMutableString *s;
+//   unsigned        dow, mask, day;
+//   BOOL            needsComma;
+  
+//   s          = [NSMutableString stringWithCapacity:20];
+//   needsComma = NO;
+//   mask       = byDay.mask;
+//   day        = iCalWeekDayMonday;
+  
+//   for (dow = 0 /* Sun */; dow < 7; dow++) {
+//     if (mask & day) {
+//       if (needsComma)
+//         [s appendString:@","];
+      
+//       if (byDay.useOccurence)
+//     // Note: we only support one occurrence for all currently
+//     [s appendFormat:@"%i", byDayOccurence1];
+      
+//       [s appendString:[self iCalRepresentationForWeekDay:day]];
+//       needsComma = YES;
+//     }
+//     day = (day << 1);
+//   }
+
+//   return s;
+// }
+
+/* parsing rrule */
+
+// - (void) _parseRuleString: (NSString *) _rrule
+// {
+//   // TODO: to be exact we would need a timezone to properly process the 'until'
+//   //       date
+//   unsigned i, count;
+//   NSString *pFrequency = nil;
+//   NSString *pUntil     = nil;
+//   NSString *pCount     = nil;
+//   NSString *pByday     = nil;
+//   NSString *pBymday    = nil;
+//   NSString *pBysetpos  = nil;
+//   NSString *pInterval  = nil;
+  
+//   for (i = 0, count = [values count]; i < count; i++) {
+//     NSString *prop, *key, *value;
+//     NSRange  r;
+//     NSString **vHolder = NULL;
+    
+//     prop = [values objectAtIndex:i];
+//     r    = [prop rangeOfString:@"="];
+//     if (r.length > 0) {
+//       key   = [prop substringToIndex:r.location];
+//       value = [prop substringFromIndex:NSMaxRange(r)];
+//     }
+//     else {
+//       key   = prop;
+//       value = nil;
+//     }
+    
+//     key = [[key stringByTrimmingSpaces] lowercaseString];
+//     if (![key isNotEmpty]) {
+//       [self errorWithFormat:@"empty component in rrule: %@", _rrule];
+//       continue;
+//     }
+    
+//     vHolder = NULL;
+//     switch ([key characterAtIndex:0]) {
+//     case 'b':
+//       if ([key isEqualToString:@"byday"])      { vHolder = &pByday;    break; }
+//       if ([key isEqualToString:@"bymonthday"]) { vHolder = &pBymday;   break; }
+//       if ([key isEqualToString:@"bysetpos"])   { vHolder = &pBysetpos; break; }
+//       break;
+//     case 'c':
+//       if ([key isEqualToString:@"count"]) { vHolder = &pCount; break; }
+//       break;
+//     case 'f':
+//       if ([key isEqualToString:@"freq"]) { vHolder = &pFrequency; break; }
+//       break;
+//     case 'i':
+//       if ([key isEqualToString:@"interval"]) { vHolder = &pInterval; break; }
+//       break;
+//     case 'u':
+//       if ([key isEqualToString:@"until"]) { vHolder = &pUntil; break; }
+//       break;
+//     default:
+//       break;
+//     }
+    
+//     if (vHolder != NULL) {
+//       if ([*vHolder isNotEmpty])
+//         [self errorWithFormat:@"more than one '%@' in: %@", key, _rrule];
+//       else
+//         *vHolder = [value copy];
+//     }
+//     else {
+//       // TODO: we should just parse known keys and put remainders into a
+//       //       separate dictionary
+//       [self logWithFormat:@"TODO: add explicit support for key: %@", key];
+//       [self takeValue:value forKey:key];
+//     }
+//   }
+  
+//   /* parse and fill individual values */
+//   // TODO: this method should be a class method and create a new rrule object
+  
+//   if ([pFrequency isNotEmpty])
+//     [self setNamedValue: @"FREQ" to: pFrequency];
+//   else
+//     [self errorWithFormat:@"rrule contains no frequency: '%@'", _rrule];
+//   [pFrequency release]; pFrequency = nil;
+
+//   if (pInterval != nil)
+//     interval = [pInterval intValue];
+//   [pInterval release]; pInterval = nil;
+  
+//   // TODO: we should parse byday in here
+//   if (pByday != nil) [self setByday:pByday];
+//   [pByday release]; pByday = nil;
+
+//   if (pBymday != nil) {
+//     NSArray *t;
+    
+//     t = [pBymday componentsSeparatedByString:@","];
+//     ASSIGNCOPY(byMonthDay, t);
+//   }
+//   [pBymday release]; pBymday = nil;
+  
+//   if (pBysetpos != nil)
+//     // TODO: implement
+//     [self errorWithFormat:@"rrule contains bysetpos, unsupported: %@", _rrule];
+//   [pBysetpos release]; pBysetpos = nil;
+  
+//   if (pUntil != nil) {
+//     NSCalendarDate *pUntilDate;
+    
+//     if (pCount != nil) {
+//       [self errorWithFormat:@"rrule contains 'count' AND 'until': %@", _rrule];
+//       [pCount release];
+//       pCount = nil;
+//     }
+    
+//     /*
+//       The spec says:
+//         "If specified as a date-time value, then it MUST be specified in an
+//          UTC time format."
+//       TODO: we still need some object representing a 'timeless' date.
+//     */
+//     if (![pUntil hasSuffix:@"Z"] && [pUntil length] > 8) {
+//       [self warnWithFormat:@"'until' date has no explicit UTC marker: '%@'",
+//               _rrule];
+//     }
+    
+//     pUntilDate = [NSCalendarDate calendarDateWithICalRepresentation:pUntil];
+//     if (pUntilDate != nil)
+//       [self setUntilDate:pUntilDate];
+//     else {
+//       [self errorWithFormat:@"could not parse 'until' in rrule: %@", 
+//               _rrule];
+//     }
+//   }
+//   [pUntil release]; pUntil = nil;
+  
+//   if (pCount != nil) 
+//     [self setRepeatCount:[pCount intValue]];
+//   [pCount release]; pCount = nil;
+// }
+
+/* properties */
+
+// - (void) setByday: (NSString *) _byDayList
+// {
+//   // TODO: each day can have an associated occurence, eg:
+//   //        +1MO,+2TU,-9WE
+//   // TODO: this should be moved to the parser
+//   NSArray  *days;
+//   unsigned i, count;
+//   NSString    *iCalDay;
+//   iCalWeekDay day;
+//   unsigned    len;
+//   unichar     c0;
+//   int         occurence;
+//   int offset;
+
+//   /* reset mask */
+//   byDay.mask = 0;
+//   byDay.useOccurence = 0;
+//   byDayOccurence1 = 0;
+  
+//   days  = [_byDayList componentsSeparatedByString:@","];
+//   for (i = 0, count = [days count]; i < count; i++)
+//     {
+//       iCalDay = [days objectAtIndex:i]; // eg: MO or TU
+//       if ((len = [iCalDay length]) == 0)
+//         {
+//           [self errorWithFormat:@"found an empty day in byday list: '%@'", 
+//                 _byDayList];
+//           continue;
+//         }
+    
+//       c0 = [iCalDay characterAtIndex:0];
+//       if (((c0 == '+' || c0 == '-') && len > 2) || (isdigit(c0) && len > 1)) {
+//         occurence = [iCalDay intValue];
+      
+//         offset = 1; /* skip occurence */
+//         while (offset < len && isdigit([iCalDay characterAtIndex:offset]))
+//           offset++;
+        
+//         iCalDay = [iCalDay substringFromIndex:offset];
+        
+//         if (byDay.useOccurence && (occurence != byDayOccurence1))
+//           {
+//             [self errorWithFormat:
+//                     @"we only supported one occurence (occ=%i,day=%@): '%@'", 
+//                   occurence, iCalDay, _byDayList];
+//             continue;
+//           }
+        
+//         byDay.useOccurence = 1;
+//         byDayOccurence1 = occurence;
+//       }
+//     else if (byDay.useOccurence)
+//       [self errorWithFormat:
+//           @"a byday occurence was specified on one day, but not on others"
+//             @" (unsupported): '%@'", _byDayList];
+    
+//       day = [self weekDayFromICalRepresentation:iCalDay];
+//       byDay.mask |= day;
+//     }
+// }
+
+/* key/value coding */
+
+- (void) handleTakeValue: (id) _value
+           forUnboundKey: (NSString *)_key
+{
+  [self warnWithFormat:@"Cannot handle unbound key: '%@'", _key];
+}
+
+@end /* iCalRecurrenceRule */
diff --git a/SOPE/NGCards/iCalRepeatableEntityObject.h b/SOPE/NGCards/iCalRepeatableEntityObject.h
new file mode 100644 (file)
index 0000000..d322546
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+  Copyright (C) 2004-2005 SKYRIX Software AG
+
+  This file is part of SOPE.
+
+  SOPE is free software; you can redistribute it and/or modify it under
+  the terms of the GNU Lesser General Public License as published by the
+  Free Software Foundation; either version 2, or (at your option) any
+  later version.
+
+  SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+  License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with SOPE; see the file COPYING.  If not, write to the
+  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+  02111-1307, USA.
+*/
+
+#ifndef        __NGCards_iCalRepeatableEntityObject_H_
+#define        __NGCards_iCalRepeatableEntityObject_H_
+
+#import "iCalEntityObject.h"
+
+/*
+ iCalRepeatableEntityObject
+
+ Specifies an iCal entity object which can bear a (possibly complex) set
+ of recurrence rules and exceptions thereof. According to RFC 2445 these
+ are VEVENT, VTODO and VJOURNAL.
+*/
+
+@class NSMutableArray, NGCalendarDateRange;
+
+@interface iCalRepeatableEntityObject : iCalEntityObject
+// {
+//   NSMutableArray *rRules;
+//   NSMutableArray *exRules;
+//   NSMutableArray *exDates;
+// }
+
+- (void)removeAllRecurrenceRules;
+- (void)addToRecurrenceRules:(id)_rrule;
+- (BOOL)hasRecurrenceRules;
+- (NSArray *)recurrenceRules;
+
+- (void)removeAllExceptionRules;
+- (void)addToExceptionRules:(id)_rrule;
+- (BOOL)hasExceptionRules;
+- (NSArray *)exceptionRules;
+
+- (void)removeAllExceptionDates;
+- (void)addToExceptionDates:(id)_date;
+- (BOOL)hasExceptionDates;
+- (NSArray *)exceptionDates;
+
+- (BOOL)isRecurrent;
+- (BOOL)isWithinCalendarDateRange:(NGCalendarDateRange *)_range
+  firstInstanceCalendarDateRange:(NGCalendarDateRange *)_fir;
+- (NSArray *)recurrenceRangesWithinCalendarDateRange:(NGCalendarDateRange *)_r
+  firstInstanceCalendarDateRange:(NGCalendarDateRange *)_fir;
+
+/* this is the outmost bound possible, not necessarily the real last date */
+- (NSCalendarDate *)lastPossibleRecurrenceStartDateUsingFirstInstanceCalendarDateRange:(NGCalendarDateRange *)_r;
+
+@end
+
+#endif /* __NGCards_iCalRepeatableEntityObject_H_ */
diff --git a/SOPE/NGCards/iCalRepeatableEntityObject.m b/SOPE/NGCards/iCalRepeatableEntityObject.m
new file mode 100644 (file)
index 0000000..1a772d0
--- /dev/null
@@ -0,0 +1,182 @@
+/*
+  Copyright (C) 2004-2005 SKYRIX Software AG
+
+  This file is part of SOPE.
+
+  SOPE is free software; you can redistribute it and/or modify it under
+  the terms of the GNU Lesser General Public License as published by the
+  Free Software Foundation; either version 2, or (at your option) any
+  later version.
+
+  SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+  License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with SOPE; see the file COPYING.  If not, write to the
+  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+  02111-1307, USA.
+*/
+
+#include "iCalRepeatableEntityObject.h"
+#include <NGExtensions/NGCalendarDateRange.h>
+#include "iCalRecurrenceRule.h"
+#include "iCalRecurrenceCalculator.h"
+#include "common.h"
+
+@implementation iCalRepeatableEntityObject
+
+- (Class) classForTag: (NSString *) classTag
+{
+  Class tagClass;
+
+  if ([classTag isEqualToString: @"RRULE"])
+    tagClass = [iCalRecurrenceRule class];
+  else
+    tagClass = [super classForTag: classTag];
+
+  return tagClass;
+}
+
+/* Accessors */
+
+- (void) removeAllRecurrenceRules
+{
+  [children removeObjectsInArray: [self childrenWithTag: @"rrule"]];
+}
+
+- (void) addToRecurrenceRules: (id) _rrule
+{
+  [self addChild: _rrule];
+}
+
+- (void) setRecurrenceRules: (NSArray *) _rrules
+{
+  [children removeObjectsInArray: [self childrenWithTag: @"rrule"]];
+  [self addChildren: _rrules];
+}
+
+- (BOOL) hasRecurrenceRules
+{
+  return ([[self childrenWithTag: @"rrule"] count] > 0);
+}
+
+- (NSArray *) recurrenceRules
+{
+  return [self childrenWithTag: @"rrule"];
+}
+
+- (void) removeAllExceptionRules
+{
+  [children removeObjectsInArray: [self childrenWithTag: @"exrule"]];
+}
+
+- (void) addToExceptionRules: (id) _rrule
+{
+  [self addChild: _rrule];
+}
+
+- (void) setExceptionRules: (NSArray *) _rrules
+{
+  [children removeObjectsInArray: [self childrenWithTag: @"exrule"]];
+  [self addChildren: _rrules];
+}
+
+- (BOOL) hasExceptionRules
+{
+  return ([[self childrenWithTag: @"exrule"] count] > 0);
+}
+
+- (NSArray *) exceptionRules
+{
+  return [self childrenWithTag: @"exrule"];
+}
+
+- (void) removeAllExceptionDates
+{
+  [children removeObjectsInArray: [self childrenWithTag: @"exdate"]];
+}
+
+- (void) addToExceptionDates: (id) _rdate
+{
+  [self addChild: _rdate];
+}
+
+- (void) setExceptionDates: (NSArray *) _rdates
+{
+  [children removeObjectsInArray: [self childrenWithTag: @"exdate"]];
+  [self addChildren: _rdates];
+}
+
+- (BOOL) hasExceptionDates
+{
+  return ([[self childrenWithTag: @"exdate"] count] > 0);
+}
+
+- (NSArray *) exceptionDates
+{
+  return [self childrenWithTag: @"exdate"];
+}
+
+/* Convenience */
+
+- (BOOL) isRecurrent
+{
+  return [self hasRecurrenceRules];
+}
+
+/* Matching */
+
+- (BOOL) isWithinCalendarDateRange: (NGCalendarDateRange *) _range
+    firstInstanceCalendarDateRange: (NGCalendarDateRange *) _fir
+{
+  NSArray *ranges;
+  
+  ranges = [self recurrenceRangesWithinCalendarDateRange:_range
+                 firstInstanceCalendarDateRange:_fir];
+  return [ranges count] > 0;
+}
+
+- (NSArray *) recurrenceRangesWithinCalendarDateRange: (NGCalendarDateRange *)_r
+                       firstInstanceCalendarDateRange: (NGCalendarDateRange *)_fir
+{
+  return [iCalRecurrenceCalculator recurrenceRangesWithinCalendarDateRange: _r
+                                   firstInstanceCalendarDateRange: _fir
+                                   recurrenceRules: [self recurrenceRules]
+                                   exceptionRules: [self exceptionRules]
+                                   exceptionDates: [self exceptionDates]];
+}
+
+
+/* this is the outmost bound possible, not necessarily the real last date */
+-    (NSCalendarDate *)
+lastPossibleRecurrenceStartDateUsingFirstInstanceCalendarDateRange: (NGCalendarDateRange *)_r
+{
+  NSCalendarDate *date;
+  NSEnumerator *rRules;
+  iCalRecurrenceRule *rule;
+  iCalRecurrenceCalculator *calc;
+  NSCalendarDate *rdate;
+
+  date  = nil;
+
+  rRules = [[self recurrenceRules] objectEnumerator];
+  rule = [rRules nextObject];
+  while (rule && ![rule isInfinite] & !date)
+    {
+      calc = [iCalRecurrenceCalculator
+               recurrenceCalculatorForRecurrenceRule: rule
+               withFirstInstanceCalendarDateRange: _r];
+      rdate = [[calc lastInstanceCalendarDateRange] startDate];
+      if (!date
+          || ([date compare: rdate] == NSOrderedAscending))
+        date = rdate;
+      else
+        rule = [rRules nextObject];
+    }
+
+  return date;
+}
+
+@end
diff --git a/SOPE/NGCards/iCalTimeZone.h b/SOPE/NGCards/iCalTimeZone.h
new file mode 100644 (file)
index 0000000..ba62118
--- /dev/null
@@ -0,0 +1,39 @@
+/* iCalTimeZone.h - this file is part of SOPE
+ *
+ * Copyright (C) 2006 Inverse groupe conseil
+ *
+ * Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This file 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; see the file COPYING.  If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef ICALTIMEZONE_H
+#define ICALTIMEZONE_H
+
+@class NSString;
+@class NSCalendarDate;
+
+#import "CardGroup.h"
+
+@interface iCalTimeZone : CardGroup
+
+- (NSString *) tzId;
+- (NSString *) dateTimeStringForDate: (NSCalendarDate *) date;
+- (NSCalendarDate *) dateForDateTimeString: (NSString *) string;
+
+@end
+
+#endif /* ICALTIMEZONE_H */
diff --git a/SOPE/NGCards/iCalTimeZone.m b/SOPE/NGCards/iCalTimeZone.m
new file mode 100644 (file)
index 0000000..51bd372
--- /dev/null
@@ -0,0 +1,132 @@
+/* iCalTimeZone.m - this file is part of SOPE
+ *
+ * Copyright (C) 2006 Inverse groupe conseil
+ *
+ * Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This file 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; see the file COPYING.  If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#import <Foundation/NSArray.h>
+#import <Foundation/NSString.h>
+#import <Foundation/NSCalendarDate.h>
+
+#import "NSCalendarDate+NGCards.h"
+#import "NSString+NGCards.h"
+#import "iCalTimeZonePeriod.h"
+
+#import "iCalTimeZone.h"
+
+@implementation iCalTimeZone
+
+- (Class) classForTag: (NSString *) classTag
+{
+  Class tagClass;
+
+  if ([classTag isEqualToString: @"STANDARD"]
+      || [classTag isEqualToString: @"DAYLIGHT"])
+    tagClass = [iCalTimeZonePeriod class];
+  else if ([classTag isEqualToString: @"TZID"])
+    tagClass = [CardElement class];
+  else
+    tagClass = [super classForTag: classTag];
+
+  return tagClass;
+}
+
+- (void) setTzId: (NSString *) tzId
+{
+  [[self uniqueChildWithTag: @"tzid"] setValue: 0 to: tzId];
+}
+
+- (NSString *) tzId
+{
+  return [[self uniqueChildWithTag: @"tzid"] value: 0];
+}
+
+- (NSCalendarDate *) _occurenceForPeriodNamed: (NSString *) pName
+                                      forDate: (NSCalendarDate *) aDate
+{
+  NSArray *periods;
+  iCalTimeZonePeriod *period;
+  NSCalendarDate *occurence;
+
+  periods = [self childrenWithTag: pName];
+  if ([periods count])
+    {
+      period = (iCalTimeZonePeriod *) [periods objectAtIndex: 0];
+      occurence = [period occurenceForDate: aDate];
+    }
+  else
+    occurence = nil;
+
+  return occurence;
+}
+
+- (iCalTimeZonePeriod *) periodForDate: (NSCalendarDate *) date
+{
+  NSCalendarDate *daylightOccurence, *standardOccurence;
+  iCalTimeZonePeriod *period;
+
+  /* FIXME, this could cause crashes when timezones are not properly
+     specified, but let's say it won't happen often... */
+
+  daylightOccurence = [self _occurenceForPeriodNamed: @"daylight"
+                            forDate: date];
+  standardOccurence = [self _occurenceForPeriodNamed: @"standard"
+                            forDate: date];
+  if ([date earlierDate: daylightOccurence] == date
+      || [date earlierDate: standardOccurence] == standardOccurence)
+    period = (iCalTimeZonePeriod *) [self uniqueChildWithTag: @"standard"];
+  else
+    period = (iCalTimeZonePeriod *) [self uniqueChildWithTag: @"daylight"];
+
+  NSLog (@"chosen period: '%@'", [period tag]);
+
+  return period;
+}
+
+- (NSString *) dateTimeStringForDate: (NSCalendarDate *) date
+{
+  NSCalendarDate *tmpDate;
+  NSTimeZone *utc;
+
+  utc = [NSTimeZone timeZoneWithName: @"GMT"];
+  tmpDate = [date copy];
+  [tmpDate autorelease];
+  [tmpDate setTimeZone: utc];
+  tmpDate
+    = [tmpDate addYear: 0 month: 0 day: 0
+               hour: 0 minute: 0
+               second: [[self periodForDate: date] secondsOffsetFromGMT]];
+
+  return [tmpDate iCalFormattedDateTimeString];
+}
+
+- (NSCalendarDate *) dateForDateTimeString: (NSString *) string
+{
+  NSCalendarDate *tmpDate;
+  iCalTimeZonePeriod *period;
+
+  tmpDate = [string asCalendarDate];
+  period = [self periodForDate: tmpDate];
+
+  return [tmpDate addYear: 0 month: 0 day: 0
+                  hour: 0 minute: 0
+                  second: -[period secondsOffsetFromGMT]];
+}
+
+@end
diff --git a/SOPE/NGCards/iCalTimeZonePeriod.h b/SOPE/NGCards/iCalTimeZonePeriod.h
new file mode 100644 (file)
index 0000000..d7d54f8
--- /dev/null
@@ -0,0 +1,35 @@
+/* iCalTimeZonePeriod.h - this file is part of SOPE
+ *
+ * Copyright (C) 2006 Inverse groupe conseil
+ *
+ * Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This file 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; see the file COPYING.  If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef ICALTIMEZONEPERIOD_H
+#define ICALTIMEZONEPERIOD_H
+
+#import "CardGroup.h"
+
+@interface iCalTimeZonePeriod : CardGroup
+
+- (NSCalendarDate *) occurenceForDate: (NSCalendarDate *) refDate;
+- (int) secondsOffsetFromGMT;
+
+@end
+
+#endif /* ICALTIMEZONEPERIOD_H */
diff --git a/SOPE/NGCards/iCalTimeZonePeriod.m b/SOPE/NGCards/iCalTimeZonePeriod.m
new file mode 100644 (file)
index 0000000..03d6cb4
--- /dev/null
@@ -0,0 +1,140 @@
+/* iCalTimeZonePeriod.m - this file is part of SOPE
+ *
+ * Copyright (C) 2006 Inverse groupe conseil
+ *
+ * Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This file 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; see the file COPYING.  If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#import <Foundation/NSCalendarDate.h>
+#import <Foundation/NSString.h>
+
+#import "iCalDateTime.h"
+#import "iCalRecurrenceRule.h"
+#import "CardGroup+iCal.h"
+
+#import "iCalTimeZonePeriod.h"
+
+@implementation iCalTimeZonePeriod
+
+- (Class) classForTag: (NSString *) classTag
+{
+  Class tagClass;
+
+  if ([classTag isEqualToString: @"RRULE"])
+    tagClass = [iCalRecurrenceRule class];
+  else if ([classTag isEqualToString: @"DTSTART"])
+    tagClass = [iCalDateTime class];
+  else if ([classTag isEqualToString: @"TZNAME"]
+           || [classTag isEqualToString: @"TZOFFSETFROM"]
+           || [classTag isEqualToString: @"TZOFFSETTO"])
+    tagClass = [CardElement class];
+  else
+    tagClass = [super classForTag: classTag];
+
+  return tagClass;
+}
+
+- (int) _secondsOfOffset: (NSString *) offsetName
+{
+  NSString *offsetTo;
+  BOOL negative;
+  NSRange cursor;
+  unsigned int length;
+  unsigned int seconds;
+
+  seconds = 0;
+
+  offsetTo = [[self uniqueChildWithTag: offsetName]
+               value: 0];
+  length = [offsetTo length];
+  negative = [offsetTo hasPrefix: @"-"];
+  if (negative)
+    {
+      length--;
+      cursor = NSMakeRange(1, 2);
+    }
+  else if ([offsetTo hasPrefix: @"+"])
+    {
+      length--;
+      cursor = NSMakeRange(1, 2);
+    }
+  else
+    cursor = NSMakeRange(0, 2);
+
+  seconds = 3600 * [[offsetTo substringWithRange: cursor] intValue];
+  cursor.location += 2;
+  seconds += 60 * [[offsetTo substringWithRange: cursor] intValue];
+  if (length == 6)
+    {
+      cursor.location += 2;
+      seconds += [[offsetTo substringWithRange: cursor] intValue];
+    }
+
+  return ((negative) ? -seconds : seconds);
+}
+
+- (unsigned int) dayOfWeekFromRruleDay: (iCalWeekDay) day
+{
+  unsigned int dayOfWeek;
+
+  dayOfWeek = 1;
+  while (day >> dayOfWeek)
+    dayOfWeek++;
+
+  return dayOfWeek;
+}
+
+- (NSCalendarDate *) occurenceForDate: (NSCalendarDate *) refDate;
+{
+  NSCalendarDate *tmpDate;
+  iCalRecurrenceRule *rrule;
+  NSString *byDay;
+  int dayOfWeek, dateDayOfWeek, offset, pos;
+
+  rrule = (iCalRecurrenceRule *) [self uniqueChildWithTag: @"rrule"];
+  byDay = [rrule namedValue: @"byday"];
+  dayOfWeek = [self dayOfWeekFromRruleDay: [rrule byDayMask]];
+  pos = [[byDay substringToIndex: 2] intValue];
+  if (!pos)
+    pos = 1;
+
+  tmpDate = [NSCalendarDate
+              dateWithYear: [refDate yearOfCommonEra]
+              month: [[rrule namedValue: @"bymonth"] intValue]
+              day: 1 hour: 0 minute: 0 second: 0
+              timeZone: [NSTimeZone timeZoneWithName: @"GMT"]];
+  tmpDate = [tmpDate addYear: 0 month: ((pos > 0) ? 0 : 1)
+                     day: 0 hour: 0 minute: 0
+                     second: -[self _secondsOfOffset: @"tzoffsetfrom"]];
+  dateDayOfWeek = [tmpDate dayOfWeek];
+  offset = (dayOfWeek - dateDayOfWeek);
+  if (pos > 0 && offset < 0)
+    offset += 7;
+  offset += (pos * 7);
+  tmpDate = [tmpDate addYear: 0 month: 0 day: offset
+                     hour: 0 minute: 0 second: 0];
+
+  return tmpDate;
+}
+
+- (int) secondsOffsetFromGMT
+{
+  return [self _secondsOfOffset: @"tzoffsetto"];
+}
+
+@end
diff --git a/SOPE/NGCards/iCalToDo.h b/SOPE/NGCards/iCalToDo.h
new file mode 100644 (file)
index 0000000..1ec3126
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+  Copyright (C) 2000-2005 SKYRIX Software AG
+
+  This file is part of SOPE.
+
+  SOPE is free software; you can redistribute it and/or modify it under
+  the terms of the GNU Lesser General Public License as published by the
+  Free Software Foundation; either version 2, or (at your option) any
+  later version.
+
+  SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+  License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with SOPE; see the file COPYING.  If not, write to the
+  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+  02111-1307, USA.
+*/
+
+#ifndef __NGCards_iCalToDo_H__
+#define __NGCards_iCalToDo_H__
+
+#import "iCalRepeatableEntityObject.h"
+
+/*
+  iCalToDo
+  
+  This class keeps the attributes of an iCalendar todo record (a task).
+*/
+
+@class NSCalendarDate;
+@class NSString;
+
+@interface iCalToDo : iCalRepeatableEntityObject
+
+- (void) setPercentComplete: (NSString *) _value;
+- (NSString *) percentComplete;
+
+- (void) setDue: (NSCalendarDate *) _date;
+- (NSCalendarDate *) due;
+
+- (void) setCompleted: (NSCalendarDate *) _date;
+- (NSCalendarDate *) completed;
+
+@end
+
+#endif /* __NGCards_iCalToDo_H__ */
diff --git a/SOPE/NGCards/iCalToDo.m b/SOPE/NGCards/iCalToDo.m
new file mode 100644 (file)
index 0000000..e5e2c0a
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+  Copyright (C) 2000-2005 SKYRIX Software AG
+
+  This file is part of SOPE.
+
+  SOPE is free software; you can redistribute it and/or modify it under
+  the terms of the GNU Lesser General Public License as published by the
+  Free Software Foundation; either version 2, or (at your option) any
+  later version.
+
+  SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+  License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with SOPE; see the file COPYING.  If not, write to the
+  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+  02111-1307, USA.
+*/
+
+#import <Foundation/NSString.h>
+
+#import "NSCalendarDate+NGCards.h"
+#import "CardGroup+iCal.h"
+
+#import "iCalDateTime.h"
+#import "iCalToDo.h"
+
+@implementation iCalToDo
+
+- (Class) classForTag: (NSString *) classTag
+{
+  Class tagClass;
+
+  if ([classTag isEqualToString: @"DUE"]
+      || [classTag isEqualToString: @"COMPLETED"])
+    tagClass = [iCalDateTime class];
+  else if ([classTag isEqualToString: @"PERCENT-COMPLETE"])
+    tagClass = [CardElement class];
+  else
+    tagClass = [super classForTag: classTag];
+
+  return tagClass;
+}
+
+/* accessors */
+
+- (void) setPercentComplete: (NSString *) _value
+{
+  [[self uniqueChildWithTag: @"percent-complete"] setValue: 0
+                                                  to: _value];
+}
+
+- (NSString *) percentComplete
+{
+  return [[self uniqueChildWithTag: @"percent-complete"] value: 0];
+}
+
+- (void) setDue: (NSCalendarDate *) _date
+{
+  [self setDate: _date
+        forDateTimeValue: @"due"];
+}
+
+- (NSCalendarDate *) due
+{
+  return [self dateForDateTimeValue: @"due"];
+//   return [[self uniqueChildWithTag: @"percent-complete"] asCalendarDate];
+}
+
+- (void) setCompleted: (NSCalendarDate *) _date
+{
+  [self setStatus: @"COMPLETED"];
+  [self setDate: _date forDateTimeValue: @"completed"];
+}
+
+- (NSCalendarDate *) completed
+{
+  return [self dateForDateTimeValue: @"completed"];
+}
+
+/* ical typing */
+
+- (NSString *) entityName
+{
+  return @"vtodo";
+}
+
+// /* descriptions */
+
+// - (NSString *)description {
+//   NSMutableString *ms;
+
+//   ms = [NSMutableString stringWithCapacity:128];
+//   [ms appendFormat:@"<0x%p[%@]:", self, NSStringFromClass([self class])];
+
+//   if (uid)       [ms appendFormat:@" uid=%@", uid];
+//   if (startDate) [ms appendFormat:@" start=%@", startDate];
+//   if (due)       [ms appendFormat:@" due=%@", due];
+//   if (priority)  [ms appendFormat:@" pri=%@", priority];
+
+//   if (completed) 
+//     [ms appendFormat:@" completed=%@", completed];
+//   if (percentComplete) 
+//     [ms appendFormat:@" complete=%@", percentComplete];
+//   if (accessClass) 
+//     [ms appendFormat:@" class=%@", accessClass];
+  
+//   if (summary)
+//     [ms appendFormat:@" summary=%@", summary];
+
+//   [ms appendString:@">"];
+//   return ms;
+// }
+
+@end /* iCalToDo */
diff --git a/SOPE/NGCards/iCalTrigger.h b/SOPE/NGCards/iCalTrigger.h
new file mode 100644 (file)
index 0000000..811b638
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+  Copyright (C) 2000-2005 SKYRIX Software AG
+
+  This file is part of SOPE.
+
+  SOPE is free software; you can redistribute it and/or modify it under
+  the terms of the GNU Lesser General Public License as published by the
+  Free Software Foundation; either version 2, or (at your option) any
+  later version.
+
+  SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+  License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with SOPE; see the file COPYING.  If not, write to the
+  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+  02111-1307, USA.
+*/
+
+#ifndef __NGCards_iCalTrigger_H__
+#define __NGCards_iCalTrigger_H__
+
+#import "CardElement.h"
+
+@interface iCalTrigger : CardElement
+
+- (void) setValue: (NSString *) aValue;
+- (NSString *) value;
+
+- (void) setValueType: (NSString *) aType;
+- (NSString *) valueType;
+
+@end
+
+#endif /* __NGCards_iCalTrigger_H__ */
diff --git a/SOPE/NGCards/iCalTrigger.m b/SOPE/NGCards/iCalTrigger.m
new file mode 100644 (file)
index 0000000..ac26958
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+  Copyright (C) 2000-2005 SKYRIX Software AG
+
+  This file is part of SOPE.
+
+  SOPE is free software; you can redistribute it and/or modify it under
+  the terms of the GNU Lesser General Public License as published by the
+  Free Software Foundation; either version 2, or (at your option) any
+  later version.
+
+  SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+  License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with SOPE; see the file COPYING.  If not, write to the
+  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+  02111-1307, USA.
+*/
+
+#include "iCalTrigger.h"
+#include "common.h"
+
+@implementation iCalTrigger
+
+/* accessors */
+
+- (void) setValue: (NSString *) _value
+{
+  [self setValue: 0 to: _value];
+}
+
+- (NSString *) value
+{
+  return [self value: 0];
+}
+
+- (void) setValueType: (NSString *) _value
+{
+  [self setValue: 0 ofAttribute: @"type" to: _value];
+}
+
+- (NSString *) valueType
+{
+  return [self value: 0 ofAttribute: @"type"];
+}
+
+@end /* iCalTrigger */
diff --git a/SOPE/NGCards/iCalWeeklyRecurrenceCalculator.m b/SOPE/NGCards/iCalWeeklyRecurrenceCalculator.m
new file mode 100644 (file)
index 0000000..1db42c8
--- /dev/null
@@ -0,0 +1,190 @@
+/*
+  Copyright (C) 2004-2005 SKYRIX Software AG
+  
+  This file is part of SOPE.
+  
+  SOPE is free software; you can redistribute it and/or modify it under
+  the terms of the GNU Lesser General Public License as published by the
+  Free Software Foundation; either version 2, or (at your option) any
+  later version.
+  
+  SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+  License for more details.
+  
+  You should have received a copy of the GNU Lesser General Public
+  License along with SOPE; see the file COPYING.  If not, write to the
+  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+  02111-1307, USA.
+*/
+
+#include "iCalRecurrenceCalculator.h"
+
+@interface iCalWeeklyRecurrenceCalculator : iCalRecurrenceCalculator
+@end
+
+#include <NGExtensions/NGCalendarDateRange.h>
+#include "iCalRecurrenceRule.h"
+#include "NSCalendarDate+ICal.h"
+#include "common.h"
+
+@interface iCalRecurrenceCalculator (PrivateAPI)
+
+- (NSCalendarDate *)lastInstanceStartDate;
+
+- (unsigned)offsetFromSundayForJulianNumber:(long)_jn;
+- (unsigned)offsetFromSundayForWeekDay:(iCalWeekDay)_weekDay;
+- (unsigned)offsetFromSundayForCurrentWeekStart;
+  
+- (iCalWeekDay)weekDayForJulianNumber:(long)_jn;
+
+@end
+
+/*
+   TODO: If BYDAY is specified, lastInstanceStartDate and recurrences will
+         differ significantly!
+*/
+@implementation iCalWeeklyRecurrenceCalculator
+
+- (NSArray *)recurrenceRangesWithinCalendarDateRange:(NGCalendarDateRange *)_r {
+  NSMutableArray *ranges;
+  NSCalendarDate *firStart;
+  long           i, jnFirst, jnStart, jnEnd, startEndCount;
+  unsigned       interval, byDayMask;
+
+  firStart = [self->firstRange startDate];
+  jnFirst  = [firStart julianNumber];
+  jnEnd    = [[_r endDate] julianNumber];
+  
+  if (jnFirst > jnEnd)
+    return nil;
+  
+  jnStart  = [[_r startDate] julianNumber];
+  interval = [self->rrule repeatInterval];
+  
+  /* if rule is bound, check the bounds */
+  if (![self->rrule isInfinite]) {
+    NSCalendarDate *until;
+    long           jnRuleLast;
+    
+    until = [self->rrule untilDate];
+    if (until) {
+      if ([until compare:[_r startDate]] == NSOrderedAscending)
+        return nil;
+      jnRuleLast = [until julianNumber];
+    }
+    else {
+      jnRuleLast = (interval * [self->rrule repeatCount] * 7)
+      + jnFirst;
+      if (jnRuleLast < jnStart)
+        return nil;
+    }
+    /* jnStart < jnRuleLast < jnEnd ? */
+    if (jnEnd > jnRuleLast)
+      jnEnd = jnRuleLast;
+  }
+  
+  startEndCount = (jnEnd - jnStart) + 1;
+  ranges        = [NSMutableArray arrayWithCapacity:startEndCount];
+  byDayMask     = [self->rrule byDayMask];
+  if (!byDayMask) {
+    for (i = 0 ; i < startEndCount; i++) {
+      long jnCurrent;
+      
+      jnCurrent = jnStart + i;
+      if (jnCurrent >= jnFirst) {
+        long jnDiff;
+        
+        jnDiff = jnCurrent - jnFirst; /* difference in days */
+        if ((jnDiff % (interval * 7)) == 0) {
+          NSCalendarDate      *start, *end;
+          NGCalendarDateRange *r;
+          
+          start = [NSCalendarDate dateForJulianNumber:jnCurrent];
+          [start setTimeZone:[firStart timeZone]];
+          start = [start hour:  [firStart hourOfDay]
+                         minute:[firStart minuteOfHour]
+                         second:[firStart secondOfMinute]];
+          end   = [start addTimeInterval:[self->firstRange duration]];
+          r     = [NGCalendarDateRange calendarDateRangeWithStartDate:start
+                                       endDate:end];
+          if ([_r containsDateRange:r])
+            [ranges addObject:r];
+        }
+      }
+    }
+  }
+  else {
+    long jnFirstWeekStart, weekStartOffset;
+
+    /* calculate jnFirst's week start - this depends on our setting of week
+       start */
+    weekStartOffset = [self offsetFromSundayForJulianNumber:jnFirst] -
+                      [self offsetFromSundayForCurrentWeekStart];
+
+    jnFirstWeekStart = jnFirst - weekStartOffset;
+
+    for (i = 0 ; i < startEndCount; i++) {
+      long jnCurrent;
+
+      jnCurrent = jnStart + i;
+      if (jnCurrent >= jnFirst) {
+        long jnDiff;
+        
+        /* we need to calculate a difference in weeks */
+        jnDiff = (jnCurrent - jnFirstWeekStart) % 7;
+        if ((jnDiff % interval) == 0) {
+          BOOL isRecurrence = NO;
+            
+          if (jnCurrent == jnFirst) {
+            isRecurrence = YES;
+          }
+          else {
+            iCalWeekDay weekDay;
+
+            weekDay = [self weekDayForJulianNumber:jnCurrent];
+            isRecurrence = (weekDay & [self->rrule byDayMask]) ? YES : NO;
+          }
+          if (isRecurrence) {
+            NSCalendarDate      *start, *end;
+            NGCalendarDateRange *r;
+                
+            start = [NSCalendarDate dateForJulianNumber:jnCurrent];
+            [start setTimeZone:[firStart timeZone]];
+            start = [start hour:  [firStart hourOfDay]
+                           minute:[firStart minuteOfHour]
+                           second:[firStart secondOfMinute]];
+            end   = [start addTimeInterval:[self->firstRange duration]];
+            r     = [NGCalendarDateRange calendarDateRangeWithStartDate:start
+                                         endDate:end];
+            if ([_r containsDateRange:r])
+              [ranges addObject:r];
+          }
+        }
+      }
+    }
+  }
+  return ranges;
+}
+
+- (NSCalendarDate *)lastInstanceStartDate {
+  if ([self->rrule repeatCount] > 0) {
+    long           jnFirst, jnRuleLast;
+    NSCalendarDate *firStart, *until;
+    
+    firStart   = [self->firstRange startDate];
+    jnFirst    = [firStart julianNumber];
+    jnRuleLast = ([self->rrule repeatInterval] *
+                  [self->rrule repeatCount] * 7) +
+                  jnFirst;
+    until      = [NSCalendarDate dateForJulianNumber:jnRuleLast];
+    until      = [until hour:  [firStart hourOfDay]
+                        minute:[firStart minuteOfHour]
+                        second:[firStart secondOfMinute]];
+    return until;
+  }
+  return [super lastInstanceStartDate];
+}
+
+@end /* iCalWeeklyRecurrenceCalculator */
diff --git a/SOPE/NGCards/iCalYearlyRecurrenceCalculator.m b/SOPE/NGCards/iCalYearlyRecurrenceCalculator.m
new file mode 100644 (file)
index 0000000..b940b26
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+  Copyright (C) 2004-2005 SKYRIX Software AG
+  
+  This file is part of SOPE.
+  
+  SOPE is free software; you can redistribute it and/or modify it under
+  the terms of the GNU Lesser General Public License as published by the
+  Free Software Foundation; either version 2, or (at your option) any
+  later version.
+  
+  SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+  License for more details.
+  
+  You should have received a copy of the GNU Lesser General Public
+  License along with SOPE; see the file COPYING.  If not, write to the
+  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+  02111-1307, USA.
+*/
+
+#include "iCalRecurrenceCalculator.h"
+
+@interface iCalYearlyRecurrenceCalculator : iCalRecurrenceCalculator
+@end
+
+#include <NGExtensions/NGCalendarDateRange.h>
+#include "iCalRecurrenceRule.h"
+#include "NSCalendarDate+ICal.h"
+#include "common.h"
+
+@interface iCalRecurrenceCalculator(PrivateAPI)
+- (NSCalendarDate *)lastInstanceStartDate;
+@end
+
+@implementation iCalYearlyRecurrenceCalculator
+
+- (NSArray *)recurrenceRangesWithinCalendarDateRange:(NGCalendarDateRange *)_r{
+  NSMutableArray *ranges;
+  NSCalendarDate *firStart, *rStart, *rEnd, *until;
+  unsigned       i, count, interval;
+  int            diff;
+  
+  firStart = [self->firstRange startDate];
+  rStart   = [_r startDate];
+  rEnd     = [_r endDate];
+  interval = [self->rrule repeatInterval];
+  until    = [self lastInstanceStartDate];
+  
+  if (until) {
+    if ([until compare:rStart] == NSOrderedAscending)
+      return nil;
+    if ([until compare:rEnd] == NSOrderedDescending)
+      rEnd = until;
+  }
+  
+  diff   = [firStart yearsBetweenDate:rStart];
+  if ((diff != 0) && [rStart compare:firStart] == NSOrderedAscending)
+    diff = -diff;
+
+  count  = [rStart yearsBetweenDate:rEnd] + 1;
+  ranges = [NSMutableArray arrayWithCapacity:count];
+  for (i = 0 ; i < count; i++) {
+    int test;
+
+    test = diff + i;
+    if ((test >= 0) && (test % interval) == 0) {
+      NSCalendarDate      *start, *end;
+      NGCalendarDateRange *r;
+      
+      start = [firStart dateByAddingYears:diff + i
+                        months:0
+                        days:0];
+      [start setTimeZone:[firStart timeZone]];
+      end   = [start addTimeInterval:[self->firstRange duration]];
+      r     = [NGCalendarDateRange calendarDateRangeWithStartDate:start
+                                   endDate:end];
+      if ([_r containsDateRange:r])
+        [ranges addObject:r];
+    }
+  }
+  return ranges;
+}
+
+- (NSCalendarDate *)lastInstanceStartDate {
+  if ([self->rrule repeatCount] > 0) {
+    NSCalendarDate *until;
+    unsigned       years, interval;
+    
+    interval = [self->rrule repeatInterval];
+    years    = [self->rrule repeatCount] * interval;
+    until    = [[self->firstRange startDate] dateByAddingYears:years
+                                             months:0
+                                             days:0];
+    return until;
+  }
+  return [super lastInstanceStartDate];
+}
+
+@end /* iCalYearlyRecurrenceCalculator */
diff --git a/SOPE/NGCards/samples/COPYING b/SOPE/NGCards/samples/COPYING
new file mode 100644 (file)
index 0000000..161a3d1
--- /dev/null
@@ -0,0 +1,482 @@
+                 GNU LIBRARY GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1991 Free Software Foundation, Inc.
+ 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the library GPL.  It is
+ numbered 2 because it goes with version 2 of the ordinary GPL.]
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Library General Public License, applies to some
+specially designated Free Software Foundation software, and to any
+other libraries whose authors decide to use it.  You can use it for
+your libraries, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if
+you distribute copies of the library, or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link a program with the library, you must provide
+complete object files to the recipients so that they can relink them
+with the library, after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  Our method of protecting your rights has two steps: (1) copyright
+the library, and (2) offer you this license which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  Also, for each distributor's protection, we want to make certain
+that everyone understands that there is no warranty for this free
+library.  If the library is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original
+version, so that any problems introduced by others will not reflect on
+the original authors' reputations.
+\f
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that companies distributing free
+software will individually obtain patent licenses, thus in effect
+transforming the program into proprietary software.  To prevent this,
+we have made it clear that any patent must be licensed for everyone's
+free use or not licensed at all.
+
+  Most GNU software, including some libraries, is covered by the ordinary
+GNU General Public License, which was designed for utility programs.  This
+license, the GNU Library General Public License, applies to certain
+designated libraries.  This license is quite different from the ordinary
+one; be sure to read it in full, and don't assume that anything in it is
+the same as in the ordinary license.
+
+  The reason we have a separate public license for some libraries is that
+they blur the distinction we usually make between modifying or adding to a
+program and simply using it.  Linking a program with a library, without
+changing the library, is in some sense simply using the library, and is
+analogous to running a utility program or application program.  However, in
+a textual and legal sense, the linked executable is a combined work, a
+derivative of the original library, and the ordinary General Public License
+treats it as such.
+
+  Because of this blurred distinction, using the ordinary General
+Public License for libraries did not effectively promote software
+sharing, because most developers did not use the libraries.  We
+concluded that weaker conditions might promote sharing better.
+
+  However, unrestricted linking of non-free programs would deprive the
+users of those programs of all benefit from the free status of the
+libraries themselves.  This Library General Public License is intended to
+permit developers of non-free programs to use free libraries, while
+preserving your freedom as a user of such programs to change the free
+libraries that are incorporated in them.  (We have not seen how to achieve
+this as regards changes in header files, but we have achieved it as regards
+changes in the actual functions of the Library.)  The hope is that this
+will lead to faster development of free libraries.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, while the latter only
+works together with the library.
+
+  Note that it is possible for a library to be covered by the ordinary
+General Public License rather than by this special one.
+\f
+                 GNU LIBRARY GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library which
+contains a notice placed by the copyright holder or other authorized
+party saying it may be distributed under the terms of this Library
+General Public License (also called "this License").  Each licensee is
+addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+  
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+\f
+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+\f
+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+\f
+  6. As an exception to the Sections above, you may also compile or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Accompany the work with a written offer, valid for at
+    least three years, to give the same user the materials
+    specified in Subsection 6a, above, for a charge no more
+    than the cost of performing this distribution.
+
+    c) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    d) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the source code distributed need not include anything that is normally
+distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+\f
+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+\f
+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded.  In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Library General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+\f
+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+                           NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+\f
+     Appendix: How to Apply These Terms to Your New Libraries
+
+  If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change.  You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+  To apply these terms, attach the following notices to the library.  It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the library's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library 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
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public
+    License along with this library; if not, write to the Free
+    Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+    MA 02111-1307, USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the
+  library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+  <signature of Ty Coon>, 1 April 1990
+  Ty Coon, President of Vice
+
+That's all there is to it!
diff --git a/SOPE/NGCards/samples/ChangeLog b/SOPE/NGCards/samples/ChangeLog
new file mode 100644 (file)
index 0000000..611bc48
--- /dev/null
@@ -0,0 +1,157 @@
+2005-09-19  Helge Hess  <helge.hess@opengroupware.org>
+
+       * ievalrrule.m: started tool to evaluate iCal rrules on the shell
+
+2005-08-16  Helge Hess  <helge.hess@opengroupware.org>
+
+       * link tools against OSX frameworks if configured so
+
+2005-05-06  Helge Hess  <helge.hess@opengroupware.org>
+
+       * renamed ical3.m to icalds.m, ical2.m to icalparsetest
+
+2005-04-25  Helge Hess  <helge.hess@opengroupware.org>
+
+       * properly include config.make
+
+       * fixed some gcc 4.0 warnings
+
+2004-09-26  Helge Hess  <helge.hess@opengroupware.org>
+
+       * GNUmakefile.preamble: fixed makefiles for SOPE inline compilation
+
+2004-08-29  Helge Hess  <helge.hess@opengroupware.org>
+
+       * added hack to install the tools in FHS locations - the executables
+         will be installed in FHS_INSTALL_ROOT if specified (eg make
+         FHS_INSTALL_ROOT=/usr/local)
+
+2004-08-17  Helge Hess  <helge.hess@opengroupware.org>
+
+       * subclassing.m: fixed a compiler warning
+
+2004-07-09  Helge Hess  <helge.hess@skyrix.com>
+
+       * bmlookup.m: major code cleanups, improved output
+
+2004-06-27  Helge Hess  <helge.hess@opengroupware.org>
+
+       * ldap2dsml.m: fixed a compile warning
+
+2004-06-21  Helge Hess  <helge.hess@opengroupware.org>
+
+       * common.h: fixed compatibility with gstep-base
+
+2004-06-17  Helge Hess  <helge.hess@opengroupware.org>
+
+       * added 'testurl' to test NSURL for the Cocoa Foundation bug wrt the
+         trailing suffix in -path
+
+2004-05-09  Helge Hess  <helge.hess@opengroupware.org>
+
+       * subclassing.m: added support for Apple runtime
+
+2004-04-12  Helge Hess  <helge.hess@opengroupware.org>
+
+       * ImapListTool, ImapTool.m, imapls.m: minor code cleanups
+
+2004-01-19  Helge Hess  <helge.hess@opengroupware.org>
+
+       * ldapls.m: minor fixes (moved NSAutoreleasePool to correct place)
+
+2004-01-11  Helge Hess  <helge.hess@opengroupware.org>
+
+       * minor cleanups to log messages
+
+2003-10-12  Helge Hess  <helge@opengroupware.org>
+
+       * EOQualTool.m: fix: returned value in a void method
+
+       * GNUmakefile (TOOL_NAME): do not compile subclassing.m on
+         MacOSX
+
+       * EncodingTool.m: fixed compilation on MacOSX
+
+2003-05-15  Helge Hess  <helge.hess@skyrix.com>
+
+       * added parserule for testing parsing of NGRule objects
+
+2003-05-14  Helge Hess  <helge.hess@skyrix.com>
+
+       * some gcc 3.3 warnings fixed
+
+2003-04-03  Helge Hess  <helge.hess@skyrix.com>
+
+       * GNUmakefile: added ldapchkpwd tool
+
+       * ldap2dsml.m: added autorelease-pool to main()
+
+2003-04-01  GNUstep User  <helge.hess@skyrix.com>
+
+       * ImapListTool.m: do not use NSFileIdentifier constant
+
+       * EncodingTool.m: removed some undeclared encodings when compiling for
+         GNUstep Base
+
+2003-02-12  Helge Hess  <helge.hess@skyrix.com>
+
+       * added ical2.m, ical3.m - examples for NGiCal
+
+2003-01-30  Helge Hess  <helge.hess@skyrix.com>
+       
+       * added httpu_notify, a small tool to send out HTTP-over-UDP 
+         NOTIFY notifications
+
+2003-01-08  Helge Hess  <helge.hess@skyrix.com>
+
+       * testsock.m: improved test code
+
+2003-01-07  Helge Hess  <helge.hess@skyrix.com>
+
+       * subclassing.m, ImapQuotaTool.m, imapquota.m: fixed compilation 
+         warnings
+         
+       * moved in testsock.m from NGStreams
+
+       * changes for improved compilation on MacOSX, replaced RETAIN macros
+         with methods
+
+Fri Dec 27 10:54:40 2002  Helge Hess  <helge.hess@skyrix.com>
+
+       * Mime2XmlTool.m: fixed a protocol warning
+
+Mon Dec 23 15:36:01 2002  Helge Hess  <helge.hess@skyrix.com>
+
+       * ImapListTool.m: pass correct filemanager as a parameter
+
+2002-12-08  Helge Hess  <helge.hess@skyrix.com>
+
+       * testdirenum.m: cleanups
+
+Tue Dec 17 15:07:40 2002    <jan@skyrix.com>
+
+       * imapquota.m: add quota test
+
+2002-11-21  Jan Reichmann <jr@skyrix.com>
+
+       * added Imap-Super class, improve imapls
+
+2002-11-21  Helge Hess  <helge.hess@skyrix.com>
+
+       * added ldapls, ldap2dsml as examples for NGLdap
+
+2002-11-15  Helge Hess  <helge.hess@skyrix.com>
+
+       * EOQualTool.m: added test for complex cast
+
+2002-10-29  Helge Hess  <helge.hess@skyrix.com>
+
+       * EOQualTool.m: added support for parsing SQL using the new EOSQLParser
+
+2002-10-25  Helge Hess  <helge.hess@skyrix.com>
+
+       * added eoqual tool for parsing EOQualifier's (useful for syntax 
+         checks)
+
+       * created ChangeLog
+
diff --git a/SOPE/NGCards/samples/GNUmakefile b/SOPE/NGCards/samples/GNUmakefile
new file mode 100644 (file)
index 0000000..b7a0389
--- /dev/null
@@ -0,0 +1,43 @@
+# GNUstep makefile
+
+-include ../../config.make
+include $(GNUSTEP_MAKEFILES)/common.make
+
+TOOL_NAME = vcardtest versittest icalparsetest icalds vcf2xml vcfparsetest ievalrrule
+
+ADDITIONAL_INCLUDE_DIRS += -I../NGCards
+
+vcardtest_OBJC_FILES = unittest.m vcardtest.m CardElement.m CardGroup.m CardVersitRenderer.m NSArray+NGCards.m NSDictionary+NGCards.m
+versittest_OBJC_FILES = unittest.m versittest.m CardElement.m CardGroup.m CardVersitRenderer.m NSArray+NGCards.m NSDictionary+NGCards.m NSString+NGCards.m
+icalparsetest_OBJC_FILES = icalparsetest.m
+icalds_OBJC_FILES        = icalds.m
+vcf2xml_OBJC_FILES       = vcf2xml.m
+vcfparsetest_OBJC_FILES  = vcfparsetest.m CardElement.m CardGroup.m CardVersitRenderer.m NSArray+NGCards.m NSDictionary+NGCards.m NSString+NGCards.m
+ievalrrule_OBJC_FILES    = ievalrrule.m
+
+-include GNUmakefile.preamble
+include $(GNUSTEP_MAKEFILES)/tool.make
+-include GNUmakefile.postamble
+-include fhs.make
+
+CardElement.m: ../NGCards/CardElement.m
+       ln -sf ../NGCards/CardElement.m ./
+
+CardGroup.m: ../NGCards/CardGroup.m
+       ln -sf ../NGCards/CardGroup.m ./
+
+CardVersitRenderer.m: ../NGCards/CardVersitRenderer.m
+       ln -sf ../NGCards/CardVersitRenderer.m ./
+
+NSArray+NGCards.m: ../NGCards/NSArray+NGCards.m
+       ln -sf ../NGCards/NSArray+NGCards.m ./
+
+NSDictionary+NGCards.m: ../NGCards/NSDictionary+NGCards.m
+       ln -sf ../NGCards/NSDictionary+NGCards.m ./
+
+NSString+NGCards.m: ../NGCards/NSString+NGCards.m
+       ln -sf ../NGCards/NSString+NGCards.m ./
+
+
+distclean clean::
+       -rm -f CardElement.m CardGroup.m CardVersitRenderer.m NSArray+NGCards.m NSDictionary+NGCards.m NSString+NGCards.m
diff --git a/SOPE/NGCards/samples/GNUmakefile.preamble b/SOPE/NGCards/samples/GNUmakefile.preamble
new file mode 100644 (file)
index 0000000..db95dc4
--- /dev/null
@@ -0,0 +1,64 @@
+# compilation flags
+
+SOPE_ROOT=../..
+SOPE_CORE="../../sope-core"
+SOPE_XML="../../sope-xml"
+
+ADDITIONAL_INCLUDE_DIRS += \
+       -I..                    \
+       -I$(SOPE_CORE)/NGStreams        \
+       -I$(SOPE_CORE)/NGExtensions     \
+
+
+# PCH
+
+icalparsetest_PCH_FILE = common.h
+icalds_PCH_FILE        = common.h
+vcf2xml_PCH_FILE       = common.h
+vcfparsetest_PCH_FILE  = common.h
+ievalrrule_PCH_FILE    = common.h
+
+
+# dependencies
+
+ifneq ($(frameworks),yes)
+icalparsetest_TOOL_LIBS += -lNGCards
+icalds_TOOL_LIBS        += -lNGCards
+vcfparsetest_TOOL_LIBS  += -lNGCards
+ievalrrule_TOOL_LIBS    += -lNGCards
+
+ADDITIONAL_TOOL_LIBS += \
+       -lNGStreams -lNGExtensions -lEOControl  \
+       -lDOM -lSaxObjC
+else
+icalparsetest_TOOL_LIBS += -framework NGCards
+icalds_TOOL_LIBS        += -framework NGCards
+vcfparsetest_TOOL_LIBS  += -framework NGCards
+ievalrrule_TOOL_LIBS    += -framework NGCards
+
+ADDITIONAL_TOOL_LIBS += \
+       -framework NGStreams -framework NGExtensions -framework EOControl \
+       -framework DOM -framework SaxObjC
+endif
+
+
+# library/framework search pathes
+
+DEP_DIRS = \
+       ../NGCards/ \
+       $(SOPE_ROOT)/sope-core/NGExtensions     \
+       $(SOPE_ROOT)/sope-core/NGStreams        \
+       $(SOPE_ROOT)/sope-core/EOControl        \
+       $(SOPE_ROOT)/sope-xml/DOM               \
+       $(SOPE_ROOT)/sope-xml/SaxObjC
+
+ifneq ($(frameworks),yes)
+ADDITIONAL_LIB_DIRS += \
+       $(foreach dir,$(DEP_DIRS),\
+         -L$(GNUSTEP_BUILD_DIR)/$(dir)/$(GNUSTEP_OBJ_DIR_NAME))
+else
+ADDITIONAL_LIB_DIRS += \
+       $(foreach dir,$(DEP_DIRS),-F$(GNUSTEP_BUILD_DIR)/$(dir))
+endif
+
+SYSTEM_LIB_DIR += -L/usr/local/lib -L/usr/lib
diff --git a/SOPE/NGCards/samples/README b/SOPE/NGCards/samples/README
new file mode 100644 (file)
index 0000000..6b22a35
--- /dev/null
@@ -0,0 +1,9 @@
+sope-ical/samples
+
+This directory contains sample programs for the sope-ical libraries.
+
+Tools
+=====
+
+ical2 - uses SaxObjectDecoder with NGiCal.xmap
+ical3 - uses iCalDataSource to run queries on iCal files
diff --git a/SOPE/NGCards/samples/common.h b/SOPE/NGCards/samples/common.h
new file mode 100644 (file)
index 0000000..98f27bd
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+  Copyright (C) 2000-2005 SKYRIX Software AG
+
+  This file is part of SOPE.
+
+  SOPE is free software; you can redistribute it and/or modify it under
+  the terms of the GNU Lesser General Public License as published by the
+  Free Software Foundation; either version 2, or (at your option) any
+  later version.
+
+  SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+  License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with SOPE; see the file COPYING.  If not, write to the
+  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+  02111-1307, USA.
+*/
+
+#import <Foundation/Foundation.h>
+
+#if LIB_FOUNDATION_LIBRARY
+#  include <Foundation/exceptions/GeneralExceptions.h>
+#else
+#  include <NGExtensions/NGObjectMacros.h>
+#  include <NGExtensions/NSString+Ext.h>
+#endif
+
+#include <NGExtensions/NGExtensions.h>
diff --git a/SOPE/NGCards/samples/fhs.make b/SOPE/NGCards/samples/fhs.make
new file mode 100644 (file)
index 0000000..27c85d1
--- /dev/null
@@ -0,0 +1,26 @@
+# postprocessing
+
+# FHS support (this is a hack and is going to be done by gstep-make!)
+
+ifneq ($(FHS_INSTALL_ROOT),)
+
+FHS_INCLUDE_DIR=$(FHS_INSTALL_ROOT)/include/
+FHS_LIB_DIR=$(FHS_INSTALL_ROOT)/lib/
+FHS_BIN_DIR=$(FHS_INSTALL_ROOT)/bin/
+
+fhs-bin-dirs ::
+       $(MKDIRS) $(FHS_BIN_DIR)
+
+NONFHS_BINDIR="$(GNUSTEP_TOOLS)/$(GNUSTEP_TARGET_LDIR)"
+
+move-tools-to-fhs :: fhs-bin-dirs
+       @echo "moving tools from $(NONFHS_BINDIR) to $(FHS_BIN_DIR) .."
+       for i in $(TOOL_NAME); do \
+         mv "$(NONFHS_BINDIR)/$${i}" $(FHS_BIN_DIR); \
+       done
+
+move-to-fhs :: move-tools-to-fhs
+
+after-install :: move-to-fhs
+
+endif
diff --git a/SOPE/NGCards/samples/icalds.m b/SOPE/NGCards/samples/icalds.m
new file mode 100644 (file)
index 0000000..1ae3106
--- /dev/null
@@ -0,0 +1,167 @@
+/*
+  Copyright (C) 2000-2005 SKYRIX Software AG
+
+  This file is part of SOPE.
+
+  SOPE is free software; you can redistribute it and/or modify it under
+  the terms of the GNU Lesser General Public License as published by the
+  Free Software Foundation; either version 2, or (at your option) any
+  later version.
+
+  SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+  License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with SOPE; see the file COPYING.  If not, write to the
+  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+  02111-1307, USA.
+*/
+
+#import <Foundation/Foundation.h>
+
+@class EOQualifier, NSString, EOSortOrdering;
+
+@interface iCal3Tool : NSObject
+{
+  EOQualifier *qualifier;
+  NSString    *entityName;
+  NSArray     *sortOrderings;
+}
+
+- (int)runWithArguments:(NSArray *)_args;
+
+@end
+
+#include <NGCards/iCalDataSource.h>
+#include <NGCards/iCalObject.h>
+#include <EOControl/EOQualifier.h>
+#include <EOControl/EOSortOrdering.h>
+#include "common.h"
+
+@implementation iCal3Tool
+
+- (id)init {
+  if ((self = [super init])) {
+    NSUserDefaults *ud;
+    id tmp;
+    
+    /* collect options */
+    ud = [NSUserDefaults standardUserDefaults];
+
+    self->entityName = [[ud stringForKey:@"entity"] copy];
+    
+    if ((tmp = [ud objectForKey:@"qualifier"]) != nil) {
+      self->qualifier = [[EOQualifier alloc] initWithPropertyList:tmp 
+                                            owner:nil];
+    }
+
+    if ((tmp = [ud objectForKey:@"sort"]) != nil) {
+      tmp = [[EOSortOrdering alloc] initWithPropertyList:tmp owner:nil];
+      if (tmp != nil) {
+       self->sortOrderings = [[NSArray alloc] initWithObjects:&tmp count:1];
+       [tmp release]; tmp = nil;
+      }
+    }
+  }
+  return self;
+}
+- (void)dealloc {
+  [self->sortOrderings release];
+  [self->qualifier     release];
+  [self->entityName    release];
+  [super dealloc];
+}
+
+/* run */
+
+- (void)printObject:(id)_object {
+  printf("object: %s\n", [[_object description] cString]);
+}
+
+- (int)runWithArguments:(NSArray *)_args {
+  NSEnumerator *args;
+  NSString     *arg;
+  
+  /* begin processing */
+  
+  args = [_args objectEnumerator];
+  [args nextObject]; // process name ...
+  
+  while ((arg = [args nextObject])) {
+    NSAutoreleasePool *pool2;
+    
+    if ([arg hasPrefix:@"-"]) { /* consume defaults */
+      [args nextObject];
+      continue;
+    }
+    
+    pool2 = [[NSAutoreleasePool alloc] init];
+    {    
+      iCalDataSource       *ds;
+      EOFetchSpecification *fspec;
+      NSArray    *objs;
+      iCalObject *obj;
+      
+      /* setup fetch specification */
+      
+      fspec = [[[EOFetchSpecification alloc] init] autorelease];
+      [fspec setEntityName:self->entityName];
+      [fspec setQualifier:self->qualifier];
+      
+      /* setup datasource */
+      
+      ds = [iCalDataSource alloc]; // keep gcc happy
+      ds = [[ds initWithPath:arg] autorelease];
+      [ds setFetchSpecification:fspec];
+
+      /* perform fetch */
+      
+      if ((objs = [ds fetchObjects]) == nil) {
+       /* fetch failed */
+       
+       NSLog(@"fetch on ical file failed: %@", arg);
+      }
+      else {
+       /* process results */
+       NSEnumerator *e;
+       
+       e = [objs objectEnumerator];
+       while ((obj = [e nextObject])) {
+         [self printObject:obj];
+       }
+      }
+    }
+    [pool2 release];
+  }
+  return 0;
+}
+
+@end /* iCal3Tool */
+
+int main(int argc, char **argv, char **env)  {
+  NSAutoreleasePool *pool;
+  iCal3Tool *tool;
+  int rc;
+
+  pool = [[NSAutoreleasePool alloc] init];
+#if LIB_FOUNDATION_LIBRARY  
+  [NSProcessInfo initializeWithArguments:argv count:argc environment:env];
+#endif
+  
+  if ((tool = [[iCal3Tool alloc] init])) {
+    NS_DURING
+      rc = [tool runWithArguments:[[NSProcessInfo processInfo] arguments]];
+    NS_HANDLER
+      abort();
+    NS_ENDHANDLER;
+    
+    [tool release];
+  }
+  else
+    rc = 1;
+  
+  [pool release];
+  return rc;
+}
diff --git a/SOPE/NGCards/samples/icalparsetest.m b/SOPE/NGCards/samples/icalparsetest.m
new file mode 100644 (file)
index 0000000..c00f660
--- /dev/null
@@ -0,0 +1,154 @@
+/*
+  Copyright (C) 2000-2005 SKYRIX Software AG
+
+  This file is part of SOPE.
+
+  SOPE is free software; you can redistribute it and/or modify it under
+  the terms of the GNU Lesser General Public License as published by the
+  Free Software Foundation; either version 2, or (at your option) any
+  later version.
+
+  SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+  License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with SOPE; see the file COPYING.  If not, write to the
+  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+  02111-1307, USA.
+*/
+
+#import <Foundation/Foundation.h>
+#include <SaxObjC/SaxObjC.h>
+
+@interface iCal2Tool : NSObject
+{
+  id<NSObject,SaxXMLReader> parser;
+  SaxObjectDecoder *sax;
+}
+
+- (int)runWithArguments:(NSArray *)_args;
+
+@end
+
+@implementation iCal2Tool
+
+- (id)init {
+  if ((self = [super init])) {
+    self->parser =
+      [[[SaxXMLReaderFactory standardXMLReaderFactory] 
+                            createXMLReaderForMimeType:@"text/calendar"]
+                             retain];
+    if (self->parser == nil) {
+      NSLog(@"%s: did not find a parser for text/calendar !",
+           __PRETTY_FUNCTION__);
+      [self release];
+      return nil;
+    }
+    
+    /* ensure that NGCards.xmap can be found ! (Library/SaxMappings) */
+    self->sax = [[SaxObjectDecoder alloc] initWithMappingNamed:@"NGCards"];
+    if (self->sax == nil) {
+      NSLog(@"could not create the iCal SAX handler !");
+      [self release];
+      return nil;
+    }
+    
+    [self->parser setContentHandler:self->sax];
+    [self->parser setErrorHandler:self->sax];
+  }
+  return self;
+}
+- (void)dealloc {
+  [self->sax    release];
+  [self->parser release];
+  [super dealloc];
+}
+
+/* parsing */
+
+- (id)parseFile:(NSString *)_path {
+  if ([_path length] == 0) return nil;
+  
+  _path = [@"file://" stringByAppendingString:_path];
+  
+  [self->parser parseFromSystemId:_path];
+  
+  return [self->sax rootObject];
+}
+
+- (void)printParsedObject:(id)_object {
+  NSLog(@"component: %@",       _object);
+#if 0
+  NSLog(@"  subcomponents: %@", [_object subComponents]);
+  
+  printf("%s", [[_object icalString] cString]);
+#endif
+}
+
+/* run */
+
+- (int)runWithArguments:(NSArray *)_args {
+  NSEnumerator      *args;
+  NSString          *arg;
+  
+  args = [_args objectEnumerator];
+  [args nextObject]; // process name ...
+
+  while ((arg = [args nextObject])) {
+    NSAutoreleasePool *pool2;
+
+    if ([arg hasPrefix:@"-"]) { /* consume defaults */
+      [args nextObject];
+      continue;
+    }
+    
+    pool2 = [[NSAutoreleasePool alloc] init];
+    {    
+      id component;
+      
+      NS_DURING
+       component = [self parseFile:arg];
+      NS_HANDLER
+       abort();
+      NS_ENDHANDLER;
+      
+      if (component == nil)
+       NSLog(@"could not parse file: '%@'", arg);
+      else
+       [self printParsedObject:component];
+    }
+    [pool2 release];
+  }
+  return 0;
+}
+
+@end /* iCal2Tool */
+
+int main(int argc, char **argv, char **env)  {
+  NSAutoreleasePool *pool;
+  iCal2Tool *tool;
+  int rc;
+
+#if LIB_FOUNDATION_LIBRARY  
+  [NSProcessInfo initializeWithArguments:argv count:argc environment:env];
+#endif
+  
+  pool = [[NSAutoreleasePool alloc] init];
+  
+  if ((tool = [[iCal2Tool alloc] init])) {
+    NS_DURING
+      rc = [tool runWithArguments:[[NSProcessInfo processInfo] arguments]];
+    NS_HANDLER
+      abort();
+    NS_ENDHANDLER;
+    
+    [tool release];
+  }
+  else
+    rc = 1;
+  
+  [pool release];
+  return rc;
+}
diff --git a/SOPE/NGCards/samples/ievalrrule.m b/SOPE/NGCards/samples/ievalrrule.m
new file mode 100644 (file)
index 0000000..38e8c86
--- /dev/null
@@ -0,0 +1,166 @@
+/*
+  Copyright (C) 2005 SKYRIX Software AG
+
+  This file is part of SOPE.
+
+  SOPE is free software; you can redistribute it and/or modify it under
+  the terms of the GNU Lesser General Public License as published by the
+  Free Software Foundation; either version 2, or (at your option) any
+  later version.
+
+  SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+  License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with SOPE; see the file COPYING.  If not, write to the
+  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+  02111-1307, USA.
+*/
+
+#include <NGCards/iCalRecurrenceRule.h>
+#include <NGCards/iCalRecurrenceCalculator.h>
+#include <NGExtensions/NGCalendarDateRange.h>
+#include "common.h"
+
+static NSCalendarDate *dateForString(NSString *_s) {
+  // copied from ogo-chkaptconflicts, maybe move to NGExtensions?
+  static NSCalendarDate *now = nil;
+  static NSCalendarDate *mon = nil;
+  
+  if (now == nil) now = [[NSCalendarDate date] retain];
+  if (mon == nil) mon = [[now mondayOfWeek] retain];
+  _s = [_s lowercaseString];
+  
+  if ([_s isEqualToString:@"now"])       return now;
+  if ([_s isEqualToString:@"tomorrow"])  return [now tomorrow];
+  if ([_s isEqualToString:@"yesterday"]) return [now yesterday];
+  
+  if ([_s hasPrefix:@"mon"]) return mon;
+  if ([_s hasPrefix:@"tue"]) return [mon dateByAddingYears:0 months:0 days:1];
+  if ([_s hasPrefix:@"wed"]) return [mon dateByAddingYears:0 months:0 days:2];
+  if ([_s hasPrefix:@"thu"]) return [mon dateByAddingYears:0 months:0 days:3];
+  if ([_s hasPrefix:@"fri"]) return [mon dateByAddingYears:0 months:0 days:4];
+  if ([_s hasPrefix:@"sat"]) return [mon dateByAddingYears:0 months:0 days:5];
+  if ([_s hasPrefix:@"sun"]) return [mon dateByAddingYears:0 months:0 days:6];
+  
+  switch ([_s length]) {
+  case 6:
+    return [NSCalendarDate dateWithString:_s calendarFormat:@"%Y%m"];
+  case 8:
+    return [NSCalendarDate dateWithString:_s calendarFormat:@"%Y%m%d"];
+  case 10:
+    return [NSCalendarDate dateWithString:_s calendarFormat:@"%Y%-m-%d"];
+  case 13:
+    return [NSCalendarDate dateWithString:_s calendarFormat:@"%Y%m%d %H%M"];
+  case 14:
+    return [NSCalendarDate dateWithString:_s calendarFormat:@"%Y%m%d %H:%M"];
+  case 16:
+    return [NSCalendarDate dateWithString:_s calendarFormat:@"%Y-%m-%d %H:%M"];
+  default:
+    return nil;
+  }
+}
+
+static int usage(NSArray *args) {
+  fprintf(stderr,
+         "usage: %s <rrule> <startdate> <enddate> <cycleend>\n"
+         "\n"
+         "sample:\n"
+         "  %s 'FREQ=MONTHLY;BYDAY=2TU' '20050901 14:00' '20050901 15:00' "
+         "20060921\n",
+         [[args objectAtIndex:0] cString],
+         [[args objectAtIndex:0] cString]);
+  return 1;
+}
+
+static void printInstances(NSArray *instances) {
+  unsigned i, count;
+  
+  if ((count = [instances count]) == 0) {
+    printf("no reccurrences in given range\n");
+    return;
+  }
+  
+  for (i = 0; i < count; i++) {
+    NGCalendarDateRange *instance;
+    NSString *s;
+    
+    instance = [instances objectAtIndex:i];
+
+    s = [[instance startDate] descriptionWithCalendarFormat:
+                               @"%a, %Y-%m-%d at %H:%M"];
+    printf("%s - ", [s cString]);
+
+    s = [[instance endDate] descriptionWithCalendarFormat:
+                             [[instance startDate] isDateOnSameDay:
+                                                     [instance endDate]]
+                           ? @"%H:%M"
+                           : @"%a, %Y-%m-%d at %H:%M"];
+    printf("%s\n", [s cString]);
+  }
+}
+
+static int runIt(NSArray *args) {
+  iCalRecurrenceCalculator *cpu;
+  iCalRecurrenceRule  *rrule;
+  NGCalendarDateRange *startRange, *calcRange;
+  NSCalendarDate      *from, *to, *cycleTo;
+  NSString            *pattern;
+  NSArray             *instances;
+  
+  if ([args count] < 5)
+    return usage(args);
+  
+  pattern = [args objectAtIndex:1];
+  from    = dateForString([args objectAtIndex:2]);
+  to      = dateForString([args objectAtIndex:3]);
+  cycleTo = dateForString([args objectAtIndex:4]);
+  
+  if (from == nil || to == nil || cycleTo == nil || ![pattern isNotEmpty])
+    return usage(args);
+  
+  startRange =
+    [NGCalendarDateRange calendarDateRangeWithStartDate:from endDate:to];
+  
+  calcRange =
+    [NGCalendarDateRange calendarDateRangeWithStartDate:from endDate:cycleTo];
+  
+  /* parse rrule */
+
+  if ((rrule = [[iCalRecurrenceRule alloc] initWithString:pattern]) == nil) {
+    usage(args);
+    fprintf(stderr, "error: could not parse reccurence rule: '%s'\n",
+           [pattern cString]);
+    return 2;
+  }
+  
+  NSLog(@"from: %@ to: %@, cycle %@", from, to, cycleTo);
+  NSLog(@"rrule: %@", rrule);
+  
+  /* calculate */
+  
+  cpu = [iCalRecurrenceCalculator 
+         recurrenceCalculatorForRecurrenceRule:rrule
+         withFirstInstanceCalendarDateRange:startRange];
+  
+  instances = [cpu recurrenceRangesWithinCalendarDateRange:calcRange];
+  printInstances(instances);
+  
+  return 0;
+}
+
+int main(int argc, char **argv, char **env)  {
+  NSAutoreleasePool *pool;
+  int rc;
+  
+  pool = [[NSAutoreleasePool alloc] init];
+#if LIB_FOUNDATION_LIBRARY  
+  [NSProcessInfo initializeWithArguments:argv count:argc environment:env];
+#endif
+  
+  rc = runIt([[NSProcessInfo processInfo] argumentsWithoutDefaults]);
+  [pool release];
+  return rc;
+}
diff --git a/SOPE/NGCards/samples/unittest.h b/SOPE/NGCards/samples/unittest.h
new file mode 100644 (file)
index 0000000..305e831
--- /dev/null
@@ -0,0 +1,88 @@
+/* unittest.h - this file is part of $PROJECT_NAME_HERE$
+ *
+ * Copyright (C) 2006 Inverse groupe conseil
+ *
+ * Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This file 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; see the file COPYING.  If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef UNITTEST_H
+#define UNITTEST_H
+
+#import <Foundation/NSObject.h>
+
+@interface unittest : NSObject
+
+- (void) run;
+
+@end
+
+#define testEqual(x,y) \
+        if ((x) != (y)) { \
+                  NSLog (@"%s: values not equal at line %d", \
+                         __PRETTY_FUNCTION__, __LINE__); \
+                  return NO; \
+        }
+
+#define testObjectsEqual(x,y) \
+        if (![(x) isEqual: (y)]) { \
+                  NSLog (@"%s: objects not equal at line %d", \
+                         __PRETTY_FUNCTION__, __LINE__); \
+                  return NO; \
+        }
+
+#define testStringsEqual(x,y) \
+        if (!([(x) isKindOfClass: [NSString class]] \
+            && [(y) isKindOfClass: [NSString class]] ) \
+            || ![(x) isEqualToString: (y)]) { \
+                  NSLog (@"%s: strings \"%@\" and \"%@\" not equal at" \
+                         @" line %d", __PRETTY_FUNCTION__, (x), (y), \
+                          __LINE__); \
+                  return NO; \
+        }
+
+#define testNotEqual(x,y) \
+        if ((x) == (y)) { \
+                  NSLog (@"%s: values equal at line %d", \
+                         __PRETTY_FUNCTION__, __LINE__); \
+                  return NO; \
+        }
+
+#define testObjectsNotEqual(x,y) \
+        if ([(x) isEqual: (y)]) { \
+                  NSLog (@"%s: objects not equal at line %d", \
+                         __PRETTY_FUNCTION__, __LINE__); \
+                  return NO; \
+        }
+
+#define testStringsNotEqual(x,y) \
+        if (!([(x) isKindOfClass: [NSString class]] \
+            && [(y) isKindOfClass: [NSString class]]) \
+            || [(x) isEqualToString: (y)]) { \
+                  NSLog (@"%s: strings not equal at line %d", \
+                         __PRETTY_FUNCTION__, __LINE__); \
+                  return NO; \
+        }
+
+@interface unittest (OptionalMethods)
+
+- (void) setUp;
+- (void) tearDown;
+
+@end
+
+#endif /* UNITTEST_H */
diff --git a/SOPE/NGCards/samples/unittest.m b/SOPE/NGCards/samples/unittest.m
new file mode 100644 (file)
index 0000000..34e1644
--- /dev/null
@@ -0,0 +1,98 @@
+/* unittest.m - this file is part of $PROJECT_NAME_HERE$
+ *
+ * Copyright (C) 2006 Inverse groupe conseil
+ *
+ * Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This file 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; see the file COPYING.  If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#import <Foundation/NSArray.h>
+#import <Foundation/NSString.h>
+
+#import "unittest.h"
+
+@implementation unittest
+
+- (NSArray *) _testMethods
+{
+  Class testClass;
+  MethodList *methodList;
+  NSMutableArray *methods;
+  NSString *methodName;
+  unsigned int count, max;
+
+  testClass = [self class];
+
+  methods = [NSMutableArray new];
+  [methods autorelease];
+
+  while (testClass)
+    {
+      methodList = testClass->methods;
+      max = methodList->method_count;
+
+      for (count = 0; count < max; count++)
+        {
+          methodName
+            = NSStringFromSelector (methodList->method_list[count].method_name);
+          if ([methodName hasPrefix: @"test"])
+            [methods addObject: methodName];
+        }
+
+      testClass = testClass->super_class;
+    }
+
+  return methods;
+}
+
+- (void) run
+{
+  NSEnumerator *methods;
+  NSString *methodName;
+  unsigned int count, successes, failures;
+  BOOL success;
+
+  methods = [[self _testMethods] objectEnumerator];
+  methodName = [methods nextObject];
+
+  count = 0;
+  successes = 0;
+  failures = 0;
+  NSLog (@"-- start: %@", NSStringFromClass([self class]));
+  while (methodName)
+    {
+      count++;
+      if ([self respondsToSelector: @selector (setUp)])
+        [self setUp];
+      success
+        = (BOOL) [self performSelector: NSSelectorFromString (methodName)];
+      NSLog (@"%@ '%@'",
+             ((success) ? @"PASSED" : @"FAILED"), methodName);
+      if ([self respondsToSelector: @selector (tearDown)])
+        [self tearDown];
+      if (success)
+        successes++;
+      else
+        failures++;
+      methodName = [methods nextObject];
+    }
+
+  NSLog (@"-- end: %d methods, %d successes, %d failures",
+         count, successes, failures);
+}
+
+@end
diff --git a/SOPE/NGCards/samples/vcardtest.m b/SOPE/NGCards/samples/vcardtest.m
new file mode 100644 (file)
index 0000000..dfeb557
--- /dev/null
@@ -0,0 +1,369 @@
+/* vcardtest.m - this file is part of $PROJECT_NAME_HERE$
+ *
+ * Copyright (C) 2006 Inverse groupe conseil
+ *
+ * Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This file 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; see the file COPYING.  If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#import <Foundation/Foundation.h>
+
+#import "CardElement.h"
+#import "CardGroup.h"
+
+#import "unittest.h"
+
+@interface subtest: unittest
+{
+  CardElement *element;
+}
+@end
+
+@implementation subtest: unittest
+
+- (void) setUp
+{
+  element = [CardElement new];
+}
+
+- (void) tearDown
+{
+  [element release];
+}
+
+- (BOOL) testSimpleElementWithTag_value
+{
+  CardElement *testElement;
+  NSArray *values;
+
+  testElement = [CardElement simpleElementWithTag: @"pouet"
+                             value: @"tutu"];
+  testStringsEqual([testElement tag], @"pouet");
+
+  values = [testElement values];
+  testNotEqual(values, nil);
+  testEqual([values count], 1);
+  testStringsEqual([values objectAtIndex: 0], @"tutu");
+
+  return YES;
+}
+
+- (BOOL) testSimpleElementWithTag_singleType_value
+{
+  CardElement *testElement;
+  NSArray *values, *keys;
+  NSDictionary *attrs;
+
+  testElement = [CardElement simpleElementWithTag: @"pouet"
+                             singleType: @"coucou"
+                             value: @"tutu"];
+  testStringsEqual([testElement tag], @"pouet");
+
+  values = [testElement values];
+  testNotEqual(values, nil);
+  testEqual([values count], 1);
+  testStringsEqual([values objectAtIndex: 0], @"tutu");
+
+  attrs = [testElement attributes];
+  keys = [attrs allKeys];
+  testEqual([keys count], 1);
+  testStringsEqual([[keys objectAtIndex: 0] uppercaseString], @"TYPE");
+  
+  return YES;
+}
+
+- (BOOL) testElementWithTag_attributes_values
+{
+  NSMutableDictionary *addedAttrs;
+  NSMutableArray *addedValues;
+  NSArray *rValues, *keys;
+  NSDictionary *rAttrs;
+  CardElement *testElement;
+
+  addedAttrs = [NSMutableDictionary new];
+  [addedAttrs setObject: [NSMutableArray arrayWithObjects:
+                                           @"value1", @"blabla", nil]
+              forKey: @"key1"];
+  [addedAttrs setObject: [NSMutableArray arrayWithObject: @"test2"]
+              forKey: @"key2"];
+  addedValues = [NSMutableArray new];
+  [addedValues addObject: @"value1"];
+  [addedValues addObject: @"pouetpouet2"];
+
+  testElement = [CardElement elementWithTag: @"pouet"
+                             attributes: addedAttrs
+                             values: addedValues];
+
+
+  testStringsEqual([testElement tag], @"pouet");
+  rValues = [testElement values];
+  rAttrs = [testElement attributes];
+  keys = [rAttrs allKeys];
+
+  testNotEqual(rValues, nil);
+  testNotEqual(rAttrs, nil);
+  testEqual([rValues count], 2);
+  testEqual([keys count], 2);
+  testNotEqual(addedValues, rValues);
+  testNotEqual(addedAttrs, rAttrs);
+
+  testStringsEqual([rValues objectAtIndex: 0], @"value1");
+  testStringsEqual([rValues objectAtIndex: 1], @"pouetpouet2");
+  testNotEqual([rAttrs objectForKey: @"key1"], nil);
+  testNotEqual([rAttrs objectForKey: @"key2"], nil);
+  testEqual([[rAttrs objectForKey: @"key1"] count], 2);
+  testEqual([[rAttrs objectForKey: @"key2"] count], 1);
+
+  testStringsEqual([[rAttrs objectForKey: @"key1"] objectAtIndex: 0],
+                   @"value1");
+  testStringsEqual([[rAttrs objectForKey: @"key1"] objectAtIndex: 1],
+                   @"blabla");
+  testStringsEqual([[rAttrs objectForKey: @"key2"] objectAtIndex: 0],
+                   @"test2");
+
+  return YES;
+}
+
+- (BOOL) testAddValue
+{
+  NSArray *values;
+
+  values = [element values];
+  testEqual([values count], 0);
+  [element addValue: @"test"];
+  testEqual([values count], 1);
+  testStringsEqual([values objectAtIndex: 0], @"test");
+  [element addValue: @"coucou"];
+  testEqual([values count], 2);
+  testStringsEqual([values objectAtIndex: 0], @"test");
+  testStringsEqual([values objectAtIndex: 1], @"coucou");
+
+  return YES;
+}
+
+- (BOOL) testAddValues
+{
+  NSArray *values;
+
+  values = [element values];
+  [element addValue: @"cuicui"];
+  testEqual([values count], 1);
+
+  [element addValues: [NSArray arrayWithObject: @"coucou"]];
+  testEqual([values count], 2);
+
+  [element addValue: @"cuicui2"];
+  testEqual([values count], 3);
+  testStringsEqual([values objectAtIndex: 1], @"coucou");
+  testStringsEqual([values objectAtIndex: 2], @"cuicui2");
+
+  return YES;
+}
+
+- (BOOL) testAddType
+{
+  NSMutableDictionary *attrs;
+  NSArray *types;
+
+  attrs = (NSMutableDictionary *) [element attributes];
+  testEqual([attrs objectForKey: @"TYPE"], nil);
+  [element addType: @"INTERNET"];
+  types = [attrs objectForKey: @"type"];
+  testNotEqual(types, nil);
+  testEqual([types count], 1);
+  testStringsEqual([types objectAtIndex: 0],
+                   @"INTERNET");
+
+  [element addType: @"pref"];
+  testEqual([types count], 2);
+  testStringsEqual([types objectAtIndex: 0],
+                   @"INTERNET");
+  testStringsEqual([types objectAtIndex: 1],
+                   @"pref");
+
+  return YES;
+}
+
+- (BOOL) testAddAttribute_value
+{
+  NSMutableDictionary *attrs;
+
+  attrs = (NSMutableDictionary *) [element attributes];
+  testEqual([attrs objectForKey: @"attr1"], nil);
+  [element addAttribute: @"attr1" value: @"value1"];
+  testNotEqual([attrs objectForKey: @"attr1"], nil);
+  testEqual([[attrs objectForKey: @"attr1"] count], 1);
+  testStringsEqual([[attrs objectForKey: @"attr1"] objectAtIndex: 0],
+                   @"value1");
+
+  [element addAttribute: @"attr1" value: @"value2"];
+  testEqual([[attrs objectForKey: @"attr1"] count], 2);
+  testStringsEqual([[attrs objectForKey: @"attr1"] objectAtIndex: 0],
+                   @"value1");
+  testStringsEqual([[attrs objectForKey: @"attr1"] objectAtIndex: 1],
+                   @"value2");
+
+  return YES;
+}
+
+- (BOOL) testAddAttributes
+{
+  NSMutableDictionary *attrs, *addedAttrs;
+  NSArray *keys;
+
+  attrs = (NSMutableDictionary *) [element attributes];
+  keys = [attrs allKeys];
+  testEqual([keys count], 0);
+
+  [attrs setObject: [NSMutableArray arrayWithObject: @"test1"]
+         forKey: @"key1"];
+  keys = [attrs allKeys];
+  testEqual([keys count], 1);
+
+  addedAttrs = [NSMutableDictionary new];
+  [addedAttrs setObject: [NSMutableArray arrayWithObject: @"test2"]
+              forKey: @"key2"];
+  [element addAttributes: addedAttrs];
+  [addedAttrs release];
+  keys = [attrs allKeys];
+  testEqual([keys count], 2);
+  testNotEqual([attrs objectForKey: @"key2"], nil);
+  testStringsEqual([[attrs objectForKey: @"key2"] objectAtIndex: 0],
+                   @"test2");
+
+  testNotEqual([attrs objectForKey: @"key1"], nil);
+  testStringsEqual([[attrs objectForKey: @"key1"] objectAtIndex: 0],
+                   @"test1");
+  testEqual([[attrs objectForKey: @"key1"] count], 1);
+  addedAttrs = [NSMutableDictionary new];
+  [addedAttrs setObject: [NSMutableArray arrayWithObject: @"test3"]
+              forKey: @"key1"];
+  [element addAttributes: addedAttrs];
+  [addedAttrs release];
+  testStringsEqual([[attrs objectForKey: @"key1"] objectAtIndex: 1],
+                   @"test3");
+
+  return YES;
+}
+
+- (BOOL) testSetValue_To
+{
+  NSArray *values;
+
+  values = [element values];
+  testEqual([values count], 0);
+
+  [element setValue: 2 to: @"coucou"];
+  testStringsEqual([values objectAtIndex: 0], @"");
+  testStringsEqual([values objectAtIndex: 1], @"");
+  testStringsEqual([values objectAtIndex: 2], @"coucou");
+
+  [element setValue: 0 to: @"cuicui"];
+  testStringsEqual([values objectAtIndex: 0], @"cuicui");
+  testStringsEqual([values objectAtIndex: 1], @"");
+  testStringsEqual([values objectAtIndex: 2], @"coucou");
+
+  return YES;
+}
+
+// BEGIN:VCALENDAR
+// CALSCALE:GREGORIAN
+// X-WR-TIMEZONE;VALUE=TEXT:Europe/Berlin
+// PRODID:-//Apple Computer\, Inc//iCal 1.0//EN
+// X-WR-CALNAME;VALUE=TEXT:shire-cal1
+// X-WR-RELCALID;VALUE=TEXT:C3CBA0E4-DBCC-11D6-A381-00039340AF4A
+// VERSION:2.0
+// BEGIN:VEVENT
+// DTSTAMP:20021008T155243Z
+// SUMMARY:work
+// UID:C3CB87C0-DBCC-11D6-A381-00039340AF4A
+// DTSTART;TZID=Europe/Berlin:20021009T120000
+// DURATION:PT3H45M
+// END:VEVENT
+// BEGIN:VEVENT
+// ATTENDEE;CN=Anja Berlin:mailto:anja.berlin@regiocom.net
+// ATTENDEE;CN=Marcus Müller:mailto:mm@codeon.de
+// DTSTAMP:20021009T211904Z
+// SUMMARY:trink
+// UID:C3CB8E9A-DBCC-11D6-A381-00039340AF4A
+// ORGANIZER;CN=Helge Heß:mailto:helge.hess@skyrix.com
+// DTSTART;TZID=Europe/Berlin:20021010T190000
+// DURATION:PT45M
+// BEGIN:VALARM
+// TRIGGER;VALUE=DURATION:-PT15M
+// ACTION:DISPLAY
+// DESCRIPTION:Event reminder
+// END:VALARM
+// BEGIN:VALARM
+// ATTACH;VALUE=URI:Ping
+// TRIGGER;VALUE=DURATION:-PT15M
+// ACTION:AUDIO
+// END:VALARM
+// END:VEVENT
+// BEGIN:VEVENT
+// DTSTAMP:20021008T155256Z
+// SUMMARY:Zahnarzt
+// DTEND;TZID=Europe/Berlin:20021009T110000
+// UID:C3CB92B0-DBCC-11D6-A381-00039340AF4A
+// DTSTART;TZID=Europe/Berlin:20021009T094500
+// END:VEVENT
+// BEGIN:VTODO
+// DTSTAMP:20021009T154221Z
+// SUMMARY:testjob
+// UID:C3CB96C7-DBCC-11D6-A381-00039340AF4A
+// DUE;TZID=Europe/Berlin:20021011T175228
+// PRIORITY:5
+// DTSTART;TZID=Europe/Berlin:20021008T175228
+// END:VTODO
+// BEGIN:VTODO
+// UID:C3CB9A9F-DBCC-11D6-A381-00039340AF4A
+// DTSTART;TZID=Europe/Berlin:20021010T000000
+// DTSTAMP:20021009T154205Z
+// SUMMARY:testjob2
+// END:VTODO
+// END:VCALENDAR
+
+@end
+
+int main (int argc, char **argv, char **env)
+{
+  NSAutoreleasePool *pool;
+  subtest *test;
+  int rc;
+
+  pool = [[NSAutoreleasePool alloc] init];
+#if LIB_FOUNDATION_LIBRARY  
+  [NSProcessInfo initializeWithArguments:argv count:argc environment:env];
+#endif
+  
+  test = [subtest new];
+  if (test) {
+    NS_DURING
+      [test run];
+    NS_HANDLER
+      abort();
+    NS_ENDHANDLER;
+    
+    [test release];
+  }
+  else
+    rc = 1;
+  
+  [pool release];
+
+  return rc;
+}
diff --git a/SOPE/NGCards/samples/vcf2xml.m b/SOPE/NGCards/samples/vcf2xml.m
new file mode 100644 (file)
index 0000000..3f56c2c
--- /dev/null
@@ -0,0 +1,342 @@
+/*
+  Copyright (C) 2005 Helge Hess
+
+  This file is part of SOPE.
+
+  SOPE is free software; you can redistribute it and/or modify it under
+  the terms of the GNU Lesser General Public License as published by the
+  Free Software Foundation; either version 2, or (at your option) any
+  later version.
+
+  SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+  License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with SOPE; see the file COPYING.  If not, write to the
+  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+  02111-1307, USA.
+*/
+
+// Note: this does not yet produce valid XML output
+
+#import <Foundation/NSObject.h>
+#include <SaxObjC/SaxObjC.h>
+
+@interface vcf2xml : NSObject
+{
+  id<NSObject,SaxXMLReader> parser;
+  id sax;
+}
+
+- (int)runWithArguments:(NSArray *)_args;
+
+@end
+
+#include "common.h"
+
+@interface MySAXHandler : SaxDefaultHandler
+{
+  id  locator;
+  int indent;
+
+  NSString *lastNS;
+}
+
+- (void)indent;
+
+@end
+
+@implementation vcf2xml
+
+- (id)init {
+  if ((self = [super init]) != nil) {
+    self->parser = [[[SaxXMLReaderFactory standardXMLReaderFactory] 
+                     createXMLReaderForMimeType:@"text/x-vcard"] retain];
+    if (parser == nil) {
+      fprintf(stderr, "Error: could not load a vCard SAX driver bundle!\n");
+      exit(2);
+    }
+    //NSLog(@"Using parser: %@", self->parser);
+    
+    self->sax = [[MySAXHandler alloc] init];
+    [parser setContentHandler:self->sax];
+    [parser setErrorHandler:self->sax];
+  }
+  return self;
+}
+
+- (void)dealloc {
+  [self->sax    release];
+  [self->parser release];
+  [super dealloc];
+}
+
+/* process files */
+
+- (void)processFile:(NSString *)_path {
+  [self->parser parseFromSystemId:_path];
+}
+
+/* error handling */
+
+- (NSException *)handleException:(NSException *)_exc onPath:(NSString *)_p {
+  fprintf(stderr, "Error: catched exception on path '%s': %s\n",
+         [_p cString], [[_exc description] cString]);
+  return nil;
+}
+
+/* main entry */
+
+- (int)runWithArguments:(NSArray *)_args {
+  NSEnumerator *args;
+  NSString     *arg;
+  
+  /* begin processing */
+  
+  args = [_args objectEnumerator];
+  [args nextObject]; // skip tool name ...
+  
+  while ((arg = [args nextObject]) != nil) {
+    NSAutoreleasePool *pool2;
+    
+    if ([arg hasPrefix:@"-"]) { /* consume defaults */
+      [args nextObject];
+      continue;
+    }
+    
+    pool2 = [[NSAutoreleasePool alloc] init];
+
+
+    if (![arg isAbsolutePath]) {
+      arg = [[[NSFileManager defaultManager] currentDirectoryPath]
+             stringByAppendingPathComponent:arg];
+    }
+    
+    NS_DURING
+      [self->parser parseFromSystemId:arg];
+    NS_HANDLER
+      [[self handleException:localException onPath:arg] raise];
+    NS_ENDHANDLER;
+    
+    [pool2 release];
+  }
+  return 0;
+}
+
+@end /* vcf2xml */
+
+
+@implementation MySAXHandler
+
+- (void)dealloc {
+  [self->lastNS  release];
+  [self->locator release];
+  [super dealloc];
+}
+
+/* output */
+
+- (void)indent {
+  int i;
+  
+  for (i = 0; i < (self->indent * 4); i++)
+    fputc(' ', stdout);
+}
+
+/* documents */
+
+- (void)setDocumentLocator:(id<NSObject,SaxLocator>)_loc {
+  [self->locator autorelease];
+  self->locator = [_loc retain];
+}
+
+- (void)startDocument {
+  //puts("start document ..");
+  //self->indent++;
+}
+- (void)endDocument {
+  //self->indent--;
+  //puts("end document.");
+}
+
+- (void)startPrefixMapping:(NSString *)_prefix uri:(NSString *)_uri {
+  [self indent];
+  //printf("ns-map: %s=%s\n", [_prefix cString], [_uri cString]);
+}
+- (void)endPrefixMapping:(NSString *)_prefix {
+  [self indent];
+  //printf("ns-unmap: %s\n", [_prefix cString]);
+}
+
+- (void)startElement:(NSString *)_localName
+  namespace:(NSString *)_ns
+  rawName:(NSString *)_rawName
+  attributes:(id<SaxAttributes>)_attrs
+{
+  int i, c;
+  [self indent];
+  printf("<%s", [_localName cString]);
+  
+  if ([_ns length] > 0) {
+    if ([_ns isEqualToString:self->lastNS])
+      ;
+    else {
+      printf(" xmlns='%s'", [_ns cString]);
+      ASSIGNCOPY(self->lastNS, _ns);
+    }
+  }
+  
+  for (i = 0, c = [_attrs count]; i < c; i++) {
+    NSString *type;
+    NSString *ans;
+
+    ans = [_attrs uriAtIndex:i];
+    
+    printf(" %s=\"%s\"",
+           [[_attrs nameAtIndex:i] cString],
+           [[_attrs valueAtIndex:i] cString]);
+    
+    if (![_ns isEqualToString:ans])
+      printf("(ns=%s)", [ans cString]);
+    
+    type = [_attrs typeAtIndex:i];
+    if (![type isEqualToString:@"CDATA"] && (type != nil))
+      printf("[%s]", [type cString]);
+  }
+  puts(">");
+  self->indent++;
+}
+- (void)endElement:(NSString *)_localName
+  namespace:(NSString *)_ns
+  rawName:(NSString *)_rawName
+{
+  self->indent--;
+  [self indent];
+  printf("</%s>\n", [_localName cString]);
+}
+
+- (void)characters:(unichar *)_chars length:(int)_len {
+  NSString *str;
+  id tmp;
+  unsigned i, len;
+
+  if (_len == 0) {
+    [self indent];
+    printf("\"\"\n");
+    return;
+  }
+  
+  for (i = 0; i < (unsigned)_len; i++) {
+    if (_chars[i] > 255) {
+      NSLog(@"detected large char: o%04o d%03i h%04X",
+            _chars[i], _chars[i], _chars[i]);
+    }
+  }
+  
+  str = [NSString stringWithCharacters:_chars length:_len];
+  len = [str length];
+  
+  tmp = [str componentsSeparatedByString:@"\n"];
+  str = [tmp componentsJoinedByString:@"\\n"];
+  tmp = [str componentsSeparatedByString:@"\r"];
+  str = [tmp componentsJoinedByString:@"\\r"];
+  
+  [self indent];
+  printf("\"%s\"\n", [str cString]);
+}
+- (void)ignorableWhitespace:(unichar *)_chars length:(int)_len {
+  NSString *data;
+  id tmp;
+
+  data = [NSString stringWithCharacters:_chars length:_len];
+  tmp  = [data componentsSeparatedByString:@"\n"];
+  data = [tmp componentsJoinedByString:@"\\n"];
+  tmp  = [data componentsSeparatedByString:@"\r"];
+  data = [tmp componentsJoinedByString:@"\\r"];
+  
+  [self indent];
+  printf("whitespace: \"%s\"\n", [data cString]);
+}
+
+- (void)processingInstruction:(NSString *)_pi data:(NSString *)_data {
+  [self indent];
+  printf("PI: '%s' '%s'\n", [_pi cString], [_data cString]);
+}
+
+#if 0
+- (xmlEntityPtr)getEntity:(NSString *)_name {
+  NSLog(@"get entity %@", _name);
+  return NULL;
+}
+- (xmlEntityPtr)getParameterEntity:(NSString *)_name {
+  NSLog(@"get para entity %@", _name);
+  return NULL;
+}
+#endif
+
+/* entities */
+
+- (id)resolveEntityWithPublicId:(NSString *)_pubId
+  systemId:(NSString *)_sysId
+{
+  [self indent];
+  printf("shall resolve entity with '%s' '%s'",
+         [_pubId cString], [_sysId cString]);
+  return nil;
+}
+
+/* errors */
+
+- (void)warning:(SaxParseException *)_exception {
+  NSLog(@"warning(%@:%i): %@",
+        [[_exception userInfo] objectForKey:@"publicId"],
+        [[[_exception userInfo] objectForKey:@"line"] intValue],
+        [_exception reason]);
+}
+
+- (void)error:(SaxParseException *)_exception {
+  NSLog(@"error(%@:%i): %@",
+        [[_exception userInfo] objectForKey:@"publicId"],
+        [[[_exception userInfo] objectForKey:@"line"] intValue],
+        [_exception reason]);
+}
+
+- (void)fatalError:(SaxParseException *)_exception {
+  NSLog(@"fatal error(%@:%i): %@",
+        [[_exception userInfo] objectForKey:@"publicId"],
+        [[[_exception userInfo] objectForKey:@"line"] intValue],
+        [_exception reason]);
+  [_exception raise];
+}
+
+@end /* MySAXHandler */
+
+
+
+int main(int argc, char **argv, char **env)  {
+  NSAutoreleasePool *pool;
+  vcf2xml *tool;
+  int rc;
+
+  pool = [[NSAutoreleasePool alloc] init];
+#if LIB_FOUNDATION_LIBRARY  
+  [NSProcessInfo initializeWithArguments:argv count:argc environment:env];
+#endif
+  
+  if ((tool = [[vcf2xml alloc] init])) {
+    NS_DURING
+      rc = [tool runWithArguments:[[NSProcessInfo processInfo] arguments]];
+    NS_HANDLER
+      abort();
+    NS_ENDHANDLER;
+    
+    [tool release];
+  }
+  else
+    rc = 1;
+  
+  [pool release];
+  return rc;
+}
diff --git a/SOPE/NGCards/samples/vcfparsetest.m b/SOPE/NGCards/samples/vcfparsetest.m
new file mode 100644 (file)
index 0000000..8b84605
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+  Copyright (C) 2005 Helge Hess
+
+  This file is part of SOPE.
+
+  SOPE is free software; you can redistribute it and/or modify it under
+  the terms of the GNU Lesser General Public License as published by the
+  Free Software Foundation; either version 2, or (at your option) any
+  later version.
+
+  SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+  License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with SOPE; see the file COPYING.  If not, write to the
+  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+  02111-1307, USA.
+*/
+
+#import <Foundation/Foundation.h>
+#import <NGCards/NGVCard.h>
+
+@interface vcsparsetest : NSObject
+{
+}
+
+- (int)runWithArguments:(NSArray *)_args;
+
+@end
+
+@implementation vcsparsetest
+
+- (id)init {
+  if ((self = [super init])) {
+  }
+  return self;
+}
+
+- (void)dealloc {
+  [super dealloc];
+}
+
+/* parsing */
+
+- (id)parseFile:(NSString *)_path {
+  if ([_path length] == 0) return nil;
+  return [NGVCard parseVCardsFromSource:[NSURL fileURLWithPath:_path]];
+}
+
+- (void)printParsedObject: (NSArray *)_cards
+{
+  NSEnumerator *cardObjects;
+  NGVCard *card;
+
+  cardObjects = [_cards objectEnumerator];
+  card = [cardObjects nextObject];
+  while (card)
+    {
+      NSLog (@"-------s\n%@\ne-------", [card versitString]);
+      card = [cardObjects nextObject];
+    }
+
+#if 0
+  NSLog(@"  subcomponents: %@", [_object subComponents]);
+  
+  printf("%s", [[_object icalString] cString]);
+#endif
+}
+
+/* run */
+
+- (int)runWithArguments:(NSArray *)_args {
+  NSEnumerator      *args;
+  NSString          *arg;
+  
+  args = [_args objectEnumerator];
+  [args nextObject]; // process name ...
+
+  while ((arg = [args nextObject])) {
+    NSAutoreleasePool *pool2;
+
+    if ([arg hasPrefix:@"-"]) { /* consume defaults */
+      [args nextObject];
+      continue;
+    }
+    
+    pool2 = [[NSAutoreleasePool alloc] init];
+    {    
+      NSArray *cards;
+      
+      NS_DURING
+       cards = [self parseFile:arg];
+      NS_HANDLER
+       abort();
+      NS_ENDHANDLER;
+      
+      if (!cards)
+       NSLog(@"could not parse file: '%@'", arg);
+      else
+       [self printParsedObject:cards];
+    }
+    [pool2 release];
+  }
+  return 0;
+}
+
+@end /* vcsparsetest */
+
+int main(int argc, char **argv, char **env)  {
+  NSAutoreleasePool *pool;
+  vcsparsetest *tool;
+  int rc;
+
+  pool = [[NSAutoreleasePool alloc] init];
+#if LIB_FOUNDATION_LIBRARY  
+  [NSProcessInfo initializeWithArguments:argv count:argc environment:env];
+#endif
+  
+  if ((tool = [[vcsparsetest alloc] init]) != nil) {
+    NS_DURING
+      rc = [tool runWithArguments:[[NSProcessInfo processInfo] arguments]];
+    NS_HANDLER
+      abort();
+    NS_ENDHANDLER;
+    
+    [tool release];
+  }
+  else
+    rc = 1;
+  
+  [pool release];
+  return rc;
+}
diff --git a/SOPE/NGCards/samples/versittest.m b/SOPE/NGCards/samples/versittest.m
new file mode 100644 (file)
index 0000000..ec314f5
--- /dev/null
@@ -0,0 +1,218 @@
+/* versittest.m - this file is part of $PROJECT_NAME_HERE$
+ *
+ * Copyright (C) 2006 Inverse groupe conseil
+ *
+ * Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This file 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; see the file COPYING.  If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#import <Foundation/Foundation.h>
+
+#import "CardElement.h"
+#import "CardGroup.h"
+#import "CardVersitRenderer.h"
+#import "NSString+NGCards.h"
+
+#import "unittest.h"
+
+@interface versittest : unittest
+
+@end
+
+@implementation versittest
+
+- (BOOL) testRendering
+{
+  NSLog (@"rendering>");
+  CardVersitRenderer *renderer;
+  CardGroup *g, *g2;
+  CardElement *e;
+
+  g = [CardGroup groupWithTag: @"VCALENDAR"];
+  e = [CardElement simpleElementWithTag: @"CALSCALE"
+                   value: @"GREGORIAN"];
+  [g addChild: e];
+  e = [CardElement simpleElementWithTag: @"X-WR-TIMEZONE"
+                   value: @"Europe/Berlin"];
+  [e addAttribute: @"VALUE" value: @"TEXT"];
+  [g addChild: e];
+  e = [CardElement simpleElementWithTag: @"PRODID"
+                   value: @"-//Apple Computer, Inc//iCal 1.0//EN"];
+  [g addChild: e];
+  e = [CardElement simpleElementWithTag: @"X-WR-CALNAME"
+                   value: @"shire-cal1"];
+  [e addAttribute: @"VALUE" value: @"TEXT"];
+  [e setGroup: @"item1"];
+  [g addChild: e];
+  e = [CardElement simpleElementWithTag: @"X-WR-RELCALID"
+                   value: @"C3CBA0E4-DBCC-11D6-A381-00039340AF4A"];
+  [e addAttribute: @"VALUE" value: @"TEXT"];
+  [g addChild: e];
+  e = [CardElement simpleElementWithTag: @"VERSION"
+                   value: @"2.0"];
+  [g addChild: e];
+
+  g2 = [CardGroup groupWithTag: @"VEVENT"];
+  e = [CardElement simpleElementWithTag: @"DTSTAMP"
+                   value: @"20021008T155243Z"];
+  [g2 addChild: e];
+  e = [CardElement simpleElementWithTag: @"SUMMARY"
+                   value: @"work"];
+  [g2 addChild: e];
+  e = [CardElement simpleElementWithTag: @"UID"
+                   value: @"C3CB87C0-DBCC-11D6-A381-00039340AF4A"];
+  [g2 addChild: e];
+  e = [CardElement simpleElementWithTag: @"DTSTART"
+                   value: @"20021009T120000"];
+  [e addAttribute: @"TZID" value: @"Europe/Berlin"];
+  [g2 addChild: e];
+  e = [CardElement simpleElementWithTag: @"DURATION"
+                   value: @"PT3H45M"];
+  [g2 addChild: e];
+  [g addChild: g2];
+
+  g2 = [CardGroup groupWithTag: @"VEVENT"];
+  e = [CardElement simpleElementWithTag: @"ATTENDEE"
+                   value: @"mailto:anja.berlin@regiocom.net"];
+  [e addAttribute: @"CN" value: @"Anja Berlin"];
+  [g2 addChild: e];
+  e = [CardElement simpleElementWithTag: @"ATTENDEE"
+                   value: @"mailto:mm@codeon.de"];
+  [e addAttribute: @"CN" value: @"Marcus Müller"];
+  [g2 addChild: e];
+  e = [CardElement simpleElementWithTag: @"DTSTAMP"
+                   value: @"20021009T211904Z"];
+  [g2 addChild: e];
+  e = [CardElement simpleElementWithTag: @"SUMMARY"
+                   value: @"trink"];
+  [g2 addChild: e];
+  e = [CardElement simpleElementWithTag: @"UID"
+                   value: @"C3CB8E9A-DBCC-11D6-A381-00039340AF4A"];
+  [g2 addChild: e];
+  e = [CardElement simpleElementWithTag: @"ORGANIZER"
+                   value: @"mailto:helge.hess@skyrix.com"];
+  [e addAttribute: @"CN" value: @"Helge Heß"];
+  [g2 addChild: e];
+  e = [CardElement simpleElementWithTag: @"DTSTART"
+                   value: @"20021010T190000"];
+  [e addAttribute: @"TZID" value: @"Europe/Berlin"];
+  [g2 addChild: e];
+  e = [CardElement simpleElementWithTag: @"DURATION"
+                   value: @"PT45M"];
+  [g2 addChild: e];
+  [g addChild: g2];
+
+  renderer = [CardVersitRenderer new];
+  NSLog (@"\n\n%@\n", [renderer render: g]);
+  [renderer release];
+
+  NSLog (@"<done");
+
+  return YES;
+}
+
+- (BOOL) testNSStringFoldedForVersitCards
+{
+  NSString *shortString, *longString, *foldedString;
+
+  shortString = @"PATATE";
+  testStringsEqual(shortString, [shortString foldedForVersitCards]);
+  
+  longString = @"blabla PATATE blabla PATATE blabla PATATE blabla PATATE blabla PATATE blabl"
+    @"a PATATE blabla PATATE blabla PATATE blabla PATATE blabla PATATE blabla PA"
+    @"TATE blabla PATATE blabla PATATE blabla PATATE blabla PATATE blabla PATATE"
+    @"blabla PATATE blabla PATATE blabla PATATE";
+
+  foldedString = @"blabla PATATE blabla PATATE blabla PATATE blabla PATATE blabla PATATE blabl\r\n"
+    @" a PATATE blabla PATATE blabla PATATE blabla PATATE blabla PATATE blabla PA\r\n"
+    @" TATE blabla PATATE blabla PATATE blabla PATATE blabla PATATE blabla PATATE\r\n"
+    @" blabla PATATE blabla PATATE blabla PATATE";
+
+  testStringsEqual(foldedString, [longString foldedForVersitCards]);
+
+  return YES;
+}
+
+- (BOOL) testNSStringAsCardValues
+{
+  NSString *values[5];
+  NSArray *array[5];
+  NSArray *testarray[5];
+  unsigned int i, count, max;
+
+// patate,pouetpouet
+  values[0] = @"patate,pouetpouet";
+// patate\,pouetpouet
+  values[1] = @"patate\\,pouetpouet";
+// patate\\,pouetpouet
+  values[2] = @"patate\\\\,pouetpouet";
+// patate\\\,pouetpouet
+  values[3] = @"patate\\\\\\,pouetpouet";
+// patate,pouetpouet
+  values[4] = @"patate, pouetpouet";
+
+  testarray[0] = [NSArray arrayWithObjects: @"patate", @"pouetpouet", nil];
+  testarray[1] = [NSArray arrayWithObjects: @"patate,pouetpouet", nil];
+// should give [ @"patate\\", @"pouetpouet" ] instead...:
+  testarray[2] = [NSArray arrayWithObjects: @"patate\\,pouetpouet", nil];
+  testarray[3] = [NSArray arrayWithObjects: @"patate\\\\,pouetpouet", nil];
+  testarray[4] = [NSArray arrayWithObjects: @"patate", @"pouetpouet", nil];
+
+  for (i = 0; i < (sizeof(values) / sizeof(NSString *)); i++)
+    {
+      array[i] = [values[i] asCardAttributeValues];
+      max = [testarray[i] count];
+      for (count = 0; count < max; count++)
+        testStringsEqual([testarray[i] objectAtIndex: count],
+                         [array[i] objectAtIndex: count]);
+    }
+
+  return YES;
+}
+
+@end
+
+int main (int argc, char **argv, char **env)
+{
+  NSAutoreleasePool *pool;
+  versittest *test;
+  int rc;
+
+  pool = [[NSAutoreleasePool alloc] init];
+#if LIB_FOUNDATION_LIBRARY  
+  [NSProcessInfo initializeWithArguments: argv
+                 count: argc
+                 environment: env];
+#endif
+  
+  test = [versittest new];
+  if (test) {
+    NS_DURING
+      [test run];
+    NS_HANDLER
+      abort();
+    NS_ENDHANDLER;
+    
+    [test release];
+  }
+  else
+    rc = 1;
+  
+  [pool release];
+
+  return rc;
+}
diff --git a/SOPE/NGCards/tests/NGiCalTests-Info.plist b/SOPE/NGCards/tests/NGiCalTests-Info.plist
new file mode 100644 (file)
index 0000000..643cb20
--- /dev/null
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+       <key>CFBundleDevelopmentRegion</key>
+       <string>English</string>
+       <key>CFBundleExecutable</key>
+       <string>NGiCalTests</string>
+       <key>CFBundleGetInfoString</key>
+       <string></string>
+       <key>CFBundleIdentifier</key>
+       <string>org.OpenGroupware.SOPE.ical.NGiCalTests</string>
+       <key>CFBundleInfoDictionaryVersion</key>
+       <string>6.0</string>
+       <key>CFBundlePackageType</key>
+       <string>FMWK</string>
+       <key>CFBundleShortVersionString</key>
+       <string></string>
+       <key>CFBundleSignature</key>
+       <string>????</string>
+       <key>CFBundleVersion</key>
+       <string>4.5</string>
+</dict>
+</plist>
diff --git a/SOPE/NGCards/tests/README b/SOPE/NGCards/tests/README
new file mode 100644 (file)
index 0000000..5541041
--- /dev/null
@@ -0,0 +1,8 @@
+This folder contains unit tests for the NGiCal project.
+
+It uses SEN:TE's OCUnit project which can be found at
+http://www.sente.ch/software/ocunit/
+
+I used OCUnitRoot v38, later versions might work as well.
+
+NOTE: This is currently provided in Xcode only.
\ No newline at end of file
diff --git a/SOPE/NGCards/tests/common.h b/SOPE/NGCards/tests/common.h
new file mode 100644 (file)
index 0000000..ae2f086
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+  Copyright (C) 2000-2005 SKYRIX Software AG
+
+  This file is part of SOPE.
+
+  SOPE is free software; you can redistribute it and/or modify it under
+  the terms of the GNU Lesser General Public License as published by the
+  Free Software Foundation; either version 2, or (at your option) any
+  later version.
+
+  SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+  License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with SOPE; see the file COPYING.  If not, write to the
+  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+  02111-1307, USA.
+*/
+
+#ifndef __NGiCalTests_common_H__
+#define __NGiCalTests_common_H__
+
+#import <Foundation/Foundation.h>
+#import <SenTestingKit/SenTestingKit.h>
+#include <NGExtensions/NGExtensions.h>
+#include <NGExtensions/NGCalendarDateRange.h>
+
+#endif /* __NGiCalTests_common_H__ */
diff --git a/SOPE/NGCards/tests/iCalRecurrenceCalculatorTests.m b/SOPE/NGCards/tests/iCalRecurrenceCalculatorTests.m
new file mode 100644 (file)
index 0000000..eb6719b
--- /dev/null
@@ -0,0 +1,191 @@
+/*
+  Copyright (C) 2000-2005 SKYRIX Software AG
+
+  This file is part of SOPE.
+
+  SOPE is free software; you can redistribute it and/or modify it under
+  the terms of the GNU Lesser General Public License as published by the
+  Free Software Foundation; either version 2, or (at your option) any
+  later version.
+
+  SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+  License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with SOPE; see the file COPYING.  If not, write to the
+  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+  02111-1307, USA.
+*/
+
+#include "common.h"
+
+@class NGCalendarDateRange;
+@class iCalRecurrenceRule;
+
+@interface iCalRecurrenceCalculatorTests : SenTestCase
+{
+  NSTimeZone          *gmt;
+  NGCalendarDateRange *fir;
+  NGCalendarDateRange *tr1;
+  NGCalendarDateRange *dr1;
+}
+
+- (iCalRecurrenceRule *)ruleWithICalString:(NSString *)_rule;
+
+@end
+
+#include "iCalRecurrenceRule.h"
+#include "iCalRecurrenceCalculator.h"
+
+@implementation iCalRecurrenceCalculatorTests
+
+/* Setup / Teardown */
+
+- (void)setUp {
+  NSCalendarDate *sd, *ed;
+
+  gmt = [[NSTimeZone timeZoneForSecondsFromGMT:0] retain];
+
+  sd = [NSCalendarDate dateWithYear:2005
+                       month:2
+                       day:6
+                       hour:12
+                       minute:0
+                       second:0
+                       timeZone:self->gmt];
+  ed = [NSCalendarDate dateWithYear:2005
+                       month:2
+                       day:6
+                       hour:15
+                       minute:30
+                       second:0
+                       timeZone:self->gmt];
+
+  self->fir = [[NGCalendarDateRange calendarDateRangeWithStartDate:sd
+                                    endDate:ed] retain];
+
+  sd = [NSCalendarDate dateWithYear:2005
+                       month:2
+                       day:11
+                       hour:0
+                       minute:0
+                       second:0
+                       timeZone:self->gmt];
+  ed = [NSCalendarDate dateWithYear:2005
+                       month:2
+                       day:13
+                       hour:23
+                       minute:59
+                       second:59
+                       timeZone:self->gmt];
+  
+  self->tr1 = [[NGCalendarDateRange calendarDateRangeWithStartDate:sd
+                                    endDate:ed] retain];
+  
+
+  sd = [NSCalendarDate dateWithYear:2005
+                       month:2
+                       day:6
+                       hour:0
+                       minute:0
+                       second:0
+                       timeZone:self->gmt];
+  ed = [NSCalendarDate dateWithYear:2005
+                       month:2
+                       day:13
+                       hour:23
+                       minute:59
+                       second:59
+                       timeZone:self->gmt];
+  
+  self->dr1 = [[NGCalendarDateRange calendarDateRangeWithStartDate:sd
+                                    endDate:ed] retain];
+}
+
+- (void)tearDown {
+  [self->gmt release];
+  [self->fir release];
+  [self->tr1 release];
+  [self->dr1 release];
+}
+
+/* Private Helper */
+
+- (iCalRecurrenceRule *)ruleWithICalString:(NSString *)_rule {
+  iCalRecurrenceRule *rule;
+  
+  rule = [[[iCalRecurrenceRule alloc] init] autorelease];
+  [rule setRrule:_rule];
+  return rule;
+}
+
+- (void)testUnboundDailyRecurrence {
+  iCalRecurrenceRule       *rule;
+  iCalRecurrenceCalculator *calc;
+  BOOL                     result;
+  
+  /* recurrence occurs within range, 02/14/2005 */
+  rule = [self ruleWithICalString:@"FREQ=DAILY;INTERVAL=2"];
+  calc = [iCalRecurrenceCalculator recurrenceCalculatorForRecurrenceRule:rule
+                                   withFirstInstanceCalendarDateRange:fir];
+  result = [calc doesRecurrWithinCalendarDateRange:self->tr1];
+  STAssertTrue(result, @"missed recurrence!");
+  
+  /* recurrence outside of range */
+  rule = [self ruleWithICalString:@"FREQ=DAILY;INTERVAL=4"];
+  calc = [iCalRecurrenceCalculator recurrenceCalculatorForRecurrenceRule:rule
+                                   withFirstInstanceCalendarDateRange:fir];
+  result = [calc doesRecurrWithinCalendarDateRange:self->tr1];
+  
+  STAssertFalse(result, @"recurrence unexpected!");
+}
+
+- (void)testBoundDailyRecurrence {
+  iCalRecurrenceRule       *rule;
+  iCalRecurrenceCalculator *calc;
+  BOOL                     result;
+  NSArray                  *ranges;
+
+  /* recurrence outside of range */
+  rule = [self ruleWithICalString:@"FREQ=DAILY;INTERVAL=2;COUNT=2"];
+  calc = [iCalRecurrenceCalculator recurrenceCalculatorForRecurrenceRule:rule
+                                   withFirstInstanceCalendarDateRange:fir];
+  result = [calc doesRecurrWithinCalendarDateRange:self->tr1];
+  STAssertFalse(result, @"recurrence!");
+  
+  /* recurrence within range */
+  rule = [self ruleWithICalString:@"FREQ=DAILY;INTERVAL=2;UNTIL=20050212T120000Z"];
+  calc = [iCalRecurrenceCalculator recurrenceCalculatorForRecurrenceRule:rule
+                                   withFirstInstanceCalendarDateRange:fir];
+  result = [calc doesRecurrWithinCalendarDateRange:self->tr1];
+  STAssertTrue(result, @"didn't spot expected recurrence!");
+  ranges = [calc recurrenceRangesWithinCalendarDateRange:self->tr1];
+  STAssertTrue([ranges count] == 1, @"didn't spot expected recurrence!");
+  ranges = [calc recurrenceRangesWithinCalendarDateRange:self->dr1];
+  STAssertTrue([ranges count] == 4, @"didn't spot expected recurrence!");
+}
+
+
+- (void)testBoundWeeklyRecurrence {
+  iCalRecurrenceRule       *rule;
+  iCalRecurrenceCalculator *calc;
+  BOOL                     result;
+  
+  /* recurrence outside of range */
+  rule = [self ruleWithICalString:@"FREQ=WEEKLY;INTERVAL=1;UNTIL=20050210T225959Z;BYDAY=WE;WKST=MO"];
+  calc = [iCalRecurrenceCalculator recurrenceCalculatorForRecurrenceRule:rule
+                                   withFirstInstanceCalendarDateRange:fir];
+  result = [calc doesRecurrWithinCalendarDateRange:self->tr1];
+  STAssertFalse(result, @"recurrence!");
+  
+  /* recurrence outside of range */
+  rule = [self ruleWithICalString:@"FREQ=WEEKLY;INTERVAL=1;COUNT=3;BYDAY=WE"];
+  calc = [iCalRecurrenceCalculator recurrenceCalculatorForRecurrenceRule:rule
+                                   withFirstInstanceCalendarDateRange:fir];
+  result = [calc doesRecurrWithinCalendarDateRange:self->tr1];
+  STAssertFalse(result, @"recurrence!");
+}
+
+@end
diff --git a/SOPE/NGCards/versitCardsSaxDriver/AUTHORS b/SOPE/NGCards/versitCardsSaxDriver/AUTHORS
new file mode 100644 (file)
index 0000000..5a4b225
--- /dev/null
@@ -0,0 +1,2 @@
+Max Berger <max@berger.name>
+Marcus Mueller <znek@mulle-kybernetik.com>
diff --git a/SOPE/NGCards/versitCardsSaxDriver/COPYING b/SOPE/NGCards/versitCardsSaxDriver/COPYING
new file mode 100644 (file)
index 0000000..1adca66
--- /dev/null
@@ -0,0 +1,437 @@
+                 GNU LIBRARY GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1991 Free Software Foundation, Inc.
+ 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the library GPL.  It is
+ numbered 2 because it goes with version 2 of the ordinary GPL.]
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Library General Public License, applies to some
+specially designated Free Software Foundation software, and to any
+other libraries whose authors decide to use it.  You can use it for
+your libraries, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if
+you distribute copies of the library, or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link a program with the library, you must provide
+complete object files to the recipients so that they can relink them
+with the library, after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  Our method of protecting your rights has two steps: (1) copyright
+the library, and (2) offer you this license which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  Also, for each distributor's protection, we want to make certain
+that everyone understands that there is no warranty for this free
+library.  If the library is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original
+version, so that any problems introduced by others will not reflect on
+the original authors' reputations.
+\f
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that companies distributing free
+software will individually obtain patent licenses, thus in effect
+transforming the program into proprietary software.  To prevent this,
+we have made it clear that any patent must be licensed for everyone's
+free use or not licensed at all.
+
+  Most GNU software, including some libraries, is covered by the ordinary
+GNU General Public License, which was designed for utility programs.  This
+license, the GNU Library General Public License, applies to certain
+designated libraries.  This license is quite different from the ordinary
+one; be sure to read it in full, and don't assume that anything in it is
+the same as in the ordinary license.
+
+  The reason we have a separate public license for some libraries is that
+they blur the distinction we usually make between modifying or adding to a
+program and simply using it.  Linking a program with a library, without
+changing the library, is in some sense simply using the library, and is
+analogous to running a utility program or application program.  However, in
+a textual and legal sense, the linked executable is a combined work, a
+derivative of the original library, and the ordinary General Public License
+treats it as such.
+
+  Because of this blurred distinction, using the ordinary General
+Public License for libraries did not effectively promote software
+sharing, because most developers did not use the libraries.  We
+concluded that weaker conditions might promote sharing better.
+
+  However, unrestricted linking of non-free programs would deprive the
+users of those programs of all benefit from the free status of the
+libraries themselves.  This Library General Public License is intended to
+permit developers of non-free programs to use free libraries, while
+preserving your freedom as a user of such programs to change the free
+libraries that are incorporated in them.  (We have not seen how to achieve
+this as regards changes in header files, but we have achieved it as regards
+changes in the actual functions of the Library.)  The hope is that this
+will lead to faster development of free libraries.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, while the latter only
+works together with the library.
+
+  Note that it is possible for a library to be covered by the ordinary
+General Public License rather than by this special one.
+\f
+                 GNU LIBRARY GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library which
+contains a notice placed by the copyright holder or other authorized
+party saying it may be distributed under the terms of this Library
+General Public License (also called "this License").  Each licensee is
+addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+  
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+\f
+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+\f
+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+\f
+  6. As an exception to the Sections above, you may also compile or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Accompany the work with a written offer, valid for at
+    least three years, to give the same user the materials
+    specified in Subsection 6a, above, for a charge no more
+    than the cost of performing this distribution.
+
+    c) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    d) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the source code distributed need not include anything that is normally
+distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+\f
+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+\f
+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded.  In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Library General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+\f
+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+                           NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
diff --git a/SOPE/NGCards/versitCardsSaxDriver/COPYRIGHT b/SOPE/NGCards/versitCardsSaxDriver/COPYRIGHT
new file mode 100644 (file)
index 0000000..2a5be29
--- /dev/null
@@ -0,0 +1,4 @@
+Copyright (C) 2003-2004 Max Berger <max@berger.name>
+Copyright (C) 2004 OpenGroupware.org
+
+Contact: info@opengroupware.org
diff --git a/SOPE/NGCards/versitCardsSaxDriver/ChangeLog b/SOPE/NGCards/versitCardsSaxDriver/ChangeLog
new file mode 100644 (file)
index 0000000..b264443
--- /dev/null
@@ -0,0 +1,295 @@
+2006-07-04  Helge Hess  <helge.hess@opengroupware.org>
+
+       * use %p for pointer formats, fixed gcc 4.1 warnings (v4.5.24)
+
+2006-04-21  Marcus Mueller  <znek@mulle-kybernetik.com>
+
+       * GNUmakefile: properly declare principal class (v4.5.23)
+
+2006-04-08  Marcus Mueller  <znek@mulle-kybernetik.com>
+
+       * VSSaxDriver.m: improved error reporting in case no data could be
+         retrieved from a URL (v4.5.22)
+       
+2005-12-05  Helge Hess  <helge.hess@skyrix.com>
+
+       * v4.5.21
+
+       * VSSaxDriver.m: added some debug logs, throw an error if a tagline
+         starts with a colon/semicolon (tagname missing)
+
+       * VSvCardSaxDriver.m: minor code cleanups
+
+2005-11-17  Helge Hess  <helge.hess@opengroupware.org>
+
+       * VSSaxDriver.m: renamed internal -error: method to -reportError: to
+         avoid a conflict with gstep-base (v4.5.20)
+
+2005-09-28  Helge Hess  <helge.hess@opengroupware.org>
+
+       * GNUmakefile.preamble: install bundle in proper SaxObjC framework
+         location (v4.5.19)
+
+2005-08-16  Helge Hess  <helge.hess@opengroupware.org>
+
+       * install into /Library/SaxDrivers-4.5 when compiling for frameworks
+         (v4.5.18)
+
+       * GNUmakefile.preamble: added support for OSX frameworks (v4.5.17)
+
+2005-06-02  Helge Hess  <helge.hess@skyrix.com>
+       
+       * VSSaxDriver.m: transparently decode property values if
+         ENCODING=QUOTED-PRINTABLE is set as an attribute (used by Outlook
+         vCards, needs testing against umlauts/charsets) (v4.5.16)
+
+2005-05-06  Helge Hess  <helge.hess@opengroupware.org>
+
+       * VSSaxDriver.m: more reorganisations, added support for groupings
+         (v4.5.15)
+
+2005-05-05  Helge Hess  <helge.hess@opengroupware.org>
+
+       * VSSaxDriver.m: code cleanups / reorgs, properly embed reported
+         contents in <vCardSet> tag, added support for vCards in Unicode
+         16-bit encoding (v4.5.14)
+       
+       * VSSaxDriver.m: improved parsing entry methods, added support for SAX
+         error handlers (v4.5.13)
+
+2005-04-25  Helge Hess  <helge.hess@opengroupware.org>
+
+       * VSSaxDriver.m: fixed a gcc 4.0 warning (v4.5.12)
+
+2004-12-14  Marcus Mueller  <znek@mulle-kybernetik.com>
+
+       * versitSaxDriver.xcode: minor changes and updated
+
+2004-10-20  Marcus Mueller  <znek@mulle-kybernetik.com>
+
+       * VSSaxDriver.m: fixed edge case problem introduced in v4.3.10
+         (v4.3.11)
+
+       * VSSaxDriver.m: remove surrounding double quotes from attribute values
+         if any. During parsing, check if end tags match expectations and
+         issue warnings if they don't. Added some logic to get parsing
+         straight nevertheless in such events. (v4.3.10)
+
+2004-10-19  Marcus Mueller  <znek@mulle-kybernetik.com>
+
+       * v4.3.9
+
+       * VSSaxDriver.m: improved robustness in respect to illegal content
+         lines - the parser shouldn't crash anymore. Also, changed the
+         "OGoDebugVersitSaxDriver" default to "VSSaxDriverDebugEnabled".
+
+       * README: documented the default
+
+2004-10-18  Marcus Mueller  <znek@mulle-kybernetik.com>
+
+       * v4.3.8
+
+       * VSSaxDriver.m: added support in _parseString: for Unix style
+         terminated content lines. Such content lines clearly violate the
+         RFC but such ical files happen to appear in the wildlife.
+
+       * README: updated
+
+       * v4.3.7
+
+       * Version: removed major and minor. This effectively bumps the version
+         to v4.3.7 which is in sync with the rest of sope-ical.
+
+2004-10-17  Helge Hess  <helge.hess@opengroupware.org>
+
+       * added fhs.make, some other minor fixes to makefiles (v1.0.6)
+
+2004-10-16  Marcus Mueller  <znek@mulle-kybernetik.com>
+
+       * v1.0.5
+
+       * VSSaxDriver.m: rewrote _parseLine: to properly parse content lines
+         according to RFC2445.
+
+       * README: updated
+
+       * v1.0.4
+
+       * VSStringFormatter.m: properly unescape '\N'
+
+       * README: updated, need to fix _parseLine:
+
+       * v1.0.3
+
+       * VSStringFormatter.[hm]: new singleton to perform unescaping on
+         iCal content/attributes.
+
+       * VSSaxDriver.m: uses the new VSStringFormatter in some funky places.
+         Unescaping is pretty memory efficient, hence its slight overuse is
+         justifyable I guess.
+
+2004-10-15  Marcus Mueller  <znek@mulle-kybernetik.com>
+
+       * GNUmakefile.preamble: NGExtensions was missing for proper inline
+         compilation to work (v1.0.2)
+
+       * v1.0.1
+
+       * README: corrected the examples
+
+       * GNUmakefile.preamble: properly add paths
+
+2004-10-14  Marcus Mueller  <znek@mulle-kybernetik.com>
+
+       * v1.0.0
+
+       * README, COPYRIGHT, COPYING, AUTHORS: new files
+
+       * VersitSaxDriver.[hm]: renamed to VSSaxDriver.[hm]
+
+       * ICalendarSaxDriver.[hm]: renamed to VSiCalSaxDriver.[hm]
+
+       * VCardSaxDriver.[hm]: renamed to VSvCardSaxDriver.[hm]
+
+       * ICalendarSaxDriver.[hm]: renamed to VSiCalSaxDriver.[hm]
+
+       * VSSaxDriver.m: Major cleanups, cache all character sets as class
+         variables, rewrote _parseString: to properly do unfolding. Bugfixes.
+
+       * VSiCalSaxDriver.m, VSvCardSaxDriver.m: cache character sets, cleanup,
+         minor fixes.
+
+       * bundle-info.plist: adjusted names
+
+2004-04-09  Max Berger  <max@berger.name>
+
+       * fixed another bug related to libFoundation that applied to
+         NSMutableCharacterSet (v0.1.18)
+
+2004-02-24  Max Berger  <max@berger.name>
+
+       * fixed bug to crash on libFoundation in scanner line (v0.1.17)
+
+2004-02-25  Helge Hess  <helge.hess@skyrix.com>
+
+       * v0.1.16
+
+       * GNUmakefile.preamble: properly link with OGo gstep-make
+
+       * added a common.h file (and use that in the source files)
+
+       * VersitSaxDriver.m: fixed type, fixed a "==" vs "=" bug in -init,
+         minor code cleanups
+
+2004-02-24  Max Berger  <max@berger.name>
+
+       * v0.1.15
+
+       * added new initialize function to check for debug property
+
+       * added debug messages to parseFromSource
+
+       * updated Copyright in VersitSaxDriver.m
+  
+2003-12-13  Max Berger  <max@berger.name>
+
+       * replaces commas by spaces in attrs (as in spec) (v0.1.15)
+
+2003-12-13  Max Berger  <max@berger.name>
+
+       * v0.1.14
+
+       * added support for apple item1.adr elements
+
+       * added support for multiple attributes of same type
+
+
+2003-12-12  Max Berger  <max@berger.name>
+
+       * added support for subitems, added subitems for ical and vcard (v0.1.13)
+
+2003-12-12  Max Berger  <max@berger.name>
+
+       * added mappings into vcard driver (v0.1.12)
+
+2003-12-11  Max Berger  <max@berger.name>
+
+       * collapsed drivers for vcard 21 and 30 (v0.1.11)
+
+2003-12-11  Max Berger  <max@berger.name>
+
+       * fixed range check (v0.1.10)
+
+2003-12-11  Max Berger  <max@berger.name>
+
+       * fixed another missing () in malloc (v0.1.9)
+
+2003-12-11  Max Berger  <max@berger.name>
+
+       * fixed a missing () in malloc (v0.1.8)
+
+2003-12-11  Helge Hess  <helge.hess@opengroupware.org>
+
+       * v0.1.7
+
+       * minor tweaks to sourcecode style ;-)
+
+       * created GNUmakefile's
+
+2003-11-23  Max Berger  <max@berger.name>
+
+       * v0.1.6
+
+       * added GNUmakefile for unix
+
+       * fixed bundle-info.plist
+
+       * fixed a bug that caused libFondation to crash
+
+2003-11-23  Max Berger  <max@berger.name>
+
+       * v0.1.5
+
+       * Implemented attribute mapping
+
+       * Added list of attributes for xcal
+
+2003-11-23  Max Berger  <max@berger.name>
+
+       * v0.1.4
+
+       * implemented handling via list and stack
+        
+       * added support for name mapping and mapping to attributes
+
+       * added complete iCalendar Name mapping
+
+2003-11-23  Max Berger  <max@berger.name>
+
+       * v0.1.3
+
+       * VersitSaxDriver.m: Split up in lines works correctly
+
+       * VersitSaxDriver: added support for contentHandler
+
+       * VersitSaxDriver: added support for namespace
+
+       * VersitSaxDriver: now has begin/end document
+
+       * ICalendarSaxDriver: now set correct namespace
+
+       * VersitSaxDriver: now parses Tags, attibutes and values
+
+2003-11-23  Max Berger  <max@berger.name>
+
+       * v0.1.2
+
+       * fixed Copyright notice
+
+       * added Data Retrieval
+        
+       * added unfold method (doesnt do anything yet)
+
+2003-11-23  Max Berger  <max@berger.name>
+
+       * Initial Version (v0.1.1)
diff --git a/SOPE/NGCards/versitCardsSaxDriver/GNUmakefile b/SOPE/NGCards/versitCardsSaxDriver/GNUmakefile
new file mode 100644 (file)
index 0000000..82c2d31
--- /dev/null
@@ -0,0 +1,25 @@
+# GNUstep makefile
+
+-include ../../../config.make
+include $(GNUSTEP_MAKEFILES)/common.make
+-include ./Version
+
+BUNDLE_NAME        = versitCardsSaxDriver
+BUNDLE_EXTENSION   = .sax
+BUNDLE_INSTALL_DIR = $(GNUSTEP_INSTALLATION_DIR)/Library/SaxDrivers-$(MAJOR_VERSION).$(MINOR_VERSION)/
+
+versitCardsSaxDriver_PRINCIPAL_CLASS = VSSaxDriver
+
+versitCardsSaxDriver_PCH_FILE = common.h
+
+versitCardsSaxDriver_OBJC_FILES =      \
+       VSSaxDriver.m           \
+       VSCardSaxDriver.m       \
+       VSStringFormatter.m     \
+
+versitCardsSaxDriver_RESOURCE_FILES = bundle-info.plist
+
+-include GNUmakefile.preamble
+include $(GNUSTEP_MAKEFILES)/bundle.make
+-include GNUmakefile.postamble
+include fhs.make
diff --git a/SOPE/NGCards/versitCardsSaxDriver/GNUmakefile.postamble b/SOPE/NGCards/versitCardsSaxDriver/GNUmakefile.postamble
new file mode 100644 (file)
index 0000000..e36f8d6
--- /dev/null
@@ -0,0 +1,5 @@
+# $Id: GNUmakefile.postamble 257 2004-10-14 21:01:23Z znek $
+
+after-all ::
+       @(cp bundle-info.plist \
+         $(GNUSTEP_BUILD_DIR)/$(BUNDLE_NAME)$(BUNDLE_EXTENSION))
diff --git a/SOPE/NGCards/versitCardsSaxDriver/GNUmakefile.preamble b/SOPE/NGCards/versitCardsSaxDriver/GNUmakefile.preamble
new file mode 100644 (file)
index 0000000..eb3ae25
--- /dev/null
@@ -0,0 +1,48 @@
+# compilation settings
+
+SOPE_ROOT=../..
+SOPE_OBJ_ROOT=$(GNUSTEP_BUILD_DIR)/$(SOPE_ROOT)
+
+ifeq ($(frameworks),yes)
+# hm, we might prefer /Library/SaxDrivers-$(MAJOR_VERSION).$(MINOR_VERSION)/
+# but this is harder with the FRAMEWORK_INSTALL_DIR
+BUNDLE_INSTALL_DIR := $(FRAMEWORK_INSTALL_DIR)/SaxObjC.framework/Versions/A/Resources/SaxDrivers/
+endif
+
+
+ADDITIONAL_INCLUDE_DIRS +=                     \
+       -I../..                                 \
+       -I$(SOPE_ROOT)/sope-xml                 \
+       -I$(SOPE_ROOT)/sope-core/NGExtensions
+
+
+# dependencies
+
+ifneq ($(frameworks),yes)
+BUNDLE_LIBS += -lSaxObjC
+else
+BUNDLE_LIBS += -framework SaxObjC
+endif
+ADDITIONAL_BUNDLE_LIBS += $(BUNDLE_LIBS)
+
+
+# library/framework search pathes
+
+DEP_DIRS = \
+       $(SOPE_ROOT)/sope-core/NGExtensions     \
+       $(SOPE_ROOT)/sope-xml/SaxObjC
+
+ifneq ($(frameworks),yes)
+ADDITIONAL_LIB_DIRS += \
+       $(foreach dir,$(DEP_DIRS),\
+         -L$(GNUSTEP_BUILD_DIR)/$(dir)/$(GNUSTEP_OBJ_DIR_NAME))
+else
+ADDITIONAL_LIB_DIRS += \
+       $(foreach dir,$(DEP_DIRS),-F$(GNUSTEP_BUILD_DIR)/$(dir))
+endif
+
+ifeq ($(findstring _64, $(GNUSTEP_TARGET_CPU)), _64)
+SYSTEM_LIB_DIR += -L/usr/local/lib64 -L/usr/lib64
+else
+SYSTEM_LIB_DIR += -L/usr/local/lib -L/usr/lib
+endif
diff --git a/SOPE/NGCards/versitCardsSaxDriver/README b/SOPE/NGCards/versitCardsSaxDriver/README
new file mode 100644 (file)
index 0000000..85d148e
--- /dev/null
@@ -0,0 +1,53 @@
+Overview
+========
+
+Two SaxObjC drivers for iCalendar and vCard files, initially written by
+Max Berger <max@berger.name>.
+
+VSiCalSaxDriver basically maps iCal 2.0 components, properties and parameters
+to the XML events according to the xCal 02 draft (iCal 3.0).
+
+Having a SAX driver for iCal might seem strange and a bit inefficient at
+first look, but the time saved for the application-level developer is
+significant, since he only needs to learn (or usually already knows) the
+SAX or DOM APIs and any XML API based on them (like XPATH, XQUERY).
+
+The VSiCalSaxDriver is aimed to be a replacement for the older (but better
+tested as of now) iCalSaxDriver. However, the iCalSaxDriver depends on the
+abandoned and hardly maintainable libical, which itself is an additional
+dependency to the OGo project and thus a welcome candidate for replacement.
+
+The VSSaxDriver attempts to follow RFC2445 closely, however the parser is
+written to be robust when it comes to parsing real life content. Currently,
+unescaping is done for more characters then it MUST according to RFC2445, but
+this is probably not a bad idea - wrongly escaped characters will still be
+parsed according to the original intent. Also, the VSSaxDriver supports Unix
+style terminated lines/folding.
+
+ToDo
+====
+
+- improve error handling (SaxExceptions !)
+- make the driver fully xCal compliant
+
+
+Defaults
+========
+
+Name                            Type        Description
+------------------------------------------------------------------------------
+VSSaxDriverDebugEnabled         BOOL        YES -> log some debug information
+                                                   via NSLog
+
+
+Examples
+========
+
+To "convert" an iCalendar to xCal (the test programs print out some XML):
+
+  saxxml -XMLReader VSiCalSaxDriver test1.ics
+  domxml -XMLReader VSiCalSaxDriver -xml  test1.ics
+
+To "convert" an iCalendar to PYX:
+
+  domxml -XMLReader VSiCalSaxDriver -pyx  test1.ics
diff --git a/SOPE/NGCards/versitCardsSaxDriver/VSCardSaxDriver.h b/SOPE/NGCards/versitCardsSaxDriver/VSCardSaxDriver.h
new file mode 100644 (file)
index 0000000..a2a43fc
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ Copyright (C) 2003-2004 Max Berger
+ Copyright (C) 2004-2005 OpenGroupware.org
+ This file is part of versitCardsSaxDriver, written for the OpenGroupware.org 
+ project (OGo).
+ SOPE is free software; you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the
+ Free Software Foundation; either version 2, or (at your option) any
+ later version.
+ SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+ License for more details.
+ You should have received a copy of the GNU Lesser General Public
+ License along with SOPE; see the file COPYING.  If not, write to the
+ Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA.
+ */
+
+#ifndef __versitCardsSaxDriver_VSCardSaxDriver_H__
+#define __versitCardsSaxDriver_VSCardSaxDriver_H__
+
+#include "VSSaxDriver.h"
+
+// TODO: photo is reported incorrectly
+
+@interface VSCardSaxDriver : VSSaxDriver 
+{
+}
+
+@end
+
+#endif /* __versitCardsSaxDriver_VSCardSaxDriver_H__ */
diff --git a/SOPE/NGCards/versitCardsSaxDriver/VSCardSaxDriver.m b/SOPE/NGCards/versitCardsSaxDriver/VSCardSaxDriver.m
new file mode 100644 (file)
index 0000000..46110fb
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+  Copyright (C) 2003-2004 Max Berger
+  Copyright (C) 2004-2005 OpenGroupware.org
+  
+  This file is part of versitCardsSaxDriver, written for the OpenGroupware.org
+  project (OGo).
+  
+  SOPE is free software; you can redistribute it and/or modify it under
+  the terms of the GNU Lesser General Public License as published by the
+  Free Software Foundation; either version 2, or (at your option) any
+  later version.
+  
+  SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+  License for more details.
+  
+  You should have received a copy of the GNU Lesser General Public
+  License along with SOPE; see the file COPYING.  If not, write to the
+  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+  02111-1307, USA.
+*/
+
+#include "VSCardSaxDriver.h"
+#include "common.h"
+
+#define XMLNS_VSvCard \
+  @"http://www.ietf.org/internet-drafts/draft-dawson-vcard-xml-dtd-03.txt"
+
+@implementation VSCardSaxDriver
+
+static NSSet *defElementNames = nil;
+
++ (void)initialize {
+  static BOOL didInit = NO;
+  
+  if(didInit)
+    return;
+  didInit = YES;
+  
+  defElementNames = [[NSSet alloc] initWithObjects:
+    @"class", @"prodid", @"rev", @"uid", @"version", nil];
+}
+
+- (id)init {
+  if ((self = [super init]) != nil) {
+    [self setPrefixURI:XMLNS_VSvCard];
+//     [self setElementMapping:[[self class] xcardMapping]];
+//     [self setAttributeElements:defElementNames];
+  }
+  return self;
+}
+
+/* top level parsing method */
+
+- (void)reportDocStart {
+  [super reportDocStart];
+  
+  [self->contentHandler startElement:@"vCardSet" namespace:self->prefixURI
+                        rawName:@"vCardSet" attributes:nil];
+}
+- (void)reportDocEnd {
+  [self->contentHandler endElement:@"vCardSet" namespace:self->prefixURI
+                        rawName:@"vCardSet"];
+  
+  [super reportDocEnd];
+}
+
+@end /* VCardSaxDriver */
diff --git a/SOPE/NGCards/versitCardsSaxDriver/VSSaxDriver.h b/SOPE/NGCards/versitCardsSaxDriver/VSSaxDriver.h
new file mode 100644 (file)
index 0000000..c6eb74f
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ Copyright (C) 2003-2004 Max Berger
+ Copyright (C) 2004-2005 OpenGroupware.org
+ This file is part of versitCardsSaxDriver, written for the OpenGroupware.org 
+ project (OGo).
+ SOPE is free software; you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the
+ Free Software Foundation; either version 2, or (at your option) any
+ later version.
+ SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+ License for more details.
+ You should have received a copy of the GNU Lesser General Public
+ License along with SOPE; see the file COPYING.  If not, write to the
+ Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA.
+ */
+
+#ifndef __versitCardsSaxDriver_VSSaxDriver_H__
+#define __versitCardsSaxDriver_VSSaxDriver_H__
+
+#import <Foundation/NSObject.h>
+#include <SaxObjC/SaxXMLReader.h>
+
+@class NSString, NSSet, NSDictionary, NSMutableArray, NSMutableDictionary;
+
+@interface VSSaxDriver : NSObject < SaxXMLReader > 
+{
+  id<NSObject,SaxContentHandler> contentHandler;
+  id<NSObject,SaxErrorHandler>   errorHandler;
+  NSString                       *prefixURI;
+  NSMutableArray                 *cardStack;
+  NSMutableArray                 *elementList; /* a list of tags to be rep. */
+  
+  NSSet                          *attributeElements;
+}
+
+- (void)setPrefixURI:(NSString*)_uri;
+- (NSString *)prefixURI;
+
+/* events */
+
+- (void)reportDocStart;
+- (void)reportDocEnd;
+
+@end
+
+#endif /* __versitCardsSaxDriver_VersitCardsSaxDriver_H__ */
diff --git a/SOPE/NGCards/versitCardsSaxDriver/VSSaxDriver.m b/SOPE/NGCards/versitCardsSaxDriver/VSSaxDriver.m
new file mode 100644 (file)
index 0000000..e59b7f0
--- /dev/null
@@ -0,0 +1,1219 @@
+/*
+  Copyright (C) 2003-2004 Max Berger
+  Copyright (C) 2004-2005 OpenGroupware.org
+  This file is part of versitCardsSaxDriver, written for the OpenGroupware.org 
+  project (OGo).
+  
+  SOPE is free software; you can redistribute it and/or modify it under
+  the terms of the GNU Lesser General Public License as published by the
+  Free Software Foundation; either version 2, or (at your option) any
+  later version.
+  
+  SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+  License for more details.
+  
+  You should have received a copy of the GNU Lesser General Public
+  License along with SOPE; see the file COPYING.  If not, write to the
+  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+  02111-1307, USA.
+*/
+
+/* FIXME: this class is badly designed. It is expected to feed
+          NGVCardSaxHandler with correct values but it won't handle escaped
+          commas. Also, it should handle CardGroups and CardElements
+          correctly: treat the former as open/close tags and the latter as
+          simple tags. Wrt that, the methods startGroupElement/endGroupElement
+          are not expected in a sax handler... this is all wrong. */
+
+#import "VSSaxDriver.h"
+#import "VSStringFormatter.h"
+#import <SaxObjC/SaxException.h>
+#import <NGExtensions/NGQuotedPrintableCoding.h>
+#import <NGCards/NSString+NGCards.h>
+#import "common.h"
+
+@interface VSSaxTag : NSObject
+{
+@private;
+  char          type;
+  NSString      *tagName;
+  NSString      *group;
+@public;
+  SaxAttributes *attrs;
+  unichar       *data;
+  unsigned int  datalen;
+  BOOL groupElement;
+}
+
++ (id) beginTag: (NSString *) _tag
+          group: (NSString *) _group
+     attributes: (SaxAttributes *) _attrs;
+- (id) initEndTag: (NSString *) _tag;
+
+- (id) initWithContentString: (NSString *) _data;
+- (void) setGroupElement: (BOOL) aBool;
+
+- (NSString *) tagName;
+- (NSString *) group;
+- (BOOL) isStartTag;
+- (BOOL) isEndTag;
+- (BOOL) isTag;
+
+@end
+
+@implementation VSSaxTag
+
++ (id) beginTag: (NSString *) _tag
+          group: (NSString *) _group
+     attributes: (SaxAttributes *) _attrs
+{
+  VSSaxTag *tag;
+
+  tag = [[self new] autorelease];
+  tag->type = 'B';
+  tag->tagName = [_tag copy];
+  tag->group = [_group copy];
+  tag->attrs = [_attrs retain];
+
+  return tag;
+}
+
+- (id) init
+{
+  if ((self = [super init]))
+    {
+      groupElement = NO;
+    }
+
+  return self;
+}
+
+- (id) initEndTag: (NSString *) _tag
+{
+  type = 'E';
+  tagName = [_tag copy];
+
+  return self;
+}
+
+- (id) initWithContentString: (NSString *) _data
+{
+  if (!_data)
+    {
+      [self release];
+      return nil;
+    }
+
+  datalen = [_data length];
+  data = calloc(datalen + 1, sizeof(unichar));
+  [_data getCharacters: data range: NSMakeRange(0, datalen)];
+  return self;
+}
+
+- (void) setGroupElement: (BOOL) aBool
+{
+  groupElement = aBool;
+}
+
+- (void) dealloc {
+  if (data) free(data);
+  [group   release];
+  [tagName release];
+  [attrs   release];
+  [super dealloc];
+}
+
+- (char) tagType
+{
+  return type;
+}
+
+/* accessors */
+
+- (NSString *) tagName {
+  return tagName;
+}
+- (NSString *) group {
+  return group;
+}
+
+- (BOOL) isStartTag {
+  return type == 'B' ? YES : NO;
+}
+- (BOOL) isEndTag {
+  return type == 'E' ? YES : NO;
+}
+- (BOOL) isTag {
+  return (type == 'B' || type == 'E') ? YES : NO;
+}
+
+@end /* VSSaxTag */
+
+@implementation VSSaxDriver
+
+static BOOL debugOn = NO;
+
+static NSCharacterSet *dotCharSet = nil;
+static NSCharacterSet *equalSignCharSet = nil;
+static NSCharacterSet *commaCharSet = nil;
+static NSCharacterSet *colonAndSemicolonCharSet = nil;
+static NSCharacterSet *colonSemicolonAndDquoteCharSet = nil;
+static NSCharacterSet *whitespaceCharSet = nil;
+
+static VSStringFormatter *stringFormatter = nil;
+
++ (void) initialize
+{
+  static BOOL didInit = NO;
+  NSUserDefaults *ud;
+
+  if (didInit)
+    return;
+  didInit = YES;
+
+  ud = [NSUserDefaults standardUserDefaults];
+  debugOn = [ud boolForKey: @"VSSaxDriverDebugEnabled"];
+
+  dotCharSet =
+    [[NSCharacterSet characterSetWithCharactersInString: @"."] retain];
+  equalSignCharSet =
+    [[NSCharacterSet characterSetWithCharactersInString: @"="] retain];
+  commaCharSet =
+    [[NSCharacterSet characterSetWithCharactersInString: @","] retain];
+  colonAndSemicolonCharSet =
+    [[NSCharacterSet characterSetWithCharactersInString: @": ;"] retain];
+  colonSemicolonAndDquoteCharSet =
+    [[NSCharacterSet characterSetWithCharactersInString: @": ;\""] retain];
+  whitespaceCharSet =
+    [[NSCharacterSet whitespaceCharacterSet] retain];
+
+  stringFormatter = [VSStringFormatter sharedFormatter];
+}
+
+- (id) init {
+  if ((self = [super init]))
+    {
+      prefixURI = @"";
+      cardStack = [[NSMutableArray alloc]      initWithCapacity: 4];
+      elementList = [[NSMutableArray alloc]      initWithCapacity: 8];
+    }
+
+  return self;
+}
+
+- (void) dealloc
+{
+  [contentHandler    release];
+  [errorHandler      release];
+  [prefixURI         release];
+  [cardStack         release];
+  [elementList       release];
+  [super dealloc];
+}
+
+/* accessors */
+
+- (void) setFeature: (NSString *) _name to: (BOOL) _value
+{
+}
+
+- (BOOL) feature: (NSString *) _name
+{
+  return NO;
+}
+
+- (void) setProperty: (NSString *) _name to: (id) _value
+{
+}
+
+- (id) property: (NSString *) _name
+{
+  return nil;
+}
+
+/* handlers */
+
+- (void) setContentHandler: (id<NSObject,SaxContentHandler>) _handler
+{
+  ASSIGN(contentHandler, _handler);
+}
+
+- (void) setDTDHandler: (id<NSObject,SaxDTDHandler>) _handler
+{
+  // FIXME
+}
+
+- (void) setErrorHandler: (id<NSObject,SaxErrorHandler>) _handler
+{
+  ASSIGN(errorHandler, _handler);
+}
+
+- (void) setEntityResolver: (id<NSObject,SaxEntityResolver>) _handler
+{
+  // FIXME
+}
+
+- (id<NSObject,SaxContentHandler>) contentHandler
+{
+  return contentHandler;
+}
+
+- (id<NSObject,SaxDTDHandler>) dtdHandler
+{
+  // FIXME
+  return nil;
+}
+
+- (id<NSObject,SaxErrorHandler>) errorHandler
+{
+  return errorHandler;
+}
+
+- (id<NSObject,SaxEntityResolver>) entityResolver
+{
+  // FIXME
+  return nil;
+}
+
+- (void) setPrefixURI: (NSString *) _uri
+{
+  ASSIGNCOPY(prefixURI, _uri);
+}
+
+- (NSString *) prefixURI
+{
+  return prefixURI;
+}
+
+/* parsing */
+
+- (NSString *) _groupFromTagName: (NSString *) _tagName
+{
+  NSRange  r;
+  
+  r = [_tagName rangeOfCharacterFromSet: dotCharSet];
+  if (!r.length)
+    return nil;
+  
+  return [_tagName substringToIndex: r.location];
+}
+
+- (NSString *) _mapTagName: (NSString *) _tagName
+{
+  NSString *ret;
+  NSRange  r;
+
+  //NSLog(@"Unknown Key: %@ in %@",_tagName,elementMapping);
+  ret = _tagName;
+  
+  /*
+    This is to allow parsing of vCards produced by Apple
+    Addressbook.
+    The dot-notation is described as 'grouping' in RFC 2425, section 5.8.2.
+  */
+  r = [_tagName rangeOfCharacterFromSet: dotCharSet];
+  if (r.length > 0)
+    ret = [self _mapTagName: [_tagName substringFromIndex: (r.location + 1)]];
+  
+  return ret;
+}
+
+- (void) _parseAttr: (NSString *) _attr 
+             forTag: (NSString *) _tagName
+           intoAttr: (NSString **) attr_
+          intoValue: (NSString **) value_
+{
+  NSRange  r;
+  NSString *attrName, *attrValue;
+  
+  r = [_attr rangeOfCharacterFromSet: equalSignCharSet];
+  if (r.length > 0)
+    {
+      unsigned left, right;
+
+      attrName = [[_attr substringToIndex: r.location] uppercaseString];
+      left = NSMaxRange(r);
+      right = [_attr length] - 1;
+      if (left < right)
+        {
+          if (([_attr characterAtIndex: left]  == '"') &&
+              ([_attr characterAtIndex: right] == '"'))
+            {
+              left += 1;
+              r = NSMakeRange(left, right - left);
+              attrValue = [_attr substringWithRange: r];
+            }
+          else
+            {
+              attrValue = [_attr substringFromIndex: left];
+            }
+        }
+      else if (left == right)
+        {
+          attrValue = [_attr substringFromIndex: left];
+        }
+      else
+        {
+          attrValue = @"";
+        }
+    }
+  else
+    {
+      if ([[_attr uppercaseString] isEqualToString: @"QUOTED-PRINTABLE"])
+        attrName = @"ENCODING";
+      else
+        attrName = @"TYPE";
+      attrValue = _attr;
+    }
+  
+#if 0
+  // ZNeK: what's this for?
+  r = [attrValue rangeOfCharacterFromSet: commaCharSet];
+  while (r.length > 0)
+    {
+      [attrValue replaceCharactersInRange: r withString: @" "];
+      r = [attrValue rangeOfCharacterFromSet: commaCharSet];
+    }
+#endif
+
+  *attr_ = attrName;
+  *value_ = [attrValue unescapedFromCard];
+//   *value_ = [stringFormatter stringByUnescapingRFC2445Text: attrValue];
+}
+
+- (SaxAttributes *) _mapAttrs: (NSArray *) _attrs
+                       forTag: (NSString *) _tagName
+{
+  SaxAttributes *retAttrs;
+  NSEnumerator *attrEnum, *values;
+  NSString *curAttr, *mappedAttr, *mappedValue, *curValue;
+
+  /*
+    TODO: values are not always mapped to CDATA! Eg in the dawson draft: 
+    | TYPE for TEL   | tel.type   | NMTOKENS  | 'VOICE'         |
+    | TYPE for EMAIL | email.type | NMTOKENS  | 'INTERNET'      |
+    | TYPE for PHOTO,| img.type   | CDATA     | REQUIRED        |
+    |  and LOGO      |            |           |                 |
+    | TYPE for SOUND | aud.type   | CDATA     | REQUIRED        |
+    | VALUE          | value      | NOTATION  | See elements    |
+  */
+
+  if (_attrs && [_attrs count] > 0)
+    {
+      retAttrs = [[SaxAttributes alloc] init];
+      [retAttrs autorelease];
+
+      attrEnum = [_attrs objectEnumerator];
+      curAttr = [attrEnum nextObject];
+      while (curAttr)
+        {
+          [self _parseAttr: curAttr
+                forTag: _tagName
+                intoAttr: &mappedAttr
+                intoValue: &mappedValue];
+          values = [[mappedValue asCardAttributeValues] objectEnumerator];
+          curValue = [values nextObject];
+          while (curValue)
+            {
+              [retAttrs addAttribute: mappedAttr
+                        uri: prefixURI
+                        rawName: mappedAttr
+                        type: @"CDATA"
+                        value: curValue];
+              curValue = [values nextObject];
+            }
+          curAttr = [attrEnum nextObject];
+        }
+    }
+  else
+    retAttrs = nil;
+
+  return retAttrs;
+}
+
+- (VSSaxTag *) _beginTag: (NSString *) _tagName
+                   group: (NSString *) _group
+               withAttrs: (SaxAttributes *) _attrs
+{
+  VSSaxTag *tag;
+  
+  tag = [VSSaxTag beginTag: [_tagName uppercaseString]
+                  group: _group attributes: _attrs];
+  [elementList addObject: tag];
+
+  return tag;
+}
+
+- (void) _endTag: (NSString *) _tagName
+{
+  VSSaxTag *tag;
+  
+  tag = [[VSSaxTag alloc] initEndTag: _tagName];
+  [elementList addObject: tag];
+  [tag release]; tag = nil;
+}
+
+- (void) _endGroupElementTag: (NSString *) _tagName
+{
+  VSSaxTag *tag;
+  
+  tag = [[VSSaxTag alloc] initEndTag: _tagName];
+  [elementList addObject: tag];
+  [tag setGroupElement: YES];
+  [tag release]; tag = nil;
+}
+
+- (void) _reportContentAsTag: (NSString *) _tagName
+                       group: (NSString *) _group
+                   withAttrs: (SaxAttributes *) _attrs 
+                  andContent: (NSString *) _content 
+{
+  /*
+    This is called for all non-BEGIN|END types.
+  */
+
+//   _content = [stringFormatter stringByUnescapingRFC2445Text: _content];
+
+  /* check whether type should be reported as an attribute in XML */
+  
+  [self _beginTag: _tagName group: _group withAttrs: _attrs];
+  
+  if ([_content length] > 0)
+    {
+      VSSaxTag *a;
+      
+      a = [(VSSaxTag *)[VSSaxTag alloc]
+                       initWithContentString: [_content unescapedFromCard]];
+      if (a)
+        {
+          [elementList addObject: a];
+          [a release];
+        }
+    }
+
+  [self _endTag: _tagName];
+}
+
+/* report events for collected elements */
+
+- (void) reportStartGroup: (NSString *) _group
+{
+  SaxAttributes *attrs;
+  
+  attrs = [[SaxAttributes alloc] init];
+  [attrs addAttribute: @"name" uri: prefixURI rawName: @"name"
+        type: @"CDATA" value: _group];
+  
+  [contentHandler startElement: @"group"
+                  namespace: prefixURI
+                  rawName: @"group"
+                  attributes: attrs];
+  [attrs release];
+}
+
+- (void) reportEndGroup
+{
+  [contentHandler endElement: @"group"
+                  namespace: prefixURI
+                  rawName: @"group"];
+}
+
+- (void) reportStartContainer: (NSString *) _container
+{
+  SaxAttributes *attrs;
+  
+  attrs = [[SaxAttributes alloc] init];
+  [attrs addAttribute: @"name"
+         uri: prefixURI
+         rawName: @"name"
+        type: @"CDATA"
+         value: _container];
+  [contentHandler startElement: @"container"
+                  namespace: prefixURI
+                  rawName: @"container"
+                  attributes: attrs];
+  [attrs release];
+}
+
+- (void) reportEndContainer
+{
+  [contentHandler endElement: @"container"
+                  namespace: prefixURI
+                  rawName: @"container"];
+}
+
+- (void) reportQueuedTags
+{
+  /*
+    Why does the parser need the list instead of reporting the events
+    straight away?
+    
+    Because some vCard tags like the 'version' are reported as attributes
+    on the container tag. So we have a sequence like: 
+    BEGIN: VCARD
+    ...
+    VERSION: 3.0
+    which will get reported as: 
+    <vcard version="3.0">
+  */
+  NSEnumerator *enu;
+  VSSaxTag *tagToReport;
+  NSString *lastGroup, *tg, *tagName;
+  
+  lastGroup = nil;
+
+  enu = [elementList objectEnumerator];
+  tagToReport = [enu nextObject];
+  while (tagToReport)
+    {
+      tagName = [tagToReport tagName];
+
+      if ([tagToReport isStartTag])
+        {
+         tg = [tagToReport group];
+          if (![lastGroup isEqualToString: tg]
+              && lastGroup != tg)
+            {
+              if (lastGroup) [self reportEndGroup];
+              ASSIGNCOPY(lastGroup, tg);
+              if (lastGroup) [self reportStartGroup: lastGroup];
+            }
+        }
+
+      if ([tagToReport isStartTag])
+        {
+          if (tagToReport->groupElement)
+            [self reportStartContainer: tagName];
+          else
+            [contentHandler startElement: tagName
+                            namespace: prefixURI
+                            rawName: tagName
+                            attributes: tagToReport->attrs];
+        }
+      else if ([tagToReport isEndTag])
+        {
+          if (tagToReport->groupElement)
+            [self reportEndContainer];
+          else
+            [contentHandler endElement: tagName
+                            namespace: prefixURI
+                            rawName: tagName];
+        }
+      else
+        [contentHandler characters: tagToReport->data
+                        length: tagToReport->datalen];
+
+      tagToReport = [enu nextObject];
+    }
+
+  /* flush event group */
+  [elementList removeAllObjects];
+
+  /* close open groups */
+  if (lastGroup)
+    {
+      [self reportEndGroup];
+      [lastGroup release];
+      lastGroup = nil;
+    }
+}
+
+/* errors */
+
+- (void) reportError: (NSString *) _text
+{
+  SaxParseException *e;
+
+  e = (id)[SaxParseException exceptionWithName: @"SaxParseException"
+                            reason: _text
+                            userInfo: nil];
+  [errorHandler error: e];
+}
+
+- (void) warn: (NSString *) _warn
+{
+  SaxParseException *e;
+
+  e = (id)[SaxParseException exceptionWithName: @"SaxParseException"
+                            reason: _warn
+                            userInfo: nil];
+  [errorHandler warning: e];
+}
+
+/* parsing raw string */
+
+- (void) _beginComponentWithValue: (NSString *) tagValue
+{
+  VSSaxTag *tag;
+
+  tag = [self _beginTag: [self _mapTagName: tagValue]
+             group: nil
+             withAttrs: [[[SaxAttributes alloc] init] autorelease]];
+  [tag setGroupElement: YES];
+  [cardStack addObject: tag];
+}
+
+- (void) _endComponent: (NSString *) tagName
+                 value: (NSString *) tagValue
+{
+  NSString *mtName;
+  
+  mtName = [[self _mapTagName: tagValue] uppercaseString];
+  if ([cardStack count] > 0)
+    {
+      NSString *expectedName;
+      
+      expectedName = [(VSSaxTag *)[cardStack lastObject] tagName];
+      if (![expectedName isEqualToString: mtName])
+        {
+          NSString *s;
+       
+          // TODO: rather report an error?
+          // TODO: setup userinfo dict with details
+          s = [NSString stringWithFormat: 
+                          @"Found end tag '%@' which does not match expected "
+                       @"name '%@'!"
+                       @" Tag '%@' has not been closed properly. Given "
+                       @"document contains errors!",
+                       mtName, expectedName, expectedName];
+          [self reportError: s];
+       
+          /* probably futile attempt to parse anyways */
+          if (debugOn)
+            {
+              NSLog(@"%s trying to fix previous error by inserting bogus end "
+                    @"tag.",
+                    __PRETTY_FUNCTION__);
+            }
+          [self _endGroupElementTag: expectedName];
+          [cardStack removeLastObject];
+        }
+    }
+  else
+    {
+      // TOOD: generate error?
+      [self reportError: [@"found end tag without any open tags left: "
+                           stringByAppendingString: mtName]];
+    }
+  [self _endGroupElementTag: mtName];
+  [cardStack removeLastObject];
+    
+  /* report parsed elements */
+    
+  if ([cardStack count] == 0)
+    [self reportQueuedTags];
+}
+
+- (void) _parseLine: (NSString *) _line
+{
+  NSString       *tagName, *tagValue;
+  NSMutableArray *tagAttributes;
+  NSRange        r, todoRange;
+  unsigned       length;
+  
+#if 0
+  if (debugOn)
+    NSLog(@"%s: parse line: '%@'", __PRETTY_FUNCTION__, _line);
+#endif
+
+  length = [_line length];
+  todoRange = NSMakeRange(0, length);
+  r = [_line rangeOfCharacterFromSet: colonAndSemicolonCharSet
+             options: 0
+             range: todoRange];
+  /* is line well-formed? */
+  if (!r.length || !r.location)
+    {
+#if 0
+      NSLog(@"todo-range: %i-%i, range: %i-%i, length %i, str-class %@",
+            todoRange.location, todoRange.length,
+            r.location, r.length,
+            length, NSStringFromClass([_line class]));
+#endif
+
+      [self reportError: 
+              [@"got an improper content line! (did not find colon) ->\n" 
+                stringByAppendingString: _line]];
+      return;
+    }
+  
+  /* tagname is everything up to a ': ' or  ';' (value or parameter) */
+  tagName = [[_line substringToIndex: r.location] uppercaseString];
+  tagAttributes = [[NSMutableArray alloc] initWithCapacity: 16];
+  
+  if (debugOn && ([tagName length] == 0))
+    {
+      [self reportError: [@"got an improper content line! ->\n" 
+                           stringByAppendingString: _line]];
+      return;
+    }
+  
+  /* 
+     possible shortcut: if we spotted a ': ', we don't have to do "expensive"
+     argument scanning/processing.
+  */
+  if ([_line characterAtIndex: r.location] != ':')
+    {
+      BOOL isAtEnd = NO;
+      BOOL isInDquote = NO;
+      unsigned start;
+    
+      start = NSMaxRange(r);
+      todoRange = NSMakeRange(start, length - start);
+      while(!isAtEnd)
+        {
+          BOOL skip = YES;
+
+          /* scan for parameters */
+          r = [_line rangeOfCharacterFromSet: colonSemicolonAndDquoteCharSet
+                     options: 0
+                     range: todoRange];
+      
+          /* is line well-formed? */
+          if (!r.length || !r.location)
+            {
+              [self reportError: [@"got an improper content line! ->\n" 
+                                   stringByAppendingString: _line]];
+              [tagAttributes release]; tagAttributes = nil;
+              return;
+            }
+      
+          /* first check if delimiter candidate is escaped */
+          if ([_line characterAtIndex: (r.location - 1)] != '\\')
+            {
+              unichar delimiter;
+              NSRange copyRange;
+
+              delimiter = [_line characterAtIndex: r.location];
+              if (delimiter == '\"')
+                {
+                  /* not a real delimiter - toggle isInDquote for proper escaping */
+                  isInDquote = !isInDquote;
+                }
+              else
+                {
+                  if (!isInDquote)
+                    {
+                      /* is a delimiter, which one? */
+                      skip = NO;
+                      if (delimiter == ':')
+                        {
+                          isAtEnd = YES;
+                        }
+                      copyRange = NSMakeRange(start, r.location - start);
+                      [tagAttributes addObject: [_line substringWithRange: copyRange]];
+                      if (!isAtEnd)
+                        {
+                          /* adjust start, todoRange */
+                          start = NSMaxRange(r);
+                          todoRange = NSMakeRange(start, length - start);
+                        }
+                    }
+                }
+            }
+          if (skip)
+            {
+              /* adjust todoRange */
+              unsigned offset = NSMaxRange(r);
+              todoRange = NSMakeRange(offset, length - offset);
+            }
+        }
+    }
+  tagValue = [_line substringFromIndex: NSMaxRange(r)];
+  
+  if (debugOn && ([tagName length] == 0))
+    {
+      NSLog(@"%s: missing tagname in line: '%@'", 
+            __PRETTY_FUNCTION__, _line);
+    }
+  
+  /*
+    At this point we have: 
+    name:       'BEGIN', 'TEL', 'EMAIL', 'ITEM1.ADR' etc
+    value:      ';;;Magdeburg;;;Germany'
+    attributes: ("type=INTERNET", "type=HOME", "type=pref")
+  */
+
+#if 0
+#  warning DEBUG LOG ENABLED
+  NSLog(@"TAG: %@, value %@ attrs %@",
+        tagName, tagValue, tagAttributes);
+#endif
+  
+  /* process tag */
+  
+  if ([tagName isEqualToString: @"BEGIN"])
+    {
+      if ([tagAttributes count] > 0)
+        [self warn: @"Losing unexpected parameters of BEGIN line."];
+      [self _beginComponentWithValue: tagValue];
+    }
+  else if ([tagName isEqualToString: @"END"])
+    {
+      if ([tagAttributes count] > 0)
+        [self warn: @"Losing unexpected parameters of END line."];
+      [self _endComponent: tagName value: tagValue];
+    }
+  else
+    {
+      /* a regular content tag */
+    
+      /* 
+         check whether the tga value is encoded in quoted printable,
+         this one is used with Outlook vCards (see data/ for examples)
+      */
+      // TODO: make the encoding check more generic
+      if ([tagAttributes containsObject: @"ENCODING=QUOTED-PRINTABLE"])
+        {
+          // TODO: QP is charset specific! The one below decodes in Unicode!
+          tagValue = [tagValue stringByDecodingQuotedPrintable];
+          [tagAttributes removeObject: @"ENCODING=QUOTED-PRINTABLE"];
+        }
+    
+      [self _reportContentAsTag: [self _mapTagName: tagName]
+            group: [self _groupFromTagName: tagName]
+            withAttrs: [self _mapAttrs: tagAttributes forTag: tagName]
+            andContent: tagValue];
+    }
+  
+  [tagAttributes release];
+}
+
+
+/* top level parsing method */
+
+- (void) reportDocStart
+{
+  [contentHandler startDocument];
+  [contentHandler startPrefixMapping: @"" uri: prefixURI];
+}
+
+- (void) reportDocEnd
+{
+  [contentHandler endPrefixMapping: @""];
+  [contentHandler endDocument];
+}
+
+- (void) _parseString: (NSString *) _rawString
+{
+  /*
+    This method split the string into content lines for actual vCard
+    parsing.
+
+    RFC2445: 
+    contentline = name *(";" param ) ": " value CRLF
+    ; When parsing a content line, folded lines MUST first
+    ; be unfolded
+  */
+  NSMutableString *line;
+  unsigned pos, length;
+  NSRange  r;
+
+  [self reportDocStart];
+  
+  /* start parsing */
+  
+  length = [_rawString length];
+  r = NSMakeRange(0, 0);
+  line = [[NSMutableString alloc] initWithCapacity: 75 + 2];
+  
+  for (pos = 0; pos < length; pos++)
+    {
+      unichar c;
+    
+      c = [_rawString characterAtIndex: pos];
+    
+      if (c == '\r')
+        {
+          if (((length - 1) - pos) >= 1)
+            {
+              if ([_rawString characterAtIndex: pos + 1] == '\n')
+                {
+                  BOOL isAtEndOfLine = YES;
+         
+                  /* test for folding first */
+                  if (((length - 1) - pos) >= 2)
+                    {
+                      unichar ws;
+           
+                      ws = [_rawString characterAtIndex: pos + 2];
+                      isAtEndOfLine = [whitespaceCharSet characterIsMember: ws] ? NO : YES;
+                      if (!isAtEndOfLine)
+                        {
+                          /* assemble part of line up to pos */
+                          if (r.length > 0)
+                            {
+                              [line appendString: [_rawString substringWithRange: r]];
+                            }
+                          /* unfold */
+                          pos += 2;
+                          r = NSMakeRange(pos + 1, 0); /* begin new range */
+                        }
+                    }
+                  if (isAtEndOfLine)
+                    {
+                      /* assemble part of line up to pos */
+                      if (r.length > 0)
+                        {
+                          [line appendString: [_rawString substringWithRange: r]];
+                        }
+                      [self _parseLine: line];
+                      /* reset line */
+                      [line deleteCharactersInRange: NSMakeRange(0, [line length])];
+                      pos += 1;
+                      r = NSMakeRange(pos + 1, 0); /* begin new range */
+                    }
+                }
+            }
+          else
+            {
+              /* garbled last line! */
+              [self warn: @"last line is truncated, trying to parse anyways!"];
+            }
+        }
+      else if (c == '\n')
+        { /* broken, non-standard */
+          BOOL isAtEndOfLine = YES;
+      
+          /* test for folding first */
+          if (((length - 1) - pos) >= 1)
+            {
+              unichar ws;
+       
+              ws = [_rawString characterAtIndex: (pos + 1)];
+       
+              isAtEndOfLine = [whitespaceCharSet characterIsMember: ws] ? NO : YES;
+              if (!isAtEndOfLine)
+                {
+                  /* assemble part of line up to pos */
+                  if (r.length > 0)
+                    {
+                      [line appendString: [_rawString substringWithRange: r]];
+                    }
+                  /* unfold */
+                  pos += 1;
+                  r = NSMakeRange(pos + 1, 0); /* begin new range */
+                }
+            }
+          if (isAtEndOfLine)
+            {
+              /* assemble part of line up to pos */
+              if (r.length > 0)
+                {
+                  [line appendString: [_rawString substringWithRange: r]];
+                }
+              [self _parseLine: line];
+              /* reset line */
+              [line deleteCharactersInRange: NSMakeRange(0, [line length])];
+              r = NSMakeRange(pos + 1, 0); /* begin new range */
+            }
+        }
+      else
+        {
+          r.length += 1;
+        }
+    }
+  if (r.length > 0)
+    {
+      [self warn: @"Last line of parse string is not properly terminated!"];
+      [line appendString: [_rawString substringWithRange: r]];
+      [self _parseLine: line];
+    }
+  
+  if ([cardStack count] != 0)
+    {
+      [self warn: @"found elements on cardStack. This indicates an improper "
+            @"nesting structure! Not all required events will have been "
+            @"generated, leading to unpredictable results!"];
+      [cardStack removeAllObjects]; // clean up
+    }
+  
+  [line release]; line = nil;
+  
+  [self reportDocEnd];
+}
+
+/* main entry functions */
+
+- (id) sourceForData: (NSData *) _data
+            systemId: (NSString *) _sysId
+{
+  SaxParseException *e = nil;
+  NSStringEncoding encoding;
+  unsigned len;
+  const unsigned char *bytes;
+  id source;
+  
+  if (debugOn)
+    {
+      NSLog(@"%s: trying to decode data (0x%p,len=%d) ...",
+           __PRETTY_FUNCTION__, _data, [_data length]);
+    }
+  
+  if ((len = [_data length]) == 0)
+    {
+      e = (id)[SaxParseException exceptionWithName: @"SaxIOException"
+                                 reason: @"Got no parsing data!"
+                                 userInfo: nil];
+      [errorHandler fatalError: e];
+      return nil;
+    }
+  if (len < 10)
+    {
+      e = (id)[SaxParseException exceptionWithName: @"SaxIOException"
+                                 reason: @"Input data to short for vCard!"
+                                 userInfo: nil];
+      [errorHandler fatalError: e];
+      return nil;
+    }
+  
+  bytes = [_data bytes];
+  if ((bytes[0] == 0xFF && bytes[1] == 0xFE) ||
+      (bytes[0] == 0xFE && bytes[1] == 0xFF))
+    encoding = NSUnicodeStringEncoding;
+  else
+    encoding = NSUTF8StringEncoding;
+
+  // FIXME: Data is not always utf-8.....
+  source = [[[NSString alloc] initWithData: _data encoding: encoding]
+            autorelease];
+  if (!source)
+    {
+      e = (id)[SaxParseException exceptionWithName: @"SaxIOException"
+                                 reason: @"Could not convert input to string!"
+                                 userInfo: nil];
+      [errorHandler fatalError: e];
+    }
+  return source;
+}
+
+- (void) parseFromSource: (id) _source
+                systemId: (NSString *) _sysId
+{
+  if (debugOn)
+    NSLog(@"%s: parse: %@ (sysid=%@)", __PRETTY_FUNCTION__, _source, _sysId);
+  
+  if ([_source isKindOfClass: [NSURL class]])
+    {
+      NSURL *url;
+
+      url = _source;
+      if (!_sysId) _sysId = [url absoluteString];
+
+      if (debugOn)
+        {
+          NSLog(@"%s: trying to load URL: %@ (sysid=%@)",__PRETTY_FUNCTION__, 
+                url, _sysId);
+        }
+    
+      // TODO: remember encoding of source
+      _source = [url resourceDataUsingCache: NO];
+      if (!_source || ![_source length])
+        {
+          SaxParseException *e;
+          NSString          *s;
+    
+          if (debugOn) 
+            NSLog(@"%s: got no data from url: %@", __PRETTY_FUNCTION__, url);
+    
+          s = [NSString stringWithFormat: @"got no data from url: %@", url]; 
+          e = (id)[SaxParseException exceptionWithName: @"SaxIOException"
+                                     reason: s
+                                     userInfo: nil];
+          [errorHandler fatalError: e];
+          return;
+        }
+    }
+  
+  if ([_source isKindOfClass: [NSData class]])
+    {
+      if (!_sysId) _sysId = @"<data>";
+      if ((_source = [self sourceForData: _source systemId: _sysId]) == nil)
+        return;
+    }
+
+  if (![_source isKindOfClass: [NSString class]])
+    {
+      SaxParseException *e;
+      NSString *s;
+    
+      if (debugOn) 
+        NSLog(@"%s: unrecognizable source: %@", __PRETTY_FUNCTION__,_source);
+    
+      s = [@"cannot handle data-source: " stringByAppendingString: 
+              [_source description]];
+      e = (id)[SaxParseException exceptionWithName: @"SaxIOException"
+                                 reason: s
+                                 userInfo: nil];
+    
+      [errorHandler fatalError: e];
+      return;
+    }
+
+  /* ensure consistent state */
+
+  [cardStack   removeAllObjects];
+  [elementList removeAllObjects];
+  
+  /* start parsing */
+  
+  if (debugOn)
+    {
+      NSLog(@"%s: trying to parse string (0x%p,len=%d) ...",
+            __PRETTY_FUNCTION__, _source, [_source length]);
+    }
+  if (!_sysId) _sysId = @"<string>";
+  [self _parseString: _source];
+  
+  /* tear down */
+  
+  [cardStack   removeAllObjects];
+  [elementList removeAllObjects];
+}
+
+- (void) parseFromSource: (id) _source
+{
+  [self parseFromSource: _source systemId: nil];
+}
+
+- (void) parseFromSystemId: (NSString *) _sysId
+{
+  NSURL *url;
+  
+  if (![_sysId rangeOfString: @"://"].length)
+    {
+      /* seems to be a path, path to be a proper URL */
+      url = [NSURL fileURLWithPath: _sysId];
+    }
+  else
+    {
+      /* Note: Cocoa NSURL doesn't complain on "/abc/def" like input! */
+      url = [NSURL URLWithString: _sysId];
+    }
+  
+  if (!url)
+    {
+      SaxParseException *e;
+    
+      e = (id)[SaxParseException exceptionWithName: @"SaxIOException"
+                                 reason: @"cannot handle system-id"
+                                 userInfo: nil];
+      [errorHandler fatalError: e];
+      return;
+    }
+  
+  [self parseFromSource: url systemId: _sysId];
+}
+
+/* debugging */
+
+- (BOOL) isDebuggingEnabled 
+{
+  return debugOn;
+}
+
+@end /* VersitCardsSaxDriver */
+
diff --git a/SOPE/NGCards/versitCardsSaxDriver/VSStringFormatter.h b/SOPE/NGCards/versitCardsSaxDriver/VSStringFormatter.h
new file mode 100644 (file)
index 0000000..13d9012
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ Copyright (C) 2004-2005 OpenGroupware.org
+ This file is part of versitCardsSaxDriver, written for the OpenGroupware.org
+ project (OGo).
+ SOPE is free software; you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the
+ Free Software Foundation; either version 2, or (at your option) any
+ later version.
+ SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+ License for more details.
+ You should have received a copy of the GNU Lesser General Public
+ License along with SOPE; see the file COPYING.  If not, write to the
+ Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA.
+ */
+
+
+#ifndef        __VSStringFormatter_H_
+#define        __VSStringFormatter_H_
+
+#import <Foundation/Foundation.h>
+
+@interface VSStringFormatter : NSObject
+{
+}
+
++ (id)sharedFormatter;
+- (NSString *)stringByUnescapingRFC2445Text:(NSString *)_s;
+
+@end
+
+#endif /* __VSStringFormatter_H_ */
diff --git a/SOPE/NGCards/versitCardsSaxDriver/VSStringFormatter.m b/SOPE/NGCards/versitCardsSaxDriver/VSStringFormatter.m
new file mode 100644 (file)
index 0000000..5535e5e
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+  Copyright (C) 2004-2005 OpenGroupware.org
+  This file is part of versitCardsSaxDriver, written for the OpenGroupware.org
+  project (OGo).
+  SOPE is free software; you can redistribute it and/or modify it under
+  the terms of the GNU Lesser General Public License as published by the
+  Free Software Foundation; either version 2, or (at your option) any
+  later version.
+  SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+  License for more details.
+  You should have received a copy of the GNU Lesser General Public
+  License along with SOPE; see the file COPYING.  If not, write to the
+  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+  02111-1307, USA.
+*/
+
+#include "VSStringFormatter.h"
+#include "common.h"
+
+@implementation VSStringFormatter
+
+static NSCharacterSet *escSet = nil;
+
++ (void)initialize {
+  static BOOL didInit = NO;
+  
+  if(didInit)
+    return;
+  
+  didInit = YES;
+  escSet = [[NSCharacterSet characterSetWithCharactersInString:@"\\"] retain];
+}
+
++ (id)sharedFormatter {
+  static id sharedInstance = nil;
+  if(!sharedInstance) {
+    sharedInstance = [[self alloc] init];
+  }
+  return sharedInstance;
+}
+
+- (NSString *)stringByUnescapingRFC2445Text:(NSString *)_s {
+  NSMutableString *safeString;
+  unsigned        length;
+  NSRange         prevRange, escRange;
+  NSRange         todoRange;
+  BOOL            needsEscaping;
+
+  length    = [_s length];
+  prevRange = NSMakeRange(0, length);
+  escRange  = [_s rangeOfCharacterFromSet:escSet options:0 range:prevRange];
+
+  needsEscaping = escRange.length > 0 ? YES : NO;
+  if (!needsEscaping)
+    return _s; /* cheap */
+  
+  safeString = [NSMutableString stringWithCapacity:length];
+  
+  do {
+    prevRange.length = escRange.location - prevRange.location;
+    if (prevRange.length > 0)
+      [safeString appendString:[_s substringWithRange:prevRange]];
+
+    /* test edge case */
+    if(NSMaxRange(escRange) < length) {
+      unichar c = [_s characterAtIndex:escRange.location + 1];
+      if(c == 'n' || c == 'N') {
+        [safeString appendString:@"\n"];
+        escRange.length += 1; /* skip the 'n' */
+      }
+    }
+    
+    prevRange.location = NSMaxRange(escRange);
+    todoRange.location = prevRange.location;
+    todoRange.length   = length - prevRange.location;
+    escRange           = [_s rangeOfCharacterFromSet:escSet
+                             options:0
+                             range:todoRange];
+  }
+  while(escRange.length > 0);
+  
+  if (todoRange.length > 0)
+    [safeString appendString:[_s substringWithRange:todoRange]];
+  
+  return safeString;
+}
+
+@end /* VSStringFormatter */
diff --git a/SOPE/NGCards/versitCardsSaxDriver/Version b/SOPE/NGCards/versitCardsSaxDriver/Version
new file mode 100644 (file)
index 0000000..462cacf
--- /dev/null
@@ -0,0 +1,7 @@
+# Version file
+
+# SOPE version:
+MAJOR_VERSION=4
+MINOR_VERSION=5
+
+SUBMINOR_VERSION:=24
diff --git a/SOPE/NGCards/versitCardsSaxDriver/bundle-info.plist b/SOPE/NGCards/versitCardsSaxDriver/bundle-info.plist
new file mode 100644 (file)
index 0000000..41acb50
--- /dev/null
@@ -0,0 +1,19 @@
+{
+  requires = {
+    bundleManagerVersion = 1;
+    classes = ( { name = NSObject; } );
+  };
+  
+  provides = {
+    SAXDrivers = ( 
+      {
+        name        = "VSCardSaxDriver";
+        sourceTypes = ( "text/x-vcard", "text/vcard",
+                        "text/x-calendar", "text/calendar" );
+      }
+    );
+    classes = ( 
+      { name = "VSCardSaxDriver"; }
+    );
+  };
+}
diff --git a/SOPE/NGCards/versitCardsSaxDriver/common.h b/SOPE/NGCards/versitCardsSaxDriver/common.h
new file mode 100644 (file)
index 0000000..1df5915
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ Copyright (C) 2004 OpenGroupware.org
+ This file is part of versitCardsSaxDriver, written for the OpenGroupware.org 
+ project (OGo).
+ SOPE is free software; you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the
+ Free Software Foundation; either version 2, or (at your option) any
+ later version.
+ SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+ License for more details.
+ You should have received a copy of the GNU Lesser General Public
+ License along with SOPE; see the file COPYING.  If not, write to the
+ Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA.
+*/
+
+#import <Foundation/Foundation.h>
+
+#include <NGExtensions/NGObjectMacros.h>
diff --git a/SOPE/NGCards/versitCardsSaxDriver/fhs.make b/SOPE/NGCards/versitCardsSaxDriver/fhs.make
new file mode 100644 (file)
index 0000000..1c47818
--- /dev/null
@@ -0,0 +1,29 @@
+# postprocessing
+
+# FHS support (this is a hack and is going to be done by gstep-make!)
+
+ifneq ($(FHS_INSTALL_ROOT),)
+
+ifeq ($(findstring _64, $(GNUSTEP_TARGET_CPU)), _64)
+FHS_LIB_DIR=$(FHS_INSTALL_ROOT)/lib64/
+else
+FHS_LIB_DIR=$(FHS_INSTALL_ROOT)/lib/
+endif
+FHS_SAX_DIR=$(FHS_LIB_DIR)sope-$(MAJOR_VERSION).$(MINOR_VERSION)/saxdrivers/
+
+fhs-sax-dirs ::
+       $(MKDIRS) $(FHS_SAX_DIR)
+
+move-bundles-to-fhs :: fhs-sax-dirs
+       @echo "moving bundles $(BUNDLE_INSTALL_DIR) to $(FHS_SAX_DIR) .."
+       for i in $(BUNDLE_NAME); do \
+          j="$(FHS_SAX_DIR)/$${i}$(BUNDLE_EXTENSION)"; \
+         if test -d $$j; then rm -r $$j; fi; \
+         mv "$(BUNDLE_INSTALL_DIR)/$${i}$(BUNDLE_EXTENSION)" $$j; \
+       done
+
+move-to-fhs :: move-bundles-to-fhs
+
+after-install :: move-to-fhs
+
+endif
diff --git a/SOPE/NGCards/versitCardsSaxDriver/versitCardsSaxDriver-Info.plist b/SOPE/NGCards/versitCardsSaxDriver/versitCardsSaxDriver-Info.plist
new file mode 100644 (file)
index 0000000..bc6c308
--- /dev/null
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+       <key>CFBundleDevelopmentRegion</key>
+       <string>English</string>
+       <key>CFBundleExecutable</key>
+       <string>versitCardsSaxDriver</string>
+       <key>CFBundleGetInfoString</key>
+       <string></string>
+       <key>CFBundleIconFile</key>
+       <string></string>
+       <key>CFBundleIdentifier</key>
+       <string>org.OpenGroupware.SOPE.sope-cards.versitCardsSaxDriver</string>
+       <key>CFBundleInfoDictionaryVersion</key>
+       <string>6.0</string>
+       <key>CFBundlePackageType</key>
+       <string>BNDL</string>
+       <key>CFBundleShortVersionString</key>
+       <string></string>
+       <key>CFBundleSignature</key>
+       <string>????</string>
+       <key>CFBundleVersion</key>
+       <string>1.0</string>
+</dict>
+</plist>
diff --git a/SOPE/NGCards/versitCardsSaxDriver/versitCardsSaxDriver.xcodeproj/project.pbxproj b/SOPE/NGCards/versitCardsSaxDriver/versitCardsSaxDriver.xcodeproj/project.pbxproj
new file mode 100644 (file)
index 0000000..05873fc
--- /dev/null
@@ -0,0 +1,389 @@
+// !$*UTF8*$!
+{
+       archiveVersion = 1;
+       classes = {
+       };
+       objectVersion = 42;
+       objects = {
+
+/* Begin PBXBuildFile section */
+               1656CD26058806680012D2BC /* VSSaxDriver.h in Headers */ = {isa = PBXBuildFile; fileRef = 1656CD24058806680012D2BC /* VSSaxDriver.h */; };
+               1656CD27058806680012D2BC /* VSSaxDriver.m in Sources */ = {isa = PBXBuildFile; fileRef = 1656CD25058806680012D2BC /* VSSaxDriver.m */; };
+               1656CD38058806CE0012D2BC /* bundle-info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 1656CD37058806CE0012D2BC /* bundle-info.plist */; };
+               1656CD7E058809660012D2BC /* VSCardSaxDriver.h in Headers */ = {isa = PBXBuildFile; fileRef = 1656CD7C058809660012D2BC /* VSCardSaxDriver.h */; };
+               1656CD7F058809660012D2BC /* VSCardSaxDriver.m in Sources */ = {isa = PBXBuildFile; fileRef = 1656CD7D058809660012D2BC /* VSCardSaxDriver.m */; };
+               AD0CFACC071FFF0000E72147 /* VSStringFormatter.h in Headers */ = {isa = PBXBuildFile; fileRef = AD0CFACA071FFF0000E72147 /* VSStringFormatter.h */; };
+               AD0CFACD071FFF0000E72147 /* VSStringFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = AD0CFACB071FFF0000E72147 /* VSStringFormatter.m */; };
+               AD5010A6071F18AE0020300E /* README in Resources */ = {isa = PBXBuildFile; fileRef = AD5010A5071F18AE0020300E /* README */; };
+               AD5010A8071F18B80020300E /* AUTHORS in Resources */ = {isa = PBXBuildFile; fileRef = AD5010A7071F18B80020300E /* AUTHORS */; };
+               AD5010BF071F18F00020300E /* COPYING in Resources */ = {isa = PBXBuildFile; fileRef = AD5010BE071F18F00020300E /* COPYING */; };
+               AD5010C1071F18FA0020300E /* COPYRIGHT in Resources */ = {isa = PBXBuildFile; fileRef = AD5010C0071F18FA0020300E /* COPYRIGHT */; };
+               AD665AB0071EB30200EC5911 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AD665AAF071EB30200EC5911 /* Foundation.framework */; };
+               AD665AB4071EB36D00EC5911 /* SaxObjC.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AD665AB3071EB36D00EC5911 /* SaxObjC.framework */; };
+               AD665AB6071EB38C00EC5911 /* common.h in Headers */ = {isa = PBXBuildFile; fileRef = AD665AB5071EB38C00EC5911 /* common.h */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXBuildStyle section */
+               1656CC9F058802980012D2BC /* Development */ = {
+                       isa = PBXBuildStyle;
+                       buildSettings = {
+                               COPY_PHASE_STRIP = NO;
+                               GCC_DYNAMIC_NO_PIC = NO;
+                               GCC_ENABLE_FIX_AND_CONTINUE = YES;
+                               GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
+                               GCC_OPTIMIZATION_LEVEL = 0;
+                               ZERO_LINK = YES;
+                       };
+                       name = Development;
+               };
+               1656CCA0058802990012D2BC /* Deployment */ = {
+                       isa = PBXBuildStyle;
+                       buildSettings = {
+                               COPY_PHASE_STRIP = YES;
+                               GCC_ENABLE_FIX_AND_CONTINUE = NO;
+                               ZERO_LINK = NO;
+                       };
+                       name = Deployment;
+               };
+/* End PBXBuildStyle section */
+
+/* Begin PBXFileReference section */
+               1656CCB80588036A0012D2BC /* versitCardsSaxDriver-Info.plist */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.plist; path = "versitCardsSaxDriver-Info.plist"; sourceTree = "<group>"; };
+               1656CD24058806680012D2BC /* VSSaxDriver.h */ = {isa = PBXFileReference; fileEncoding = 5; indentWidth = 2; lastKnownFileType = sourcecode.c.h; path = VSSaxDriver.h; sourceTree = "<group>"; };
+               1656CD25058806680012D2BC /* VSSaxDriver.m */ = {isa = PBXFileReference; fileEncoding = 5; indentWidth = 2; lastKnownFileType = sourcecode.c.objc; path = VSSaxDriver.m; sourceTree = "<group>"; };
+               1656CD37058806CE0012D2BC /* bundle-info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist; path = "bundle-info.plist"; sourceTree = "<group>"; };
+               1656CD7C058809660012D2BC /* VSCardSaxDriver.h */ = {isa = PBXFileReference; fileEncoding = 5; indentWidth = 2; lastKnownFileType = sourcecode.c.h; path = VSCardSaxDriver.h; sourceTree = "<group>"; };
+               1656CD7D058809660012D2BC /* VSCardSaxDriver.m */ = {isa = PBXFileReference; fileEncoding = 5; indentWidth = 2; lastKnownFileType = sourcecode.c.objc; path = VSCardSaxDriver.m; sourceTree = "<group>"; };
+               16800E2F05880E8E007700A5 /* versitCardsSaxDriver.sax */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = versitCardsSaxDriver.sax; sourceTree = BUILT_PRODUCTS_DIR; };
+               16800E4205880EC4007700A5 /* ChangeLog */ = {isa = PBXFileReference; explicitFileType = text; fileEncoding = 30; indentWidth = 8; path = ChangeLog; sourceTree = "<group>"; tabWidth = 8; usesTabs = 1; };
+               16800E4305880EC4007700A5 /* Version */ = {isa = PBXFileReference; explicitFileType = sourcecode.make; fileEncoding = 30; indentWidth = 8; path = Version; sourceTree = "<group>"; tabWidth = 8; };
+               AD0CFACA071FFF0000E72147 /* VSStringFormatter.h */ = {isa = PBXFileReference; fileEncoding = 5; indentWidth = 2; lastKnownFileType = sourcecode.c.h; path = VSStringFormatter.h; sourceTree = "<group>"; };
+               AD0CFACB071FFF0000E72147 /* VSStringFormatter.m */ = {isa = PBXFileReference; fileEncoding = 5; indentWidth = 2; lastKnownFileType = sourcecode.c.objc; path = VSStringFormatter.m; sourceTree = "<group>"; };
+               AD5010A5071F18AE0020300E /* README */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = README; sourceTree = "<group>"; };
+               AD5010A7071F18B80020300E /* AUTHORS */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = AUTHORS; sourceTree = "<group>"; };
+               AD5010BE071F18F00020300E /* COPYING */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = COPYING; sourceTree = "<group>"; };
+               AD5010C0071F18FA0020300E /* COPYRIGHT */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = COPYRIGHT; sourceTree = "<group>"; };
+               AD665AAF071EB30200EC5911 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = "<absolute>"; };
+               AD665AB3071EB36D00EC5911 /* SaxObjC.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SaxObjC.framework; path = "$(USER_LIBRARY_DIR)/EmbeddedFrameworks/Wrapper/SaxObjC.framework"; sourceTree = "<absolute>"; };
+               AD665AB5071EB38C00EC5911 /* common.h */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.h; path = common.h; sourceTree = "<group>"; };
+               AD665AB7071EB39600EC5911 /* GNUmakefile */ = {isa = PBXFileReference; explicitFileType = sourcecode.make; fileEncoding = 5; indentWidth = 8; path = GNUmakefile; sourceTree = "<group>"; tabWidth = 8; };
+               AD665AB8071EB39600EC5911 /* GNUmakefile.postamble */ = {isa = PBXFileReference; explicitFileType = sourcecode.make; fileEncoding = 5; indentWidth = 8; path = GNUmakefile.postamble; sourceTree = "<group>"; tabWidth = 8; };
+               AD665AB9071EB39600EC5911 /* GNUmakefile.preamble */ = {isa = PBXFileReference; explicitFileType = sourcecode.make; fileEncoding = 5; indentWidth = 8; path = GNUmakefile.preamble; sourceTree = "<group>"; tabWidth = 8; };
+               ADB07DC207666226003C5ACF /* fhs.make */ = {isa = PBXFileReference; explicitFileType = sourcecode.make; fileEncoding = 5; indentWidth = 8; path = fhs.make; sourceTree = "<group>"; tabWidth = 8; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+               1656CCAD058802B80012D2BC /* Frameworks */ = {
+                       isa = PBXFrameworksBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               AD665AB0071EB30200EC5911 /* Foundation.framework in Frameworks */,
+                               AD665AB4071EB36D00EC5911 /* SaxObjC.framework in Frameworks */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+               1656CC9D058802980012D2BC = {
+                       isa = PBXGroup;
+                       children = (
+                               AD5010A5071F18AE0020300E /* README */,
+                               AD5010C0071F18FA0020300E /* COPYRIGHT */,
+                               AD5010BE071F18F00020300E /* COPYING */,
+                               AD5010A7071F18B80020300E /* AUTHORS */,
+                               16800E4205880EC4007700A5 /* ChangeLog */,
+                               16800E4305880EC4007700A5 /* Version */,
+                               AD665AAE071EB2C200EC5911 /* Makefiles */,
+                               AD665ABF071EB3D500EC5911 /* Headers */,
+                               1656CD1F0588063D0012D2BC /* Classes */,
+                               1656CD8F058809E30012D2BC /* Resources */,
+                               1656CD92058809FD0012D2BC /* Frameworks */,
+                               16800E3905880EA1007700A5 /* Products */,
+                       );
+                       sourceTree = "<group>";
+               };
+               1656CD1F0588063D0012D2BC /* Classes */ = {
+                       isa = PBXGroup;
+                       children = (
+                               AD665AB5071EB38C00EC5911 /* common.h */,
+                               1656CD25058806680012D2BC /* VSSaxDriver.m */,
+                               1656CD7D058809660012D2BC /* VSCardSaxDriver.m */,
+                               AD0CFACB071FFF0000E72147 /* VSStringFormatter.m */,
+                       );
+                       name = Classes;
+                       sourceTree = "<group>";
+               };
+               1656CD8F058809E30012D2BC /* Resources */ = {
+                       isa = PBXGroup;
+                       children = (
+                               1656CCB80588036A0012D2BC /* versitCardsSaxDriver-Info.plist */,
+                               1656CD37058806CE0012D2BC /* bundle-info.plist */,
+                       );
+                       name = Resources;
+                       sourceTree = "<group>";
+               };
+               1656CD92058809FD0012D2BC /* Frameworks */ = {
+                       isa = PBXGroup;
+                       children = (
+                               AD665AB3071EB36D00EC5911 /* SaxObjC.framework */,
+                               AD665AAF071EB30200EC5911 /* Foundation.framework */,
+                       );
+                       name = Frameworks;
+                       sourceTree = "<group>";
+               };
+               16800E3905880EA1007700A5 /* Products */ = {
+                       isa = PBXGroup;
+                       children = (
+                               16800E2F05880E8E007700A5 /* versitCardsSaxDriver.sax */,
+                       );
+                       name = Products;
+                       sourceTree = "<group>";
+               };
+               AD665AAE071EB2C200EC5911 /* Makefiles */ = {
+                       isa = PBXGroup;
+                       children = (
+                               AD665AB7071EB39600EC5911 /* GNUmakefile */,
+                               AD665AB9071EB39600EC5911 /* GNUmakefile.preamble */,
+                               AD665AB8071EB39600EC5911 /* GNUmakefile.postamble */,
+                               ADB07DC207666226003C5ACF /* fhs.make */,
+                       );
+                       name = Makefiles;
+                       sourceTree = "<group>";
+               };
+               AD665ABF071EB3D500EC5911 /* Headers */ = {
+                       isa = PBXGroup;
+                       children = (
+                               1656CD24058806680012D2BC /* VSSaxDriver.h */,
+                               1656CD7C058809660012D2BC /* VSCardSaxDriver.h */,
+                               AD0CFACA071FFF0000E72147 /* VSStringFormatter.h */,
+                       );
+                       name = Headers;
+                       sourceTree = "<group>";
+               };
+/* End PBXGroup section */
+
+/* Begin PBXHeadersBuildPhase section */
+               1656CCAA058802B80012D2BC /* Headers */ = {
+                       isa = PBXHeadersBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               1656CD26058806680012D2BC /* VSSaxDriver.h in Headers */,
+                               1656CD7E058809660012D2BC /* VSCardSaxDriver.h in Headers */,
+                               AD665AB6071EB38C00EC5911 /* common.h in Headers */,
+                               AD0CFACC071FFF0000E72147 /* VSStringFormatter.h in Headers */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+/* End PBXHeadersBuildPhase section */
+
+/* Begin PBXNativeTarget section */
+               1656CCAE058802B80012D2BC /* versitCardsSaxDriver */ = {
+                       isa = PBXNativeTarget;
+                       buildConfigurationList = ADA07A610857395900993825 /* Build configuration list for PBXNativeTarget "versitCardsSaxDriver" */;
+                       buildPhases = (
+                               1656CCAA058802B80012D2BC /* Headers */,
+                               1656CCAB058802B80012D2BC /* Resources */,
+                               1656CCAC058802B80012D2BC /* Sources */,
+                               1656CCAD058802B80012D2BC /* Frameworks */,
+                       );
+                       buildRules = (
+                       );
+                       buildSettings = {
+                               FRAMEWORK_SEARCH_PATHS = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+                               GCC_PRECOMPILE_PREFIX_HEADER = YES;
+                               GCC_PREFIX_HEADER = common.h;
+                               INFOPLIST_FILE = "versitCardsSaxDriver-Info.plist";
+                               INSTALL_PATH = "$(USER_LIBRARY_DIR)/SaxDrivers-4.5";
+                               PRODUCT_NAME = versitCardsSaxDriver;
+                               WARNING_CFLAGS = "-Wmost";
+                               WRAPPER_EXTENSION = sax;
+                       };
+                       dependencies = (
+                       );
+                       name = versitCardsSaxDriver;
+                       productName = versitCardsSaxDriver;
+                       productReference = 16800E2F05880E8E007700A5 /* versitCardsSaxDriver.sax */;
+                       productSettingsXML = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
+<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">
+<plist version=\"1.0\">
+<dict>
+       <key>CFBundleDevelopmentRegion</key>
+       <string>English</string>
+       <key>CFBundleExecutable</key>
+       <string>versitCardsSaxDriver</string>
+       <key>CFBundleGetInfoString</key>
+       <string></string>
+       <key>CFBundleIconFile</key>
+       <string></string>
+       <key>CFBundleIdentifier</key>
+       <string>com.MySoftwareCompany.versitCardsSaxDriver</string>
+       <key>CFBundleInfoDictionaryVersion</key>
+       <string>6.0</string>
+       <key>CFBundlePackageType</key>
+       <string>APPL</string>
+       <key>CFBundleShortVersionString</key>
+       <string></string>
+       <key>CFBundleSignature</key>
+       <string>????</string>
+       <key>CFBundleVersion</key>
+       <string>1.0.0d1</string>
+</dict>
+</plist>
+";
+                       productType = "com.apple.product-type.bundle";
+               };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+               1656CCA1058802990012D2BC /* Project object */ = {
+                       isa = PBXProject;
+                       buildConfigurationList = ADA07A650857395900993825 /* Build configuration list for PBXProject "versitCardsSaxDriver" */;
+                       buildSettings = {
+                       };
+                       buildStyles = (
+                               1656CC9F058802980012D2BC /* Development */,
+                               1656CCA0058802990012D2BC /* Deployment */,
+                       );
+                       hasScannedForEncodings = 1;
+                       mainGroup = 1656CC9D058802980012D2BC;
+                       productRefGroup = 1656CC9D058802980012D2BC;
+                       projectDirPath = "";
+                       targets = (
+                               1656CCAE058802B80012D2BC /* versitCardsSaxDriver */,
+                       );
+               };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+               1656CCAB058802B80012D2BC /* Resources */ = {
+                       isa = PBXResourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               1656CD38058806CE0012D2BC /* bundle-info.plist in Resources */,
+                               AD5010A6071F18AE0020300E /* README in Resources */,
+                               AD5010A8071F18B80020300E /* AUTHORS in Resources */,
+                               AD5010BF071F18F00020300E /* COPYING in Resources */,
+                               AD5010C1071F18FA0020300E /* COPYRIGHT in Resources */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+               1656CCAC058802B80012D2BC /* Sources */ = {
+                       isa = PBXSourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               1656CD27058806680012D2BC /* VSSaxDriver.m in Sources */,
+                               1656CD7F058809660012D2BC /* VSCardSaxDriver.m in Sources */,
+                               AD0CFACD071FFF0000E72147 /* VSStringFormatter.m in Sources */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin XCBuildConfiguration section */
+               ADA07A620857395900993825 /* Development */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               COPY_PHASE_STRIP = NO;
+                               FRAMEWORK_SEARCH_PATHS = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+                               GCC_DYNAMIC_NO_PIC = NO;
+                               GCC_ENABLE_FIX_AND_CONTINUE = YES;
+                               GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
+                               GCC_OPTIMIZATION_LEVEL = 0;
+                               GCC_PRECOMPILE_PREFIX_HEADER = YES;
+                               GCC_PREFIX_HEADER = common.h;
+                               INFOPLIST_FILE = "versitCardsSaxDriver-Info.plist";
+                               INSTALL_PATH = "$(USER_LIBRARY_DIR)/SaxDrivers-4.5";
+                               PRODUCT_NAME = versitCardsSaxDriver;
+                               WARNING_CFLAGS = "-Wmost";
+                               WRAPPER_EXTENSION = sax;
+                               ZERO_LINK = YES;
+                       };
+                       name = Development;
+               };
+               ADA07A630857395900993825 /* Wrapper */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               COPY_PHASE_STRIP = YES;
+                               FRAMEWORK_SEARCH_PATHS = "$(USER_LIBRARY_DIR)/EmbeddedFrameworks";
+                               GCC_ENABLE_FIX_AND_CONTINUE = NO;
+                               GCC_PRECOMPILE_PREFIX_HEADER = YES;
+                               GCC_PREFIX_HEADER = common.h;
+                               INFOPLIST_FILE = "versitCardsSaxDriver-Info.plist";
+                               INSTALL_PATH = "$(USER_LIBRARY_DIR)/SaxDrivers-4.5";
+                               PRODUCT_NAME = versitCardsSaxDriver;
+                               SYMROOT = "$(USER_LIBRARY_DIR)/EmbeddedFrameworks";
+                               TEMP_DIR = "$(SYMROOT)/$(PROJECT_NAME).build";
+                               WARNING_CFLAGS = "-Wmost";
+                               WRAPPER_EXTENSION = sax;
+                               ZERO_LINK = NO;
+                       };
+                       name = Wrapper;
+               };
+               ADA07A640857395900993825 /* Default */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               FRAMEWORK_SEARCH_PATHS = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+                               GCC_PRECOMPILE_PREFIX_HEADER = YES;
+                               GCC_PREFIX_HEADER = common.h;
+                               INFOPLIST_FILE = "versitCardsSaxDriver-Info.plist";
+                               INSTALL_PATH = "$(USER_LIBRARY_DIR)/SaxDrivers-4.5";
+                               PRODUCT_NAME = versitCardsSaxDriver;
+                               WARNING_CFLAGS = "-Wmost";
+                               WRAPPER_EXTENSION = sax;
+                       };
+                       name = Default;
+               };
+               ADA07A660857395900993825 /* Development */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                       };
+                       name = Development;
+               };
+               ADA07A670857395900993825 /* Wrapper */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               ARCHS = (
+                                       ppc,
+                                       i386,
+                               );
+                               SDKROOT = /Developer/SDKs/MacOSX10.4u.sdk;
+                       };
+                       name = Wrapper;
+               };
+               ADA07A680857395900993825 /* Default */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                       };
+                       name = Default;
+               };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+               ADA07A610857395900993825 /* Build configuration list for PBXNativeTarget "versitCardsSaxDriver" */ = {
+                       isa = XCConfigurationList;
+                       buildConfigurations = (
+                               ADA07A620857395900993825 /* Development */,
+                               ADA07A630857395900993825 /* Wrapper */,
+                               ADA07A640857395900993825 /* Default */,
+                       );
+                       defaultConfigurationIsVisible = 0;
+                       defaultConfigurationName = Default;
+               };
+               ADA07A650857395900993825 /* Build configuration list for PBXProject "versitCardsSaxDriver" */ = {
+                       isa = XCConfigurationList;
+                       buildConfigurations = (
+                               ADA07A660857395900993825 /* Development */,
+                               ADA07A670857395900993825 /* Wrapper */,
+                               ADA07A680857395900993825 /* Default */,
+                       );
+                       defaultConfigurationIsVisible = 0;
+                       defaultConfigurationName = Default;
+               };
+/* End XCConfigurationList section */
+       };
+       rootObject = 1656CCA1058802990012D2BC /* Project object */;
+}
index ccca7fb7fc23ce31c77e24341d05c41048376111..3c7e4d78e5f56939ad747c3fd5a45a838fd43bd0 100644 (file)
 - (id)lookupHomeFolderForUID:(NSString *)_uid inContext:(id)_ctx;
 - (NSArray *)lookupCalendarFoldersForUIDs:(NSArray *)_uids inContext:(id)_ctx;
 
-/* raw saving */
-
-- (NSException *)primarySaveContentString:(NSString *)_iCalString;
-- (NSException *)primaryDelete;
-
 /* "iCal multifolder saves" */
 
 - (NSException *)saveContentString:(NSString *)_iCal baseSequence:(int)_v;
 - (NSException *)deleteWithBaseSequence:(int)_v;
 
 - (NSException *)saveContentString:(NSString *)_iCalString;
-- (NSException *)delete;
 
 - (NSException *)changeParticipationStatus:(NSString *)_status
   inContext:(id)_ctx;
index 1531823c2694a2abbdd879d4fd659443ef2c9bbd..42e4f01e5147f504b951b29050a2390febb20b4a 100644 (file)
 @interface SOGoAppointmentObject (PrivateAPI)
 - (NSString *) homePageURLForPerson: (iCalPerson *) _person;
   
-- (void)sendEMailUsingTemplateNamed:(NSString *)_pageName
-  forOldAppointment:(iCalEvent *)_newApt
-  andNewAppointment:(iCalEvent *)_oldApt
-  toAttendees:(NSArray *)_attendees;
-
-- (void)sendInvitationEMailForAppointment:(iCalEvent *)_apt
-  toAttendees:(NSArray *)_attendees;
-- (void)sendAppointmentUpdateEMailForOldAppointment:(iCalEvent *)_oldApt
-  newAppointment:(iCalEvent *)_newApt
-  toAttendees:(NSArray *)_attendees;
-- (void)sendAttendeeRemovalEMailForAppointment:(iCalEvent *)_apt
-  toAttendees:(NSArray *)_attendees;
-- (void)sendAppointmentDeletionEMailForAppointment:(iCalEvent *)_apt
-  toAttendees:(NSArray *)_attendees;
+- (void) sendEMailUsingTemplateNamed: (NSString *) _pageName
+                   forOldAppointment: (iCalEvent *) _newApt
+                   andNewAppointment: (iCalEvent *) _oldApt
+                         toAttendees: (NSArray *) _attendees;
+
+- (void) sendInvitationEMailForAppointment: (iCalEvent *) _apt
+                               toAttendees: (NSArray *) _attendees;
+- (void) sendAppointmentUpdateEMailForOldAppointment: (iCalEvent *) _oldApt
+                                      newAppointment: (iCalEvent *) _newApt
+                                         toAttendees: (NSArray *) _attendees;
+- (void) sendAttendeeRemovalEMailForAppointment: (iCalEvent *) _apt
+                                    toAttendees: (NSArray *) _attendees;
+- (void) sendAppointmentDeletionEMailForAppointment: (iCalEvent *) _apt
+                                        toAttendees: (NSArray *) _attendees;
 @end
 
 @implementation SOGoAppointmentObject
 
-static NSString                  *mailTemplateDefaultLanguage = nil;
+static NSString *mailTemplateDefaultLanguage = nil;
+static BOOL sendEMailNotifications = NO;
 
-+ (void)initialize {
++ (void) initialize
+{
   NSUserDefaults      *ud;
   static BOOL         didInit = NO;
   
-  if (didInit) return;
-  didInit = YES;
+  if (!didInit)
+    {
+      didInit = YES;
   
-  ud = [NSUserDefaults standardUserDefaults];
-  mailTemplateDefaultLanguage = [[ud stringForKey:@"SOGoDefaultLanguage"]
-                                     retain];
-  if (!mailTemplateDefaultLanguage)
-    mailTemplateDefaultLanguage = @"French";
+      ud = [NSUserDefaults standardUserDefaults];
+      mailTemplateDefaultLanguage = [[ud stringForKey:@"SOGoDefaultLanguage"]
+                                      retain];
+      if (!mailTemplateDefaultLanguage)
+        mailTemplateDefaultLanguage = @"French";
+
+      sendEMailNotifications
+        = [ud boolForKey: @"SOGoAppointmentSendEMailNotifications"];
+    }
 }
 
 /* accessors */
@@ -84,11 +91,11 @@ static NSString                  *mailTemplateDefaultLanguage = nil;
 }
 
 /* iCal handling */
-
-- (NSArray *)attendeeUIDsFromAppointment:(iCalEvent *)_apt {
+- (NSArray *) attendeeUIDsFromAppointment: (iCalEvent *) _apt
+{
   AgenorUserManager *um;
-  NSMutableArray    *uids;
-  NSArray  *attendees;
+  NSMutableArray *uids;
+  NSArray *attendees;
   unsigned i, count;
   NSString *email, *uid;
   
@@ -149,8 +156,8 @@ static NSString                  *mailTemplateDefaultLanguage = nil;
 
 - (NSException *)saveContentString:(NSString *)_iCal inUIDs:(NSArray *)_uids {
   NSEnumerator *e;
-  id           folder;
-  NSException  *allErrors = nil;
+  id folder;
+  NSException *allErrors = nil;
   id ctx;
 
   ctx = [[WOApplication application] context];
@@ -191,13 +198,15 @@ static NSString                  *mailTemplateDefaultLanguage = nil;
       allErrors = error;
     }
   }
+
   return allErrors;
 }
+
 - (NSException *)deleteInUIDs:(NSArray *)_uids {
   NSEnumerator *e;
-  id           folder;
-  NSException  *allErrors = nil;
-  id           ctx;
+  id folder;
+  NSException *allErrors = nil;
+  id ctx;
   
   ctx = [[WOApplication application] context];
   
@@ -262,13 +271,13 @@ static NSString                  *mailTemplateDefaultLanguage = nil;
   AgenorUserManager *um;
   iCalCalendar *newCalendar;
   iCalEvent *oldApt, *newApt;
-  iCalEventChanges  *changes;
-  iCalPerson        *organizer;
-  NSString          *oldContent, *uid;
-  NSArray           *uids, *props;
-  NSMutableArray    *attendees, *storeUIDs, *removedUIDs;
-  NSException       *storeError, *delError;
-  BOOL              updateForcesReconsider;
+  iCalEventChanges *changes;
+  iCalPerson *organizer;
+  NSString *oldContent, *uid;
+  NSArray *uids, *props;
+  NSMutableArray *attendees, *storeUIDs, *removedUIDs;
+  NSException *storeError, *delError;
+  BOOL updateForcesReconsider;
   
   updateForcesReconsider = NO;
 
@@ -312,14 +321,14 @@ static NSString                  *mailTemplateDefaultLanguage = nil;
   changes = [iCalEventChanges changesFromEvent: oldApt
                               toEvent: newApt];
 
-  uids        = [um getUIDsForICalPersons:[changes deletedAttendees]
+  uids = [um getUIDsForICalPersons:[changes deletedAttendees]
                     applyStrictMapping:NO];
   removedUIDs = [NSMutableArray arrayWithArray:uids];
 
-  uids        = [um getUIDsForICalPersons:[newApt attendees]
+  uids = [um getUIDsForICalPersons:[newApt attendees]
                     applyStrictMapping:NO];
-  storeUIDs   = [NSMutableArray arrayWithArray:uids];
-  props       = [changes updatedProperties];
+  storeUIDs = [NSMutableArray arrayWithArray:uids];
+  props = [changes updatedProperties];
 
   /* detect whether sequence has to be increased */
   if ([changes hasChanges])
@@ -328,7 +337,7 @@ static NSString                  *mailTemplateDefaultLanguage = nil;
   /* preserve organizer */
 
   organizer = [newApt organizer];
-  uid       = [um getUIDForICalPerson:organizer];
+  uid = [um getUIDForICalPerson:organizer];
   if (uid) {
     if (![storeUIDs containsObject:uid])
       [storeUIDs addObject:uid];
@@ -379,39 +388,43 @@ static NSString                  *mailTemplateDefaultLanguage = nil;
   /* perform storing */
 
   storeError = [self saveContentString:_iCal inUIDs:storeUIDs];
-  delError   = [self deleteInUIDs:removedUIDs];
+  delError = [self deleteInUIDs:removedUIDs];
 
   // TODO: make compound
   if (storeError != nil) return storeError;
   if (delError   != nil) return delError;
 
   /* email notifications */
+  if (sendEMailNotifications)
+    {
+      attendees = [NSMutableArray arrayWithArray:[changes insertedAttendees]];
+      [attendees removePerson:organizer];
+      [self sendInvitationEMailForAppointment:newApt
+            toAttendees:attendees];
+
+      if (updateForcesReconsider) {
+        attendees = [NSMutableArray arrayWithArray:[newApt attendees]];
+        [attendees removeObjectsInArray:[changes insertedAttendees]];
+        [attendees removePerson:organizer];
+        [self sendAppointmentUpdateEMailForOldAppointment:oldApt
+              newAppointment:newApt
+              toAttendees:attendees];
+      }
 
-  attendees = [NSMutableArray arrayWithArray:[changes insertedAttendees]];
-  [attendees removePerson:organizer];
-  [self sendInvitationEMailForAppointment:newApt
-        toAttendees:attendees];
-
-  if (updateForcesReconsider) {
-    attendees = [NSMutableArray arrayWithArray:[newApt attendees]];
-    [attendees removeObjectsInArray:[changes insertedAttendees]];
-    [attendees removePerson:organizer];
-    [self sendAppointmentUpdateEMailForOldAppointment:oldApt
-          newAppointment:newApt
-          toAttendees:attendees];
-  }
-
-  attendees = [NSMutableArray arrayWithArray:[changes deletedAttendees]];
-  [attendees removePerson: organizer];
-  if ([attendees count]) {
-    iCalEvent *canceledApt;
+      attendees = [NSMutableArray arrayWithArray:[changes deletedAttendees]];
+      [attendees removePerson: organizer];
+      if ([attendees count])
+        {
+          iCalEvent *canceledApt;
     
-    canceledApt = [newApt copy];
-    [(iCalCalendar *) [canceledApt parent] setMethod: @"cancel"];
-    [self sendAttendeeRemovalEMailForAppointment:canceledApt
-          toAttendees: attendees];
-    [canceledApt release];
-  }
+          canceledApt = [newApt copy];
+          [(iCalCalendar *) [canceledApt parent] setMethod: @"cancel"];
+          [self sendAttendeeRemovalEMailForAppointment:canceledApt
+                toAttendees: attendees];
+          [canceledApt release];
+        }
+    }
+
   return nil;
 }
 
@@ -431,8 +444,8 @@ static NSString                  *mailTemplateDefaultLanguage = nil;
      - send iMIP mail for all folders not found
   */
   iCalEvent *apt;
-  NSArray         *removedUIDs;
-  NSMutableArray  *attendees;
+  NSArray *removedUIDs;
+  NSMutableArray *attendees;
 
   /* load existing content */
 
@@ -440,116 +453,123 @@ static NSString                  *mailTemplateDefaultLanguage = nil;
   
   /* compare sequence if requested */
 
-  if (_v != 0) {
-    // TODO
-  }
+//   if (_v != 0) {
+//     // TODO
+//   }
   
   removedUIDs = [self attendeeUIDsFromAppointment:apt];
 
-  /* send notification email to attendees excluding organizer */
-  attendees = [NSMutableArray arrayWithArray:[apt attendees]];
-  [attendees removePerson:[apt organizer]];
+  if (sendEMailNotifications)
+    {
+      /* send notification email to attendees excluding organizer */
+      attendees = [NSMutableArray arrayWithArray:[apt attendees]];
+      [attendees removePerson:[apt organizer]];
   
-  /* flag appointment as being canceled */
-  [(iCalCalendar *) [apt parent] setMethod: @"cancel"];
-  [apt increaseSequence];
+      /* flag appointment as being canceled */
+      [(iCalCalendar *) [apt parent] setMethod: @"cancel"];
+      [apt increaseSequence];
 
-  /* remove all attendees to signal complete removal */
-  [apt removeAllAttendees];
+      /* remove all attendees to signal complete removal */
+      [apt removeAllAttendees];
 
-  /* send notification email */
-  [self sendAppointmentDeletionEMailForAppointment:apt
-        toAttendees:attendees];
+      /* send notification email */
+      [self sendAppointmentDeletionEMailForAppointment:apt
+            toAttendees:attendees];
+    }
 
   /* perform */
-  
+
   return [self deleteInUIDs:removedUIDs];
 }
 
-- (NSException *)saveContentString:(NSString *)_iCalString {
-  return [self saveContentString:_iCalString baseSequence:0];
+- (NSException *) saveContentString: (NSString *) _iCalString
+{
+  return [self saveContentString: _iCalString baseSequence: 0];
 }
 
-- (NSException *)changeParticipationStatus:(NSString *)_status
-  inContext:(id)_ctx
+- (NSException *) changeParticipationStatus: (NSString *) _status
+                                  inContext: (id) _ctx
 {
   iCalEvent *apt;
-  iCalPerson      *p;
-  NSString        *newContent;
-  NSException     *ex;
-  NSString        *myEMail;
+  iCalPerson *p;
+  NSString *newContent;
+  NSException *ex;
+  NSString *myEMail;
   
+  ex = nil;
+
   // TODO: do we need to use SOGoAppointment? (prefer iCalEvent?)
   apt = [self event];
 
-  if (apt == nil) {
-    return [NSException exceptionWithHTTPStatus:500 /* Server Error */
-                        reason:@"unable to parse appointment record"];
-  }
-  
-  myEMail = [[_ctx activeUser] email];
-  if ((p = [apt findParticipantWithEmail:myEMail]) == nil) {
-    return [NSException exceptionWithHTTPStatus:404 /* Not Found */
-                        reason:@"user does not participate in this "
-                               @"appointment"];
-  }
-  
-  [p setPartStat:_status];
-  newContent = [[apt parent] versitString];
-  
+  if (apt)
+    {
+      myEMail = [[_ctx activeUser] email];
+      p = [apt findParticipantWithEmail: myEMail];
+      if (p)
+        {
   // TODO: send iMIP reply mails?
   
-//   [apt release]; apt = nil;
-  
-  if (newContent == nil) {
-    return [NSException exceptionWithHTTPStatus:500 /* Server Error */
-                        reason:@"Could not generate iCalendar data ..."];
-  }
-  
-  if ((ex = [self saveContentString:newContent]) != nil) {
-    // TODO: why is the exception wrapped?
-    return [NSException exceptionWithHTTPStatus:500 /* Server Error */
-                        reason:[ex reason]];
-  }
-  
-  return nil /* means: no error */;
+          [p setPartStat:_status];
+          newContent = [[apt parent] versitString];
+          if (newContent)
+            {
+              ex = [self saveContentString:newContent];
+              if (ex)
+                // TODO: why is the exception wrapped?
+                /* Server Error */
+                ex = [NSException exceptionWithHTTPStatus: 500
+                                  reason: [ex reason]];
+            }
+          else
+            ex
+              = [NSException exceptionWithHTTPStatus: 500 /* Server Error */
+                             reason: @"Could not generate iCalendar data ..."];
+        }
+      else
+        ex = [NSException exceptionWithHTTPStatus: 404 /* Not Found */
+                          reason: @"user does not participate in this "
+                          @"appointment"];
+    }
+  else
+    ex = [NSException exceptionWithHTTPStatus:500 /* Server Error */
+                      reason:@"unable to parse appointment record"];
+
+  return ex;
 }
 
 
 /* message type */
 
-- (NSString *)outlookMessageClass {
+- (NSString *) outlookMessageClass
+{
   return @"IPM.Appointment";
 }
 
 /* EMail Notifications */
 
-- (NSString *)homePageURLForPerson:(iCalPerson *)_person {
-  static AgenorUserManager *um      = nil;
-  static NSString          *baseURL = nil;
+- (NSString *) homePageURLForPerson: (iCalPerson *) _person
+{
+  NSString *baseURL;
   NSString *uid;
+  WOContext *ctx;
+  NSArray *traversalObjects;
 
-  if (!um) {
-    WOContext *ctx;
-    NSArray   *traversalObjects;
-
-    um = [[AgenorUserManager sharedUserManager] retain];
-
-    /* generate URL from traversal stack */
-    ctx = [[WOApplication application] context];
-    traversalObjects = [ctx objectTraversalStack];
-    if ([traversalObjects count] >= 1) {
-      baseURL = [[[traversalObjects objectAtIndex:0] baseURLInContext:ctx]
-                                                     retain];
-    }
-    else {
-      [self warnWithFormat:@"Unable to create baseURL from context!"];
+  /* generate URL from traversal stack */
+  ctx = [[WOApplication application] context];
+  traversalObjects = [ctx objectTraversalStack];
+  if ([traversalObjects count] > 0)
+    baseURL = [[traversalObjects objectAtIndex:0] baseURLInContext:ctx];
+  else
+    {
       baseURL = @"http://localhost/";
+      [self warnWithFormat:@"Unable to create baseURL from context!"];
     }
-  }
-  uid = [um getUIDForEmail:[_person rfc822Email]];
-  if (!uid) return nil;
-  return [NSString stringWithFormat:@"%@%@", baseURL, uid];
+  uid = [[AgenorUserManager sharedUserManager]
+          getUIDForEmail: [_person rfc822Email]];
+
+  return ((uid)
+          ? [NSString stringWithFormat:@"%@%@", baseURL, uid]
+          : nil);
 }
 
 - (NSException *) saveContentString: (NSString *) contentString
@@ -581,175 +601,169 @@ static NSString                  *mailTemplateDefaultLanguage = nil;
                 baseVersion: baseVersion];
 }
 
-- (void)sendEMailUsingTemplateNamed: (NSString *)_pageName
-                  forOldAppointment: (iCalEvent *)_oldApt
-                  andNewAppointment: (iCalEvent *)_newApt
-                        toAttendees: (NSArray *)_attendees
+- (void) sendEMailUsingTemplateNamed: (NSString *) _pageName
+                   forOldAppointment: (iCalEvent *) _oldApt
+                   andNewAppointment: (iCalEvent *) _newApt
+                         toAttendees: (NSArray *) _attendees
 {
-  NSString                *pageName;
-  iCalPerson              *organizer;
-  NSString                *cn, *sender, *iCalString;
-  NGSendMail              *sendmail;
-  WOApplication           *app;
-  unsigned                i, count;
-
-  if (![_attendees count]) return; // another job neatly done :-)
-
-  /* sender */
-
-  organizer = [_newApt organizer];
-  cn        = [organizer cnWithoutQuotes];
-  if (cn) {
-    sender = [NSString stringWithFormat:@"%@ <%@>",
-                                        cn,
-                                        [organizer rfc822Email]];
-  }
-  else {
-    sender = [organizer rfc822Email];
-  }
+  NSString *pageName;
+  iCalPerson *organizer;
+  NSString *cn, *sender, *iCalString;
+  NGSendMail *sendmail;
+  WOApplication *app;
+  unsigned i, count;
+  iCalPerson *attendee;
+  NSString *recipient;
+  SOGoAptMailNotification *p;
+  NSString *subject, *text, *header;
+  NGMutableHashMap *headerMap;
+  NGMimeMessage *msg;
+  NGMimeBodyPart *bodyPart;
+  NGMimeMultipartBody *body;
+
+  if ([_attendees count])
+    {
+      /* sender */
+
+      organizer = [_newApt organizer];
+      cn = [organizer cnWithoutQuotes];
+      if (cn)
+        sender = [NSString stringWithFormat:@"%@ <%@>",
+                           cn,
+                           [organizer rfc822Email]];
+      else
+        sender = [organizer rfc822Email];
 
-  /* generate iCalString once */
-  iCalString = [[_newApt parent] versitString];
+      /* generate iCalString once */
+      iCalString = [[_newApt parent] versitString];
   
-  /* get sendmail object */
-  sendmail   = [NGSendMail sharedSendMail];
+      /* get sendmail object */
+      sendmail = [NGSendMail sharedSendMail];
 
-  /* get WOApplication instance */
-  app        = [WOApplication application];
+      /* get WOApplication instance */
+      app = [WOApplication application];
 
-  /* generate dynamic message content */
-
-  count = [_attendees count];
-  for (i = 0; i < count; i++) {
-    iCalPerson              *attendee;
-    NSString                *recipient;
-    SOGoAptMailNotification *p;
-    NSString                *subject, *text, *header;
-    NGMutableHashMap        *headerMap;
-    NGMimeMessage           *msg;
-    NGMimeBodyPart          *bodyPart;
-    NGMimeMultipartBody     *body;
-    
-    attendee  = [_attendees objectAtIndex:i];
-    
-    /* construct recipient */
-    cn        = [attendee cn];
-    if (cn) {
-      recipient = [NSString stringWithFormat:@"%@ <%@>",
-                                             cn,
-                                             [attendee rfc822Email]];
-    }
-    else {
-      recipient = [attendee rfc822Email];
-    }
+      /* generate dynamic message content */
 
-    /* create page name */
-    // TODO: select user's default language?
-    pageName   = [NSString stringWithFormat:@"SOGoAptMail%@%@",
-                                            mailTemplateDefaultLanguage,
-                                            _pageName];
-    /* construct message content */
-    p = [app pageWithName:pageName inContext:[WOContext context]];
-    [p setNewApt:_newApt];
-    [p setOldApt:_oldApt];
-    [p setHomePageURL:[self homePageURLForPerson:attendee]];
-    [p setViewTZ: [self userTimeZone: cn]];
-    subject = [p getSubject];
-    text    = [p getBody];
-
-    /* construct message */
-    headerMap = [NGMutableHashMap hashMapWithCapacity:5];
-    
-    /* NOTE: multipart/alternative seems like the correct choice but
-     * unfortunately Thunderbird doesn't offer the rich content alternative
-     * at all. Mail.app shows the rich content alternative _only_
-     * so we'll stick with multipart/mixed for the time being.
-     */
-    [headerMap setObject:@"multipart/mixed"    forKey:@"content-type"];
-    [headerMap setObject:sender                forKey:@"From"];
-    [headerMap setObject:recipient             forKey:@"To"];
-    [headerMap setObject:[NSCalendarDate date] forKey:@"date"];
-    [headerMap setObject:subject               forKey:@"Subject"];
-    msg = [NGMimeMessage messageWithHeader:headerMap];
-    
-    /* multipart body */
-    body = [[NGMimeMultipartBody alloc] initWithPart:msg];
+      count = [_attendees count];
+      for (i = 0; i < count; i++)
+        {
+          attendee = [_attendees objectAtIndex:i];
+
+          /* construct recipient */
+          cn = [attendee cn];
+          if (cn)
+            recipient = [NSString stringWithFormat: @"%@ <%@>",
+                                  cn,
+                                  [attendee rfc822Email]];
+          else
+            recipient = [attendee rfc822Email];
+
+          /* create page name */
+          // TODO: select user's default language?
+          pageName = [NSString stringWithFormat: @"SOGoAptMail%@%@",
+                               mailTemplateDefaultLanguage,
+                               _pageName];
+          /* construct message content */
+          p = [app pageWithName: pageName inContext: [WOContext context]];
+          [p setNewApt: _newApt];
+          [p setOldApt: _oldApt];
+          [p setHomePageURL: [self homePageURLForPerson: attendee]];
+          [p setViewTZ: [self userTimeZone: cn]];
+          subject = [p getSubject];
+          text = [p getBody];
+
+          /* construct message */
+          headerMap = [NGMutableHashMap hashMapWithCapacity: 5];
     
-    /* text part */
-    headerMap = [NGMutableHashMap hashMapWithCapacity:1];
-    [headerMap setObject:@"text/plain; charset=utf-8" forKey:@"content-type"];
-    bodyPart = [NGMimeBodyPart bodyPartWithHeader:headerMap];
-    [bodyPart setBody:[text dataUsingEncoding:NSUTF8StringEncoding]];
-
-    /* attach text part to multipart body */
-    [body addBodyPart:bodyPart];
+          /* NOTE: multipart/alternative seems like the correct choice but
+           * unfortunately Thunderbird doesn't offer the rich content alternative
+           * at all. Mail.app shows the rich content alternative _only_
+           * so we'll stick with multipart/mixed for the time being.
+           */
+          [headerMap setObject: @"multipart/mixed" forKey: @"content-type"];
+          [headerMap setObject: sender forKey: @"From"];
+          [headerMap setObject: recipient forKey: @"To"];
+          [headerMap setObject: [NSCalendarDate date] forKey: @"date"];
+          [headerMap setObject: subject forKey: @"Subject"];
+          msg = [NGMimeMessage messageWithHeader: headerMap];
+
+          /* multipart body */
+          body = [[NGMimeMultipartBody alloc] initWithPart: msg];
     
-    /* calendar part */
-    header     = [NSString stringWithFormat:@"text/calendar; method=%@;"
-                                            @" charset=utf-8",
-                           [(iCalCalendar *) [_newApt parent] method]];
-    headerMap  = [NGMutableHashMap hashMapWithCapacity:1];
-    [headerMap setObject:header forKey:@"content-type"];
-    bodyPart   = [NGMimeBodyPart bodyPartWithHeader:headerMap];
-    [bodyPart setBody:[iCalString dataUsingEncoding:NSUTF8StringEncoding]];
+          /* text part */
+          headerMap = [NGMutableHashMap hashMapWithCapacity: 1];
+          [headerMap setObject: @"text/plain; charset=utf-8"
+                     forKey: @"content-type"];
+          bodyPart = [NGMimeBodyPart bodyPartWithHeader: headerMap];
+          [bodyPart setBody: [text dataUsingEncoding: NSUTF8StringEncoding]];
+
+          /* attach text part to multipart body */
+          [body addBodyPart: bodyPart];
     
-    /* attach calendar part to multipart body */
-    [body addBodyPart:bodyPart];
+          /* calendar part */
+          header = [NSString stringWithFormat: @"text/calendar; method=%@;"
+                             @" charset=utf-8",
+                             [(iCalCalendar *) [_newApt parent] method]];
+          headerMap = [NGMutableHashMap hashMapWithCapacity: 1];
+          [headerMap setObject:header forKey: @"content-type"];
+          bodyPart = [NGMimeBodyPart bodyPartWithHeader: headerMap];
+          [bodyPart setBody: [iCalString dataUsingEncoding: NSUTF8StringEncoding]];
+
+          /* attach calendar part to multipart body */
+          [body addBodyPart: bodyPart];
     
-    /* attach multipart body to message */
-    [msg setBody:body];
-    [body release];
-
-    /* send the damn thing */
-    [sendmail sendMimePart:msg
-              toRecipients:[NSArray arrayWithObject:[attendee rfc822Email]]
-              sender:[organizer rfc822Email]];
-  }
+          /* attach multipart body to message */
+          [msg setBody: body];
+          [body release];
+
+          /* send the damn thing */
+          [sendmail sendMimePart: msg
+                    toRecipients: [NSArray arrayWithObject: [attendee rfc822Email]]
+                    sender: [organizer rfc822Email]];
+        }
+    }
 }
 
-- (void)sendInvitationEMailForAppointment:(iCalEvent *)_apt
-  toAttendees:(NSArray *)_attendees
+- (void) sendInvitationEMailForAppointment: (iCalEvent *) _apt
+                               toAttendees: (NSArray *) _attendees
 {
-  if (![_attendees count]) return; // another job neatly done :-)
-
-  [self sendEMailUsingTemplateNamed:@"Invitation"
-        forOldAppointment:nil
-        andNewAppointment:_apt
-        toAttendees:_attendees];
+  if ([_attendees count])
+    [self sendEMailUsingTemplateNamed: @"Invitation"
+          forOldAppointment: nil
+          andNewAppointment: _apt
+          toAttendees: _attendees];
 }
 
-- (void)sendAppointmentUpdateEMailForOldAppointment:(iCalEvent *)_oldApt
-  newAppointment:(iCalEvent *)_newApt
-  toAttendees:(NSArray *)_attendees
+- (void) sendAppointmentUpdateEMailForOldAppointment: (iCalEvent *) _oldApt
+                                      newAppointment: (iCalEvent *) _newApt
+                                         toAttendees: (NSArray *) _attendees
 {
-  if (![_attendees count]) return;
-  
-  [self sendEMailUsingTemplateNamed:@"Update"
-        forOldAppointment:_oldApt
-        andNewAppointment:_newApt
-        toAttendees:_attendees];
+  if ([_attendees count])
+    [self sendEMailUsingTemplateNamed: @"Update"
+          forOldAppointment: _oldApt
+          andNewAppointment: _newApt
+          toAttendees: _attendees];
 }
 
-- (void)sendAttendeeRemovalEMailForAppointment:(iCalEvent *)_apt
-  toAttendees:(NSArray *)_attendees
+- (void) sendAttendeeRemovalEMailForAppointment: (iCalEvent *) _apt
+                                    toAttendees: (NSArray *) _attendees
 {
-  if (![_attendees count]) return;
-
-  [self sendEMailUsingTemplateNamed:@"Removal"
-        forOldAppointment:nil
-        andNewAppointment:_apt
-        toAttendees:_attendees];
+  if ([_attendees count])
+    [self sendEMailUsingTemplateNamed: @"Removal"
+          forOldAppointment: nil
+          andNewAppointment: _apt
+          toAttendees: _attendees];
 }
 
-- (void)sendAppointmentDeletionEMailForAppointment:(iCalEvent *)_apt
-  toAttendees:(NSArray *)_attendees
+- (void) sendAppointmentDeletionEMailForAppointment: (iCalEvent *) _apt
+                                        toAttendees: (NSArray *) _attendees
 {
-  if (![_attendees count]) return;
-
-  [self sendEMailUsingTemplateNamed:@"Deletion"
-        forOldAppointment:nil
-        andNewAppointment:_apt
-        toAttendees:_attendees];
+  if ([_attendees count])
+    [self sendEMailUsingTemplateNamed: @"Deletion"
+          forOldAppointment: nil
+          andNewAppointment: _apt
+          toAttendees: _attendees];
 }
 
 - (NSString *) davContentType
index 6d93a4afe64542513f5faa133576109b9c90868e..637c6f67d98e1bdb5db7d470812a2af395137001 100644 (file)
@@ -39,6 +39,9 @@
 - (NSString *) iCalString;
 - (iCalCalendar *) calendar;
 
+- (NSException *) primarySaveContentString: (NSString *) _iCalString;
+- (NSException *) primaryDelete;
+
 - (NSException *) delete;
 
 @end
index 40f29e1a19fc4d2b01ff2f14acfa172441b8ed5e..adb0cc801a67ed750a2004d27f38c27f8c5d040b 100644 (file)
 
 /* raw saving */
 
-- (NSException *)primarySaveContentString:(NSString *)_iCalString {
+- (NSException *) primarySaveContentString: (NSString *) _iCalString
+{
   return [super saveContentString:_iCalString];
 }
 
-- (NSException *)primaryDelete {
+- (NSException *) primaryDelete
+{
   return [super delete];
 }
 
-- (NSException *)deleteWithBaseSequence: (int) a
+- (NSException *) deleteWithBaseSequence: (int) a
 {
   [self subclassResponsibility: _cmd];
 
index 20ad9f726d7e8140e6d3bf1b240049c66ebedb69..d0603786588aa6e3250e8bcc7406bb7f9fed93a1 100644 (file)
 - (id)lookupHomeFolderForUID:(NSString *)_uid inContext:(id)_ctx;
 - (NSArray *)lookupCalendarFoldersForUIDs:(NSArray *)_uids inContext:(id)_ctx;
 
-/* raw saving */
-
-- (NSException *)primarySaveContentString:(NSString *)_iCalString;
-- (NSException *)primaryDelete;
-
 /* "iCal multifolder saves" */
 
 - (NSException *)saveContentString:(NSString *)_iCal baseSequence:(int)_v;
 - (NSException *)deleteWithBaseSequence:(int)_v;
 
 - (NSException *)saveContentString:(NSString *)_iCalString;
-- (NSException *)delete;
 
 - (NSException *)changeParticipationStatus:(NSString *)_status
                                  inContext:(id)_ctx;
index e685b16b1ade19746ab0d8e3a79917a1bf7e16bb..4fad16f24900d83df31e64c3f2bb4e8b56a0d667 100644 (file)
@@ -8,6 +8,6 @@ SUBPROJECTS = \
        Appointments    \
        Contacts        \
        Mailer          \
-       Sieve           \
+#      Sieve           \
 
 include $(GNUSTEP_MAKEFILES)/aggregate.make
index 45d76d7b27c1aa7d571d7306d563084005aa7768..285c3296bb7ce4f05942e90adce7b7cd50dc99fb 100644 (file)
@@ -1,4 +1,4 @@
-/* NSString+URL.h - this file is part of SOGo
+/* NSString+Utilities.h - this file is part of SOGo
  *
  * Copyright (C) 2006 Inverse groupe conseil
  *
index ae0b79e85181fd98855b1d44a56ea593ec1cb9a5..03609d0c536588aa936deb060c79a7505dbeb8b7 100644 (file)
@@ -1,4 +1,4 @@
-/* NSString+URL.m - this file is part of SOGo
+/* NSString+Utilities.m - this file is part of SOGo
  *
  * Copyright (C) 2006  Inverse group conseil
  *
index 0aa6552e5cc45c97635ee452c19a959ab8877127..a44d55325dcf9e9f34472a60862e48e7d8759cd5 100644 (file)
@@ -94,32 +94,39 @@ static SOGoAuthenticator *auth = nil;
 - (SoUser *) userInContext:(WOContext *)_ctx
 {
   static SoUser *anonymous = nil, *freebusy;
-  NSString  *login;
-  
+  SoUser *user;
+  NSArray *traversalPath;
+  NSString *login;
+
   if (!anonymous)
     anonymous
       = [[SOGoUser alloc] initWithLogin:@"anonymous"
                          roles: [NSArray arrayWithObject: SoRole_Anonymous]];
-
   if (!freebusy)
     freebusy
       = [[SOGoUser alloc] initWithLogin: @"freebusy"
                           roles: [NSArray arrayWithObject: SOGoRole_FreeBusy]];
 
-  if ((login = [self checkCredentialsInContext:_ctx]) == nil)
-    /* some error (otherwise result would have been anonymous */
-    return nil;
-  
-  if ([login isEqualToString: @"anonymous"])
-    return anonymous;
-  else if ([login isEqualToString: @"freebusy"])
-    return freebusy;
-
-//   uroles = [NSMutableArray arrayWithArray: ];
+  login = [self checkCredentialsInContext:_ctx];
+  if (login)
+    {
+      if ([login isEqualToString: @"anonymous"])
+        {
+          traversalPath = [_ctx objectForKey: @"SoRequestTraversalPath"];
+          if ([[traversalPath lastObject] isEqualToString: @"freebusy.ifb"])
+            user = freebusy;
+          else
+            user = anonymous;
+        }
+      else
+        user = [[[SOGoUser alloc] initWithLogin: login
+                                  roles: [self rolesForLogin: login]]
+                 autorelease];
+    }
+  else
+    user = nil;
 
-  return [[[SOGoUser alloc] initWithLogin: login
-                            roles: [self rolesForLogin: login]]
-          autorelease];
+  return user;
 }
 
 // - (BOOL) renderException: (NSException *) exception
index 8b1fb43d98ffb0bf2f9ebee77630fd9d7d11d639..3684a1e4612e50857436515d560cbcbc277e9e9c 100644 (file)
@@ -10,14 +10,15 @@ BUNDLE_INSTALL_DIR   = $(GNUSTEP_USER_ROOT)/Library/SOGo-$(MAJOR_VERSION).$(MINO
 WOBUNDLE_EXTENSION   = $(BUNDLE_EXTENSION)
 WOBUNDLE_INSTALL_DIR = $(BUNDLE_INSTALL_DIR)
 
-SYSTEM_LIB_DIR += -L/usr/local/lib -L/usr/lib
+SYSTEM_LIB_DIR += -L/usr/local/lib -L/usr/lib
 
 ADDITIONAL_INCLUDE_DIRS += \
        -I.. \
        -I../..
 
 ADDITIONAL_LIB_DIRS += \
-       -L../SOGo/$(GNUSTEP_OBJ_DIR)/ \
+        -L../SOGo/$(GNUSTEP_OBJ_DIR)/ \
+       -L../../SOGo/$(GNUSTEP_OBJ_DIR)/ \
        -L../../OGoContentStore/$(GNUSTEP_OBJ_DIR)/
 
 BUNDLE_LIBS += \
index 676fddfbc2c04c0b4f64da8b71029044169827b5..563bdabe7c00d795542764a15b75f6a032e2e8ae 100644 (file)
@@ -27,7 +27,7 @@
 #import <SoObjects/Mailer/SOGoMailObject.h>
 #import "common.h"
 
-#import <SOGo/NSString+URL.h>
+#import <SOGo/NSString+Utilities.h>
 
 @implementation UIxMailEditorAction
 
index 77e8d59114369a3dd0ccae61d398c8c316de514b..8411b0e1b11926a1fd4dbb7d13f9983628bfba43 100644 (file)
@@ -453,6 +453,9 @@ static int attachmentFlagSize = 8096;
   NSString *specificMessage;
 
   request = [[self context] request];
+
+  [[self clientObject] flushMailCaches];
+
   specificMessage = [request formValueForKey: @"pageforuid"];
   self->firstMessageNumber
     = ((specificMessage)
index 5a353f21360b53e9288ce68e8e9b5da8b890dcdd..59ab845d6144ef34ea6d9a7f9dc5b684cbd34445 100644 (file)
@@ -25,7 +25,7 @@
 #import <NGObjWeb/SoHTTPAuthenticator.h>
 #import <NGObjWeb/WOResourceManager.h>
 
-#import <SOGo/NSString+URL.h>
+#import <SOGo/NSString+Utilities.h>
 
 #import <SOGo/SOGoUser.h>
 #import <SOGo/SOGoObject.h>
index c84369be94f0b0978b508260cd1bd94710e8df8c..b25de8200dcef728631bcc6b5c2f87eea5fa5e17 100644 (file)
@@ -104,8 +104,6 @@ darkenedColor (const char value)
                         (255 / colorTable[0]) - 1];
     }
 
-  NSLog(@"color = '%@'", color);
-
   return color;
 }
 
index 97c87337f0a87aed124a8c9eaf72d1a3d7f141e2..213e48b0187681f5e14b1d6b17d0674c91d141c8 100644 (file)
@@ -8,39 +8,40 @@
     xmlns:label="OGo:label"
     className="UIxPageFrame"
     title="panelTitle"
-    const:popup="YES"
-    >
-    <div id="headerArea">
-      <div id="attachmentsArea">
-        <var:string label:value="Attachments:" />
-        <div id="compose_attachments_list"
-          onclick="clickedEditorAttach(this);"
-          ><var:foreach list="attachmentNames" item="attachmentName">
+    const:popup="YES">
+    <form name="pageform">
+      <div id="headerArea">
+        <div id="attachmentsArea">
+          <var:string label:value="Attachments:" />
+          <div id="compose_attachments_list"
+            onclick="clickedEditorAttach(this);"
+            ><var:foreach list="attachmentNames" item="attachmentName">
             <var:string value="attachmentName" /><br />
-          </var:foreach>
+            </var:foreach>
+          </div>
         </div>
+        <span class="headerField"><var:string label:value="From" />:</span>
+        <var:popup const:name="from"
+          list="fromEMails"
+          item="item"
+          selection="from"
+          /><br />
+        <div>
+          <var:component className="UIxMailToSelection"
+            to="to" cc="cc" bcc="bcc" />
+        </div>
+        <div class="addressListElement" id="subjectRow"
+          ><span class="headerField"><var:string label:value="Subject"
+              />:</span
+            ><span class="headerInput"
+            ><input name="subject"
+              type="text"
+              class="textField"
+              var:value="subject"
+              /></span></div>
       </div>
-      <span class="headerField"><var:string label:value="From" />:</span>
-      <var:popup const:name="from"
-        list="fromEMails"
-        item="item"
-        selection="from"
-        /><br />
-      <div>
-        <var:component className="UIxMailToSelection"
-          to="to" cc="cc" bcc="bcc" />
-      </div>
-      <div class="addressListElement" id="subjectRow"
-        ><span class="headerField"><var:string label:value="Subject"
-            />:</span
-          ><span class="headerInput"
-          ><input name="subject"
-            type="text"
-            class="textField"
-            var:value="subject"
-            /></span></div>
-    </div>
     <!-- separator line -->
-    <textarea name="content" var:value="text" />
+      <textarea name="content" var:value="text" />
     <!-- img rsrc:src="tbird_073_compose.png" alt="screenshot" / -->
+    </form>
   </var:component>
index 3c150cfbbbfb90b1361498798391bdfaa2854614..b526b960eb2c9272f1c3b63a0af05563f60d4148 100644 (file)
@@ -398,11 +398,13 @@ function onMenuDeleteMessage(event) {
 }
 
 function onMailboxTreeItemClick(event) {
-  var topNode = $('d');
+  var topNode = $("d");
   var mailbox = this.parentNode.getAttribute("dataname");
 
-  if (topNode.selectedEntry)
+  if (topNode.selectedEntry) {
+    log ("deselecting");
     topNode.selectedEntry.deselect();
+  }
   this.select();
   topNode.selectedEntry = this;
 
@@ -532,8 +534,8 @@ function onFolderMenuClick(event)
   var onhide, menuName;
   
   var menutype = this.parentNode.getAttribute("datatype");
-  log("parentNode: " + this.parentNode.tagName);
-  log("menutype: " + menutype);
+//   log("parentNode: " + this.parentNode.tagName);
+//   log("menutype: " + menutype);
   if (menutype) {
     if (menutype == "inbox") {
       menuName = "inboxIconMenu";
@@ -813,7 +815,7 @@ function expandUpperTree(node)
 function initMailboxSelection(mailboxName)
 {
   currentMailbox = mailboxName;
-
+  log("initMailboxSelection: " + mailboxName);
   var tree = $("d");
   var treeNodes = document.getElementsByClassName("dTreeNode", tree);
   var i = 0;
@@ -821,6 +823,7 @@ function initMailboxSelection(mailboxName)
          && treeNodes[i].getAttribute("dataname") != currentMailbox)
     i++;
   if (i < treeNodes.length) {
+    log ("found mailbox");
     var links = document.getElementsByClassName("node", treeNodes[i]);
     if (tree.selectedEntry)
       tree.selectedEntry.deselect();
@@ -924,7 +927,7 @@ var messageListGhost = function () {
 }
 
 var messageListData = function(type) {
-  var rows = this.getSelectedRowsId();
+  var rows = this.parentNode.parentNode.getSelectedRowsId();
   var msgIds = new Array();
   for (var i = 0; i < rows.length; i++)
     msgIds.push(rows[i].substr(4));
@@ -985,7 +988,7 @@ function configureDragHandles() {
 
 /* dnd */
 function initDnd() {
-  log ("MailerUI initDnd");
+//   log ("MailerUI initDnd");
 
   var tree = $("d");
   if (tree) {
@@ -1017,6 +1020,8 @@ function refreshContacts() {
 function openInbox(node) {
   var done = false;
   openMailbox(node.parentNode.getAttribute("dataname"));
+  var tree = $("d");
+  tree.selectedEntry = node;
   node.select();
   var currentNode = node.parentNode.parentNode;
   while (!done) {
index e0773d1e32e34518da5e3b2e339b5bd1bf591aa5..be68c9cb26f447cd7af91abef0c280dd78061cda 100644 (file)
@@ -23,7 +23,7 @@ var SOGODragAndDropSourceInterface = {
     }
   },
   dragGestureMouseUpHandler: function (event) {
-    log("mouseup");
+//     log("mouseup");
     document._currentMouseGestureObject._removeGestureHandlers();
   },
   dragGestureMouseMoveHandler: function (event) {
@@ -160,13 +160,19 @@ document.DNDManager = {
                                  document.DNDManager.destinationExit, false);
       if (operation.destination == event.target) {
 //         log("drag / drop: " + operation.source + " to " + operation.destination);
-        if (operation.destination.dndExit)
-          event.target.dndExit();
+        if (operation.destination.dndExit) {
+//           log ("destination.dndExit...");
+          operation.destination.dndExit();
+        }
         if (operation.destination.dndDrop) {
+//           log ("destination.dndDrop...");
           var data = null;
+//           log ("optype: " + operation.type);
           if (operation.source.dndDataForType)
             data = operation.source.dndDataForType(operation.type);
-          var result = event.target.dndDrop(data);
+//           log ("data: " + data);
+          var result = operation.destination.dndDrop(data);
+//           log ("result: " + result);
           if (operation.ghost) {
             if (result)
               operation.bustGhost();
index 908bf71938648d49df78016636d73b9553188213..cb615858d8f5dc62b5d3ebf69475b48fc0589e7a 100644 (file)
@@ -687,7 +687,6 @@ TABLE.monthOverview DIV.appointmentInside
   width: auto;
   position: static; }
 
-
 DIV.daysView > DIV.hours
 { position: absolute;
   border: 0px;
@@ -834,6 +833,9 @@ DIV.appointments > DIV.appointment
   right: 0px;
   padding: 0px; }
 
+DIV[class~="appointment"]._selected > DIV.appointmentInside
+{ background-color: #ffa !important; }
+
 DIV.appointment > DIV
 { position: absolute;
   overflow: hidden;
index 764603ef41d4cabfac2606c5d097f555fd68d23e..f2d800c3c1837ad06d602b4713a963b45ff5b2ed 100644 (file)
@@ -3,6 +3,7 @@ var sortKey = '';
 var listFilter = 'view_today';
 
 var listOfSelection = null;
+var selectedCalendarCell;
 
 var hideCompletedTasks = 0;
 
@@ -57,6 +58,9 @@ function editEvent() {
     for (var i = 0; i < nodes.length; i++)
       _editEventId(nodes[i].getAttribute("id"),
                    nodes[i].getAttribute("owner"));
+  } else if (selectedCalendarCell) {
+      _editEventId(selectedCalendarCell.getAttribute("aptCName"),
+                   selectedCalendarCell.getAttribute("owner"));
   }
 
   return false; /* stop following the link */
@@ -108,6 +112,18 @@ function deleteEvent()
       }
     }
   }
+  else if (selectedCalendarCell) {
+     var label = labels["appointmentDeleteConfirmation"].decodeEntities();
+     if (confirm(label)) {
+        if (document.deleteEventAjaxRequest) {
+           document.deleteEventAjaxRequest.aborted = true;
+           document.deleteEventAjaxRequest.abort();
+        }
+        eventsToDelete.push([selectedCalendarCell.getAttribute("aptCName")]);
+        ownersOfEventsToDelete.push(selectedCalendarCell.getAttribute("owner"));
+        _batchDeleteEvents();
+     }
+  }
   else
     window.alert("no selection");
 
@@ -145,8 +161,10 @@ function modifyEventCallback(http) {
       log("closing window...?");
       if (queryParameters["mail-invitation"] == "yes")
         closeInvitationWindow();
-      else
-        window.close();
+      else {
+        window.opener.setTimeout("refreshAppointmentsAndDisplay();", 100);
+        window.setTimeout("window.close();", 100);
+      }
     }
     else {
       log("showing alert...");
@@ -160,10 +178,11 @@ function deleteEventCallback(http)
 {
   if (http.readyState == 4
       && http.status == 200) {
-    var nodes = $(http.callbackData);
+    var nodes = http.callbackData;
     for (var i = 0; i < nodes.length; i++) {
       var node = $(nodes[i]);
-      node.parentNode.removeChild(node);
+      if (node)
+        node.parentNode.removeChild(node);
     }
     if (eventsToDelete.length)
       _batchDeleteEvents();
@@ -379,6 +398,8 @@ function changeCalendarDisplay(time, newView)
 {
   var url = ApplicationBaseURL + ((newView) ? newView : currentView);
 
+  selectedCalendarCell = null;
+
   var day = null;
   var hour = null;
   if (time) {
@@ -433,7 +454,6 @@ function onMonthOverview()
 
 function scrollDayView(hour)
 {
-  log("stest1");
   var rowNumber;
   if (hour) {
     if (hour.length == 3)
@@ -451,8 +471,6 @@ function scrollDayView(hour)
   var hours = daysView.childNodesWithTag("div")[0].childNodesWithTag("div");
   if (hours.length > 0)
     daysView.parentNode.scrollTop = hours[rowNumber + 1].offsetTop;
-
-  log("stest2");
 }
 
 function onClickableCellsDblClick(event) {
@@ -742,6 +760,10 @@ function onCalendarSelectAppointment() {
   list.deselectAll();
 
   var aptCName = this.getAttribute("aptCName");
+  if (selectedCalendarCell)
+    selectedCalendarCell.deselect();
+  this.select();
+  selectedCalendarCell = this;
   var row = $(aptCName);
   if (row) {
     var div = row.parentNode.parentNode.parentNode;
@@ -761,6 +783,11 @@ function onCalendarSelectDay(event) {
     changeMonthCalendarDisplayOfSelectedDay(this);
   changeDateSelectorDisplay(day);
 
+  if (listOfSelection) {
+    listOfSelection.addClassName("_unfocused");
+    listOfSelection = null;
+  }
+
   if (needRefresh)
     refreshAppointments();
 }
index 81961292033dc4faeed85c46a0a8f45cd8992b95..b25c6d4a505d5d104a03a763c2d0f87788fafcb2 100644 (file)
@@ -300,7 +300,7 @@ DIV.contactSelector DIV.contactList
 TEXTAREA
 { vertical-align: top; }
 
-DIV, TEXTAREA, INPUT
+DIV, TEXTAREA, INPUT, SELECT
 { font-family: inherit;
   font-size: inherit; }
 
index 479b08d084f43a59c59fccb5e049d76e275dafbd..2d55e455e8df1aa52aee2c643f05f97e0c177acc 100644 (file)
@@ -901,11 +901,13 @@ function disableAnchor(anchor) {
 
 function d2h(d) {
   var hD = "0123456789abcdef";
-  var h = hD.substr(d&15,1);
-  while (d>15) {
-    d>>=4;
-    h=hD.substr(d&15,1)+h;
+  var h = hD.substr(d & 15, 1);
+
+  while (d > 15) {
+    d >>= 4;
+    h = hD.substr(d & 15, 1) + h;
   }
+
   return h;
 }
 
index f1c5fdb8f866ade9ef91d920dd573908286a47a0..0481cca4c394b527a14a6b44df210f6ad985b5ea 100644 (file)
@@ -12,15 +12,19 @@ ADDITIONAL_INCLUDE_DIRS += \
        -I..            \
        -I../..         \
        -I../../..      \
-       -I../../SoObjects
+       -I../../SoObjects \
+       -I../../SOPE
 
 ifeq ($(GNUSTEP_BUILD_DIR),)
 
 ADDITIONAL_LIB_DIRS +=                                 \
+        -L../../SOPE/NGCards/$(GNUSTEP_OBJ_DIR)        \
        -L../SOGoUI/$(GNUSTEP_OBJ_DIR)          \
        -L../../SoObjects/SOGo/$(GNUSTEP_OBJ_DIR)
 
 else
+RELBUILD_DIR_libNGCards = \
+       $(GNUSTEP_BUILD_DIR)/../../SOPE/NGCards/$(GNUSTEP_OBJ_DIR_NAME)
 RELBUILD_DIR_libSOGo = \
        $(GNUSTEP_BUILD_DIR)/../../SoObjects/SOGo/$(GNUSTEP_OBJ_DIR_NAME)
 RELBUILD_DIR_libSOGoUI = \
@@ -29,6 +33,7 @@ RELBUILD_DIR_libOGoContentStore = \
        $(GNUSTEP_BUILD_DIR)/../../OGoContentStore/$(GNUSTEP_OBJ_DIR_NAME)
 
 ADDITIONAL_LIB_DIRS +=                                 \
+       -L$(RELBUILD_DIR_libNGCards)            \
        -L$(RELBUILD_DIR_libSOGo)               \
        -L$(RELBUILD_DIR_libSOGoUI)             \
        -L$(RELBUILD_DIR_libOGoContentStore)