update to newer sabredav version
authormario_matzulla <mario_matzulla@735d13b6-9817-0410-8766-e36946ffe9aa>
Sun, 1 Jul 2012 13:02:46 +0000 (13:02 +0000)
committermario_matzulla <mario_matzulla@735d13b6-9817-0410-8766-e36946ffe9aa>
Sun, 1 Jul 2012 13:02:46 +0000 (13:02 +0000)
added TCEMAIN clearCacheCmd

git-svn-id: https://svn.typo3.org/TYPO3v4/Extensions/caldav/trunk@64096 735d13b6-9817-0410-8766-e36946ffe9aa

169 files changed:
ChangeLog
LICENSE [new file with mode: 0755]
ext_emconf.php
ext_icon.gif
ext_tables.php
hooks/class.tx_caldav_tcemain_processdatamap.php
lib/Sabre.includes.php
lib/Sabre/CalDAV/Backend/Abstract.php
lib/Sabre/CalDAV/Backend/PDO.php
lib/Sabre/CalDAV/Backend/TYPO3.php
lib/Sabre/CalDAV/Calendar.php
lib/Sabre/CalDAV/CalendarObject.php
lib/Sabre/CalDAV/CalendarRootNode.php
lib/Sabre/CalDAV/Exception/InvalidICalendarObject.php
lib/Sabre/CalDAV/ICSExportPlugin.php [new file with mode: 0755]
lib/Sabre/CalDAV/ICalendar.php [new file with mode: 0755]
lib/Sabre/CalDAV/ICalendarObject.php [new file with mode: 0755]
lib/Sabre/CalDAV/ICalendarUtil.php
lib/Sabre/CalDAV/Plugin.php
lib/Sabre/CalDAV/Principal/Collection.php [new file with mode: 0755]
lib/Sabre/CalDAV/Principal/ProxyRead.php [new file with mode: 0755]
lib/Sabre/CalDAV/Principal/ProxyWrite.php [new file with mode: 0755]
lib/Sabre/CalDAV/Principal/User.php [new file with mode: 0755]
lib/Sabre/CalDAV/Property/SupportedCalendarComponentSet.php
lib/Sabre/CalDAV/Property/SupportedCalendarData.php
lib/Sabre/CalDAV/Property/SupportedCollationSet.php
lib/Sabre/CalDAV/Server.php
lib/Sabre/CalDAV/TYPO3Server.php
lib/Sabre/CalDAV/UserCalendars.php
lib/Sabre/CalDAV/Version.php
lib/Sabre/CalDAV/XMLUtil.php
lib/Sabre/CardDAV/AddressBook.php [new file with mode: 0755]
lib/Sabre/CardDAV/AddressBookQueryParser.php [new file with mode: 0755]
lib/Sabre/CardDAV/AddressBookRoot.php [new file with mode: 0755]
lib/Sabre/CardDAV/Backend/Abstract.php [new file with mode: 0755]
lib/Sabre/CardDAV/Backend/PDO.php [new file with mode: 0755]
lib/Sabre/CardDAV/Card.php [new file with mode: 0755]
lib/Sabre/CardDAV/IAddressBook.php [new file with mode: 0755]
lib/Sabre/CardDAV/ICard.php [new file with mode: 0755]
lib/Sabre/CardDAV/IDirectory.php [new file with mode: 0755]
lib/Sabre/CardDAV/Plugin.php [new file with mode: 0755]
lib/Sabre/CardDAV/Property/SupportedAddressData.php [new file with mode: 0755]
lib/Sabre/CardDAV/UserAddressBooks.php [new file with mode: 0755]
lib/Sabre/CardDAV/Version.php [new file with mode: 0755]
lib/Sabre/DAV/Auth/Backend/AbstractBasic.php
lib/Sabre/DAV/Auth/Backend/AbstractDigest.php
lib/Sabre/DAV/Auth/Backend/Apache.php
lib/Sabre/DAV/Auth/Backend/File.php
lib/Sabre/DAV/Auth/Backend/PDO.php
lib/Sabre/DAV/Auth/Backend/TYPO3.php
lib/Sabre/DAV/Auth/IBackend.php [new file with mode: 0755]
lib/Sabre/DAV/Auth/Plugin.php
lib/Sabre/DAV/Browser/GuessContentType.php
lib/Sabre/DAV/Browser/MapGetToPropFind.php
lib/Sabre/DAV/Browser/Plugin.php
lib/Sabre/DAV/Client.php [new file with mode: 0755]
lib/Sabre/DAV/Collection.php [new file with mode: 0755]
lib/Sabre/DAV/Directory.php
lib/Sabre/DAV/Exception.php
lib/Sabre/DAV/Exception/BadRequest.php
lib/Sabre/DAV/Exception/Conflict.php
lib/Sabre/DAV/Exception/ConflictingLock.php
lib/Sabre/DAV/Exception/FileNotFound.php
lib/Sabre/DAV/Exception/Forbidden.php
lib/Sabre/DAV/Exception/InsufficientStorage.php
lib/Sabre/DAV/Exception/InvalidResourceType.php
lib/Sabre/DAV/Exception/LockTokenMatchesRequestUri.php
lib/Sabre/DAV/Exception/Locked.php
lib/Sabre/DAV/Exception/MethodNotAllowed.php
lib/Sabre/DAV/Exception/NotAuthenticated.php
lib/Sabre/DAV/Exception/NotImplemented.php
lib/Sabre/DAV/Exception/PreconditionFailed.php
lib/Sabre/DAV/Exception/ReportNotImplemented.php
lib/Sabre/DAV/Exception/RequestedRangeNotSatisfiable.php
lib/Sabre/DAV/Exception/UnsupportedMediaType.php
lib/Sabre/DAV/FS/Directory.php
lib/Sabre/DAV/FS/File.php
lib/Sabre/DAV/FS/Node.php
lib/Sabre/DAV/FSExt/Directory.php
lib/Sabre/DAV/FSExt/File.php
lib/Sabre/DAV/FSExt/Node.php
lib/Sabre/DAV/File.php
lib/Sabre/DAV/ICollection.php
lib/Sabre/DAV/IExtendedCollection.php
lib/Sabre/DAV/IFile.php
lib/Sabre/DAV/ILockable.php
lib/Sabre/DAV/INode.php
lib/Sabre/DAV/IProperties.php
lib/Sabre/DAV/IQuota.php
lib/Sabre/DAV/Locks/Backend/Abstract.php
lib/Sabre/DAV/Locks/Backend/FS.php
lib/Sabre/DAV/Locks/Backend/File.php [new file with mode: 0755]
lib/Sabre/DAV/Locks/Backend/PDO.php
lib/Sabre/DAV/Locks/LockInfo.php
lib/Sabre/DAV/Locks/Plugin.php
lib/Sabre/DAV/Mount/Plugin.php
lib/Sabre/DAV/Node.php
lib/Sabre/DAV/ObjectTree.php
lib/Sabre/DAV/Property.php
lib/Sabre/DAV/Property/GetLastModified.php
lib/Sabre/DAV/Property/Href.php
lib/Sabre/DAV/Property/HrefList.php [new file with mode: 0755]
lib/Sabre/DAV/Property/IHref.php
lib/Sabre/DAV/Property/LockDiscovery.php
lib/Sabre/DAV/Property/ResourceType.php
lib/Sabre/DAV/Property/Response.php
lib/Sabre/DAV/Property/ResponseList.php [new file with mode: 0755]
lib/Sabre/DAV/Property/SupportedLock.php
lib/Sabre/DAV/Property/SupportedReportSet.php
lib/Sabre/DAV/Server.php
lib/Sabre/DAV/ServerPlugin.php
lib/Sabre/DAV/SimpleCollection.php [new file with mode: 0755]
lib/Sabre/DAV/SimpleDirectory.php
lib/Sabre/DAV/SimpleFile.php [new file with mode: 0755]
lib/Sabre/DAV/StringUtil.php [new file with mode: 0755]
lib/Sabre/DAV/TemporaryFileFilterPlugin.php
lib/Sabre/DAV/Tree.php
lib/Sabre/DAV/Tree/Filesystem.php
lib/Sabre/DAV/URLUtil.php
lib/Sabre/DAV/UUIDUtil.php [new file with mode: 0755]
lib/Sabre/DAV/Version.php
lib/Sabre/DAV/XMLUtil.php
lib/Sabre/DAVACL/AbstractPrincipalCollection.php [new file with mode: 0755]
lib/Sabre/DAVACL/Exception/AceConflict.php [new file with mode: 0755]
lib/Sabre/DAVACL/Exception/NeedPrivileges.php [new file with mode: 0755]
lib/Sabre/DAVACL/Exception/NoAbstract.php [new file with mode: 0755]
lib/Sabre/DAVACL/Exception/NotRecognizedPrincipal.php [new file with mode: 0755]
lib/Sabre/DAVACL/Exception/NotSupportedPrivilege.php [new file with mode: 0755]
lib/Sabre/DAVACL/IACL.php [new file with mode: 0755]
lib/Sabre/DAVACL/IPrincipal.php [new file with mode: 0755]
lib/Sabre/DAVACL/IPrincipalBackend.php [new file with mode: 0755]
lib/Sabre/DAVACL/Plugin.php [new file with mode: 0755]
lib/Sabre/DAVACL/Principal.php [new file with mode: 0755]
lib/Sabre/DAVACL/PrincipalBackend/PDO.php [new file with mode: 0755]
lib/Sabre/DAVACL/PrincipalBackend/TYPO3.php [new file with mode: 0644]
lib/Sabre/DAVACL/PrincipalCollection.php [new file with mode: 0755]
lib/Sabre/DAVACL/Property/Acl.php [new file with mode: 0755]
lib/Sabre/DAVACL/Property/CurrentUserPrivilegeSet.php [new file with mode: 0755]
lib/Sabre/DAVACL/Property/Principal.php [new file with mode: 0755]
lib/Sabre/DAVACL/Property/SupportedPrivilegeSet.php [new file with mode: 0755]
lib/Sabre/DAVACL/Version.php [new file with mode: 0755]
lib/Sabre/HTTP/AWSAuth.php
lib/Sabre/HTTP/AbstractAuth.php
lib/Sabre/HTTP/BasicAuth.php
lib/Sabre/HTTP/DigestAuth.php
lib/Sabre/HTTP/Request.php
lib/Sabre/HTTP/Response.php
lib/Sabre/HTTP/Util.php
lib/Sabre/HTTP/Version.php
lib/Sabre/VObject/Component.php [new file with mode: 0755]
lib/Sabre/VObject/Element.php [new file with mode: 0755]
lib/Sabre/VObject/Element/DateTime.php [new file with mode: 0755]
lib/Sabre/VObject/Element/MultiDateTime.php [new file with mode: 0755]
lib/Sabre/VObject/ElementList.php [new file with mode: 0755]
lib/Sabre/VObject/Node.php [new file with mode: 0755]
lib/Sabre/VObject/Parameter.php [new file with mode: 0755]
lib/Sabre/VObject/ParseException.php [new file with mode: 0755]
lib/Sabre/VObject/Property.php [new file with mode: 0755]
lib/Sabre/VObject/Reader.php [new file with mode: 0755]
lib/Sabre/VObject/Version.php [new file with mode: 0755]
lib/Sabre/VObject/includes.php [new file with mode: 0755]
lib/Sabre/autoload.php
mod1/class.tx_cal_ics_generator.php [new file with mode: 0755]
mod1/clear.gif [new file with mode: 0755]
mod1/conf.php [new file with mode: 0644]
mod1/index.php [new file with mode: 0644]
mod1/locallang.xml [new file with mode: 0644]
mod1/locallang_mod.xml [new file with mode: 0644]
mod1/moduleicon.gif [new file with mode: 0755]

index 5b87d83..df57f26 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
-2011-05-03  jeff
-
-       * ext_icon.gif: Updated extension icon.
-       * ext_emconf.php: Raised state to beta.
-       * ChangeLog, LICENSE, lib/ChangeLog, lib/LICENSE: Moved external
-         license and changelog files into lib directory.
-
-2011-02-10  mario
-
-       * doc/manual.sxw, ext_conf_template.txt: updated pid configuration
-
-2010-11-16  mario
-
-       * doc/manual.sxw: updated manual
-
-2010-11-13  mario
-
-       * ext_emconf.php: added dependency version for cal
-       * ext_emconf.php: updated version and md5 values
-       * ext_tables.sql: fixed sql
-       * doc, doc/manual.sxw: added docu
-
-2010-11-10  mario
-
-       * ext_conf_template.txt, lib/Sabre/DAV/Auth/Backend/TYPO3.php:
-         switched from digest to basic auth -> using TYPO3 auth
-
-2010-11-01  mario
-
-       * .buildpath, .project, ChangeLog, LICENSE, _htaccess, caldav.php,
-         ext_autoload.php, ext_emconf.php, ext_icon.gif,
-         ext_localconf.php, ext_tables.php, ext_tables.sql, hooks,
-         hooks/class.tx_caldav_tcemain_processdatamap.php, lib, lib/Sabre,
-         lib/Sabre.autoload.php, lib/Sabre.includes.php, lib/Sabre/CalDAV,
-         lib/Sabre/CalDAV/Backend, lib/Sabre/CalDAV/Backend/Abstract.php,
-         lib/Sabre/CalDAV/Backend/PDO.php,
-         lib/Sabre/CalDAV/Backend/TYPO3.php,
-         lib/Sabre/CalDAV/Calendar.php,
-         lib/Sabre/CalDAV/CalendarObject.php,
-         lib/Sabre/CalDAV/CalendarRootNode.php,
-         lib/Sabre/CalDAV/Exception,
-         lib/Sabre/CalDAV/Exception/InvalidICalendarObject.php,
-         lib/Sabre/CalDAV/ICalendarUtil.php, lib/Sabre/CalDAV/Plugin.php,
-         lib/Sabre/CalDAV/Property,
-         lib/Sabre/CalDAV/Property/SupportedCalendarComponentSet.php,
-         lib/Sabre/CalDAV/Property/SupportedCalendarData.php,
-         lib/Sabre/CalDAV/Property/SupportedCollationSet.php,
-         lib/Sabre/CalDAV/Server.php, lib/Sabre/CalDAV/TYPO3Server.php,
-         lib/Sabre/CalDAV/UserCalendars.php, lib/Sabre/CalDAV/Version.php,
-         lib/Sabre/CalDAV/XMLUtil.php, lib/Sabre/DAV, lib/Sabre/DAV/Auth,
-         lib/Sabre/DAV/Auth/Backend,
-         lib/Sabre/DAV/Auth/Backend/Abstract.php,
-         lib/Sabre/DAV/Auth/Backend/AbstractBasic.php,
-         lib/Sabre/DAV/Auth/Backend/AbstractDigest.php,
-         lib/Sabre/DAV/Auth/Backend/Apache.php,
-         lib/Sabre/DAV/Auth/Backend/File.php,
-         lib/Sabre/DAV/Auth/Backend/PDO.php,
-         lib/Sabre/DAV/Auth/Backend/TYPO3.php,
-         lib/Sabre/DAV/Auth/Plugin.php, lib/Sabre/DAV/Auth/Principal.php,
-         lib/Sabre/DAV/Auth/PrincipalCollection.php,
-         lib/Sabre/DAV/Browser,
-         lib/Sabre/DAV/Browser/GuessContentType.php,
-         lib/Sabre/DAV/Browser/MapGetToPropFind.php,
-         lib/Sabre/DAV/Browser/Plugin.php, lib/Sabre/DAV/Directory.php,
-         lib/Sabre/DAV/Exception, lib/Sabre/DAV/Exception.php,
-         lib/Sabre/DAV/Exception/BadRequest.php,
-         lib/Sabre/DAV/Exception/Conflict.php,
-         lib/Sabre/DAV/Exception/ConflictingLock.php,
-         lib/Sabre/DAV/Exception/FileNotFound.php,
-         lib/Sabre/DAV/Exception/Forbidden.php,
-         lib/Sabre/DAV/Exception/InsufficientStorage.php,
-         lib/Sabre/DAV/Exception/InvalidResourceType.php,
-         lib/Sabre/DAV/Exception/LockTokenMatchesRequestUri.php,
-         lib/Sabre/DAV/Exception/Locked.php,
-         lib/Sabre/DAV/Exception/MethodNotAllowed.php,
-         lib/Sabre/DAV/Exception/NotAuthenticated.php,
-         lib/Sabre/DAV/Exception/NotImplemented.php,
-         lib/Sabre/DAV/Exception/PreconditionFailed.php,
-         lib/Sabre/DAV/Exception/ReportNotImplemented.php,
-         lib/Sabre/DAV/Exception/RequestedRangeNotSatisfiable.php,
-         lib/Sabre/DAV/Exception/UnsupportedMediaType.php,
-         lib/Sabre/DAV/FS, lib/Sabre/DAV/FS/Directory.php,
-         lib/Sabre/DAV/FS/File.php, lib/Sabre/DAV/FS/Node.php,
-         lib/Sabre/DAV/FSExt, lib/Sabre/DAV/FSExt/Directory.php,
-         lib/Sabre/DAV/FSExt/File.php, lib/Sabre/DAV/FSExt/Node.php,
-         lib/Sabre/DAV/File.php, lib/Sabre/DAV/ICollection.php,
-         lib/Sabre/DAV/IExtendedCollection.php, lib/Sabre/DAV/IFile.php,
-         lib/Sabre/DAV/ILockable.php, lib/Sabre/DAV/INode.php,
-         lib/Sabre/DAV/IProperties.php, lib/Sabre/DAV/IQuota.php,
-         lib/Sabre/DAV/Locks, lib/Sabre/DAV/Locks/Backend,
-         lib/Sabre/DAV/Locks/Backend/Abstract.php,
-         lib/Sabre/DAV/Locks/Backend/FS.php,
-         lib/Sabre/DAV/Locks/Backend/PDO.php,
-         lib/Sabre/DAV/Locks/LockInfo.php, lib/Sabre/DAV/Locks/Plugin.php,
-         lib/Sabre/DAV/Mount, lib/Sabre/DAV/Mount/Plugin.php,
-         lib/Sabre/DAV/Node.php, lib/Sabre/DAV/ObjectTree.php,
-         lib/Sabre/DAV/Property, lib/Sabre/DAV/Property.php,
-         lib/Sabre/DAV/Property/GetLastModified.php,
-         lib/Sabre/DAV/Property/Href.php,
-         lib/Sabre/DAV/Property/IHref.php,
-         lib/Sabre/DAV/Property/LockDiscovery.php,
-         lib/Sabre/DAV/Property/Principal.php,
-         lib/Sabre/DAV/Property/ResourceType.php,
-         lib/Sabre/DAV/Property/Response.php,
-         lib/Sabre/DAV/Property/SupportedLock.php,
-         lib/Sabre/DAV/Property/SupportedReportSet.php,
-         lib/Sabre/DAV/Server.php, lib/Sabre/DAV/ServerPlugin.php,
-         lib/Sabre/DAV/SimpleDirectory.php,
-         lib/Sabre/DAV/TemporaryFileFilterPlugin.php, lib/Sabre/DAV/Tree,
-         lib/Sabre/DAV/Tree.php, lib/Sabre/DAV/Tree/Filesystem.php,
-         lib/Sabre/DAV/URLUtil.php, lib/Sabre/DAV/Version.php,
-         lib/Sabre/DAV/XMLUtil.php, lib/Sabre/HTTP,
-         lib/Sabre/HTTP/AWSAuth.php, lib/Sabre/HTTP/AbstractAuth.php,
-         lib/Sabre/HTTP/BasicAuth.php, lib/Sabre/HTTP/DigestAuth.php,
-         lib/Sabre/HTTP/Request.php, lib/Sabre/HTTP/Response.php,
-         lib/Sabre/HTTP/Util.php, lib/Sabre/HTTP/Version.php,
-         lib/Sabre/autoload.php, locallang_db.xml: initial upload
-       * .: Share project "caldav" into
-         "http://svn.webempoweredchurch.org/repos/calendar"
+1.3.0 (2010-10-14)
+       * Added: childExists method to Sabre_DAV_ICollection. This is an api
+         break, so if you implement Sabre_DAV_ICollection directly, add the method.
+       * Changed: Almost all HTTP method implementations now take a uri argument,
+         including events. This allows for internal rerouting of certain calls.
+         If you have custom plugins, make sure they use this argument. If they
+         don't, they will likely still work, but it might get in the way of
+         future changes.
+       * Changed: All getETag methods MUST now surround the etag with
+         double-quotes. This was a mistake made in all previous SabreDAV
+         versions. If you don't do this, any If-Match, If-None-Match and If:
+         headers using Etags will work incorrectly. (Issue 85). 
+       * Added: Sabre_DAV_Auth_Backend_AbstractBasic class, which can be used to
+         easily implement basic authentication.
+       * Removed: Sabre_DAV_PermissionDenied class. Use Sabre_DAV_Forbidden
+         instead.
+       * Removed: Sabre_DAV_IDirectory interface, use Sabre_DAV_ICollection
+         instead. 
+       * Added: Browser plugin now uses {DAV:}displayname if this property is
+         available.
+       * Added: Cache layer in the ObjectTree.
+       * Added: Tree classes now have a delete and getChildren method.
+       * Fixed: If-Modified-Since and If-Unmodified-Since would be incorrect if
+         the date is an exact match.
+       * Fixed: Support for multiple ETags in If-Match and If-None-Match headers.
+       * Fixed: Improved baseUrl handling.
+       * Fixed: Issue 67: Non-seekable stream support in ::put()/::get().
+       * Fixed: Issue 65: Invalid dates are now ignored.
+       * Updated: Refactoring in Sabre_CalDAV to make everything a bit more
+         ledgable.
+       * Fixed: Issue 88, Issue 89: Fixed compatibility for running SabreDAV on 
+          Windows.
+       * Fixed: Issue 86: Fixed Content-Range top-boundary from 'file size' to
+         'file size'-1. 
 
+1.2.4 (2010-07-13)
+       * Fixed: Issue 62: Guessing baseUrl fails when url contains a
+         query-string.
+       * Added: Apache configuration sample for CGI/FastCGI setups.
+       * Fixed: Issue 64: Only returning calendar-data when it was actually
+         requested.
+
+1.2.3 (2010-06-26)
+       * Fixed: Issue 57: Supporting quotes around etags in If-Match and
+         If-None-Match
+
+1.2.2 (2010-06-21)
+       * Updated: SabreDAV now attempts to guess the BaseURI if it's not set.
+       * Updated: Better compatibility with BitKinex
+       * Fixed: Issue 56: Incorrect behaviour for If-None-Match headers and GET
+         requests.
+       * Fixed: Issue with certain encoded paths in Browser Plugin.
+
+1.2.1 (2010-06-07)
+       * Fixed: Issue 50, patch by Mattijs Hoitink.
+       * Fixed: Issue 51, Adding windows 7 lockfiles to TemporaryFileFilter.
+       * Fixed: Issue 38, Allowing custom filters to be added to
+         TemporaryFileFilter.
+       * Fixed: Issue 53, ETags in the If: header were always failing. This
+         behaviour is now corrected.
+       * Added: Apache Authentication backend, in case authentication through
+         .htaccess is desired.
+       * Updated: Small improvements to example files.
+
+1.2.0 (2010-05-24)
+       * Fixed: Browser plugin now displays international characters.
+       * Changed: More properties in CalDAV classes are now protected instead of
+         private.
+
+1.2.0beta3 (2010-05-14)
+       * Fixed: Custom properties were not propertly sent back for allprops
+         requests.
+       * Fixed: Issue 49, incorrect parsing of PROPPATCH, affecting Office 2007.
+       * Changed: Removed CalDAV items from includes.php, and added a few missing
+         ones.
+
+1.2.0beta2 (2010-05-04)
+       * Fixed: Issue 46: Fatal error for some non-existant nodes.
+       * Updated: some example sql to include email address.
+       * Added: 208 and 508 statuscodes from RFC5842.
+       * Added: Apache2 configuration examples
+
+1.2.0beta1 (2010-04-28)
+       * Fixed: redundant namespace declaration in resourcetypes.
+       * Fixed: 2 locking bugs triggered by litmus when no Sabre_DAV_ILockable
+         interface is used.
+       * Changed: using http://sabredav.org/ns for all custom xml properties.
+       * Added: email address property to principals.
+       * Updated: CalendarObject validation.
+
+1.2.0alpha4 (2010-04-24)
+       * Added: Support for If-Range, If-Match, If-None-Match, If-Modified-Since,
+         If-Unmodified-Since.
+       * Changed: Brand new build system. Functionality is split up between
+         Sabre, Sabre_HTTP, Sabre_DAV and Sabre_CalDAV packages. In addition to
+         that a new non-pear package will be created with all this functionality
+         combined.
+       * Changed: Autoloader moved to Sabre/autoload.php.
+       * Changed: The Allow: header is now more accurate, with appropriate HTTP
+         methods per uri. 
+       * Changed: Now throwing back Sabre_DAV_Exception_MethodNotAllowed on a few
+         places where Sabre_DAV_Exception_NotImplemented was used.
+
+1.2.0alpha3 (2010-04-20)
+       * Update: Complete rewrite of property updating. Now easier to use and 
+         atomic.
+       * Fixed: Issue 16, automatically adding trailing / to baseUri.
+       * Added: text/plain is used for .txt files in GuessContentType plugin.
+       * Added: support for principal-property-search and
+         principal-search-property-set reports.
+       * Added: Issue 31: Hiding exception information by default. Can be turned
+         on with the Sabre_DAV_Server::$debugExceptions property.
+
+1.2.0alpha2 (2010-04-08)
+       * Added: Calendars are now private and can only be read by the owner.
+       * Fixed: double namespace declaration in multistatus responses.
+       * Added: MySQL database dumps. MySQL is now also supported next to SQLite. 
+       * Added: expand-properties REPORT from RFC 3253.
+       * Added: Sabre_DAV_Property_IHref interface for properties exposing urls.
+       * Added: Issue 25: Throwing error on broken Finder behaviour.
+       * Changed: Authentication backend is now aware of current user.
+
+1.2.0alpha1 (2010-03-31)
+       * Fixed: Issue 26: Workaround for broken GVFS behaviour with encoded
+         special characters.
+       * Fixed: Issue 34: Incorrect Lock-Token response header for LOCK. Fixes
+         Office 2010 compatibility.
+       * Added: Issue 35: SabreDAV version to header to OPTIONS response to ease
+         debugging.
+       * Fixed: Issue 36: Incorrect variable name, throwing error in some
+         requests.
+       * Fixed: Issue 37: Incorrect smultron regex in temporary filefilter.
+       * Fixed: Issue 33: Converting ISO-8859-1 characters to UTF-8.
+       * Fixed: Issue 39 & Issue 40: Basename fails on non-utf-8 locales. 
+       * Added: More unittests.
+       * Added: SabreDAV version to all error responses.
+       * Added: URLUtil class for decoding urls.
+       * Changed: Now using pear.sabredav.org pear channel.
+       * Changed: Sabre_DAV_Server::getCopyAndMoveInfo is now a public method.
+
+1.1.2-alpha (2010-03-18)
+       * Added: RFC5397 - current-user-principal support.
+       * Fixed: Issue 27: encoding entities in property responses.
+       * Added: naturalselection script now allows the user to specify a 'minimum
+         number of bytes' for deletion. This should reduce load due to less
+         crawling
+       * Added: Full support for the calendar-query report.
+       * Added: More unittests.
+       * Added: Support for complex property deserialization through the static 
+         ::unserialize() method.
+       * Added: Support for modifying calendar-component-set
+       * Fixed: Issue 29: Added TIMEOUT_INFINITE constant
+
+1.1.1-alpha (2010-03-11)
+       * Added: RFC5689 - Extended MKCOL support.
+       * Fixed: Evolution support for CalDAV.
+       * Fixed: PDO-locks backend was pretty much completely broken. This is 
+         100% unittested now.
+       * Added: support for ctags.
+       * Fixed: Comma's between HTTP methods in 'Allow' method.
+       * Changed: default argument for Sabre_DAV_Locks_Backend_FS. This means a
+         datadirectory must always be specified from now on.
+       * Changed: Moved Sabre_DAV_Server::parseProps to
+         Sabre_DAV_XMLUtil::parseProperties.
+       * Changed: Sabre_DAV_IDirectory is now Sabre_DAV_ICollection.
+       * Changed: Sabre_DAV_Exception_PermissionDenied is now
+         Sabre_DAV_Exception_Forbidden.
+       * Changed: Sabre_CalDAV_ICalendarCollection is removed.
+       * Added: Sabre_DAV_IExtendedCollection.
+       * Added: Many more unittests.
+       * Added: support for calendar-timezone property.
+
+1.1.0-alpha (2010-03-01)
+       * Added: CalDAV - RFC 4791
+       * Removed: Sabre_PHP_Exception. PHP has a built-in ErrorException for
+         this.
+       * Added: PDO authentication backend.
+       * Added: Example sql for auth, caldav, locks for sqlite.
+       * Added: Sabre_DAV_Browser_GuessContentType plugin
+       * Changed: Authentication plugin refactored, making it possible to
+         implement non-digest authentication.
+       * Fixed: Better error display in browser plugin.
+       * Added: Support for {DAV:}supported-report-set 
+       * Added: XML utility class with helper functions for the WebDAV protocol.
+       * Added: Tons of unittests
+       * Added: PrincipalCollection and Principal classes
+       * Added: Sabre_DAV_Server::getProperties for easy property retrieval
+       * Changed: {DAV:}resourceType defaults to 0
+       * Changed: Any non-null resourceType now gets a / appended to the href
+         value. Before this was just for {DAV:}collection's, but this is now also
+         the case for for example {DAV:}principal.
+       * Changed: The Href property class can now optionally create non-relative
+         uri's.
+       * Changed: Sabre_HTTP_Response now returns false if headers are already
+         sent and header-methods are called.
+       * Fixed: Issue 19: HEAD requests on Collections
+       * Fixed: Issue 21: Typo in Sabre_DAV_Property_Response
+       * Fixed: Issue 18: Doesn't work with Evolution Contacts
+
+1.0.5-stable (2010-01-22)
+       * Fixed: Fatal error when a malformed url was used for unlocking, in
+         conjuction with Sabre.autoload.php due to a incorrect filename.
+       * Fixed: Improved unittests and build system 
+
+1.0.4-stable (2010-01-11)
+       * Fixed: needed 2 different releases. One for googlecode and one for
+         pearfarm. This is to retain the old method to install SabreDAV until
+         pearfarm becomes the standard installation method.
+
+1.0.3-stable (2010-01-11)
+       * Added: RFC4709 support (davmount)
+       * Added: 6 unittests
+       * Added: naturalselection. A tool to keep cache directories below a
+         specified theshold.
+       * Changed: Now using pearfarm.org channel server.
+
+1.0.1-stable (2009-12-22)
+       * Fixed: Issue 15: typos in examples
+       * Fixed: Minor pear installation issues
+
+1.0.0-stable (2009-11-02)
+       * Added: SimpleDirectory class. This class allows creating static
+         directory structures with ease.
+       * Changed: Custom complex properties and exceptions now get an instance of
+         Sabre_DAV_Server as their first argument in serialize()
+       * Changed: Href complex property now prepends server's baseUri
+       * Changed: delete before an overwriting copy/move is now handles by server
+         class instead of tree classes
+       * Changed: events must now explicitly return false to stop execution.
+         Before, execution would be stopped by anything loosely evaluating to
+         false.
+       * Changed: the getPropertiesForPath method now takes a different set of
+         arguments, and returns a different response. This allows plugin
+         developers to return statuses for properties other than 200 and 404. The
+         hrefs are now also always calculated relative to the baseUri, and not
+         the uri of the request.
+       * Changed: generatePropFindResponse is renamed to generateMultiStatus, and
+         now takes a list of properties similar to the response of
+         getPropertiesForPath. This was also needed to improve flexibility for
+         plugin development.
+       * Changed: Auth plugins are no longer included. They were not yet stable
+         quality, so they will probably be reintroduced in a later version.
+       * Changed: PROPPATCH also used generateMultiStatus now.
+       * Removed: unknownProperties event. This is replaced by the
+         afterGetProperties event, which should provide more flexibility. 
+       * Fixed: Only calling getSize() on IFile instances in httpHead()
+       * Added: beforeBind event. This is invoked upon file or directory creation
+       * Added: beforeWriteContent event, this is invoked by PUT and LOCK on an
+         existing resource.
+       * Added: beforeUnbind event. This is invoked right before deletion of any
+         resource.
+       * Added: afterGetProperties event. This event can be used to make
+         modifications to property responses.
+       * Added: beforeLock and beforeUnlock events.
+       * Added: afterBind event.
+       * Fixed: Copy and Move could fail in the root directory. This is now
+         fixed.
+       * Added: Plugins can now be retrieved by their classname. This is useful
+         for inter-plugin communication.
+       * Added: The Auth backend can now return usernames and user-id's.
+       * Added: The Auth backend got a getUsers method
+       * Added: Sabre_DAV_FSExt_Directory now returns quota info
+
+0.12.1-beta (2009-09-11)
+       * Fixed: UNLOCK bug. Unlock didn't work at all
+
+0.12-beta (2009-09-10)
+       * Updated: Browser plugin now shows multiple {DAV:}resourcetype values
+         if available.
+       * Added: Experimental PDO backend for Locks Manager
+       * Fixed: Sending Content-Length: 0 for every empty response. This
+         improves NGinx compatibility.
+       * Fixed: Last modification time is reported in UTC timezone. This improves
+         Finder compatibility.
+
+0.11-beta (2009-08-11)
+       * Updated: Now in Beta
+       * Updated: Pear package no longer includes docs/ directory. These just
+         contained rfc's, which are publically available. This reduces the
+         package from ~800k to ~60k
+       * Added: generatePropfindResponse now takes a baseUri argument
+       * Added: ResourceType property can now contain multiple resourcetypes.
+       * Fixed: Issue 13. 
+
+0.10-alpha (2009-08-03)
+       * Added: Plugin to automatically map GET requests to non-files to
+         PROPFIND (Sabre_DAV_Browser_MapGetToPropFind). This should allow 
+         easier debugging of complicated WebDAV setups.
+       * Added: Sabre_DAV_Property_Href class. For future use.
+       * Added: Ability to choose to use auth-int, auth or both for HTTP Digest
+         authentication. (Issue 11)
+       * Changed: Made more methods in Sabre_DAV_Server public.
+       * Fixed: TemporaryFileFilter plugin now intercepts HTTP LOCK requests
+         to non-existant files. (Issue 12)
+       * Added: Central list of defined xml namespace prefixes. This can reduce
+         Bandwidth and legibility for xml bodies with user-defined namespaces.
+       * Added: now a PEAR-compatible package again, thanks to Michael Gauthier
+       * Changed: moved default copy and move logic from ObjectTree to Tree class
+
+0.9-alpha (2009-07-21)
+       * Changed: Major refactoring, removed most of the logic from the Tree
+         objects. The Server class now directly works with the INode, IFile
+         and IDirectory objects. If you created your own Tree objects,
+         this will most likely break in this release.
+       * Changed: Moved all the Locking logic from the Tree and Server classes
+         into a separate plugin.
+       * Changed: TemporaryFileFilter is now a plugin.
+       * Added: Comes with an autoloader script. This can be used instead of
+         the includer script, and is preferered by some people.
+       * Added: AWS Authentication class.
+       * Added: simpleserversetup.py script. This will quickly get a fileserver
+         up and running.
+       * Added: When subscribing to events, it is now possible to supply a
+         priority. This is for example needed to ensure that the Authentication
+         Plugin is used before any other Plugin.
+       * Added: 22 new tests.
+       * Added: Users-manager plugin for .htdigest files. Experimental and
+         subject to change.
+       * Added: RFC 2324 HTTP 418 status code
+       * Fixed: Exclusive locks could in some cases be picked up as shared locks
+       * Fixed: Digest auth for non-apache servers had a bug (still not actually
+         tested this well).
+
+0.8-alpha (2009-05-30)
+       * Changed: Renamed all exceptions! This is a compatibility break. Every
+         Exception now follows Sabre_DAV_Exception_FileNotFound convention
+         instead of Sabre_DAV_FileNotFoundException.
+       * Added: Browser plugin now allows uploading and creating directories
+         straight from the browser.
+       * Added: 12 more unittests
+       * Fixed: Locking bug, which became prevalent on Windows Vista.
+       * Fixed: Netdrive support
+       * Fixed: TemporaryFileFilter filtered out too many files. Fixed some
+         of the regexes.
+       * Fixed: Added README and ChangeLog to package
+
+0.7-alpha (2009-03-29)
+       * Added: System to return complex properties from PROPFIND.
+       * Added: support for {DAV:}supportedlock.
+       * Added: support for {DAV:}lockdiscovery.
+       * Added: 6 new tests.
+       * Added: New plugin system.
+       * Added: Simple HTML directory plugin, for browser access.
+       * Added: Server class now sends back standard pre-condition error xml
+       bodies. This was new since RFC4918.
+       * Added: Sabre_DAV_Tree_Aggregrate, which can 'host' multiple Tree objects
+       into one.
+       * Added: simple basis for HTTP REPORT method. This method is not used yet,
+       but can be used by plugins to add reports.
+       * Changed: ->getSize is only called for files, no longer for collections.
+       r303
+       * Changed: Sabre_DAV_FilterTree is now Sabre_DAV_Tree_Filter
+       * Changed: Sabre_DAV_TemporaryFileFilter is now called
+       Sabre_DAV_Tree_TemporaryFileFilter.
+       * Changed: removed functions (get(/set)HTTPRequest(/Response)) from Server
+       class, and using a public property instead.
+       * Fixed: bug related to parsing proppatch and propfind requests. Didn't
+       show up in most clients, but it needed fixing regardless. (r255)
+       * Fixed: auth-int is now properly supported within HTTP Digest.
+       * Fixed: Using application/xml for a mimetype vs. text/xml as per RFC4918
+       sec 8.2.
+       * Fixed: TemporaryFileFilter now lets through GET's if they actually
+       exist on the backend. (r274)
+       * FIxed: Some methods didn't get passed through in the FilterTree (r283).
+       * Fixed: LockManager is now slightly more complex, Tree classes slightly
+       less. (r287)
+
+0.6-alpha (2009-02-16)
+       * Added: Now uses streams for files, instead of strings.
+         This means it won't require to hold entire files in memory, which can be
+         an issue if you're dealing with big files. Note that this breaks 
+         compatibility for put() and createFile methods.
+       * Added: HTTP Digest Authentication helper class.
+       * Added: Support for HTTP Range header
+       * Added: Support for ETags within If: headers
+       * Added: The API can now return ETags and override the default Content-Type
+       * Added: starting with basic framework for unittesting, using PHPUnit.
+       * Added: 49 unittests.
+       * Added: Abstraction for the HTTP request.
+       * Updated: Using Clark Notation for tags in properties. This means tags
+       are serialized as {namespace}tagName instead of namespace#tagName
+       * Fixed: HTTP_BasicAuth class now works as expected.
+       * Fixed: DAV_Server uses / for a default baseUrl.
+       * Fixed: Last modification date is no longer ignored in PROPFIND.
+       * Fixed: PROPFIND now sends back information about the requestUri even
+         when "Depth: 1" is specified.
+
+0.5-alpha (2009-01-14)
+       * Added: Added a very simple example for implementing a mapping to PHP
+         file streams. This should allow easy implementation of for example a
+         WebDAV to FTP proxy.
+       * Added: HTTP Basic Authentication helper class.
+       * Added: Sabre_HTTP_Reponse class. This centralizes HTTP operations and
+         will be a start towards the creating of a testing framework.
+       * Updated: Backwards compatibility break: all require_once() statements
+         are removed
+         from all the files. It is now recommended to use autoloading of 
+         classes, or just including lib/Sabre.includes.php. This fix was made
+         to allow easier integration into applications not using this standard
+         inclusion model.
+       * Updated: Better in-file documentation.
+       * Updated: Sabre_DAV_Tree can now work with Sabre_DAV_LockManager.
+       * Updated: Fixes a shared-lock bug.
+       * Updated: Removed ?> from the bottom of each php file.
+       * Updated: Split up some operations from Sabre_DAV_Server to
+         Sabre_HTTP_Response.
+       * Fixed: examples are now actually included in the pear package.
+
+0.4-alpha (2008-11-05)
+       * Passes all litmus tests!
+       * Added: more examples
+       * Added: Custom property support
+       * Added: Shared lock support
+       * Added: Depth support to locks
+       * Added: Locking on unmapped urls (non-existant nodes)
+       * Fixed: Advertising as WebDAV class 3 support
+
+0.3-alpha (2008-06-29) 
+       * Fully working in MS Windows clients.
+       * Added: temporary file filter: support for smultron files.
+       * Added: Phing build scripts 
+       * Added: PEAR package 
+       * Fixed: MOVE bug identied using finder.
+       * Fixed: Using gzuncompress instead of gzdecode in the temporary file
+         filter. This seems more common.
+
+0.2-alpha (2008-05-27) 
+       * Somewhat working in Windows clients 
+       * Added: Working PROPPATCH method (doesn't support custom properties yet)
+       * Added: Temporary filename handling system 
+       * Added: Sabre_DAV_IQuota to return quota information
+       * Added: PROPFIND now reads the request body and only supplies the
+         requested properties
+
+0.1-alpha (2008-04-04)
+       * First release!
+       * Passes litmus: basic, http and copymove test.
+       * Fully working in Finder and DavFSv2
+
+Project started: 2007-12-13
diff --git a/LICENSE b/LICENSE
new file mode 100755 (executable)
index 0000000..1eba437
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,28 @@
+Copyright (C) 2007-2010 Rooftop Solutions.
+Copyright (C) 2007-2009 FileMobile inc.
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright notice,
+      this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright notice,
+      this list of conditions and the following disclaimer in the documentation
+      and/or other materials provided with the distribution.
+    * Neither the name of the SabreDAV nor the names of its contributors
+      may be used to endorse or promote products derived from this software 
+      without specific prior written permission.
+     
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+    AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
+    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
+    ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
+    LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
+    CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+    SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
+    INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
+    CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
+    ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
+    POSSIBILITY OF SUCH DAMAGE.
index b97a460..808a0f6 100755 (executable)
@@ -3,7 +3,7 @@
 ########################################################################
 # Extension Manager/Repository config file for ext "caldav".
 #
-# Auto generated 03-05-2011 15:04
+# Auto generated 01-07-2012 14:16
 #
 # Manual updates:
 # Only the data in the array - everything else is removed by next
@@ -29,7 +29,7 @@ $EM_CONF[$_EXTKEY] = array(
        'clearCacheOnLoad' => 1,
        'lockType' => '',
        'author_company' => '',
-       'version' => '0.0.1',
+       'version' => '1.0.0',
        'constraints' => array(
                'depends' => array(
                        'cal' => '1.4.0',
@@ -39,7 +39,7 @@ $EM_CONF[$_EXTKEY] = array(
                'suggests' => array(
                ),
        ),
-       '_md5_values_when_last_written' => 'a:114:{s:9:"ChangeLog";s:4:"30c4";s:9:"_htaccess";s:4:"58d9";s:10:"caldav.php";s:4:"3755";s:16:"ext_autoload.php";s:4:"a579";s:21:"ext_conf_template.txt";s:4:"61d0";s:12:"ext_icon.gif";s:4:"986e";s:17:"ext_localconf.php";s:4:"ee40";s:14:"ext_tables.php";s:4:"a0fd";s:14:"ext_tables.sql";s:4:"65fd";s:16:"locallang_db.xml";s:4:"1797";s:14:"doc/manual.sxw";s:4:"15ce";s:48:"hooks/class.tx_caldav_tcemain_processdatamap.php";s:4:"617b";s:13:"lib/ChangeLog";s:4:"0cbe";s:11:"lib/LICENSE";s:4:"fe15";s:22:"lib/Sabre.autoload.php";s:4:"3943";s:22:"lib/Sabre.includes.php";s:4:"0239";s:22:"lib/Sabre/autoload.php";s:4:"0bbb";s:29:"lib/Sabre/CalDAV/Calendar.php";s:4:"4963";s:35:"lib/Sabre/CalDAV/CalendarObject.php";s:4:"ab9c";s:37:"lib/Sabre/CalDAV/CalendarRootNode.php";s:4:"74d9";s:34:"lib/Sabre/CalDAV/ICalendarUtil.php";s:4:"5f71";s:27:"lib/Sabre/CalDAV/Plugin.php";s:4:"73d7";s:27:"lib/Sabre/CalDAV/Server.php";s:4:"e33e";s:32:"lib/Sabre/CalDAV/TYPO3Server.php";s:4:"2701";s:34:"lib/Sabre/CalDAV/UserCalendars.php";s:4:"4c2f";s:28:"lib/Sabre/CalDAV/Version.php";s:4:"2e25";s:28:"lib/Sabre/CalDAV/XMLUtil.php";s:4:"6028";s:37:"lib/Sabre/CalDAV/Backend/Abstract.php";s:4:"c8c0";s:32:"lib/Sabre/CalDAV/Backend/PDO.php";s:4:"84cb";s:34:"lib/Sabre/CalDAV/Backend/TYPO3.php";s:4:"4781";s:53:"lib/Sabre/CalDAV/Exception/InvalidICalendarObject.php";s:4:"199e";s:59:"lib/Sabre/CalDAV/Property/SupportedCalendarComponentSet.php";s:4:"5a5a";s:51:"lib/Sabre/CalDAV/Property/SupportedCalendarData.php";s:4:"5fa6";s:51:"lib/Sabre/CalDAV/Property/SupportedCollationSet.php";s:4:"d6b0";s:27:"lib/Sabre/DAV/Directory.php";s:4:"b7fd";s:27:"lib/Sabre/DAV/Exception.php";s:4:"714b";s:22:"lib/Sabre/DAV/File.php";s:4:"494c";s:29:"lib/Sabre/DAV/ICollection.php";s:4:"02de";s:37:"lib/Sabre/DAV/IExtendedCollection.php";s:4:"d793";s:23:"lib/Sabre/DAV/IFile.php";s:4:"ec5f";s:27:"lib/Sabre/DAV/ILockable.php";s:4:"a06a";s:23:"lib/Sabre/DAV/INode.php";s:4:"1632";s:29:"lib/Sabre/DAV/IProperties.php";s:4:"2667";s:24:"lib/Sabre/DAV/IQuota.php";s:4:"c9bf";s:22:"lib/Sabre/DAV/Node.php";s:4:"4269";s:28:"lib/Sabre/DAV/ObjectTree.php";s:4:"6f44";s:26:"lib/Sabre/DAV/Property.php";s:4:"bb43";s:24:"lib/Sabre/DAV/Server.php";s:4:"d700";s:30:"lib/Sabre/DAV/ServerPlugin.php";s:4:"e8d5";s:33:"lib/Sabre/DAV/SimpleDirectory.php";s:4:"28e8";s:43:"lib/Sabre/DAV/TemporaryFileFilterPlugin.php";s:4:"c9e4";s:22:"lib/Sabre/DAV/Tree.php";s:4:"e618";s:25:"lib/Sabre/DAV/URLUtil.php";s:4:"6171";s:25:"lib/Sabre/DAV/Version.php";s:4:"2e28";s:25:"lib/Sabre/DAV/XMLUtil.php";s:4:"1e75";s:29:"lib/Sabre/DAV/Auth/Plugin.php";s:4:"c8a5";s:32:"lib/Sabre/DAV/Auth/Principal.php";s:4:"1bd8";s:42:"lib/Sabre/DAV/Auth/PrincipalCollection.php";s:4:"8659";s:39:"lib/Sabre/DAV/Auth/Backend/Abstract.php";s:4:"862a";s:44:"lib/Sabre/DAV/Auth/Backend/AbstractBasic.php";s:4:"259f";s:45:"lib/Sabre/DAV/Auth/Backend/AbstractDigest.php";s:4:"f214";s:37:"lib/Sabre/DAV/Auth/Backend/Apache.php";s:4:"663f";s:35:"lib/Sabre/DAV/Auth/Backend/File.php";s:4:"0a14";s:34:"lib/Sabre/DAV/Auth/Backend/PDO.php";s:4:"061a";s:36:"lib/Sabre/DAV/Auth/Backend/TYPO3.php";s:4:"8c30";s:42:"lib/Sabre/DAV/Browser/GuessContentType.php";s:4:"8e42";s:42:"lib/Sabre/DAV/Browser/MapGetToPropFind.php";s:4:"d7f7";s:32:"lib/Sabre/DAV/Browser/Plugin.php";s:4:"c029";s:38:"lib/Sabre/DAV/Exception/BadRequest.php";s:4:"edc2";s:36:"lib/Sabre/DAV/Exception/Conflict.php";s:4:"7c59";s:43:"lib/Sabre/DAV/Exception/ConflictingLock.php";s:4:"23db";s:40:"lib/Sabre/DAV/Exception/FileNotFound.php";s:4:"1da2";s:37:"lib/Sabre/DAV/Exception/Forbidden.php";s:4:"de85";s:47:"lib/Sabre/DAV/Exception/InsufficientStorage.php";s:4:"2dcd";s:47:"lib/Sabre/DAV/Exception/InvalidResourceType.php";s:4:"f1a7";s:54:"lib/Sabre/DAV/Exception/LockTokenMatchesRequestUri.php";s:4:"30d6";s:34:"lib/Sabre/DAV/Exception/Locked.php";s:4:"68ad";s:44:"lib/Sabre/DAV/Exception/MethodNotAllowed.php";s:4:"633a";s:44:"lib/Sabre/DAV/Exception/NotAuthenticated.php";s:4:"1d72";s:42:"lib/Sabre/DAV/Exception/NotImplemented.php";s:4:"2d2c";s:46:"lib/Sabre/DAV/Exception/PreconditionFailed.php";s:4:"7a6b";s:48:"lib/Sabre/DAV/Exception/ReportNotImplemented.php";s:4:"9807";s:56:"lib/Sabre/DAV/Exception/RequestedRangeNotSatisfiable.php";s:4:"a66f";s:48:"lib/Sabre/DAV/Exception/UnsupportedMediaType.php";s:4:"e149";s:30:"lib/Sabre/DAV/FS/Directory.php";s:4:"c377";s:25:"lib/Sabre/DAV/FS/File.php";s:4:"a10d";s:25:"lib/Sabre/DAV/FS/Node.php";s:4:"a222";s:33:"lib/Sabre/DAV/FSExt/Directory.php";s:4:"0487";s:28:"lib/Sabre/DAV/FSExt/File.php";s:4:"f8ca";s:28:"lib/Sabre/DAV/FSExt/Node.php";s:4:"5e29";s:32:"lib/Sabre/DAV/Locks/LockInfo.php";s:4:"ba20";s:30:"lib/Sabre/DAV/Locks/Plugin.php";s:4:"4fdf";s:40:"lib/Sabre/DAV/Locks/Backend/Abstract.php";s:4:"3ba3";s:34:"lib/Sabre/DAV/Locks/Backend/FS.php";s:4:"5d52";s:35:"lib/Sabre/DAV/Locks/Backend/PDO.php";s:4:"c36a";s:30:"lib/Sabre/DAV/Mount/Plugin.php";s:4:"f683";s:42:"lib/Sabre/DAV/Property/GetLastModified.php";s:4:"5c93";s:31:"lib/Sabre/DAV/Property/Href.php";s:4:"57e4";s:32:"lib/Sabre/DAV/Property/IHref.php";s:4:"1f85";s:40:"lib/Sabre/DAV/Property/LockDiscovery.php";s:4:"5262";s:36:"lib/Sabre/DAV/Property/Principal.php";s:4:"423c";s:39:"lib/Sabre/DAV/Property/ResourceType.php";s:4:"717c";s:35:"lib/Sabre/DAV/Property/Response.php";s:4:"c4dc";s:40:"lib/Sabre/DAV/Property/SupportedLock.php";s:4:"8aa1";s:45:"lib/Sabre/DAV/Property/SupportedReportSet.php";s:4:"1ca8";s:33:"lib/Sabre/DAV/Tree/Filesystem.php";s:4:"e562";s:26:"lib/Sabre/HTTP/AWSAuth.php";s:4:"a9bf";s:31:"lib/Sabre/HTTP/AbstractAuth.php";s:4:"4e16";s:28:"lib/Sabre/HTTP/BasicAuth.php";s:4:"4950";s:29:"lib/Sabre/HTTP/DigestAuth.php";s:4:"dbe5";s:26:"lib/Sabre/HTTP/Request.php";s:4:"8055";s:27:"lib/Sabre/HTTP/Response.php";s:4:"6dd2";s:23:"lib/Sabre/HTTP/Util.php";s:4:"5f79";s:26:"lib/Sabre/HTTP/Version.php";s:4:"08dd";}',
+       '_md5_values_when_last_written' => 'a:176:{s:9:"ChangeLog";s:4:"0cbe";s:7:"LICENSE";s:4:"fe15";s:9:"_htaccess";s:4:"58d9";s:10:"caldav.php";s:4:"3755";s:16:"ext_autoload.php";s:4:"a579";s:21:"ext_conf_template.txt";s:4:"61d0";s:12:"ext_icon.gif";s:4:"1bdc";s:17:"ext_localconf.php";s:4:"ee40";s:14:"ext_tables.php";s:4:"3de8";s:14:"ext_tables.sql";s:4:"65fd";s:16:"locallang_db.xml";s:4:"1797";s:14:"doc/manual.sxw";s:4:"15ce";s:48:"hooks/class.tx_caldav_tcemain_processdatamap.php";s:4:"7c4a";s:22:"lib/Sabre.includes.php";s:4:"fd3c";s:22:"lib/Sabre/autoload.php";s:4:"bc96";s:29:"lib/Sabre/CalDAV/Calendar.php";s:4:"e3ed";s:35:"lib/Sabre/CalDAV/CalendarObject.php";s:4:"e00d";s:37:"lib/Sabre/CalDAV/CalendarRootNode.php";s:4:"933c";s:36:"lib/Sabre/CalDAV/ICSExportPlugin.php";s:4:"fc43";s:30:"lib/Sabre/CalDAV/ICalendar.php";s:4:"97e2";s:36:"lib/Sabre/CalDAV/ICalendarObject.php";s:4:"1f6f";s:34:"lib/Sabre/CalDAV/ICalendarUtil.php";s:4:"e047";s:27:"lib/Sabre/CalDAV/Plugin.php";s:4:"12d1";s:27:"lib/Sabre/CalDAV/Server.php";s:4:"8b06";s:32:"lib/Sabre/CalDAV/TYPO3Server.php";s:4:"33a2";s:34:"lib/Sabre/CalDAV/UserCalendars.php";s:4:"7d38";s:28:"lib/Sabre/CalDAV/Version.php";s:4:"1541";s:28:"lib/Sabre/CalDAV/XMLUtil.php";s:4:"5e46";s:37:"lib/Sabre/CalDAV/Backend/Abstract.php";s:4:"7515";s:32:"lib/Sabre/CalDAV/Backend/PDO.php";s:4:"4712";s:34:"lib/Sabre/CalDAV/Backend/TYPO3.php";s:4:"9f42";s:53:"lib/Sabre/CalDAV/Exception/InvalidICalendarObject.php";s:4:"08e7";s:41:"lib/Sabre/CalDAV/Principal/Collection.php";s:4:"5c12";s:40:"lib/Sabre/CalDAV/Principal/ProxyRead.php";s:4:"c1db";s:41:"lib/Sabre/CalDAV/Principal/ProxyWrite.php";s:4:"b0ba";s:35:"lib/Sabre/CalDAV/Principal/User.php";s:4:"7eda";s:59:"lib/Sabre/CalDAV/Property/SupportedCalendarComponentSet.php";s:4:"07bd";s:51:"lib/Sabre/CalDAV/Property/SupportedCalendarData.php";s:4:"0b05";s:51:"lib/Sabre/CalDAV/Property/SupportedCollationSet.php";s:4:"2a30";s:33:"lib/Sabre/CardDAV/AddressBook.php";s:4:"9581";s:44:"lib/Sabre/CardDAV/AddressBookQueryParser.php";s:4:"dad6";s:37:"lib/Sabre/CardDAV/AddressBookRoot.php";s:4:"3d01";s:26:"lib/Sabre/CardDAV/Card.php";s:4:"2e76";s:34:"lib/Sabre/CardDAV/IAddressBook.php";s:4:"9085";s:27:"lib/Sabre/CardDAV/ICard.php";s:4:"4a48";s:32:"lib/Sabre/CardDAV/IDirectory.php";s:4:"ca0d";s:28:"lib/Sabre/CardDAV/Plugin.php";s:4:"92dd";s:38:"lib/Sabre/CardDAV/UserAddressBooks.php";s:4:"520a";s:29:"lib/Sabre/CardDAV/Version.php";s:4:"78ea";s:38:"lib/Sabre/CardDAV/Backend/Abstract.php";s:4:"5755";s:33:"lib/Sabre/CardDAV/Backend/PDO.php";s:4:"5b7c";s:51:"lib/Sabre/CardDAV/Property/SupportedAddressData.php";s:4:"8c92";s:24:"lib/Sabre/DAV/Client.php";s:4:"40cb";s:28:"lib/Sabre/DAV/Collection.php";s:4:"e77d";s:27:"lib/Sabre/DAV/Directory.php";s:4:"b93c";s:27:"lib/Sabre/DAV/Exception.php";s:4:"9f93";s:22:"lib/Sabre/DAV/File.php";s:4:"cdb4";s:29:"lib/Sabre/DAV/ICollection.php";s:4:"7de6";s:37:"lib/Sabre/DAV/IExtendedCollection.php";s:4:"f4e8";s:23:"lib/Sabre/DAV/IFile.php";s:4:"37a3";s:27:"lib/Sabre/DAV/ILockable.php";s:4:"d3ff";s:23:"lib/Sabre/DAV/INode.php";s:4:"829f";s:29:"lib/Sabre/DAV/IProperties.php";s:4:"37c4";s:24:"lib/Sabre/DAV/IQuota.php";s:4:"652b";s:22:"lib/Sabre/DAV/Node.php";s:4:"eb40";s:28:"lib/Sabre/DAV/ObjectTree.php";s:4:"be02";s:26:"lib/Sabre/DAV/Property.php";s:4:"bd4c";s:24:"lib/Sabre/DAV/Server.php";s:4:"e2d8";s:30:"lib/Sabre/DAV/ServerPlugin.php";s:4:"f283";s:34:"lib/Sabre/DAV/SimpleCollection.php";s:4:"c717";s:33:"lib/Sabre/DAV/SimpleDirectory.php";s:4:"6c14";s:28:"lib/Sabre/DAV/SimpleFile.php";s:4:"485b";s:28:"lib/Sabre/DAV/StringUtil.php";s:4:"4433";s:43:"lib/Sabre/DAV/TemporaryFileFilterPlugin.php";s:4:"ace9";s:22:"lib/Sabre/DAV/Tree.php";s:4:"317d";s:25:"lib/Sabre/DAV/URLUtil.php";s:4:"9394";s:26:"lib/Sabre/DAV/UUIDUtil.php";s:4:"3a6f";s:25:"lib/Sabre/DAV/Version.php";s:4:"9551";s:25:"lib/Sabre/DAV/XMLUtil.php";s:4:"a554";s:31:"lib/Sabre/DAV/Auth/IBackend.php";s:4:"459f";s:29:"lib/Sabre/DAV/Auth/Plugin.php";s:4:"9064";s:44:"lib/Sabre/DAV/Auth/Backend/AbstractBasic.php";s:4:"1f89";s:45:"lib/Sabre/DAV/Auth/Backend/AbstractDigest.php";s:4:"9517";s:37:"lib/Sabre/DAV/Auth/Backend/Apache.php";s:4:"b65e";s:35:"lib/Sabre/DAV/Auth/Backend/File.php";s:4:"8aaf";s:34:"lib/Sabre/DAV/Auth/Backend/PDO.php";s:4:"b0b3";s:36:"lib/Sabre/DAV/Auth/Backend/TYPO3.php";s:4:"a134";s:42:"lib/Sabre/DAV/Browser/GuessContentType.php";s:4:"185a";s:42:"lib/Sabre/DAV/Browser/MapGetToPropFind.php";s:4:"54a1";s:32:"lib/Sabre/DAV/Browser/Plugin.php";s:4:"e26b";s:38:"lib/Sabre/DAV/Exception/BadRequest.php";s:4:"61b1";s:36:"lib/Sabre/DAV/Exception/Conflict.php";s:4:"832b";s:43:"lib/Sabre/DAV/Exception/ConflictingLock.php";s:4:"299a";s:40:"lib/Sabre/DAV/Exception/FileNotFound.php";s:4:"e0d6";s:37:"lib/Sabre/DAV/Exception/Forbidden.php";s:4:"95a3";s:47:"lib/Sabre/DAV/Exception/InsufficientStorage.php";s:4:"c661";s:47:"lib/Sabre/DAV/Exception/InvalidResourceType.php";s:4:"701b";s:54:"lib/Sabre/DAV/Exception/LockTokenMatchesRequestUri.php";s:4:"363b";s:34:"lib/Sabre/DAV/Exception/Locked.php";s:4:"d3a2";s:44:"lib/Sabre/DAV/Exception/MethodNotAllowed.php";s:4:"d9eb";s:44:"lib/Sabre/DAV/Exception/NotAuthenticated.php";s:4:"e9d7";s:42:"lib/Sabre/DAV/Exception/NotImplemented.php";s:4:"1d95";s:46:"lib/Sabre/DAV/Exception/PreconditionFailed.php";s:4:"fa08";s:48:"lib/Sabre/DAV/Exception/ReportNotImplemented.php";s:4:"159e";s:56:"lib/Sabre/DAV/Exception/RequestedRangeNotSatisfiable.php";s:4:"75c1";s:48:"lib/Sabre/DAV/Exception/UnsupportedMediaType.php";s:4:"be75";s:30:"lib/Sabre/DAV/FS/Directory.php";s:4:"af31";s:25:"lib/Sabre/DAV/FS/File.php";s:4:"1507";s:25:"lib/Sabre/DAV/FS/Node.php";s:4:"bf60";s:33:"lib/Sabre/DAV/FSExt/Directory.php";s:4:"46cd";s:28:"lib/Sabre/DAV/FSExt/File.php";s:4:"02ba";s:28:"lib/Sabre/DAV/FSExt/Node.php";s:4:"bd55";s:32:"lib/Sabre/DAV/Locks/LockInfo.php";s:4:"9c20";s:30:"lib/Sabre/DAV/Locks/Plugin.php";s:4:"5114";s:40:"lib/Sabre/DAV/Locks/Backend/Abstract.php";s:4:"124f";s:34:"lib/Sabre/DAV/Locks/Backend/FS.php";s:4:"31e7";s:36:"lib/Sabre/DAV/Locks/Backend/File.php";s:4:"a1c4";s:35:"lib/Sabre/DAV/Locks/Backend/PDO.php";s:4:"e55c";s:30:"lib/Sabre/DAV/Mount/Plugin.php";s:4:"3e78";s:42:"lib/Sabre/DAV/Property/GetLastModified.php";s:4:"3220";s:31:"lib/Sabre/DAV/Property/Href.php";s:4:"b651";s:35:"lib/Sabre/DAV/Property/HrefList.php";s:4:"78f6";s:32:"lib/Sabre/DAV/Property/IHref.php";s:4:"6d83";s:40:"lib/Sabre/DAV/Property/LockDiscovery.php";s:4:"99f5";s:39:"lib/Sabre/DAV/Property/ResourceType.php";s:4:"da8e";s:35:"lib/Sabre/DAV/Property/Response.php";s:4:"b133";s:39:"lib/Sabre/DAV/Property/ResponseList.php";s:4:"4e3c";s:40:"lib/Sabre/DAV/Property/SupportedLock.php";s:4:"e962";s:45:"lib/Sabre/DAV/Property/SupportedReportSet.php";s:4:"d1a0";s:33:"lib/Sabre/DAV/Tree/Filesystem.php";s:4:"6994";s:48:"lib/Sabre/DAVACL/AbstractPrincipalCollection.php";s:4:"2126";s:25:"lib/Sabre/DAVACL/IACL.php";s:4:"3509";s:31:"lib/Sabre/DAVACL/IPrincipal.php";s:4:"0771";s:38:"lib/Sabre/DAVACL/IPrincipalBackend.php";s:4:"58e3";s:27:"lib/Sabre/DAVACL/Plugin.php";s:4:"24ba";s:30:"lib/Sabre/DAVACL/Principal.php";s:4:"4299";s:40:"lib/Sabre/DAVACL/PrincipalCollection.php";s:4:"6307";s:28:"lib/Sabre/DAVACL/Version.php";s:4:"3f9d";s:42:"lib/Sabre/DAVACL/Exception/AceConflict.php";s:4:"462c";s:45:"lib/Sabre/DAVACL/Exception/NeedPrivileges.php";s:4:"b30a";s:41:"lib/Sabre/DAVACL/Exception/NoAbstract.php";s:4:"f6e6";s:53:"lib/Sabre/DAVACL/Exception/NotRecognizedPrincipal.php";s:4:"90a9";s:52:"lib/Sabre/DAVACL/Exception/NotSupportedPrivilege.php";s:4:"fc12";s:41:"lib/Sabre/DAVACL/PrincipalBackend/PDO.php";s:4:"c212";s:43:"lib/Sabre/DAVACL/PrincipalBackend/TYPO3.php";s:4:"03ac";s:33:"lib/Sabre/DAVACL/Property/Acl.php";s:4:"7d7d";s:53:"lib/Sabre/DAVACL/Property/CurrentUserPrivilegeSet.php";s:4:"8066";s:39:"lib/Sabre/DAVACL/Property/Principal.php";s:4:"4a8e";s:51:"lib/Sabre/DAVACL/Property/SupportedPrivilegeSet.php";s:4:"0fec";s:26:"lib/Sabre/HTTP/AWSAuth.php";s:4:"2310";s:31:"lib/Sabre/HTTP/AbstractAuth.php";s:4:"0b93";s:28:"lib/Sabre/HTTP/BasicAuth.php";s:4:"3357";s:29:"lib/Sabre/HTTP/DigestAuth.php";s:4:"2fdd";s:26:"lib/Sabre/HTTP/Request.php";s:4:"b263";s:27:"lib/Sabre/HTTP/Response.php";s:4:"690e";s:23:"lib/Sabre/HTTP/Util.php";s:4:"fda1";s:26:"lib/Sabre/HTTP/Version.php";s:4:"87a3";s:31:"lib/Sabre/VObject/Component.php";s:4:"1780";s:29:"lib/Sabre/VObject/Element.php";s:4:"4cf9";s:33:"lib/Sabre/VObject/ElementList.php";s:4:"3ee4";s:26:"lib/Sabre/VObject/Node.php";s:4:"50f0";s:31:"lib/Sabre/VObject/Parameter.php";s:4:"0659";s:36:"lib/Sabre/VObject/ParseException.php";s:4:"c6e6";s:30:"lib/Sabre/VObject/Property.php";s:4:"9164";s:28:"lib/Sabre/VObject/Reader.php";s:4:"3672";s:29:"lib/Sabre/VObject/Version.php";s:4:"bc56";s:30:"lib/Sabre/VObject/includes.php";s:4:"3aef";s:38:"lib/Sabre/VObject/Element/DateTime.php";s:4:"25fe";s:43:"lib/Sabre/VObject/Element/MultiDateTime.php";s:4:"85cd";s:35:"mod1/class.tx_cal_ics_generator.php";s:4:"d946";s:14:"mod1/clear.gif";s:4:"cc11";s:13:"mod1/conf.php";s:4:"ea47";s:14:"mod1/index.php";s:4:"7f06";s:18:"mod1/locallang.xml";s:4:"81d2";s:22:"mod1/locallang_mod.xml";s:4:"66ff";s:19:"mod1/moduleicon.gif";s:4:"5b00";}',
        'suggests' => array(
        ),
 );
index 1891519..b993e8a 100755 (executable)
Binary files a/ext_icon.gif and b/ext_icon.gif differ
index e408a6f..740b4fb 100755 (executable)
@@ -3,4 +3,8 @@ if (!defined ('TYPO3_MODE')) {
        die ('Access denied.');
 }
 
+if (TYPO3_MODE=="BE")  {
+       t3lib_extMgm::addModule("tools","calicsgenerator","",t3lib_extMgm::extPath($_EXTKEY)."mod1/");
+}
+
 ?>
\ No newline at end of file
index 7eb9100..bd951c7 100755 (executable)
@@ -65,7 +65,8 @@ class tx_caldav_tcemain_processdatamap {
                                        $tx_cal_api = &$tx_cal_api->tx_cal_api_without($pageIDForPlugin);
 
                                        if($table == 'tx_cal_event'){
-                                               $eventObject = $tx_cal_api->findEvent($event['uid'], 'tx_cal_phpicalendar', '');
+                                               $eventObject = $tx_cal_api->modelObj->findEvent($event['uid'], 'tx_cal_phpicalendar', '', false, false, false, true, true);
+
                                                if ($eventObject->conf['view.']['event.']['phpicalendarEventTemplate']) {
                                                        $oldPath = &$eventObject->conf['view.']['event.']['phpicalendarEventTemplate'];
                                                } else {
index 612e13c..c4b9369 100755 (executable)
@@ -8,7 +8,7 @@
  *
  * @package Sabre
  * @subpackage DAV
- * @copyright Copyright (C) 2007-2010 Rooftop Solutions. All rights reserved.
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
  * @author Evert Pot (http://www.rooftopsolutions.nl/) 
  * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
  */
@@ -54,9 +54,10 @@ include 'Sabre/DAV/Property/SupportedLock.php';
 include 'Sabre/DAV/Property/LockDiscovery.php';
 include 'Sabre/DAV/Property/IHref.php';
 include 'Sabre/DAV/Property/Href.php';
+include 'Sabre/DAV/Property/HrefList.php';
 include 'Sabre/DAV/Property/SupportedReportSet.php';
 include 'Sabre/DAV/Property/Response.php';
-include 'Sabre/DAV/Property/Principal.php';
+include 'Sabre/DAV/Property/ResponseList.php';
 
 /* Node interfaces */
 include 'Sabre/DAV/INode.php';
@@ -70,9 +71,11 @@ include 'Sabre/DAV/IExtendedCollection.php';
 /* Node abstract implementations */
 include 'Sabre/DAV/Node.php';
 include 'Sabre/DAV/File.php';
+include 'Sabre/DAV/Collection.php';
 include 'Sabre/DAV/Directory.php';
 
 /* Utilities */
+include 'Sabre/DAV/SimpleCollection.php';
 include 'Sabre/DAV/SimpleDirectory.php';
 include 'Sabre/DAV/XMLUtil.php';
 include 'Sabre/DAV/URLUtil.php';
@@ -113,15 +116,12 @@ include 'Sabre/DAV/TemporaryFileFilterPlugin.php';
 
 /* Authentication plugin */
 include 'Sabre/DAV/Auth/Plugin.php';
-include 'Sabre/DAV/Auth/Backend/Abstract.php';
+include 'Sabre/DAV/Auth/IBackend.php';
 include 'Sabre/DAV/Auth/Backend/AbstractDigest.php';
+include 'Sabre/DAV/Auth/Backend/AbstractBasic.php';
 include 'Sabre/DAV/Auth/Backend/File.php';
 include 'Sabre/DAV/Auth/Backend/PDO.php';
 
-include 'Sabre/DAV/Auth/Principal.php';
-include 'Sabre/DAV/Auth/PrincipalCollection.php';
-
 /* DavMount plugin */
 include 'Sabre/DAV/Mount/Plugin.php';
 
-
index c758a38..bf1274e 100755 (executable)
@@ -5,7 +5,7 @@
  * 
  * @package Sabre
  * @subpackage CalDAV
- * @copyright Copyright (C) 2007-2010 Rooftop Solutions. All rights reserved.
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
  * @author Evert Pot (http://www.rooftopsolutions.nl/) 
  * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
  */
@@ -36,20 +36,17 @@ abstract class Sabre_CalDAV_Backend_Abstract {
      * If the creation was a success, an id must be returned that can be used to reference
      * this calendar in other methods, such as updateCalendar.
      *
-     * This function must return a server-wide unique id that can be used 
-     * later to reference the calendar.
-     *
      * @param string $principalUri
      * @param string $calendarUri
      * @param array $properties
-     * @return string|int 
+     * @return void 
      */
     abstract function createCalendar($principalUri,$calendarUri,array $properties); 
 
     /**
-     * Updates properties on this node,
+     * Updates properties for a calendar.
      *
-     * The properties array uses the propertyName in clark-notation as key,
+     * The mutations array uses the propertyName in clark-notation as key,
      * and the array value for the property value. In the case a property
      * should be deleted, the property value will be null.
      *
@@ -79,10 +76,10 @@ abstract class Sabre_CalDAV_Backend_Abstract {
      * (424 Failed Dependency) because the request needs to be atomic.
      *
      * @param string $calendarId
-     * @param array $properties
+     * @param array $mutations
      * @return bool|array 
      */
-    public function updateCalendar($calendarId, array $properties) {
+    public function updateCalendar($calendarId, array $mutations) {
         
         return false; 
 
@@ -97,13 +94,23 @@ abstract class Sabre_CalDAV_Backend_Abstract {
     abstract function deleteCalendar($calendarId);
 
     /**
-     * Returns all calendar objects within a calendar object.
+     * Returns all calendar objects within a calendar.
      *
      * Every item contains an array with the following keys:
      *   * id - unique identifier which will be used for subsequent updates
      *   * calendardata - The iCalendar-compatible calnedar data
      *   * uri - a unique key which will be used to construct the uri. This can be any arbitrary string.
      *   * lastmodified - a timestamp of the last modification time
+     *   * etag - An arbitrary string, surrounded by double-quotes. (e.g.: 
+     *   '  "abcdef"')
+     *   * calendarid - The calendarid as it was passed to this function.
+     *
+     * Note that the etag is optional, but it's highly encouraged to return for 
+     * speed reasons.
+     *
+     * The calendardata is also optional. If it's not returned 
+     * 'getCalendarObject' will be called later, which *is* expected to return 
+     * calendardata.
      * 
      * @param string $calendarId 
      * @return array 
@@ -111,7 +118,12 @@ abstract class Sabre_CalDAV_Backend_Abstract {
     abstract function getCalendarObjects($calendarId);
 
     /**
-     * Returns information from a single calendar object, based on it's object uri. 
+     * Returns information from a single calendar object, based on it's object
+     * uri.
+     *
+     * The returned array must have the same keys as getCalendarObjects. The 
+     * 'calendardata' object is required here though, while it's not required 
+     * for getCalendarObjects.
      * 
      * @param string $calendarId 
      * @param string $objectUri 
index f60d173..da04f99 100755 (executable)
@@ -8,7 +8,7 @@
  * 
  * @package Sabre
  * @subpackage CalDAV
- * @copyright Copyright (C) 2007-2010 Rooftop Solutions. All rights reserved.
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
  * @author Evert Pot (http://www.rooftopsolutions.nl/) 
  * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
  */
@@ -19,7 +19,21 @@ class Sabre_CalDAV_Backend_PDO extends Sabre_CalDAV_Backend_Abstract {
      * 
      * @var PDO
      */
-    private $pdo;
+    protected $pdo;
+
+    /**
+     * The table name that will be used for calendars 
+     * 
+     * @var string 
+     */
+    protected $calendarTableName;
+
+    /**
+     * The table name that will be used for calendar objects  
+     * 
+     * @var string 
+     */
+    protected $calendarObjectTableName;
 
     /**
      * List of CalDAV properties, and how they map to database fieldnames
@@ -41,9 +55,11 @@ class Sabre_CalDAV_Backend_PDO extends Sabre_CalDAV_Backend_Abstract {
      * 
      * @param PDO $pdo 
      */
-    public function __construct(PDO $pdo) {
+    public function __construct(PDO $pdo, $calendarTableName = 'calendars', $calendarObjectTableName = 'calendarobjects') {
 
         $this->pdo = $pdo;
+        $this->calendarTableName = $calendarTableName;
+        $this->calendarObjectTableName = $calendarObjectTableName;
 
     }
 
@@ -75,7 +91,7 @@ class Sabre_CalDAV_Backend_PDO extends Sabre_CalDAV_Backend_Abstract {
 
         // Making fields a comma-delimited list 
         $fields = implode(', ', $fields);
-        $stmt = $this->pdo->prepare("SELECT " . $fields . " FROM calendars WHERE principaluri = ?"); 
+        $stmt = $this->pdo->prepare("SELECT " . $fields . " FROM `".$this->calendarTableName."` WHERE principaluri = ?"); 
         $stmt->execute(array($principalUri));
 
         $calendars = array();
@@ -113,7 +129,6 @@ class Sabre_CalDAV_Backend_PDO extends Sabre_CalDAV_Backend_Abstract {
      * @param string $principalUri
      * @param string $calendarUri
      * @param array $properties
-     * @return mixed
      */
     public function createCalendar($principalUri,$calendarUri, array $properties) {
 
@@ -149,7 +164,7 @@ class Sabre_CalDAV_Backend_PDO extends Sabre_CalDAV_Backend_Abstract {
             }
         }
 
-        $stmt = $this->pdo->prepare("INSERT INTO calendars (".implode(', ', $fieldNames).") VALUES (".implode(', ',array_keys($values)).")");
+        $stmt = $this->pdo->prepare("INSERT INTO `".$this->calendarTableName."` (".implode(', ', $fieldNames).") VALUES (".implode(', ',array_keys($values)).")");
         $stmt->execute($values);
 
         return $this->pdo->lastInsertId();
@@ -157,9 +172,9 @@ class Sabre_CalDAV_Backend_PDO extends Sabre_CalDAV_Backend_Abstract {
     }
 
     /**
-     * Updates a calendars properties 
+     * Updates properties for a calendar.
      *
-     * The properties array uses the propertyName in clark-notation as key,
+     * The mutations array uses the propertyName in clark-notation as key,
      * and the array value for the property value. In the case a property
      * should be deleted, the property value will be null.
      *
@@ -189,10 +204,10 @@ class Sabre_CalDAV_Backend_PDO extends Sabre_CalDAV_Backend_Abstract {
      * (424 Failed Dependency) because the request needs to be atomic.
      *
      * @param string $calendarId
-     * @param array $properties
+     * @param array $mutations 
      * @return bool|array 
      */
-    public function updateCalendar($calendarId, array $properties) {
+    public function updateCalendar($calendarId, array $mutations) {
 
         $newValues = array();
         $result = array(
@@ -203,13 +218,13 @@ class Sabre_CalDAV_Backend_PDO extends Sabre_CalDAV_Backend_Abstract {
 
         $hasError = false;
 
-        foreach($properties as $propertyName=>$propertyValue) {
+        foreach($mutations as $propertyName=>$propertyValue) {
 
             // We don't know about this property. 
             if (!isset($this->propertyMap[$propertyName])) {
                 $hasError = true;
                 $result[403][$propertyName] = null;
-                unset($properties[$propertyName]);
+                unset($mutations[$propertyName]);
                 continue;
             }
 
@@ -221,7 +236,7 @@ class Sabre_CalDAV_Backend_PDO extends Sabre_CalDAV_Backend_Abstract {
         // If there were any errors we need to fail the request
         if ($hasError) {
             // Properties has the remaining properties
-            foreach($properties as $propertyName=>$propertyValue) {
+            foreach($mutations as $propertyName=>$propertyValue) {
                 $result[424][$propertyName] = null;
             }
 
@@ -243,7 +258,7 @@ class Sabre_CalDAV_Backend_PDO extends Sabre_CalDAV_Backend_Abstract {
         }
         $valuesSql[] = 'ctag = ctag + 1';
 
-        $stmt = $this->pdo->prepare("UPDATE calendars SET " . implode(', ',$valuesSql) . " WHERE id = ?");
+        $stmt = $this->pdo->prepare("UPDATE `" . $this->calendarTableName . "` SET " . implode(', ',$valuesSql) . " WHERE id = ?");
         $newValues['id'] = $calendarId; 
         $stmt->execute(array_values($newValues));
 
@@ -259,36 +274,51 @@ class Sabre_CalDAV_Backend_PDO extends Sabre_CalDAV_Backend_Abstract {
      */
     public function deleteCalendar($calendarId) {
 
-        $stmt = $this->pdo->prepare('DELETE FROM calendarobjects WHERE calendarid = ?');
+        $stmt = $this->pdo->prepare('DELETE FROM `'.$this->calendarObjectTableName.'` WHERE calendarid = ?');
         $stmt->execute(array($calendarId));
 
-        $stmt = $this->pdo->prepare('DELETE FROM calendars WHERE id = ?');
+        $stmt = $this->pdo->prepare('DELETE FROM `'.$this->calendarTableName.'` WHERE id = ?');
         $stmt->execute(array($calendarId));
 
     }
 
     /**
-     * Returns all calendar objects within a calendar object.
+     * Returns all calendar objects within a calendar
      *
      * Every item contains an array with the following keys:
      *   * id - unique identifier which will be used for subsequent updates
      *   * calendardata - The iCalendar-compatible calnedar data
      *   * uri - a unique key which will be used to construct the uri. This can be any arbitrary string.
      *   * lastmodified - a timestamp of the last modification time
+     *   * etag - An arbitrary string, surrounded by double-quotes. (e.g.: 
+     *   '  "abcdef"')
+     *   * calendarid - The calendarid as it was passed to this function.
+     *
+     * Note that the etag is optional, but it's highly encouraged to return for 
+     * speed reasons.
+     *
+     * The calendardata is also optional. If it's not returned 
+     * 'getCalendarObject' will be called later, which *is* expected to return 
+     * calendardata.
      * 
      * @param string $calendarId 
      * @return array 
      */
     public function getCalendarObjects($calendarId) {
 
-        $stmt = $this->pdo->prepare('SELECT * FROM calendarobjects WHERE calendarid = ?');
+        $stmt = $this->pdo->prepare('SELECT * FROM `'.$this->calendarObjectTableName.'` WHERE calendarid = ?');
         $stmt->execute(array($calendarId));
         return $stmt->fetchAll();
 
     }
 
     /**
-     * Returns information from a single calendar object, based on it's object uri. 
+     * Returns information from a single calendar object, based on it's object
+     * uri.
+     *
+     * The returned array must have the same keys as getCalendarObjects. The 
+     * 'calendardata' object is required here though, while it's not required 
+     * for getCalendarObjects.
      * 
      * @param string $calendarId 
      * @param string $objectUri 
@@ -296,7 +326,7 @@ class Sabre_CalDAV_Backend_PDO extends Sabre_CalDAV_Backend_Abstract {
      */
     public function getCalendarObject($calendarId,$objectUri) {
 
-        $stmt = $this->pdo->prepare('SELECT * FROM calendarobjects WHERE calendarid = ? AND uri = ?');
+        $stmt = $this->pdo->prepare('SELECT * FROM `'.$this->calendarObjectTableName.'` WHERE calendarid = ? AND uri = ?');
         $stmt->execute(array($calendarId, $objectUri));
         return $stmt->fetch();
 
@@ -312,9 +342,9 @@ class Sabre_CalDAV_Backend_PDO extends Sabre_CalDAV_Backend_Abstract {
      */
     public function createCalendarObject($calendarId,$objectUri,$calendarData) {
 
-        $stmt = $this->pdo->prepare('INSERT INTO calendarobjects (calendarid, uri, calendardata, lastmodified) VALUES (?,?,?,?)');
+        $stmt = $this->pdo->prepare('INSERT INTO `'.$this->calendarObjectTableName.'` (calendarid, uri, calendardata, lastmodified) VALUES (?,?,?,?)');
         $stmt->execute(array($calendarId,$objectUri,$calendarData,time()));
-        $stmt = $this->pdo->prepare('UPDATE calendars SET ctag = ctag + 1 WHERE id = ?');
+        $stmt = $this->pdo->prepare('UPDATE `'.$this->calendarTableName.'` SET ctag = ctag + 1 WHERE id = ?');
         $stmt->execute(array($calendarId));
 
     }
@@ -329,9 +359,9 @@ class Sabre_CalDAV_Backend_PDO extends Sabre_CalDAV_Backend_Abstract {
      */
     public function updateCalendarObject($calendarId,$objectUri,$calendarData) {
 
-        $stmt = $this->pdo->prepare('UPDATE calendarobjects SET calendardata = ?, lastmodified = ? WHERE calendarid = ? AND uri = ?');
+        $stmt = $this->pdo->prepare('UPDATE `'.$this->calendarObjectTableName.'` SET calendardata = ?, lastmodified = ? WHERE calendarid = ? AND uri = ?');
         $stmt->execute(array($calendarData,time(),$calendarId,$objectUri));
-        $stmt = $this->pdo->prepare('UPDATE calendars SET ctag = ctag + 1 WHERE id = ?');
+        $stmt = $this->pdo->prepare('UPDATE `'.$this->calendarTableName.'` SET ctag = ctag + 1 WHERE id = ?');
         $stmt->execute(array($calendarId));
 
     }
@@ -345,9 +375,9 @@ class Sabre_CalDAV_Backend_PDO extends Sabre_CalDAV_Backend_Abstract {
      */
     public function deleteCalendarObject($calendarId,$objectUri) {
 
-        $stmt = $this->pdo->prepare('DELETE FROM calendarobjects WHERE calendarid = ? AND uri = ?');
+        $stmt = $this->pdo->prepare('DELETE FROM `'.$this->calendarObjectTableName.'` WHERE calendarid = ? AND uri = ?');
         $stmt->execute(array($calendarId,$objectUri));
-        $stmt = $this->pdo->prepare('UPDATE calendars SET ctag = ctag + 1 WHERE id = ?');
+        $stmt = $this->pdo->prepare('UPDATE `'. $this->calendarTableName .'` SET ctag = ctag + 1 WHERE id = ?');
         $stmt->execute(array($calendarId));
 
     }
index 10e2244..6335bf8 100755 (executable)
 <?php
 
 /**
- * PDO CalDAV backend
+ * TYPO3 CalDAV backend
+ *
+ * This backend is used to store calendar-data in a TYPO3 database
  *
- * This backend is used to store calendar-data in a PDO database, such as 
- * sqlite or MySQL
- * 
  * @package Sabre
  * @subpackage CalDAV
- * @copyright Copyright (C) 2007-2010 Rooftop Solutions. All rights reserved.
- * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @copyright Copyright (C) 2012 Mario Matzulla. All rights reserved.
+ * @author Mario Matzulla (http://www.matzullas.de)
  * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
  */
 class Sabre_CalDAV_Backend_TYPO3 extends Sabre_CalDAV_Backend_Abstract {
 
-    /**
-     * pdo 
-     * 
-     * @var PDO
-     */
-    private $pdo;
-
-    /**
-     * List of CalDAV properties, and how they map to database fieldnames
-     *
-     * Add your own properties by simply adding on to this array
-     * 
-     * @var array
-     */
-    public $propertyMap = array(
-        '{DAV:}displayname'                          => 'title',
-        '{urn:ietf:params:xml:ns:caldav}calendar-description' => 'tx_caldav_data',
-        '{urn:ietf:params:xml:ns:caldav}calendar-timezone'    => 'timezone',
-        '{http://apple.com/ns/ical/}calendar-order'  => 'calendarorder',
-        '{http://apple.com/ns/ical/}calendar-color'  => 'calendarcolor',
-    );
-
-    /**
-     * Creates the backend 
-     * 
-     * @param PDO $pdo 
-     */
-    public function __construct(PDO $pdo) {
-
-        $this->pdo = $pdo;
-
-    }
-
-    /**
-     * Returns a list of calendars for a principal.
-     *
-     * Every project is an array with the following keys:
-     *  * id, a unique id that will be used by other functions to modify the
-     *    calendar. This can be the same as the uri or a database key.
-     *  * uri, which the basename of the uri with which the calendar is 
-     *    accessed.
-     *  * principalUri. The owner of the calendar. Almost always the same as
-     *    principalUri passed to this method.
-     *
-     * Furthermore it can contain webdav properties in clark notation. A very
-     * common one is '{DAV:}displayname'. 
-     *
-     * @param string $principalUri 
-     * @return array 
-     */
-    public function getCalendarsForUser($principalUri) {
-       
-       $principalUriParts = explode("/",$principalUri);
-       $stmt = $this->pdo->prepare("SELECT uid, tx_cal_calendar FROM fe_users WHERE username = ? AND deleted=0"); 
-        $stmt->execute(array(array_pop($principalUriParts)));
-        
-        $calendars = array();
-        
-        while($user = $stmt->fetch(PDO::FETCH_ASSOC)) {
-
-               $stmt = $this->pdo->prepare("SELECT * FROM tx_cal_calendar WHERE uid = ?"); 
-               $stmt->execute(array($user['tx_cal_calendar']));
-       
-               
-               while($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
-       
-                   $components = explode(',','VEVENT,VTODO');
-       
-                   $calendar = array(
-                       'id' => $row['uid'],
-                       'uri' => $row['title'],
-                       'principaluri' => $principalUri,
-                       '{' . Sabre_CalDAV_Plugin::NS_CALENDARSERVER . '}getctag' => $row['tstamp']?$row['tstamp']:'0',
-                       '{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}supported-calendar-component-set' => new Sabre_CalDAV_Property_SupportedCalendarComponentSet($components),
-                           '{DAV:}displayname'                          => $row['title'],
-                               '{urn:ietf:params:xml:ns:caldav}calendar-description' => '',
-                               '{urn:ietf:params:xml:ns:caldav}calendar-timezone'    => null,
-                               '{http://apple.com/ns/ical/}calendar-order'  => 0,
-                               '{http://apple.com/ns/ical/}calendar-color'  => null,
-                   );
-               
-                   $calendars[] = $calendar;
-       
-               }
-        }
-
-        return $calendars;
-
-    }
-
-    /**
-     * Creates a new calendar for a principal.
-     *
-     * If the creation was a success, an id must be returned that can be used to reference
-     * this calendar in other methods, such as updateCalendar
-     *
-     * @param string $principalUri
-     * @param string $calendarUri
-     * @param array $properties
-     * @return mixed
-     */
-    public function createCalendar($principalUri,$calendarUri, array $properties) {
-
-        $fieldNames = array(
-            'principaluri',
-            'uri',
-            'ctag',
-        );
-        $values = array(
-            ':principaluri' => $principalUri,
-            ':uri'          => $calendarUri,
-            ':ctag'         => 1,
-        );
-
-        // Default value
-        $sccs = '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set';
-        $fieldNames[] = 'components';
-        if (!isset($properties[$sccs])) {
-            $values[':components'] = 'VEVENT,VTODO';
-        } else {
-            if (!($properties[$sccs] instanceof Sabre_CalDAV_Property_SupportedCalendarComponentSet)) {
-                throw new Sabre_DAV_Exception('The ' . $sccs . ' property must be of type: Sabre_CalDAV_Property_SupportedCalendarComponentSet');
-            }
-            $values[':components'] = implode(',',$properties[$sccs]->getValue());
-        }
-
-        foreach($this->propertyMap as $xmlName=>$dbName) {
-            if (isset($properties[$xmlName])) {
-
-                $myValue = $properties[$xmlName];
-                $values[':' . $dbName] = $properties[$xmlName];
-                $fieldNames[] = $dbName;
-            }
-        }
-
-        $stmt = $this->pdo->prepare("INSERT INTO tx_cal_calendar (".implode(', ', $fieldNames).") VALUES (".implode(', ',array_keys($values)).")");
-        $stmt->execute($values);
-
-        return $this->pdo->lastInsertId();
-
-    }
-
-    /**
-     * Updates a calendars properties 
-     *
-     * The properties array uses the propertyName in clark-notation as key,
-     * and the array value for the property value. In the case a property
-     * should be deleted, the property value will be null.
-     *
-     * This method must be atomic. If one property cannot be changed, the
-     * entire operation must fail.
-     *
-     * If the operation was successful, true can be returned.
-     * If the operation failed, false can be returned.
-     *
-     * Deletion of a non-existant property is always succesful.
-     *
-     * Lastly, it is optional to return detailed information about any
-     * failures. In this case an array should be returned with the following
-     * structure:
-     *
-     * array(
-     *   403 => array(
-     *      '{DAV:}displayname' => null,
-     *   ),
-     *   424 => array(
-     *      '{DAV:}owner' => null,
-     *   )
-     * )
-     *
-     * In this example it was forbidden to update {DAV:}displayname. 
-     * (403 Forbidden), which in turn also caused {DAV:}owner to fail
-     * (424 Failed Dependency) because the request needs to be atomic.
-     *
-     * @param string $calendarId
-     * @param array $properties
-     * @return bool|array 
-     */
-    public function updateCalendar($calendarId, array $properties) {
-
-        $newValues = array();
-        $result = array(
-            200 => array(), // Ok
-            403 => array(), // Forbidden
-            424 => array(), // Failed Dependency
-        );
-
-        $hasError = false;
-
-        foreach($properties as $propertyName=>$propertyValue) {
-
-            // We don't know about this property. 
-            if (!isset($this->propertyMap[$propertyName])) {
-                $hasError = true;
-                $result[403][$propertyName] = null;
-                unset($properties[$propertyName]);
-                continue;
-            }
-
-            $fieldName = $this->propertyMap[$propertyName];
-            $newValues[$fieldName] = $propertyValue;
-                
-        }
-
-        // If there were any errors we need to fail the request
-        if ($hasError) {
-            // Properties has the remaining properties
-            foreach($properties as $propertyName=>$propertyValue) {
-                $result[424][$propertyName] = null;
-            }
-
-            // Removing unused statuscodes for cleanliness
-            foreach($result as $status=>$properties) {
-                if (is_array($properties) && count($properties)===0) unset($result[$status]);
-            }
-
-            return $result;
-
-        }
-
-        // Success
-
-        // Now we're generating the sql query.
-        $valuesSql = array();
-        foreach($newValues as $fieldName=>$value) {
-            $valuesSql[] = $fieldName . ' = ?';
-        }
-        $valuesSql[] = time();
-
-        $stmt = $this->pdo->prepare("UPDATE tx_cal_calendar SET " . implode(', ',$valuesSql) . " WHERE id = ?");
-        $newValues['id'] = $calendarId; 
-        $stmt->execute(array_values($newValues));
-
-        return true; 
-
-    }
-
-    /**
-     * Delete a calendar and all it's objects 
-     * 
-     * @param string $calendarId 
-     * @return void
-     */
-    public function deleteCalendar($calendarId) {
-
-        $stmt = $this->pdo->prepare('DELETE FROM tx_cal_event WHERE calendar_id = ?');
-        $stmt->execute(array($calendarId));
-
-        $stmt = $this->pdo->prepare('DELETE FROM tx_cal_calendar WHERE uid = ?');
-        $stmt->execute(array($calendarId));
-
-    }
-
-    /**
-     * Returns all calendar objects within a calendar object.
-     *
-     * Every item contains an array with the following keys:
-     *   * id - unique identifier which will be used for subsequent updates
-     *   * calendardata - The iCalendar-compatible calnedar data
-     *   * uri - a unique key which will be used to construct the uri. This can be any arbitrary string.
-     *   * lastmodified - a timestamp of the last modification time
-     * 
-     * @param string $calendarId 
-     * @return array 
-     */
-    public function getCalendarObjects($calendarId) {
-
-        $stmt = $this->pdo->prepare('SELECT * FROM tx_cal_event WHERE calendar_id = ? AND deleted = 0');
-        $stmt->execute(array($calendarId));
-        $eventArray = $stmt->fetchAll();
-        $preparedArray = Array();
-        foreach($eventArray as $eventRow){
-               if($eventRow['tx_caldav_uid']==''){
-                       $eventRow['tx_caldav_uid'] = 'a1b2c3_'.$eventRow['calendar_id'].'_'.$eventRow['uid'];
-                       $stmt = $this->pdo->prepare("UPDATE tx_cal_event SET tx_caldav_uid = ? WHERE uid = ?");
-                       $stmt->execute(Array($eventRow['tx_caldav_uid'], $eventRow['uid']));
-               
-               }
-               $preparedArray[] = Array(
-                       'id' => $eventRow['uid'],
-                       'displayname' => $eventRow['title'],
-                       'calendardata' => $eventRow['tx_caldav_data'],
-                       'uri' => $eventRow['tx_caldav_uid'],
-                       'calendarid' => $calendarId,
-                       'lastmodified' => $eventRow['tstamp']
-               );
-        }
-        return $preparedArray;
-
-    }
-
-    /**
-     * Returns information from a single calendar object, based on it's object uri. 
-     * 
-     * @param string $calendarId 
-     * @param string $objectUri 
-     * @return array 
-     */
-    public function getCalendarObject($calendarId,$objectUri) {
-       
-       $stmt = $this->pdo->prepare('SELECT * FROM tx_cal_event WHERE calendar_id = ? AND tx_caldav_uid = ? AND deleted = 0');
-        $stmt->execute(array($calendarId, $objectUri));
-        $eventRow = $stmt->fetch();
-        if(empty($eventRow)){
-               return Array();
-        }
-        return Array(
-               'id' => $eventRow['uid'],
-               'displayname' => $eventRow['title'],
-               'calendardata' => $eventRow['tx_caldav_data'],
-               'uri' => $eventRow['icsUid'],
-               'calendarid' => $calendarId,
-               'lastmodified' => $eventRow['tstamp']
-        );
-
-    }
-
-    /**
-     * Creates a new calendar object. 
-     * 
-     * @param string $calendarId 
-     * @param string $objectUri 
-     * @param string $calendarData 
-     * @return void
-     */
-    public function createCalendarObject($calendarId,$objectUri,$calendarData) {
-
-       $stmt = $this->pdo->prepare('INSERT INTO tx_cal_event (calendar_id, tx_caldav_uid, tx_caldav_data, tstamp) VALUES (?,?,?,?)');
-        $stmt->execute(array($calendarId,$objectUri,$calendarData,time()));
-        $stmt = $this->pdo->prepare('UPDATE tx_cal_calendar SET tstamp = tstamp + 1 WHERE uid = ? AND deleted = 0');
-        $stmt->execute(array($calendarId));
-        $this->updateCalEvent($calendarId,$objectUri,$calendarData);
-    }
-
-    /**
-     * Updates an existing calendarobject, based on it's uri. 
-     * 
-     * @param string $calendarId 
-     * @param string $objectUri 
-     * @param string $calendarData 
-     * @return void
-     */
-    public function updateCalendarObject($calendarId,$objectUri,$calendarData) {
+       /**
+        * pdo
+        *
+        * @var PDO
+        */
+       private $pdo;
+
+       /**
+        * The table name that will be used for calendars
+        *
+        * @var string
+        */
+       protected $calendarTableName;
+
+       /**
+        * The table name that will be used for calendar objects
+        *
+        * @var string
+        */
+       protected $calendarObjectTableName;
+
+
+       /**
+        * List of CalDAV properties, and how they map to database fieldnames
+        *
+        * Add your own properties by simply adding on to this array
+        *
+        * @var array
+        */
+       public $propertyMap = array(
+                       '{DAV:}displayname'                          => 'title',
+                       '{urn:ietf:params:xml:ns:caldav}calendar-description' => 'tx_caldav_data',
+                       '{urn:ietf:params:xml:ns:caldav}calendar-timezone'    => 'timezone',
+                       '{http://apple.com/ns/ical/}calendar-order'  => 'calendarorder',
+                       '{http://apple.com/ns/ical/}calendar-color'  => 'calendarcolor',
+       );
+
+       /**
+        * Creates the backend
+        *
+        * @param PDO $pdo
+        */
+       public function __construct(PDO $pdo, $calendarTableName = 'calendars', $calendarObjectTableName = 'calendarobjects') {
+
+               $this->pdo = $pdo;
+               $this->calendarTableName = $calendarTableName;
+               $this->calendarObjectTableName = $calendarObjectTableName;
+
+       }
+
+       /**
+        * Returns a list of calendars for a principal.
+        *
+        * Every project is an array with the following keys:
+        *  * id, a unique id that will be used by other functions to modify the
+        *    calendar. This can be the same as the uri or a database key.
+        *  * uri, which the basename of the uri with which the calendar is
+        *    accessed.
+        *  * principalUri. The owner of the calendar. Almost always the same as
+        *    principalUri passed to this method.
+        *
+        * Furthermore it can contain webdav properties in clark notation. A very
+        * common one is '{DAV:}displayname'.
+        *
+        * @param string $principalUri
+        * @return array
+        */
+       public function getCalendarsForUser($principalUri) {
+
+               $principalUriParts = explode("/",$principalUri);
+               $stmt = $this->pdo->prepare("SELECT uid, tx_cal_calendar FROM fe_users WHERE username = ? AND deleted=0");
+               $stmt->execute(array(array_pop($principalUriParts)));
+
+               $calendars = array();
+
+               while($user = $stmt->fetch(PDO::FETCH_ASSOC)) {
+
+                       $stmt = $this->pdo->prepare("SELECT * FROM tx_cal_calendar WHERE uid in (".$user['tx_cal_calendar'].")");
+                       $stmt->execute();
+                        
+                       while($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+
+                               $components = explode(',','VEVENT,VTODO');
+                                
+                               $calendar = array(
+                                               'id' => $row['uid'],
+                                               'uri' => $row['title'],
+                                               'principaluri' => $principalUri,
+                                               '{' . Sabre_CalDAV_Plugin::NS_CALENDARSERVER . '}getctag' => $row['tstamp']?$row['tstamp']:'0',
+                                               '{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}supported-calendar-component-set' => new Sabre_CalDAV_Property_SupportedCalendarComponentSet($components),
+                                               '{DAV:}displayname'                          => $row['title'],
+                                               '{urn:ietf:params:xml:ns:caldav}calendar-description' => '',
+                                               '{urn:ietf:params:xml:ns:caldav}calendar-timezone'    => null,
+                                               '{http://apple.com/ns/ical/}calendar-order'  => 0,
+                                               '{http://apple.com/ns/ical/}calendar-color'  => null,
+                               );
+           
+                               $calendars[] = $calendar;
+
+                       }
+               }
+
+               return $calendars;
+
+       }
+
+       /**
+        * Creates a new calendar for a principal.
+        *
+        * If the creation was a success, an id must be returned that can be used to reference
+        * this calendar in other methods, such as updateCalendar
+        *
+        * @param string $principalUri
+        * @param string $calendarUri
+        * @param array $properties
+        * @return mixed
+        */
+       public function createCalendar($principalUri,$calendarUri, array $properties) {
+
+               $fieldNames = array(
+                               'principaluri',
+                               'uri',
+                               'ctag',
+               );
+               $values = array(
+                               ':principaluri' => $principalUri,
+                               ':uri'          => $calendarUri,
+                               ':ctag'         => 1,
+               );
+
+               // Default value
+               $sccs = '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set';
+               $fieldNames[] = 'components';
+               if (!isset($properties[$sccs])) {
+                       $values[':components'] = 'VEVENT,VTODO';
+               } else {
+                       if (!($properties[$sccs] instanceof Sabre_CalDAV_Property_SupportedCalendarComponentSet)) {
+                               throw new Sabre_DAV_Exception('The ' . $sccs . ' property must be of type: Sabre_CalDAV_Property_SupportedCalendarComponentSet');
+                       }
+                       $values[':components'] = implode(',',$properties[$sccs]->getValue());
+               }
+
+               foreach($this->propertyMap as $xmlName=>$dbName) {
+                       if (isset($properties[$xmlName])) {
+
+                               $myValue = $properties[$xmlName];
+                               $values[':' . $dbName] = $properties[$xmlName];
+                               $fieldNames[] = $dbName;
+                       }
+               }
+
+               $stmt = $this->pdo->prepare("INSERT INTO tx_cal_calendar (".implode(', ', $fieldNames).") VALUES (".implode(', ',array_keys($values)).")");
+               $stmt->execute($values);
+
+               return $this->pdo->lastInsertId();
+
+       }
+
+       /**
+        * Updates a calendars properties
+        *
+        * The properties array uses the propertyName in clark-notation as key,
+        * and the array value for the property value. In the case a property
+        * should be deleted, the property value will be null.
+        *
+        * This method must be atomic. If one property cannot be changed, the
+        * entire operation must fail.
+        *
+        * If the operation was successful, true can be returned.
+        * If the operation failed, false can be returned.
+        *
+        * Deletion of a non-existant property is always succesful.
+        *
+        * Lastly, it is optional to return detailed information about any
+        * failures. In this case an array should be returned with the following
+        * structure:
+        *
+        * array(
+        *   403 => array(
+        *      '{DAV:}displayname' => null,
+        *   ),
+        *   424 => array(
+        *      '{DAV:}owner' => null,
+        *   )
+        * )
+        *
+        * In this example it was forbidden to update {DAV:}displayname.
+        * (403 Forbidden), which in turn also caused {DAV:}owner to fail
+        * (424 Failed Dependency) because the request needs to be atomic.
+        *
+        * @param string $calendarId
+        * @param array $properties
+        * @return bool|array
+        */
+       public function updateCalendar($calendarId, array $properties) {
+
+               $newValues = array();
+               $result = array(
+                               200 => array(), // Ok
+                               403 => array(), // Forbidden
+                               424 => array(), // Failed Dependency
+               );
+
+               $hasError = false;
+
+               foreach($properties as $propertyName=>$propertyValue) {
+
+                       // We don't know about this property.
+                       if (!isset($this->propertyMap[$propertyName])) {
+                               $hasError = true;
+                               $result[403][$propertyName] = null;
+                               unset($properties[$propertyName]);
+                               continue;
+                       }
+
+                       $fieldName = $this->propertyMap[$propertyName];
+                       $newValues[$fieldName] = $propertyValue;
+
+               }
+
+               // If there were any errors we need to fail the request
+               if ($hasError) {
+                       // Properties has the remaining properties
+                       foreach($properties as $propertyName=>$propertyValue) {
+                               $result[424][$propertyName] = null;
+                       }
+
+                       // Removing unused statuscodes for cleanliness
+                       foreach($result as $status=>$properties) {
+                               if (is_array($properties) && count($properties)===0) unset($result[$status]);
+                       }
+                       return $result;
+
+               }
+
+               // Success
+
+               // Now we're generating the sql query.
+               $valuesSql = array();
+               foreach($newValues as $fieldName=>$value) {
+                       $valuesSql[] = $fieldName . ' = ?';
+               }
+               $valuesSql[] = time();
+
+               $stmt = $this->pdo->prepare("UPDATE tx_cal_calendar SET " . implode(', ',$valuesSql) . " WHERE id = ?");
+               $newValues['id'] = $calendarId;
+               $stmt->execute(array_values($newValues));
+
+               $stmt = $this->pdo->prepare('SELECT * FROM tx_cal_calendar WHERE uid = ?');
+               $stmt->execute(array($calendarId));
+               $calendarRow = $stmt->fetch();
+               $this->clearCache($calendarRow['pid']);
+
+               return true;
+
+       }
+
+       /**
+        * Delete a calendar and all it's objects
+        *
+        * @param string $calendarId
+        * @return void
+        */
+       public function deleteCalendar($calendarId) {
+               $stmt = $this->pdo->prepare('SELECT * FROM tx_cal_calendar WHERE uid = ?');
+               $stmt->execute(array($calendarId));
+               $calendarRow = $stmt->fetch();
+
+               $stmt = $this->pdo->prepare('DELETE FROM tx_cal_event WHERE calendar_id = ?');
+               $stmt->execute(array($calendarId));
+
+               $stmt = $this->pdo->prepare('DELETE FROM tx_cal_calendar WHERE uid = ?');
+               $stmt->execute(array($calendarId));
+               $this->clearCache($calendarRow['pid']);
+
+       }
+
+       /**
+        * Returns all calendar objects within a calendar object.
+        *
+        * Every item contains an array with the following keys:
+        *   * id - unique identifier which will be used for subsequent updates
+        *   * calendardata - The iCalendar-compatible calnedar data
+        *   * uri - a unique key which will be used to construct the uri. This can be any arbitrary string.
+        *   * lastmodified - a timestamp of the last modification time
+        *
+        * @param string $calendarId
+        * @return array
+        */
+       public function getCalendarObjects($calendarId) {
+
+               $stmt = $this->pdo->prepare('SELECT * FROM tx_cal_event WHERE calendar_id = ? AND deleted = 0');
+               $stmt->execute(array($calendarId));
+               $eventArray = $stmt->fetchAll();
+               $preparedArray = Array();
+               foreach($eventArray as $eventRow){
+                       if($eventRow['tx_caldav_uid']=='' && $eventRow['icsUid']==''){
+                               $eventRow['tx_caldav_uid'] = 'a1b2c3_'.$eventRow['calendar_id'].'_'.$eventRow['uid'];
+                               $eventRow['icsUid'] = $eventRow['tx_caldav_uid'];
+                               $stmt = $this->pdo->prepare("UPDATE tx_cal_event SET tx_caldav_uid = ?, icsUid = ? WHERE uid = ?");
+                               $stmt->execute(Array($eventRow['tx_caldav_uid'], $eventRow['icsUid'], $eventRow['uid']));
+                                
+                       } else if($eventRow['tx_caldav_uid']==''){
+                               $eventRow['tx_caldav_uid'] = $eventRow['icsUid'];
+                               $stmt = $this->pdo->prepare("UPDATE tx_cal_event SET tx_caldav_uid = ? WHERE uid = ?");
+                               $stmt->execute(Array($eventRow['tx_caldav_uid'], $eventRow['uid']));
+                                
+                       } else if($eventRow['icsUid']==''){
+                               $eventRow['icsUid'] = $eventRow['tx_caldav_uid'];
+                               $stmt = $this->pdo->prepare("UPDATE tx_cal_event SET icsUid = ? WHERE uid = ?");
+                               $stmt->execute(Array($eventRow['icsUid'], $eventRow['uid']));
+                                
+                       }
+                       $preparedArray[] = Array(
+                                       'id' => $eventRow['uid'],
+                                       'displayname' => $eventRow['title'],
+                                       'calendardata' => $eventRow['tx_caldav_data'],
+                                       'uri' => $eventRow['tx_caldav_uid'],
+                                       'calendarid' => $calendarId,
+                                       'lastmodified' => $eventRow['tstamp']
+                       );
+               }
+               return $preparedArray;
+
+       }
+
+       /**
+        * Returns information from a single calendar object, based on it's object uri.
+        *
+        * @param string $calendarId
+        * @param string $objectUri
+        * @return array
+        */
+       public function getCalendarObject($calendarId,$objectUri) {
+
+               $stmt = $this->pdo->prepare('SELECT * FROM tx_cal_event WHERE calendar_id = ? AND tx_caldav_uid = ? AND deleted = 0');
+               $stmt->execute(array($calendarId, $objectUri));
+               $eventRow = $stmt->fetch();
+               if(empty($eventRow)){
+                       return Array();
+               }
+               return Array(
+                               'id' => $eventRow['uid'],
+                               'displayname' => $eventRow['title'],
+                               'calendardata' => $eventRow['tx_caldav_data'],
+                               'uri' => $eventRow['icsUid'],
+                               'calendarid' => $calendarId,
+                               'lastmodified' => $eventRow['tstamp']
+               );
+
+       }
+
+       /**
+        * Creates a new calendar object.
+        *
+        * @param string $calendarId
+        * @param string $objectUri
+        * @param string $calendarData
+        * @return void
+        */
+       public function createCalendarObject($calendarId,$objectUri,$calendarData) {
+               $stmt = $this->pdo->prepare('SELECT * FROM tx_cal_calendar WHERE uid = ?');
+               $stmt->execute(array($calendarId));
+               $calendarRow = $stmt->fetch();
+
+               $stmt = $this->pdo->prepare('INSERT INTO tx_cal_event (pid,calendar_id, tx_caldav_uid, tx_caldav_data, tstamp) VALUES (?,?,?,?,?)');
+               $uid = $this->pdo->lastInsertId();
+               $stmt->execute(array($calendarRow['pid'],$calendarId,$objectUri,$calendarData,time()));
+               $stmt = $this->pdo->prepare('UPDATE tx_cal_calendar SET tstamp = tstamp + 1 WHERE uid = ? AND deleted = 0');
+               $stmt->execute(array($calendarId));
+               $this->updateCalEvent($calendarId,$objectUri,$calendarData);
+               $this->clearCache($calendarRow['pid']);
+       }
+
+       /**
+        * Updates an existing calendarobject, based on it's uri.
+        *
+        * @param string $calendarId
+        * @param string $objectUri
+        * @param string $calendarData
+        * @return void
+        */
+       public function updateCalendarObject($calendarId,$objectUri,$calendarData) {
+               $stmt = $this->pdo->prepare('SELECT * FROM tx_cal_event WHERE calendar_id = ?');
+               $stmt->execute(array($calendarId));
+               $calendarRow = $stmt->fetch();
                $stmt = $this->pdo->prepare('UPDATE tx_cal_event SET tx_caldav_data = ?, tstamp = ? WHERE calendar_id = ? AND icsUid = ? AND deleted = 0');
-        $stmt->execute(array($calendarData,time(),$calendarId,$objectUri));
-        $stmt = $this->pdo->prepare('UPDATE tx_cal_calendar SET tstamp = tstamp + 1 WHERE uid = ? AND deleted = 0');
-        $stmt->execute(array($calendarId));
+               $stmt->execute(array($calendarData,time(),$calendarId,$objectUri));
+               $stmt = $this->pdo->prepare('UPDATE tx_cal_calendar SET tstamp = tstamp + 1 WHERE uid = ? AND deleted = 0');
+               $stmt->execute(array($calendarId));
                $this->updateCalEvent($calendarId,$objectUri,$calendarData);
-    }
-
-    /**
-     * Deletes an existing calendar object. 
-     * 
-     * @param string $calendarId 
-     * @param string $objectUri 
-     * @return void
-     */
-    public function deleteCalendarObject($calendarId,$objectUri) {
-
-        $stmt = $this->pdo->prepare('DELETE FROM tx_cal_event WHERE calendar_id = ? AND icsUid = ? AND deleted = 0');
-        $stmt->execute(array($calendarId,$objectUri));
-        $stmt = $this->pdo->prepare('UPDATE tx_cal_calendar SET tstamp = tstamp + 1 WHERE uid = ? AND deleted = 0');
-        $stmt->execute(array($calendarId));
-
-    }
-    
-
- private function updateCalEvent($calendarId, $objectUri, $calendarData){
-               
-       $stmt = $this->pdo->prepare('SELECT * FROM tx_cal_calendar WHERE uid = ?');
-        $stmt->execute(array($calendarId));
-        $calendarRow = $stmt->fetch();
-               $pageTSConf = t3lib_befunc::getPagesTSconfig($calendarRow['pid']);
+               $this->clearCache($calendarRow['pid']);
+       }
+
+       /**
+        * Deletes an existing calendar object.
+        *
+        * @param string $calendarId
+        * @param string $objectUri
+        * @return void
+        */
+       public function deleteCalendarObject($calendarId,$objectUri) {
+
+               $stmt = $this->pdo->prepare('SELECT * FROM tx_cal_event WHERE calendar_id = ?');
+               $stmt->execute(array($calendarId));
+               $calendarRow = $stmt->fetch();
+
+               $stmt = $this->pdo->prepare('DELETE FROM tx_cal_event WHERE calendar_id = ? AND icsUid = ? AND deleted = 0');
+               $stmt->execute(array($calendarId,$objectUri));
+               $stmt = $this->pdo->prepare('UPDATE tx_cal_calendar SET tstamp = tstamp + 1 WHERE uid = ? AND deleted = 0');
+               $stmt->execute(array($calendarId));
+               $this->clearCache($calendarRow['pid']);
+       }
+
+
+       private function updateCalEvent($calendarId, $objectUri, $calendarData){
+                       
+               $stmt = $this->pdo->prepare('SELECT * FROM tx_cal_calendar WHERE uid = ?');
+               $stmt->execute(array($calendarId));
+               $calendarRow = $stmt->fetch();
+               $pageTSConf = t3lib_befunc::getPagesTSconfig($calendarRow['pid']);
                if($pageTSConf['options.']['tx_cal_controller.']['pageIDForPlugin']) {
                        $pageIDForPlugin = $pageTSConf['options.']['tx_cal_controller.']['pageIDForPlugin'];
                } else {
@@ -401,12 +448,42 @@ class Sabre_CalDAV_Backend_TYPO3 extends Sabre_CalDAV_Backend_Abstract {
                foreach($components->_components as $component){
                        if (is_a($component,'tx_iCalendar_vevent')){
                                $stmt = $this->pdo->prepare('UPDATE tx_cal_event SET icsUid = ? WHERE tx_caldav_uid = ?');
-                       $stmt->execute(array($component->getAttribute('UID'),$objectUri));
+                               $stmt->execute(array($component->getAttribute('UID'),$objectUri));
                        }
                }
                $service->insertCalEventsIntoDB($components->_components, $calendarId, $calendarRow['pid'], 0, 0);
-        require_once(t3lib_extMgm::extPath('cal').'controller/class.tx_cal_functions.php');
-        tx_cal_functions::clearCache();
-    }
+               $this->clearCache($calendarRow['pid']);
+       }
+
+       private function clearCache($pid){
+                
+               $pageTSConf = t3lib_befunc::getPagesTSconfig($pid);
+               if($pageTSConf['TCEMAIN.']['clearCacheCmd']) {
+                       $pageIDForPlugin = $pageTSConf['TCEMAIN.']['clearCacheCmd'];
+               } else {
+                       $pageIDForPlugin = $pid;
+               }
 
-}
+               define("PATH_typo3conf", dirname(dirname(dirname(dirname(dirname(dirname(dirname(__FILE__)))))))."/");
+               define("PATH_site", dirname(PATH_typo3conf)."/");
+               define("PATH_typo3", PATH_site."typo3/");       // Typo-configuraton path
+               define("PATH_t3lib", PATH_site."t3lib/");
+               define('TYPO3_MODE','BE');
+               ini_set('error_reporting', E_ALL ^ E_NOTICE);
+
+               require_once (PATH_t3lib.'class.t3lib_div.php');
+               require_once (PATH_t3lib.'class.t3lib_extmgm.php');
+               require_once (PATH_t3lib.'class.t3lib_tcemain.php');
+
+               require_once(PATH_t3lib.'config_default.php');
+
+               if (!defined ("TYPO3_db")) die ("The configuration file was not included.");
+               require_once(PATH_t3lib.'class.t3lib_db.php');          // The database library
+               $TYPO3_DB = t3lib_div::makeInstance('t3lib_db');
+               $TYPO3_DB->sql_pconnect (TYPO3_db_host, TYPO3_db_username, TYPO3_db_password);
+               $TYPO3_DB->sql_select_db (TYPO3_db);
+
+               $tce = t3lib_div::makeInstance('t3lib_TCEmain');
+               $tce->clear_cacheCmd($pageIDForPlugin);  // ID of the page for which to clear the cache
+       }
+}
\ No newline at end of file
index b99878b..c237b28 100755 (executable)
@@ -8,32 +8,32 @@
  * 
  * @package Sabre
  * @subpackage CalDAV
- * @copyright Copyright (C) 2007-2010 Rooftop Solutions. All rights reserved.
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
  * @author Evert Pot (http://www.rooftopsolutions.nl/) 
  * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
  */
-class Sabre_CalDAV_Calendar implements Sabre_DAV_ICollection, Sabre_DAV_IProperties {
+class Sabre_CalDAV_Calendar implements Sabre_CalDAV_ICalendar, Sabre_DAV_IProperties, Sabre_DAVACL_IACL {
 
     /**
      * This is an array with calendar information 
      * 
      * @var array 
      */
-    private $calendarInfo;
+    protected $calendarInfo;
 
     /**
      * CalDAV backend 
      * 
      * @var Sabre_CalDAV_Backend_Abstract 
      */
-    private $caldavBackend;
+    protected $caldavBackend;
 
     /**
-     * Authentication backend
+     * Principal backend
      * 
-     * @var Sabre_DAV_Auth_Backend_Abstract 
+     * @var Sabre_DAVACL_IPrincipalBackend
      */
-    private $authBackend;
+    protected $principalBackend;
 
     /**
      * Constructor 
@@ -42,10 +42,10 @@ class Sabre_CalDAV_Calendar implements Sabre_DAV_ICollection, Sabre_DAV_IPropert
      * @param array $calendarInfo 
      * @return void
      */
-    public function __construct(Sabre_DAV_Auth_Backend_Abstract $authBackend, Sabre_CalDAV_Backend_Abstract $caldavBackend,$calendarInfo) {
+    public function __construct(Sabre_DAVACL_IPrincipalBackend $principalBackend, Sabre_CalDAV_Backend_Abstract $caldavBackend, $calendarInfo) {
 
         $this->caldavBackend = $caldavBackend;
-        $this->authBackend = $authBackend;
+        $this->principalBackend = $principalBackend;
         $this->calendarInfo = $calendarInfo;
 
 
@@ -70,7 +70,6 @@ class Sabre_CalDAV_Calendar implements Sabre_DAV_ICollection, Sabre_DAV_IPropert
      */
     public function updateProperties($mutations) {
 
-        if (!$this->hasPrivilege()) throw new Sabre_DAV_Exception_Forbidden('Permission denied to access this calendar');
         return $this->caldavBackend->updateCalendar($this->calendarInfo['id'],$mutations);
 
     }
@@ -85,13 +84,8 @@ class Sabre_CalDAV_Calendar implements Sabre_DAV_ICollection, Sabre_DAV_IPropert
 
         $response = array();
 
-        if (!$this->hasPrivilege()) return array(); 
-
         foreach($requestedProperties as $prop) switch($prop) {
 
-            case '{DAV:}resourcetype' : 
-                $response[$prop] =  new Sabre_DAV_Property_ResourceType(array('{urn:ietf:params:xml:ns:caldav}calendar','{DAV:}collection')); 
-                break;
             case '{urn:ietf:params:xml:ns:caldav}supported-calendar-data' : 
                 $response[$prop] = new Sabre_CalDAV_Property_SupportedCalendarData(); 
                 break;
@@ -99,7 +93,7 @@ class Sabre_CalDAV_Calendar implements Sabre_DAV_ICollection, Sabre_DAV_IPropert
                 $response[$prop] =  new Sabre_CalDAV_Property_SupportedCollationSet(); 
                 break;
             case '{DAV:}owner' :
-                $response[$prop] = new Sabre_DAV_Property_Principal(Sabre_DAV_Property_Principal::HREF,$this->calendarInfo['principaluri']);
+                $response[$prop] = new Sabre_DAVACL_Property_Principal(Sabre_DAVACL_Property_Principal::HREF,$this->calendarInfo['principaluri']);
                 break;
             default : 
                 if (isset($this->calendarInfo[$prop])) $response[$prop] = $this->calendarInfo[$prop];
@@ -120,8 +114,8 @@ class Sabre_CalDAV_Calendar implements Sabre_DAV_ICollection, Sabre_DAV_IPropert
      */
     public function getChild($name) {
 
-        if (!$this->hasPrivilege()) throw new Sabre_DAV_Exception_Forbidden('Permission denied to access this calendar');
         $obj = $this->caldavBackend->getCalendarObject($this->calendarInfo['id'],$name);
+
         if (!$obj) throw new Sabre_DAV_Exception_FileNotFound('Calendar object not found');
         return new Sabre_CalDAV_CalendarObject($this->caldavBackend,$this->calendarInfo,$obj);
 
@@ -134,7 +128,6 @@ class Sabre_CalDAV_Calendar implements Sabre_DAV_ICollection, Sabre_DAV_IPropert
      */
     public function getChildren() {
 
-        if (!$this->hasPrivilege()) throw new Sabre_DAV_Exception_Forbidden('Permission denied to access this calendar');
         $objs = $this->caldavBackend->getCalendarObjects($this->calendarInfo['id']);
         $children = array();
         foreach($objs as $obj) {
@@ -152,7 +145,6 @@ class Sabre_CalDAV_Calendar implements Sabre_DAV_ICollection, Sabre_DAV_IPropert
      */
     public function childExists($name) {
 
-        if (!$this->hasPrivilege()) throw new Sabre_DAV_Exception_Forbidden('Permission denied to access this calendar');
         $obj = $this->caldavBackend->getCalendarObject($this->calendarInfo['id'],$name);
         if (!$obj) 
             return false;
@@ -171,7 +163,6 @@ class Sabre_CalDAV_Calendar implements Sabre_DAV_ICollection, Sabre_DAV_IPropert
      */
     public function createDirectory($name) {
 
-        if (!$this->hasPrivilege()) throw new Sabre_DAV_Exception_Forbidden('Permission denied to access this calendar');
         throw new Sabre_DAV_Exception_MethodNotAllowed('Creating collections in calendar objects is not allowed');
 
     }
@@ -187,10 +178,16 @@ class Sabre_CalDAV_Calendar implements Sabre_DAV_ICollection, Sabre_DAV_IPropert
      */
     public function createFile($name,$calendarData = null) {
 
-        if (!$this->hasPrivilege()) throw new Sabre_DAV_Exception_Forbidden('Permission denied to access this calendar');
         $calendarData = stream_get_contents($calendarData);
-
-        $supportedComponents = $this->calendarInfo['{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}supported-calendar-component-set']->getValue();
+        // Converting to UTF-8, if needed
+        $calendarData = Sabre_DAV_StringUtil::ensureUTF8($calendarData);
+
+        $supportedComponents = $this->calendarInfo['{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}supported-calendar-component-set'];
+        if ($supportedComponents) {
+            $supportedComponents = $supportedComponents->getValue();
+        } else {
+            $supportedComponents = null;
+        }
         Sabre_CalDAV_ICalendarUtil::validateICalendarObject($calendarData, $supportedComponents);
 
         $this->caldavBackend->createCalendarObject($this->calendarInfo['id'],$name,$calendarData);
@@ -204,7 +201,6 @@ class Sabre_CalDAV_Calendar implements Sabre_DAV_ICollection, Sabre_DAV_IPropert
      */
     public function delete() {
 
-        if (!$this->hasPrivilege()) throw new Sabre_DAV_Exception_Forbidden('Permission denied to access this calendar');
         $this->caldavBackend->deleteCalendar($this->calendarInfo['id']);
 
     }
@@ -218,7 +214,6 @@ class Sabre_CalDAV_Calendar implements Sabre_DAV_ICollection, Sabre_DAV_IPropert
      */
     public function setName($newName) {
 
-        if (!$this->hasPrivilege()) throw new Sabre_DAV_Exception_Forbidden('Permission denied to access this calendar');
         throw new Sabre_DAV_Exception_MethodNotAllowed('Renaming calendars is not yet supported');
 
     }
@@ -235,20 +230,90 @@ class Sabre_CalDAV_Calendar implements Sabre_DAV_ICollection, Sabre_DAV_IPropert
     }
 
     /**
-     * Check if user has access.
+     * Returns the owner principal
      *
-     * This method does a check if the currently logged in user
-     * has permission to access this calendar. There is only read-write
-     * access, so you're in or you're out.
+     * This must be a url to a principal, or null if there's no owner 
      * 
-     * @return bool 
+     * @return string|null
+     */
+    public function getOwner() {
+
+        return $this->calendarInfo['principaluri'];
+
+    }
+
+    /**
+     * Returns a group principal
+     *
+     * This must be a url to a principal, or null if there's no owner
+     * 
+     * @return string|null 
+     */
+    public function getGroup() {
+
+        return null;
+
+    }
+
+    /**
+     * Returns a list of ACE's for this node.
+     *
+     * Each ACE has the following properties:
+     *   * 'privilege', a string such as {DAV:}read or {DAV:}write. These are 
+     *     currently the only supported privileges
+     *   * 'principal', a url to the principal who owns the node
+     *   * 'protected' (optional), indicating that this ACE is not allowed to 
+     *      be updated. 
+     * 
+     * @return array 
      */
-    protected function hasPrivilege() {
+    public function getACL() {
+
+        return array(
+            array(
+                'privilege' => '{DAV:}read',
+                'principal' => $this->calendarInfo['principaluri'],
+                'protected' => true,
+            ),
+            array(
+                'privilege' => '{DAV:}write',
+                'principal' => $this->calendarInfo['principaluri'],
+                'protected' => true,
+            ),
+            array(
+                'privilege' => '{DAV:}read',
+                'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-write',
+                'protected' => true,
+            ),
+            array(
+                'privilege' => '{DAV:}write',
+                'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-write',
+                'protected' => true,
+            ),
+            array(
+                'privilege' => '{DAV:}read',
+                'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-read',
+                'protected' => true,
+            ),
+
+        );
 
-        if (!$user = $this->authBackend->getCurrentUser()) return false;
-        if ($user['uri']!==$this->calendarInfo['principaluri']) return false;
-        return true;
+    }
+
+    /**
+     * Updates the ACL
+     *
+     * This method will receive a list of new ACE's. 
+     * 
+     * @param array $acl 
+     * @return void
+     */
+    public function setACL(array $acl) {
+
+        throw new Sabre_DAV_Exception_MethodNotAllowed('Changing ACL is not yet supported');
 
     }
 
+
+
 }
index 937bab1..3c2a752 100755 (executable)
@@ -5,42 +5,51 @@
  * 
  * @package Sabre
  * @subpackage CalDAV
- * @copyright Copyright (C) 2007-2010 Rooftop Solutions. All rights reserved.
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
  * @author Evert Pot (http://www.rooftopsolutions.nl/) 
  * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
  */
-class Sabre_CalDAV_CalendarObject extends Sabre_DAV_File implements Sabre_DAV_IProperties {
+class Sabre_CalDAV_CalendarObject extends Sabre_DAV_File implements Sabre_CalDAV_ICalendarObject, Sabre_DAVACL_IACL {
 
     /**
      * Sabre_CalDAV_Backend_Abstract 
      * 
      * @var array 
      */
-    private $caldavBackend;
+    protected $caldavBackend;
 
     /**
      * Array with information about this CalendarObject 
      * 
      * @var array 
      */
-    private $objectData;
+    protected $objectData;
 
     /**
      * Array with information about the containing calendar
      * 
      * @var array 
      */
-    private $calendarInfo;
+    protected $calendarInfo;
 
     /**
      * Constructor 
      * 
-     * @param Sabre_CalDAV_Backend_Abstract $caldavBackend 
+     * @param Sabre_CalDAV_Backend_Abstract $caldavBackend
+     * @param array $calendarInfo
      * @param array $objectData 
      */
-    public function __construct(Sabre_CalDAV_Backend_Abstract $caldavBackend,$calendarInfo,$objectData) {
+    public function __construct(Sabre_CalDAV_Backend_Abstract $caldavBackend,array $calendarInfo,array $objectData) {
 
         $this->caldavBackend = $caldavBackend;
+
+        if (!isset($objectData['calendarid'])) {
+            throw new InvalidArgumentException('The objectData argument must contain a \'calendarid\' property');
+        }
+        if (!isset($objectData['uri'])) {
+            throw new InvalidArgumentException('The objectData argument must contain an \'uri\' property');
+        }
+
         $this->calendarInfo = $calendarInfo;
         $this->objectData = $objectData;
 
@@ -64,6 +73,11 @@ class Sabre_CalDAV_CalendarObject extends Sabre_DAV_File implements Sabre_DAV_IP
      */
     public function get() {
 
+        // Pre-populating the 'calendardata' is optional, if we don't have it
+        // already we fetch it from the backend.
+        if (!isset($this->objectData['calendardata'])) {
+            $this->objectData = $this->caldavBackend->getCalendarObject($this->objectData['calendarid'], $this->objectData['uri']);
+        }
         return $this->objectData['calendardata'];
 
     }
@@ -79,7 +93,15 @@ class Sabre_CalDAV_CalendarObject extends Sabre_DAV_File implements Sabre_DAV_IP
         if (is_resource($calendarData))
             $calendarData = stream_get_contents($calendarData);
 
-        $supportedComponents = $this->calendarInfo['{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}supported-calendar-component-set']->getValue();
+        // Converting to UTF-8, if needed
+        $calendarData = Sabre_DAV_StringUtil::ensureUTF8($calendarData);
+
+        $supportedComponents = $this->calendarInfo['{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}supported-calendar-component-set'];
+        if ($supportedComponents) {
+            $supportedComponents = $supportedComponents->getValue();
+        } else {
+            $supportedComponents = null;
+        }
         Sabre_CalDAV_ICalendarUtil::validateICalendarObject($calendarData, $supportedComponents);
 
         $this->caldavBackend->updateCalendarObject($this->calendarInfo['id'],$this->objectData['uri'],$calendarData);
@@ -118,59 +140,121 @@ class Sabre_CalDAV_CalendarObject extends Sabre_DAV_File implements Sabre_DAV_IP
      */
     public function getETag() {
 
-        return '"' . md5($this->objectData['calendardata']). '"';
+        if (isset($this->objectData['etag'])) {
+            return $this->objectData['etag'];
+        } else {
+            return '"' . md5($this->get()). '"';
+        }
 
     }
 
     /**
-     * Returns the list of properties for this object
+     * Returns the last modification date as a unix timestamp
      * 
-     * @param array $properties 
-     * @return array 
+     * @return time 
      */
-    public function getProperties($properties) {
+    public function getLastModified() {
+
+        return $this->objectData['lastmodified'];
+
+    }
 
-        $response = array();
-        if (in_array('{urn:ietf:params:xml:ns:caldav}calendar-data',$properties)) 
-            $response['{urn:ietf:params:xml:ns:caldav}calendar-data'] = str_replace("\r","",$this->objectData['calendardata']);
-       
+    /**
+     * Returns the size of this object in bytes 
+     * 
+     * @return int
+     */
+    public function getSize() {
 
-        return $response;
+        return strlen($this->objectData['calendardata']);
 
     }
 
     /**
-     * Updates properties
+     * Returns the owner principal
+     *
+     * This must be a url to a principal, or null if there's no owner 
      * 
-     * @param array $properties
-     * @return array 
+     * @return string|null
      */
-    public function updateProperties($properties) {
+    public function getOwner() {
 
-        return false;
+        return $this->calendarInfo['principaluri'];
 
     }
 
     /**
-     * Returns the last modification date as a unix timestamp
+     * Returns a group principal
+     *
+     * This must be a url to a principal, or null if there's no owner
      * 
-     * @return time 
+     * @return string|null 
      */
-    public function getLastModified() {
+    public function getGroup() {
 
-        return strtotime($this->objectData['lastmodified']);
+        return null;
 
     }
 
     /**
-     * Returns the size of this object in bytes 
+     * Returns a list of ACE's for this node.
+     *
+     * Each ACE has the following properties:
+     *   * 'privilege', a string such as {DAV:}read or {DAV:}write. These are 
+     *     currently the only supported privileges
+     *   * 'principal', a url to the principal who owns the node
+     *   * 'protected' (optional), indicating that this ACE is not allowed to 
+     *      be updated. 
      * 
-     * @return int
+     * @return array 
      */
-    public function getSize() {
+    public function getACL() {
+
+        return array(
+            array(
+                'privilege' => '{DAV:}read',
+                'principal' => $this->calendarInfo['principaluri'],
+                'protected' => true,
+            ),
+            array(
+                'privilege' => '{DAV:}write',
+                'principal' => $this->calendarInfo['principaluri'],
+                'protected' => true,
+            ),
+            array(
+                'privilege' => '{DAV:}read',
+                'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-write',
+                'protected' => true,
+            ),
+            array(
+                'privilege' => '{DAV:}write',
+                'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-write',
+                'protected' => true,
+            ),
+            array(
+                'privilege' => '{DAV:}read',
+                'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-read',
+                'protected' => true,
+            ),
+
+        );
 
-        return strlen($this->objectData['calendardata']);
+    }
+
+    /**
+     * Updates the ACL
+     *
+     * This method will receive a list of new ACE's. 
+     * 
+     * @param array $acl 
+     * @return void
+     */
+    public function setACL(array $acl) {
+
+        throw new Sabre_DAV_Exception_MethodNotAllowed('Changing ACL is not yet supported');
 
     }
+
+
 }
 
index 322722a..0f7dca7 100755 (executable)
@@ -7,18 +7,11 @@
  *
  * @package Sabre
  * @subpackage CalDAV
- * @copyright Copyright (C) 2007-2010 Rooftop Solutions. All rights reserved.
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
  * @author Evert Pot (http://www.rooftopsolutions.nl/) 
  * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
  */
-class Sabre_CalDAV_CalendarRootNode extends Sabre_DAV_Directory {
-
-    /**
-     * Authentication Backend 
-     * 
-     * @var Sabre_DAV_Auth_Backend_Abstract 
-     */
-    protected $authBackend;
+class Sabre_CalDAV_CalendarRootNode extends Sabre_DAVACL_AbstractPrincipalCollection {
 
     /**
      * CalDAV backend 
@@ -32,20 +25,30 @@ class Sabre_CalDAV_CalendarRootNode extends Sabre_DAV_Directory {
      *
      * This constructor needs both an authentication and a caldav backend.
      *
-     * @param Sabre_DAV_Auth_Backend_Abstract $authBackend 
+     * By default this class will show a list of calendar collections for 
+     * principals in the 'principals' collection. If your main principals are 
+     * actually located in a different path, use the $principalPrefix argument 
+     * to override this.
+     *
+     *
+     * @param Sabre_DAVACL_IPrincipalBackend $principalBackend 
      * @param Sabre_CalDAV_Backend_Abstract $caldavBackend 
+     * @param string $principalPrefix
      */
-    public function __construct(Sabre_DAV_Auth_Backend_Abstract $authBackend,Sabre_CalDAV_Backend_Abstract $caldavBackend) {
+    public function __construct(Sabre_DAVACL_IPrincipalBackend $principalBackend,Sabre_CalDAV_Backend_Abstract $caldavBackend, $principalPrefix = 'principals') {
 
-        $this->authBackend = $authBackend;
+        parent::__construct($principalBackend, $principalPrefix);
         $this->caldavBackend = $caldavBackend;
 
     }
 
     /**
-     * Returns the name of the node 
+     * Returns the nodename
+     *
+     * We're overriding this, because the default will be the 'principalPrefix',
+     * and we want it to be Sabre_CalDAV_Plugin::CALENDAR_ROOT 
      * 
-     * @return string 
+     * @return void
      */
     public function getName() {
 
@@ -54,20 +57,18 @@ class Sabre_CalDAV_CalendarRootNode extends Sabre_DAV_Directory {
     }
 
     /**
-     * Returns the list of users as Sabre_CalDAV_User objects. 
+     * This method returns a node for a principal.
+     *
+     * The passed array contains principal information, and is guaranteed to
+     * at least contain a uri item. Other properties may or may not be
+     * supplied by the authentication backend.
      * 
-     * @return array 
+     * @param array $principal 
+     * @return Sabre_DAV_INode 
      */
-    public function getChildren() {
-
-        $users = $this->authBackend->getUsers();
-        $children = array();
-        foreach($users as $user) {
-
-            $children[] = new Sabre_CalDAV_UserCalendars($this->authBackend, $this->caldavBackend, $user['uri']);
+    public function getChildForPrincipal(array $principal) {
 
-        }
-        return $children;
+        return new Sabre_CalDAV_UserCalendars($this->principalBackend, $this->caldavBackend, $principal['uri']);
 
     }
 
index 24873f5..24b2c82 100755 (executable)
@@ -8,7 +8,7 @@
  * 
  * @package Sabre
  * @subpackage CalDAV
- * @copyright Copyright (C) 2007-2010 Rooftop Solutions. All rights reserved.
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
  * @author Evert Pot (http://www.rooftopsolutions.nl/) 
  * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
  */
diff --git a/lib/Sabre/CalDAV/ICSExportPlugin.php b/lib/Sabre/CalDAV/ICSExportPlugin.php
new file mode 100755 (executable)
index 0000000..5a1e433
--- /dev/null
@@ -0,0 +1,124 @@
+<?php
+
+/**
+ * ICS Exporter
+ *
+ * This plugin adds the ability to export entire calendars as .ics files.
+ * This is useful for clients that don't support CalDAV yet. They often do 
+ * support ics files.
+ * 
+ * @package Sabre
+ * @subpackage CalDAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_CalDAV_ICSExportPlugin extends Sabre_DAV_ServerPlugin {
+
+    /**
+     * Reference to Server class 
+     * 
+     * @var Sabre_DAV_Server 
+     */
+    private $server;
+
+    /**
+     * Initializes the plugin and registers event handlers 
+     * 
+     * @param Sabre_DAV_Server $server 
+     * @return void
+     */
+    public function initialize(Sabre_DAV_Server $server) {
+
+        $this->server = $server;
+        $this->server->subscribeEvent('beforeMethod',array($this,'beforeMethod'), 90);
+
+    }
+
+    /**
+     * 'beforeMethod' event handles. This event handles intercepts GET requests ending
+     * with ?export
+     * 
+     * @param string $method
+     * @param string $uri
+     * @return void
+     */
+    public function beforeMethod($method, $uri) {
+
+        if ($method!='GET') return;
+        if ($this->server->httpRequest->getQueryString()!='export') return;
+
+        // splitting uri
+        list($uri) = explode('?',$uri,2);
+
+        $node = $this->server->tree->getNodeForPath($uri);
+
+        if (!($node instanceof Sabre_CalDAV_Calendar)) return;
+
+        $this->server->httpResponse->setHeader('Content-Type','text/calendar');
+        $this->server->httpResponse->sendStatus(200);
+        $this->server->httpResponse->sendBody($this->generateICS($this->server->tree->getChildren($uri)));
+
+        // Returning false to break the event chain
+        return false;
+
+    }
+
+    /**
+     * Merges all calendar objects, and builds one big ics export 
+     * 
+     * @param array $nodes 
+     * @return void
+     */
+    public function generateICS(array $nodes) {
+
+        $calendar = new Sabre_VObject_Component('vcalendar');
+        $calendar->version = '2.0';
+        $calendar->prodid = '-//SabreDAV//SabreDAV ' . Sabre_DAV_Version::VERSION . '//EN';
+        $calendar->calscale = 'GREGORIAN';
+
+        $collectedTimezones = array();
+
+        $timezones = array();
+        $objects = array();
+
+        foreach($nodes as $node) {
+
+            $nodeData = $node->get();
+            $nodeComp = Sabre_VObject_Reader::read($nodeData);
+
+            foreach($nodeComp->children() as $child) {
+
+                switch($child->name) {
+                    case 'VEVENT' :
+                    case 'VTODO' :
+                    case 'VJOURNAL' :
+                        $objects[] = $child;
+                        break;
+
+                    // VTIMEZONE is special, because we need to filter out the duplicates
+                    case 'VTIMEZONE' :
+                        // Naively just checking tzid.
+                        if (in_array((string)$child->TZID, $collectedTimezones)) continue;
+
+                        $timezones[] = $child;
+                        $collectedTimezones[] = $child->TZID;
+                        break;
+
+
+                }
+
+
+            }
+
+
+        }
+
+        foreach($timezones as $tz) $calendar->add($tz);
+        foreach($objects as $obj) $calendar->add($obj);
+
+        return $calendar->serialize();
+
+    } 
+
+}
diff --git a/lib/Sabre/CalDAV/ICalendar.php b/lib/Sabre/CalDAV/ICalendar.php
new file mode 100755 (executable)
index 0000000..291b7a7
--- /dev/null
@@ -0,0 +1,18 @@
+<?php
+
+/**
+ * Calendar interface
+ *
+ * Implement this interface to allow a node to be recognized as an calendar.
+ * 
+ * @package Sabre
+ * @subpackage CalDAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+interface Sabre_CalDAV_ICalendar extends Sabre_DAV_ICollection {
+
+
+}
diff --git a/lib/Sabre/CalDAV/ICalendarObject.php b/lib/Sabre/CalDAV/ICalendarObject.php
new file mode 100755 (executable)
index 0000000..f3b6ba8
--- /dev/null
@@ -0,0 +1,20 @@
+<?php
+
+/**
+ * CalendarObject interface 
+/**
+ * Extend the ICalendarObject interface to allow your custom nodes to be picked up as 
+ * CalendarObjects.
+ *
+ * Calendar objects are resources such as Events, Todo's or Journals.
+ * 
+ * @package Sabre
+ * @subpackage CalDAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+interface Sabre_CalDAV_ICalendarObject extends Sabre_DAV_IFile { 
+
+}
+
index 3772cc6..53280c4 100755 (executable)
@@ -3,11 +3,15 @@
 /**
  * This class contains several utilities related to the ICalendar (rfc2445) format
  *
+ * This class is now deprecated, and won't be further maintained. Please use 
+ * the Sabre_VObject package for your ics parsing needs.
+ *
  * @package Sabre
  * @subpackage CalDAV
- * @copyright Copyright (C) 2007-2010 Rooftop Solutions. All rights reserved.
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
  * @author Evert Pot (http://www.rooftopsolutions.nl/) 
  * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ * @deprecated Use Sabre_VObject instead.
  */
 class Sabre_CalDAV_ICalendarUtil {
 
@@ -16,12 +20,12 @@ class Sabre_CalDAV_ICalendarUtil {
      *
      * This method makes sure this ICalendar object is properly formatted.
      * If we can't parse it, we'll throw exceptions.
-     * 
+     *
      * @param string $icalData 
      * @param array $allowedComponents 
      * @return bool 
      */
-    static function validateICalendarObject($icalData, array $allowedComponents) {
+    static function validateICalendarObject($icalData, array $allowedComponents = null) {
 
         $xcal = simplexml_load_string(self::toXCal($icalData));
         if (!$xcal) throw new Sabre_CalDAV_Exception_InvalidICalendarObject('Invalid calendarobject format');
@@ -36,14 +40,13 @@ class Sabre_CalDAV_ICalendarUtil {
             $test = $xcal->xpath('/cal:iCalendar/cal:vcalendar/cal:' . $component);
             if (is_array($test)) $componentsFound = array_merge($componentsFound, $test);
         }
-        if (count($componentsFound)>1) {
-            throw new Sabre_CalDAV_Exception_InvalidICalendarObject('Only 1 of VEVENT, VTODO, VJOURNAL or VFREEBUSY may be specified per calendar object');
-        }
         if (count($componentsFound)<1) {
             throw new Sabre_CalDAV_Exception_InvalidICalendarObject('One VEVENT, VTODO, VJOURNAL or VFREEBUSY must be specified. 0 found.');
         }
         $component = $componentsFound[0];
-        
+
+        if (is_null($allowedComponents)) return true;
+
         // Check if the component is allowed
         $name = $component->getName();
         if (!in_array(strtoupper($name),$allowedComponents)) {
index 7a29891..58031cb 100755 (executable)
@@ -8,7 +8,7 @@
  *
  * @package Sabre
  * @subpackage CalDAV
- * @copyright Copyright (C) 2007-2010 Rooftop Solutions. All rights reserved.
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
  * @author Evert Pot (http://www.rooftopsolutions.nl/) 
  * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
  */
@@ -83,7 +83,44 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin {
      */
     public function getFeatures() {
 
-        return array('calendar-access');
+        return array('calendar-access', 'calendar-proxy');
+
+    }
+
+    /**
+     * Returns a plugin name.
+     * 
+     * Using this name other plugins will be able to access other plugins
+     * using Sabre_DAV_Server::getPlugin 
+     * 
+     * @return string 
+     */
+    public function getPluginName() {
+
+        return 'caldav';
+
+    }
+
+    /**
+     * Returns a list of reports this plugin supports.
+     *
+     * This will be used in the {DAV:}supported-report-set property.
+     * Note that you still need to subscribe to the 'report' event to actually 
+     * implement them 
+     * 
+     * @param string $uri
+     * @return array 
+     */
+    public function getSupportedReportSet($uri) {
+
+        $node = $this->server->tree->getNodeForPath($uri);
+        if ($node instanceof Sabre_CalDAV_ICalendar || $node instanceof Sabre_CalDAV_ICalendarObject) {
+            return array(
+                 '{' . self::NS_CALDAV . '}calendar-multiget',
+                 '{' . self::NS_CALDAV . '}calendar-query',
+            );
+        }
+        return array();
 
     }
 
@@ -99,13 +136,17 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin {
         $server->subscribeEvent('unknownMethod',array($this,'unknownMethod'));
         //$server->subscribeEvent('unknownMethod',array($this,'unknownMethod2'),1000);
         $server->subscribeEvent('report',array($this,'report'));
-        $server->subscribeEvent('afterGetProperties',array($this,'afterGetProperties'));
+        $server->subscribeEvent('beforeGetProperties',array($this,'beforeGetProperties'));
 
         $server->xmlNamespaces[self::NS_CALDAV] = 'cal';
         $server->xmlNamespaces[self::NS_CALENDARSERVER] = 'cs';
 
         $server->propertyMap['{' . self::NS_CALDAV . '}supported-calendar-component-set'] = 'Sabre_CalDAV_Property_SupportedCalendarComponentSet';
 
+        $server->resourceTypeMapping['Sabre_CalDAV_ICalendar'] = '{urn:ietf:params:xml:ns:caldav}calendar';
+        $server->resourceTypeMapping['Sabre_CalDAV_Principal_ProxyRead'] = '{http://calendarserver.org/ns/}calendar-proxy-read';
+        $server->resourceTypeMapping['Sabre_CalDAV_Principal_ProxyWrite'] = '{http://calendarserver.org/ns/}calendar-proxy-write';
+
         array_push($server->protectedProperties,
 
             '{' . self::NS_CALDAV . '}supported-calendar-component-set',
@@ -117,9 +158,15 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin {
             '{' . self::NS_CALDAV . '}max-attendees-per-instance',
             '{' . self::NS_CALDAV . '}calendar-home-set',
             '{' . self::NS_CALDAV . '}supported-collation-set',
+            '{' . self::NS_CALDAV . '}calendar-data',
 
             // scheduling extension
-            '{' . self::NS_CALDAV . '}calendar-user-address-set'
+            '{' . self::NS_CALDAV . '}calendar-user-address-set',
+
+            // CalendarServer extensions
+            '{' . self::NS_CALENDARSERVER . '}getctag',
+            '{' . self::NS_CALENDARSERVER . '}calendar-proxy-read-for',
+            '{' . self::NS_CALENDARSERVER . '}calendar-proxy-write-for'
 
         );
     }
@@ -156,8 +203,6 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin {
             case '{'.self::NS_CALDAV.'}calendar-query' :
                 $this->calendarQueryReport($dom);
                 return false;
-            default :
-                return true;
 
         }
 
@@ -181,16 +226,20 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin {
         //}
 
         $body = $this->server->httpRequest->getBody(true);
-        $dom = Sabre_DAV_XMLUtil::loadDOMDocument($body);
-
         $properties = array();
-        foreach($dom->firstChild->childNodes as $child) {
 
-            if (Sabre_DAV_XMLUtil::toClarkNotation($child)!=='{DAV:}set') continue;
-            foreach(Sabre_DAV_XMLUtil::parseProperties($child,$this->server->propertyMap) as $k=>$prop) {
-                $properties[$k] = $prop;
+        if ($body) {
+
+            $dom = Sabre_DAV_XMLUtil::loadDOMDocument($body);
+
+            foreach($dom->firstChild->childNodes as $child) {
+
+                if (Sabre_DAV_XMLUtil::toClarkNotation($child)!=='{DAV:}set') continue;
+                foreach(Sabre_DAV_XMLUtil::parseProperties($child,$this->server->propertyMap) as $k=>$prop) {
+                    $properties[$k] = $prop;
+                }
+            
             }
-        
         }
 
         $resourceType = array('{DAV:}collection','{urn:ietf:params:xml:ns:caldav}calendar');
@@ -202,63 +251,98 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin {
     }
 
     /**
-     * afterGetProperties
+     * beforeGetProperties
      *
-     * This method handler is invoked after properties for a specific resource
-     * are received. This allows us to add any properties that might have been
-     * missing.
+     * This method handler is invoked before any after properties for a
+     * resource are fetched. This allows us to add in any CalDAV specific 
+     * properties. 
      * 
      * @param string $path
-     * @param array $properties 
+     * @param Sabre_DAV_INode $node
+     * @param array $requestedProperties
+     * @param array $returnedProperties
      * @return void
      */
-    public function afterGetProperties($path, &$properties) {
+    public function beforeGetProperties($path, Sabre_DAV_INode $node, &$requestedProperties, &$returnedProperties) {
 
-        // Find out if we are currently looking at a principal resource
-        $currentNode = $this->server->tree->getNodeForPath($path);
-        if ($currentNode instanceof Sabre_DAV_Auth_Principal) {
+        if ($node instanceof Sabre_DAVACL_IPrincipal) {
 
             // calendar-home-set property
             $calHome = '{' . self::NS_CALDAV . '}calendar-home-set';
-            if (array_key_exists($calHome,$properties[404])) {
-                $principalId = $currentNode->getName(); 
+            if (in_array($calHome,$requestedProperties)) {
+                $principalId = $node->getName(); 
                 $calendarHomePath = self::CALENDAR_ROOT . '/' . $principalId . '/';
-                unset($properties[404][$calHome]);
-                $properties[200][$calHome] = new Sabre_DAV_Property_Href($calendarHomePath);
+                unset($requestedProperties[$calHome]);
+                $returnedProperties[200][$calHome] = new Sabre_DAV_Property_Href($calendarHomePath);
             }
 
             // calendar-user-address-set property
             $calProp = '{' . self::NS_CALDAV . '}calendar-user-address-set';
-            if (array_key_exists($calProp,$properties[404])) {
+            if (in_array($calProp,$requestedProperties)) {
 
-                // Do we have an email address?
-                $props = $currentNode->getProperties(array('{http://sabredav.org/ns}email-address'));
-                if (isset($props['{http://sabredav.org/ns}email-address'])) {
-                    $email = $props['{http://sabredav.org/ns}email-address'];
-                } else {
-                    // We're going to make up an emailaddress
-                    $email = $currentNode->getName() . '.sabredav@' . $this->server->httpRequest->getHeader('host');
+                $addresses = $node->getAlternateUriSet();
+                $addresses[] = $this->server->getBaseUri() . $node->getPrincipalUrl();
+                unset($requestedProperties[$calProp]);
+                $returnedProperties[200][$calProp] = new Sabre_DAV_Property_HrefList($addresses, false);
+
+            }
+
+            // These two properties are shortcuts for ical to easily find 
+            // other principals this principal has access to.
+            $propRead = '{' . self::NS_CALENDARSERVER . '}calendar-proxy-read-for';
+            $propWrite = '{' . self::NS_CALENDARSERVER . '}calendar-proxy-write-for';
+            if (in_array($propRead,$requestedProperties) || in_array($propWrite,$requestedProperties)) {
+
+                $membership = $node->getGroupMembership();
+                $readList = array();
+                $writeList = array();
+
+                foreach($membership as $group) {
+
+                    $groupNode = $this->server->tree->getNodeForPath($group);
+
+                    // If the node is either ap proxy-read or proxy-write 
+                    // group, we grab the parent principal and add it to the 
+                    // list.
+                    if ($groupNode instanceof Sabre_CalDAV_Principal_ProxyRead) {
+                        list($readList[]) = Sabre_DAV_URLUtil::splitPath($group);
+                    }
+                    if ($groupNode instanceof Sabre_CalDAV_Principal_ProxyWrite) {
+                        list($writeList[]) = Sabre_DAV_URLUtil::splitPath($group);
+                    }
+
+                }
+                if (in_array($propRead,$requestedProperties)) {
+                    unset($requestedProperties[$propRead]);
+                    $returnedProperties[200][$propRead] = new Sabre_DAV_Property_HrefList($readList);
+                }
+                if (in_array($propWrite,$requestedProperties)) {
+                    unset($requestedProperties[$propWrite]);
+                    $returnedProperties[200][$propWrite] = new Sabre_DAV_Property_HrefList($writeList);
                 }
-                $properties[200][$calProp] = new Sabre_DAV_Property_Href('mailto:' . $email, false);
-                unset($properties[404][$calProp]);
 
             }
 
+        } // instanceof IPrincipal
 
-        }
 
-        if ($currentNode instanceof Sabre_CalDAV_Calendar || $currentNode instanceof Sabre_CalDAV_CalendarObject) {
-            if (array_key_exists('{DAV:}supported-report-set', $properties[200])) {
-                $properties[200]['{DAV:}supported-report-set']->addReport(array(
-                     '{' . self::NS_CALDAV . '}calendar-multiget',
-                     '{' . self::NS_CALDAV . '}calendar-query',
-                //     '{' . self::NS_CALDAV . '}supported-collation-set',
-                //     '{' . self::NS_CALDAV . '}free-busy-query',
-                ));
+        if ($node instanceof Sabre_CalDAV_ICalendarObject) {
+            // The calendar-data property is not supposed to be a 'real' 
+            // property, but in large chunks of the spec it does act as such. 
+            // Therefore we simply expose it as a property.
+            $calDataProp = '{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}calendar-data';
+            if (in_array($calDataProp, $requestedProperties)) {
+                unset($requestedProperties[$calDataProp]);
+                $val = $node->get();
+                if (is_resource($val))
+                    $val = stream_get_contents($val);
+
+                // Taking out \r to not screw up the xml output
+                $returnedProperties[200][$calDataProp] = str_replace("\r","", $val);
+
             }
         }
 
-        
     }
 
     /**
@@ -360,6 +444,7 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin {
         // We are converting the calendar object to an XML structure
         // This makes it far easier to parse
         $xCalendarData = Sabre_CalDAV_ICalendarUtil::toXCal($calendarData);
+t3lib_div::sysLog($xCalendarData, 'caldav');
         $xml = simplexml_load_string($xCalendarData);
         $xml->registerXPathNamespace('c','urn:ietf:params:xml:ns:xcal');
 
@@ -391,24 +476,28 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin {
                         if ($result===false) return false;
                         break;
                     case 'vjournal' :
+                    case 'vfreebusy' :
+                    case 'valarm' :
                         // TODO: not implemented
                         break;
+
+                    /*
+
+                    case 'vjournal' :
                         $result = $this->validateTimeRangeFilterForJournal($xml,$xpath,$filter);
                         if ($result===false) return false;
                         break;
                     case 'vfreebusy' :
-                        // TODO: not implemented
-                        break;
                         $result = $this->validateTimeRangeFilterForFreeBusy($xml,$xpath,$filter);
                         if ($result===false) return false;
                         break;
                     case 'valarm' :
-                        // TODO: not implemented
-                        break;
                         $result = $this->validateTimeRangeFilterForAlarm($xml,$xpath,$filter);
                         if ($result===false) return false;
                         break;
 
+                        */
+
                 }
 
             } 
@@ -416,7 +505,7 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin {
             if (isset($filter['text-match'])) {
                 $currentString = (string)$elem;
 
-                $isMatching = $this->substringMatch($currentString, $filter['text-match']['value'], $filter['text-match']['collation']);
+                $isMatching = Sabre_DAV_StringUtil::textMatch($currentString, $filter['text-match']['value'], $filter['text-match']['collation']);
                 if ($filter['text-match']['negate-condition'] && $isMatching) return false;
                 if (!$filter['text-match']['negate-condition'] && !$isMatching) return false;
                 
@@ -427,6 +516,14 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin {
         
     }
 
+    /**
+     * Checks whether a time-range filter matches an event.
+     * 
+     * @param SimpleXMLElement $xml Event as xml object 
+     * @param string $currentXPath XPath to check 
+     * @param array $currentFilter Filter information 
+     * @return void
+     */
     private function validateTimeRangeFilterForEvent(SimpleXMLElement $xml,$currentXPath,array $currentFilter) {
 
         // Grabbing the DTSTART property
@@ -436,7 +533,7 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin {
         }
 
         // The dtstart can be both a date, or datetime property
-        if ((string)$xdtstart[0]['value']==='DATE') {
+        if ((string)$xdtstart[0]['value']==='DATE' || strlen((string)$xdtstart[0])===8) {
             $isDateTime = false;
         } else {
             $isDateTime = true;
@@ -467,7 +564,7 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin {
                 $tz = null;
             }
 
-            // Since the VALUE parameter of both DTSTART and DTEND must be the same
+            // Since the VALUE prameter of both DTSTART and DTEND must be the same
             // we can assume we don't need to check the VALUE paramter of DTEND.
             if ($isDateTime) {
                 $dtend = Sabre_CalDAV_XMLUtil::parseICalendarDateTime((string)$xdtend[0],$tz);
@@ -510,9 +607,13 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin {
                 $dtend->modify('+1 day');
             }
         }
+        // TODO: we need to properly parse RRULE's, but it's very difficult.
+        // For now, we're always returning events if they have an RRULE at all.
+        $rrule = $xml->xpath($currentXPath.'/c:rrule');
+        $hasRrule = (count($rrule))>0; 
        
-        if (!is_null($currentFilter['time-range']['start']) && $currentFilter['time-range']['start'] >= $dtend return false;
-        if (!is_null($currentFilter['time-range']['end'])   && $currentFilter['time-range']['end']   <= $dtstart) return false;
+        if (!is_null($currentFilter['time-range']['start']) && $currentFilter['time-range']['start'] >= $dtend   && !$hasRrule) return false;
+        if (!is_null($currentFilter['time-range']['end'])   && $currentFilter['time-range']['end']   <= $dtstart && !$hasRrule) return false;
         return true;
     
     }
@@ -685,23 +786,4 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin {
 
     }
 
-    public function substringMatch($haystack, $needle, $collation) {
-
-        switch($collation) {
-            case 'i;ascii-casemap' :
-                // default strtolower takes locale into consideration
-                // we don't want this.
-                $haystack = str_replace(range('a','z'), range('A','Z'), $haystack);
-                $needle = str_replace(range('a','z'), range('A','Z'), $needle);
-                return strpos($haystack, $needle)!==false;
-
-            case 'i;octet' :
-                return strpos($haystack, $needle)!==false;
-            
-            default:
-                throw new Sabre_DAV_Exception_BadRequest('Unknown collation: ' . $collation);
-        }                
-
-    }
-
 }
diff --git a/lib/Sabre/CalDAV/Principal/Collection.php b/lib/Sabre/CalDAV/Principal/Collection.php
new file mode 100755 (executable)
index 0000000..ef94bff
--- /dev/null
@@ -0,0 +1,31 @@
+<?php
+
+/**
+ * Principal collection
+ *
+ * This is an alternative collection to the standard ACL principal collection.
+ * This collection adds support for the calendar-proxy-read and 
+ * calendar-proxy-write sub-principals, as defined by the caldav-proxy 
+ * specification.
+ *
+ * @package Sabre
+ * @subpackage CalDAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_CalDAV_Principal_Collection extends Sabre_DAVACL_AbstractPrincipalCollection {
+
+    /**
+     * Returns a child object based on principal information 
+     * 
+     * @param array $principalInfo 
+     * @return Sabre_CalDAV_Principal_User 
+     */
+    public function getChildForPrincipal(array $principalInfo) {
+
+        return new Sabre_CalDAV_Principal_User($this->principalBackend, $principalInfo);
+
+    }
+
+}
diff --git a/lib/Sabre/CalDAV/Principal/ProxyRead.php b/lib/Sabre/CalDAV/Principal/ProxyRead.php
new file mode 100755 (executable)
index 0000000..d059232
--- /dev/null
@@ -0,0 +1,178 @@
+<?php
+
+/**
+ * ProxyRead principal
+ *
+ * This class represents a principal group, hosted under the main principal.
+ * This is needed to implement 'Calendar delegation' support. This class is 
+ * instantiated by Sabre_CalDAV_Principal_User.
+ * 
+ * @package Sabre
+ * @subpackage CalDAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_CalDAV_Principal_ProxyRead implements Sabre_DAVACL_IPrincipal {
+
+    /**
+     * Principal information from the parent principal. 
+     * 
+     * @var array 
+     */
+    protected $principalInfo;
+
+    /**
+     * Principal backend 
+     * 
+     * @var Sabre_DAVACL_IPrincipalBackend 
+     */
+    protected $principalBackend;
+
+    /**
+     * Creates the object.
+     *
+     * Note that you MUST supply the parent principal information. 
+     * 
+     * @param Sabre_DAVACL_IPrincipalBackend $principalBackend 
+     * @param array $principalInfo 
+     */
+    public function __construct(Sabre_DAVACL_IPrincipalBackend $principalBackend, array $principalInfo) {
+
+        $this->principalInfo = $principalInfo;
+        $this->principalBackend = $principalBackend;
+
+    }
+
+    /**
+     * Returns this principals name.
+     * 
+     * @return string 
+     */
+    public function getName() {
+
+        return 'calendar-proxy-read';
+
+    }
+
+    /**
+     * Returns the last modification time 
+     *
+     * @return null 
+     */
+    public function getLastModified() {
+
+        return null; 
+
+    }
+
+    /**
+     * Deletes the current node
+     *
+     * @throws Sabre_DAV_Exception_Forbidden
+     * @return void 
+     */
+    public function delete() {
+
+        throw new Sabre_DAV_Exception_Forbidden('Permission denied to delete node');
+
+    }
+
+    /**
+     * Renames the node
+     * 
+     * @throws Sabre_DAV_Exception_Forbidden
+     * @param string $name The new name
+     * @return void
+     */
+    public function setName($name) {
+
+        throw new Sabre_DAV_Exception_Forbidden('Permission denied to rename file');
+
+    }
+
+
+    /**
+     * Returns a list of altenative urls for a principal
+     * 
+     * This can for example be an email address, or ldap url.
+     * 
+     * @return array 
+     */
+    public function getAlternateUriSet() {
+
+        return array();
+
+    }
+
+    /**
+     * Returns the full principal url 
+     * 
+     * @return string 
+     */
+    public function getPrincipalUrl() {
+
+        return $this->principalInfo['uri'] . '/' . $this->getName(); 
+
+    }
+
+    /**
+     * Returns the list of group members
+     * 
+     * If this principal is a group, this function should return
+     * all member principal uri's for the group. 
+     * 
+     * @return array
+     */
+    public function getGroupMemberSet() {
+
+        return $this->principalBackend->getGroupMemberSet($this->getPrincipalUrl()); 
+
+    }
+
+    /**
+     * Returns the list of groups this principal is member of
+     * 
+     * If this principal is a member of a (list of) groups, this function
+     * should return a list of principal uri's for it's members. 
+     * 
+     * @return array 
+     */
+    public function getGroupMembership() {
+
+        return $this->principalBackend->getGroupMembership($this->getPrincipalUrl()); 
+
+    }
+
+    /**
+     * Sets a list of group members
+     *
+     * If this principal is a group, this method sets all the group members.
+     * The list of members is always overwritten, never appended to.
+     * 
+     * This method should throw an exception if the members could not be set. 
+     * 
+     * @param array $principals 
+     * @return void 
+     */
+    public function setGroupMemberSet(array $principals) {
+
+        $this->principalBackend->setGroupMemberSet($this->getPrincipalUrl(), $principals);
+
+    }
+
+    /**
+     * Returns the displayname
+     *
+     * This should be a human readable name for the principal.
+     * If none is available, return the nodename. 
+     * 
+     * @return string 
+     */
+    public function getDisplayName() {
+
+        return $this->getName();
+
+    }
+
+}
diff --git a/lib/Sabre/CalDAV/Principal/ProxyWrite.php b/lib/Sabre/CalDAV/Principal/ProxyWrite.php
new file mode 100755 (executable)
index 0000000..060afd1
--- /dev/null
@@ -0,0 +1,178 @@
+<?php
+
+/**
+ * ProxyWrite principal
+ *
+ * This class represents a principal group, hosted under the main principal.
+ * This is needed to implement 'Calendar delegation' support. This class is 
+ * instantiated by Sabre_CalDAV_Principal_User.
+ * 
+ * @package Sabre
+ * @subpackage CalDAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_CalDAV_Principal_ProxyWrite implements Sabre_DAVACL_IPrincipal {
+
+    /**
+     * Parent principal information 
+     * 
+     * @var array 
+     */
+    protected $principalInfo;
+
+    /**
+     * Principal Backend 
+     * 
+     * @var Sabre_DAVACL_IPrincipalBackend 
+     */
+    protected $principalBackend;
+
+    /**
+     * Creates the object
+     *
+     * Note that you MUST supply the parent principal information. 
+     * 
+     * @param Sabre_DAVACL_IPrincipalBackend $principalBackend 
+     * @param array $principalInfo 
+     */
+    public function __construct(Sabre_DAVACL_IPrincipalBackend $principalBackend, array $principalInfo) {
+
+        $this->principalInfo = $principalInfo;
+        $this->principalBackend = $principalBackend;
+
+    }
+
+    /**
+     * Returns this principals name.
+     * 
+     * @return string 
+     */
+    public function getName() {
+
+        return 'calendar-proxy-write';
+
+    }
+
+    /**
+     * Returns the last modification time 
+     *
+     * @return null 
+     */
+    public function getLastModified() {
+
+        return null; 
+
+    }
+
+    /**
+     * Deletes the current node
+     *
+     * @throws Sabre_DAV_Exception_Forbidden
+     * @return void 
+     */
+    public function delete() {
+
+        throw new Sabre_DAV_Exception_Forbidden('Permission denied to delete node');
+
+    }
+
+    /**
+     * Renames the node
+     * 
+     * @throws Sabre_DAV_Exception_Forbidden
+     * @param string $name The new name
+     * @return void
+     */
+    public function setName($name) {
+
+        throw new Sabre_DAV_Exception_Forbidden('Permission denied to rename file');
+
+    }
+
+
+    /**
+     * Returns a list of altenative urls for a principal
+     * 
+     * This can for example be an email address, or ldap url.
+     * 
+     * @return array 
+     */
+    public function getAlternateUriSet() {
+
+        return array();
+
+    }
+
+    /**
+     * Returns the full principal url 
+     * 
+     * @return string 
+     */
+    public function getPrincipalUrl() {
+
+        return $this->principalInfo['uri'] . '/' . $this->getName(); 
+
+    }
+
+    /**
+     * Returns the list of group members
+     * 
+     * If this principal is a group, this function should return
+     * all member principal uri's for the group. 
+     * 
+     * @return array
+     */
+    public function getGroupMemberSet() {
+
+        return $this->principalBackend->getGroupMemberSet($this->getPrincipalUrl()); 
+
+    }
+
+    /**
+     * Returns the list of groups this principal is member of
+     * 
+     * If this principal is a member of a (list of) groups, this function
+     * should return a list of principal uri's for it's members. 
+     * 
+     * @return array 
+     */
+    public function getGroupMembership() {
+
+        return $this->principalBackend->getGroupMembership($this->getPrincipalUrl()); 
+
+    }
+
+    /**
+     * Sets a list of group members
+     *
+     * If this principal is a group, this method sets all the group members.
+     * The list of members is always overwritten, never appended to.
+     * 
+     * This method should throw an exception if the members could not be set. 
+     * 
+     * @param array $principals 
+     * @return void 
+     */
+    public function setGroupMemberSet(array $principals) {
+
+        $this->principalBackend->setGroupMemberSet($this->getPrincipalUrl(), $principals);
+
+    }
+
+    /**
+     * Returns the displayname
+     *
+     * This should be a human readable name for the principal.
+     * If none is available, return the nodename. 
+     * 
+     * @return string 
+     */
+    public function getDisplayName() {
+
+        return $this->getName();
+
+    }
+
+}
diff --git a/lib/Sabre/CalDAV/Principal/User.php b/lib/Sabre/CalDAV/Principal/User.php
new file mode 100755 (executable)
index 0000000..b737518
--- /dev/null
@@ -0,0 +1,122 @@
+<?php
+
+/**
+ * CalDAV principal 
+ *
+ * This is a standard user-principal for CalDAV. This principal is also a 
+ * collection and returns the caldav-proxy-read and caldav-proxy-write child 
+ * principals.
+ * 
+ * @package Sabre
+ * @subpackage CalDAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_CalDAV_Principal_User extends Sabre_DAVACL_Principal implements Sabre_DAV_ICollection {
+
+    /**
+     * Creates a new file in the directory 
+     * 
+     * @param string $name Name of the file 
+     * @param resource $data Initial payload, passed as a readable stream resource. 
+     * @throws Sabre_DAV_Exception_Forbidden
+     * @return void
+     */
+    public function createFile($name, $data = null) {
+
+        throw new Sabre_DAV_Exception_Forbidden('Permission denied to create file (filename ' . $name . ')');
+
+    }
+
+    /**
+     * Creates a new subdirectory 
+     * 
+     * @param string $name 
+     * @throws Sabre_DAV_Exception_Forbidden
+     * @return void
+     */
+    public function createDirectory($name) {
+
+        throw new Sabre_DAV_Exception_Forbidden('Permission denied to create directory');
+
+    }
+
+    /**
+     * Returns a specific child node, referenced by its name 
+     * 
+     * @param string $name 
+     * @return Sabre_DAV_INode 
+     */
+    public function getChild($name) {
+
+        if ($name === 'calendar-proxy-read')
+            return new Sabre_CalDAV_Principal_ProxyRead($this->principalBackend, $this->principalProperties);
+
+        if ($name === 'calendar-proxy-write')
+            return new Sabre_CalDAV_Principal_ProxyWrite($this->principalBackend, $this->principalProperties);
+
+        throw new Sabre_DAV_Exception_FileNotFound('Node with name ' . $name . ' was not found');
+
+    }
+
+    /**
+     * Returns an array with all the child nodes 
+     * 
+     * @return Sabre_DAV_INode[] 
+     */
+    public function getChildren() {
+
+        return array(
+            new Sabre_CalDAV_Principal_ProxyRead($this->principalBackend, $this->principalProperties),
+            new Sabre_CalDAV_Principal_ProxyWrite($this->principalBackend, $this->principalProperties),
+        );
+
+    }
+
+    /**
+     * Checks if a child-node with the specified name exists 
+     * 
+     * @return bool 
+     */
+    public function childExists($name) {
+
+        return $name === 'calendar-proxy-read' ||  $name === 'calendar-proxy-write';
+
+    }
+
+    /**
+     * Returns a list of ACE's for this node.
+     *
+     * Each ACE has the following properties:
+     *   * 'privilege', a string such as {DAV:}read or {DAV:}write. These are 
+     *     currently the only supported privileges
+     *   * 'principal', a url to the principal who owns the node
+     *   * 'protected' (optional), indicating that this ACE is not allowed to 
+     *      be updated. 
+     * 
+     * @return array 
+     */
+    public function getACL() {
+
+        return array(
+            array(
+                'privilege' => '{DAV:}read',
+                'principal' => $this->principalProperties['uri'],
+                'protected' => true,
+            ),
+            array(
+                'privilege' => '{DAV:}read',
+                'principal' => $this->principalProperties['uri'] . '/calendar-proxy-read',
+                'protected' => true,
+            ),
+            array(
+                'privilege' => '{DAV:}read',
+                'principal' => $this->principalProperties['uri'] . '/calendar-proxy-write',
+                'protected' => true,
+            ),
+        );
+
+    }
+
+}
index 5a1862d..d57084c 100755 (executable)
@@ -9,7 +9,7 @@
  *
  * @package Sabre
  * @subpackage CalDAV
- * @copyright Copyright (C) 2007-2010 Rooftop Solutions. All rights reserved.
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
  * @author Evert Pot (http://www.rooftopsolutions.nl/) 
  * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
  */
index 3e9a9d7..085701b 100755 (executable)
@@ -9,7 +9,7 @@
  *
  * @package Sabre
  * @subpackage CalDAV
- * @copyright Copyright (C) 2007-2010 Rooftop Solutions. All rights reserved.
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
  * @author Evert Pot (http://www.rooftopsolutions.nl/) 
  * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
  */
index 2f96591..6f85833 100755 (executable)
@@ -8,13 +8,20 @@
  *
  * @package Sabre
  * @subpackage CalDAV
- * @copyright Copyright (C) 2007-2010 Rooftop Solutions. All rights reserved.
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
  * @author Evert Pot (http://www.rooftopsolutions.nl/) 
  * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
  */
 class Sabre_CalDAV_Property_SupportedCollationSet extends Sabre_DAV_Property {
 
-    function serialize(Sabre_DAV_Server $server,DOMElement $node) {
+    /**
+     * Serializes the property in a DOM document 
+     * 
+     * @param Sabre_DAV_Server $server 
+     * @param DOMElement $node 
+     * @return void
+     */
+    public function serialize(Sabre_DAV_Server $server,DOMElement $node) {
 
         $doc = $node->ownerDocument;
         
@@ -27,6 +34,9 @@ class Sabre_CalDAV_Property_SupportedCollationSet extends Sabre_DAV_Property {
         $node->appendChild(
             $doc->createElement($prefix . ':supported-collation','i;octet')
         );
+        $node->appendChild(
+            $doc->createElement($prefix . ':supported-collation','i;unicode-casemap')
+        );
 
 
     }
index 33f7a39..21d50c4 100755 (executable)
@@ -6,16 +6,30 @@
  * This script is a convenience script. It quickly sets up a WebDAV server
  * with caldav and ACL support, and it creates the root 'principals' and
  * 'calendars' collections.
+ *
+ * Note that if you plan to do anything moderately complex, you are advised to 
+ * not subclass this server, but use Sabre_DAV_Server directly instead. This 
+ * class is nothing more than an 'easy setup'.
  * 
  * @package Sabre
  * @subpackage CalDAV
- * @copyright Copyright (C) 2007-2010 Rooftop Solutions. All rights reserved.
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
  * @author Evert Pot (http://www.rooftopsolutions.nl/) 
  * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
  */
 class Sabre_CalDAV_Server extends Sabre_DAV_Server {
 
     /**
+     * The authentication realm
+     *
+     * Note that if this changes, the hashes in the auth backend must also 
+     * be recalculated. 
+     * 
+     * @var string
+     */
+    public $authRealm = 'SabreDAV';
+
+    /**
      * Sets up the object. A PDO object must be passed to setup all the backends.
      * 
      * @param PDO $pdo 
@@ -25,23 +39,24 @@ class Sabre_CalDAV_Server extends Sabre_DAV_Server {
         /* Backends */
         $authBackend = new Sabre_DAV_Auth_Backend_PDO($pdo);
         $calendarBackend = new Sabre_CalDAV_Backend_PDO($pdo);
+        $principalBackend = new Sabre_DAVACL_PrincipalBackend_PDO($pdo);
 
         /* Directory structure */
-        $root = new Sabre_DAV_SimpleDirectory('root');
-        $principals = new Sabre_DAV_Auth_PrincipalCollection($authBackend);
-        $root->addChild($principals);
-        $calendars = new Sabre_CalDAV_CalendarRootNode($authBackend, $calendarBackend);
-        $root->addChild($calendars);
-
-        $objectTree = new Sabre_DAV_ObjectTree($root);
-        
+        $tree = array(
+            new Sabre_CalDAV_Principal_Collection($principalBackend),
+            new Sabre_CalDAV_CalendarRootNode($principalBackend, $calendarBackend),
+        );
+
         /* Initializing server */
-        parent::__construct($objectTree);
+        parent::__construct($tree);
 
         /* Server Plugins */
-        $authPlugin = new Sabre_DAV_Auth_Plugin($authBackend,'SabreDAV');
+        $authPlugin = new Sabre_DAV_Auth_Plugin($authBackend,$this->authRealm);
         $this->addPlugin($authPlugin);
 
+        $aclPlugin = new Sabre_DAVACL_Plugin();
+        $this->addPlugin($aclPlugin);
+
         $caldavPlugin = new Sabre_CalDAV_Plugin();
         $this->addPlugin($caldavPlugin);
 
index 1e6abca..6bfdaf2 100755 (executable)
@@ -6,16 +6,30 @@
  * This script is a convenience script. It quickly sets up a WebDAV server
  * with caldav and ACL support, and it creates the root 'principals' and
  * 'calendars' collections.
+ *
+ * Note that if you plan to do anything moderately complex, you are advised to 
+ * not subclass this server, but use Sabre_DAV_Server directly instead. This 
+ * class is nothing more than an 'easy setup'.
  * 
  * @package Sabre
  * @subpackage CalDAV
- * @copyright Copyright (C) 2007-2010 Rooftop Solutions. All rights reserved.
- * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @copyright Copyright (C) 2012 Mario Matzulla. All rights reserved.
+ * @author Mario Matzulla (http://www.matzullas.de)
  * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
  */
 class Sabre_CalDAV_TYPO3Server extends Sabre_DAV_Server {
 
     /**
+     * The authentication realm
+     *
+     * Note that if this changes, the hashes in the auth backend must also 
+     * be recalculated. 
+     * 
+     * @var string
+     */
+    public $authRealm = 'SabreDAV';
+
+    /**
      * Sets up the object. A PDO object must be passed to setup all the backends.
      * 
      * @param PDO $pdo 
@@ -25,23 +39,24 @@ class Sabre_CalDAV_TYPO3Server extends Sabre_DAV_Server {
         /* Backends */
         $authBackend = new Sabre_DAV_Auth_Backend_TYPO3($pdo);
         $calendarBackend = new Sabre_CalDAV_Backend_TYPO3($pdo);
-
+        $principalBackend = new Sabre_DAVACL_PrincipalBackend_TYPO3($pdo);
+        
         /* Directory structure */
-        $root = new Sabre_DAV_SimpleDirectory('root');
-        $principals = new Sabre_DAV_Auth_PrincipalCollection($authBackend);
-        $root->addChild($principals);
-        $calendars = new Sabre_CalDAV_CalendarRootNode($authBackend, $calendarBackend);
-        $root->addChild($calendars);
-
-        $objectTree = new Sabre_DAV_ObjectTree($root);
+        $tree = array(
+                       new Sabre_CalDAV_Principal_Collection($principalBackend),
+                       new Sabre_CalDAV_CalendarRootNode($principalBackend, $calendarBackend),
+        );
         
         /* Initializing server */
-        parent::__construct($objectTree);
-
+        parent::__construct($tree);
+        
         /* Server Plugins */
-        $authPlugin = new Sabre_DAV_Auth_Plugin($authBackend,'SabreDAV');
+        $authPlugin = new Sabre_DAV_Auth_Plugin($authBackend,$this->authRealm);
         $this->addPlugin($authPlugin);
-
+        
+        $aclPlugin = new Sabre_DAVACL_Plugin();
+        $this->addPlugin($aclPlugin);
+        
         $caldavPlugin = new Sabre_CalDAV_Plugin();
         $this->addPlugin($caldavPlugin);
 
index c81b70b..3c60971 100755 (executable)
@@ -5,45 +5,45 @@
  * 
  * @package Sabre
  * @subpackage CalDAV
- * @copyright Copyright (C) 2007-2010 Rooftop Solutions. All rights reserved.
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
  * @author Evert Pot (http://www.rooftopsolutions.nl/) 
  * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
  */
-class Sabre_CalDAV_UserCalendars implements Sabre_DAV_IExtendedCollection {
+class Sabre_CalDAV_UserCalendars implements Sabre_DAV_IExtendedCollection, Sabre_DAVACL_IACL {
 
     /**
-     * Authentication backend 
+     * Principal backend 
      * 
-     * @var Sabre_DAV_Auth_Backend_Abstract 
+     * @var Sabre_DAVACL_IPrincipalBackend
      */
-    protected $authBackend;
+    protected $principalBackend;
 
     /**
-     * Array with user information 
+     * CalDAV backend
      * 
-     * @var array 
+     * @var Sabre_CalDAV_Backend_Abstract
      */
-    protected $userUri;
+    protected $caldavBackend;
 
     /**
-     * CalDAV backend
+     * Principal information 
      * 
-     * @var Sabre_CalDAV_Backend_Abstract
+     * @var array 
      */
-    protected $caldavBackend;
+    protected $principalInfo;
     
     /**
      * Constructor 
      * 
-     * @param Sabre_DAV_Auth_Backend_Abstract $authBackend 
+     * @param Sabre_DAVACL_IPrincipalBackend $principalBackend
      * @param Sabre_CalDAV_Backend_Abstract $caldavBackend 
      * @param mixed $userUri 
      */
-    public function __construct(Sabre_DAV_Auth_Backend_Abstract $authBackend, Sabre_CalDAV_Backend_Abstract $caldavBackend, $userUri) {
+    public function __construct(Sabre_DAVACL_IPrincipalBackend $principalBackend, Sabre_CalDAV_Backend_Abstract $caldavBackend, $userUri) {
 
-        $this->authBackend = $authBackend;
+        $this->principalBackend = $principalBackend;
         $this->caldavBackend = $caldavBackend;
-        $this->userUri = $userUri;
+        $this->principalInfo = $principalBackend->getPrincipalByPath($userUri);
        
     }
 
@@ -54,7 +54,7 @@ class Sabre_CalDAV_UserCalendars implements Sabre_DAV_IExtendedCollection {
      */
     public function getName() {
       
-        list(,$name) = Sabre_DAV_URLUtil::splitPath($this->userUri);
+        list(,$name) = Sabre_DAV_URLUtil::splitPath($this->principalInfo['uri']);
         return $name; 
 
     }
@@ -165,10 +165,10 @@ class Sabre_CalDAV_UserCalendars implements Sabre_DAV_IExtendedCollection {
      */
     public function getChildren() {
 
-        $calendars = $this->caldavBackend->getCalendarsForUser($this->userUri);
+        $calendars = $this->caldavBackend->getCalendarsForUser($this->principalInfo['uri']);
         $objs = array();
         foreach($calendars as $calendar) {
-            $objs[] = new Sabre_CalDAV_Calendar($this->authBackend, $this->caldavBackend, $calendar);
+            $objs[] = new Sabre_CalDAV_Calendar($this->principalBackend, $this->caldavBackend, $calendar);
         }
         return $objs;
 
@@ -186,8 +186,95 @@ class Sabre_CalDAV_UserCalendars implements Sabre_DAV_IExtendedCollection {
         if (!in_array('{urn:ietf:params:xml:ns:caldav}calendar',$resourceType) || count($resourceType)!==2) {
             throw new Sabre_DAV_Exception_InvalidResourceType('Unknown resourceType for this collection');
         }
-        $this->caldavBackend->createCalendar($this->userUri, $name, $properties);
+        $this->caldavBackend->createCalendar($this->principalInfo['uri'], $name, $properties);
+
+    }
+
+    /**
+     * Returns the owner principal
+     *
+     * This must be a url to a principal, or null if there's no owner 
+     * 
+     * @return string|null
+     */
+    public function getOwner() {
+
+        return $this->principalInfo['uri'];
 
     }
 
+    /**
+     * Returns a group principal
+     *
+     * This must be a url to a principal, or null if there's no owner
+     * 
+     * @return string|null 
+     */
+    public function getGroup() {
+
+        return null;
+
+    }
+
+    /**
+     * Returns a list of ACE's for this node.
+     *
+     * Each ACE has the following properties:
+     *   * 'privilege', a string such as {DAV:}read or {DAV:}write. These are 
+     *     currently the only supported privileges
+     *   * 'principal', a url to the principal who owns the node
+     *   * 'protected' (optional), indicating that this ACE is not allowed to 
+     *      be updated. 
+     * 
+     * @return array 
+     */
+    public function getACL() {
+
+        return array(
+            array(
+                'privilege' => '{DAV:}read',
+                'principal' => $this->principalInfo['uri'],
+                'protected' => true,
+            ),
+            array(
+                'privilege' => '{DAV:}write',
+                'principal' => $this->principalInfo['uri'],
+                'protected' => true,
+            ),
+            array(
+                'privilege' => '{DAV:}read',
+                'principal' => $this->principalInfo['uri'] . '/calendar-proxy-write',
+                'protected' => true,
+            ),
+            array(
+                'privilege' => '{DAV:}write',
+                'principal' => $this->principalInfo['uri'] . '/calendar-proxy-write',
+                'protected' => true,
+            ),
+            array(
+                'privilege' => '{DAV:}read',
+                'principal' => $this->principalInfo['uri'] . '/calendar-proxy-read',
+                'protected' => true,
+            ),
+
+        );
+
+    }
+
+    /**
+     * Updates the ACL
+     *
+     * This method will receive a list of new ACE's. 
+     * 
+     * @param array $acl 
+     * @return void
+     */
+    public function setACL(array $acl) {
+
+        throw new Sabre_DAV_Exception_MethodNotAllowed('Changing ACL is not yet supported');
+
+    }
+
+
+
 }
index d7b2e6a..2647f15 100755 (executable)
@@ -5,7 +5,7 @@
  * 
  * @package Sabre
  * @subpackage CalDAV 
- * @copyright Copyright (C) 2007-2010 Rooftop Solutions. All rights reserved.
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
  * @author Evert Pot (http://www.rooftopsolutions.nl/)
  * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
  */
@@ -14,7 +14,7 @@ class Sabre_CalDAV_Version {
     /**
      * Full version number
      */
-    const VERSION = '1.3.0';
+    const VERSION = '1.5.5';
 
     /**
      * Stability : alpha, beta, stable
index dfa58a0..0b29f84 100755 (executable)
@@ -8,7 +8,7 @@
  *
  * @package Sabre
  * @subpackage CalDAV
- * @copyright Copyright (C) 2007-2010 Rooftop Solutions. All rights reserved.
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
  * @author Evert Pot (http://www.rooftopsolutions.nl/) 
  * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
  */
@@ -100,7 +100,7 @@ class Sabre_CalDAV_XMLUtil {
                     if (!$collation) $collation = 'i;ascii-casemap';
 
                     $filters[$basePath]['text-match'] = array(
-                        'collation' => $collation,
+                        'collation' => ($collation == 'default'?'i;ascii-casemap':$collation),
                         'negate-condition' => $child->getAttribute('negate-condition')==='yes',
                         'value' => $child->nodeValue,
                     );
diff --git a/lib/Sabre/CardDAV/AddressBook.php b/lib/Sabre/CardDAV/AddressBook.php
new file mode 100755 (executable)
index 0000000..adb4dfa
--- /dev/null
@@ -0,0 +1,293 @@
+<?php
+
+/**
+ * The AddressBook class represents a CardDAV addressbook, owned by a specific user
+ *
+ * The AddressBook can contain multiple vcards
+ *
+ * @package Sabre
+ * @subpackage CardDAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_CardDAV_AddressBook extends Sabre_DAV_Collection implements Sabre_CardDAV_IAddressBook, Sabre_DAV_IProperties, Sabre_DAVACL_IACL {
+
+    /**
+     * This is an array with addressbook information 
+     * 
+     * @var array 
+     */
+    private $addressBookInfo;
+
+    /**
+     * CardDAV backend 
+     * 
+     * @var Sabre_CardDAV_Backend_Abstract 
+     */
+    private $carddavBackend;
+
+    /**
+     * Constructor 
+     * 
+     * @param Sabre_CardDAV_Backend_Abstract $carddavBackend 
+     * @param array $addressBookInfo 
+     * @return void
+     */
+    public function __construct(Sabre_CardDAV_Backend_Abstract $carddavBackend,array $addressBookInfo) {
+
+        $this->carddavBackend = $carddavBackend;
+        $this->addressBookInfo = $addressBookInfo;
+
+    }
+
+    /**
+     * Returns the name of the addressbook 
+     * 
+     * @return string 
+     */
+    public function getName() {
+
+        return $this->addressBookInfo['uri'];
+
+    }
+
+    /**
+     * Returns a card
+     *
+     * @param string $name 
+     * @return Sabre_DAV_Card
+     */
+    public function getChild($name) {
+
+        $obj = $this->carddavBackend->getCard($this->addressBookInfo['id'],$name);
+        if (!$obj) throw new Sabre_DAV_Exception_FileNotFound('Card not found');
+        return new Sabre_CardDAV_Card($this->carddavBackend,$this->addressBookInfo,$obj);
+
+    }
+
+    /**
+     * Returns the full list of cards
+     * 
+     * @return array 
+     */
+    public function getChildren() {
+
+        $objs = $this->carddavBackend->getCards($this->addressBookInfo['id']);
+        $children = array();
+        foreach($objs as $obj) {
+            $children[] = new Sabre_CardDAV_Card($this->carddavBackend,$this->addressBookInfo,$obj);
+        }
+        return $children;
+
+    }
+
+    /**
+     * Creates a new directory
+     *
+     * We actually block this, as subdirectories are not allowed in addressbooks. 
+     * 
+     * @param string $name 
+     * @return void
+     */
+    public function createDirectory($name) {
+
+        throw new Sabre_DAV_Exception_MethodNotAllowed('Creating collections in addressbooks is not allowed');
+
+    }
+
+    /**
+     * Creates a new file
+     *
+     * The contents of the new file must be a valid VCARD
+     * 
+     * @param string $name 
+     * @param resource $vcardData 
+     * @return void
+     */
+    public function createFile($name,$vcardData = null) {
+
+        $vcardData = stream_get_contents($vcardData);
+        // Converting to UTF-8, if needed
+        $vcardData = Sabre_DAV_StringUtil::ensureUTF8($vcardData);
+
+        $this->carddavBackend->createCard($this->addressBookInfo['id'],$name,$vcardData);
+
+    }
+
+    /**
+     * Deletes the entire addressbook. 
+     * 
+     * @return void
+     */
+    public function delete() {
+
+        $this->carddavBackend->deleteAddressBook($this->addressBookInfo['id']);
+
+    }
+
+    /**
+     * Renames the addressbook
+     * 
+     * @param string $newName 
+     * @return void
+     */
+    public function setName($newName) {
+
+        throw new Sabre_DAV_Exception_MethodNotAllowed('Renaming addressbooks is not yet supported');
+
+    }
+
+    /**
+     * Returns the last modification date as a unix timestamp.
+     * 
+     * @return void
+     */
+    public function getLastModified() {
+
+        return null;
+
+    }
+
+    /**
+     * Updates properties on this node,
+     *
+     * The properties array uses the propertyName in clark-notation as key,
+     * and the array value for the property value. In the case a property
+     * should be deleted, the property value will be null.
+     *
+     * This method must be atomic. If one property cannot be changed, the
+     * entire operation must fail.
+     *
+     * If the operation was successful, true can be returned.
+     * If the operation failed, false can be returned.
+     *
+     * Deletion of a non-existant property is always succesful.
+     *
+     * Lastly, it is optional to return detailed information about any
+     * failures. In this case an array should be returned with the following
+     * structure:
+     *
+     * array(
+     *   403 => array(
+     *      '{DAV:}displayname' => null,
+     *   ),
+     *   424 => array(
+     *      '{DAV:}owner' => null,
+     *   )
+     * )
+     *
+     * In this example it was forbidden to update {DAV:}displayname. 
+     * (403 Forbidden), which in turn also caused {DAV:}owner to fail
+     * (424 Failed Dependency) because the request needs to be atomic.
+     *
+     * @param array $mutations 
+     * @return bool|array 
+     */
+    public function updateProperties($mutations) {
+
+        return $this->carddavBackend->updateAddressBook($this->addressBookInfo['id'], $mutations); 
+
+    }
+
+    /**
+     * Returns a list of properties for this nodes.
+     *
+     * The properties list is a list of propertynames the client requested,
+     * encoded in clark-notation {xmlnamespace}tagname
+     *
+     * If the array is empty, it means 'all properties' were requested.
+     *
+     * @param array $properties 
+     * @return void
+     */
+    public function getProperties($properties) {
+
+        $response = array();
+        foreach($properties as $propertyName) {
+
+            if (isset($this->addressBookInfo[$propertyName])) {
+
+                $response[$propertyName] = $this->addressBookInfo[$propertyName];
+
+            }
+
+        }
+
+        return $response;
+
+    }
+
+    /**
+     * Returns the owner principal
+     *
+     * This must be a url to a principal, or null if there's no owner 
+     * 
+     * @return string|null
+     */
+    public function getOwner() {
+
+        return $this->addressBookInfo['principaluri'];
+
+    }
+
+    /**
+     * Returns a group principal
+     *
+     * This must be a url to a principal, or null if there's no owner
+     * 
+     * @return string|null 
+     */
+    public function getGroup() {
+
+        return null;
+
+    }
+
+    /**
+     * Returns a list of ACE's for this node.
+     *
+     * Each ACE has the following properties:
+     *   * 'privilege', a string such as {DAV:}read or {DAV:}write. These are 
+     *     currently the only supported privileges
+     *   * 'principal', a url to the principal who owns the node
+     *   * 'protected' (optional), indicating that this ACE is not allowed to 
+     *      be updated. 
+     * 
+     * @return array 
+     */
+    public function getACL() {
+
+        return array(
+            array(
+                'privilege' => '{DAV:}read',
+                'principal' => $this->addressBookInfo['principaluri'],
+                'protected' => true,
+            ),
+            array(
+                'privilege' => '{DAV:}write',
+                'principal' => $this->addressBookInfo['principaluri'],
+                'protected' => true,
+            ),
+
+        );
+
+    }
+
+    /**
+     * Updates the ACL
+     *
+     * This method will receive a list of new ACE's. 
+     * 
+     * @param array $acl 
+     * @return void
+     */
+    public function setACL(array $acl) {
+
+        throw new Sabre_DAV_Exception_MethodNotAllowed('Changing ACL is not yet supported');
+
+    }
+
+
+
+}
diff --git a/lib/Sabre/CardDAV/AddressBookQueryParser.php b/lib/Sabre/CardDAV/AddressBookQueryParser.php
new file mode 100755 (executable)
index 0000000..2815ce3
--- /dev/null
@@ -0,0 +1,211 @@
+<?php
+
+/**
+ * Parses the addressbook-query report request body.
+ *
+ * Whoever designed this format, and the CalDAV equavalent even more so, 
+ * has no feel for design.
+ * 
+ * @package Sabre
+ * @subpackage CardDAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_CardDAV_AddressBookQueryParser {
+
+    const TEST_ANYOF = 'anyof';
+    const TEST_ALLOF = 'allof';
+
+    /**
+     * List of requested properties the client wanted
+     * 
+     * @var array 
+     */
+    public $requestedProperties;
+
+    /**
+     * The number of results the client wants
+     *
+     * null means it wasn't specified, which in most cases means 'all results'.
+     * 
+     * @var int|null 
+     */
+    public $limit;
+
+    /**
+     * List of property filters.
+     *
+     * @var array 
+     */
+    public $filters;
+
+    /**
+     * Either TEST_ANYOF or TEST_ALLOF
+     * 
+     * @var string 
+     */
+    public $test;
+
+    /**
+     * DOM Document
+     * 
+     * @var DOMDocument 
+     */
+    protected $dom;
+
+    /**
+     * DOM XPath object 
+     * 
+     * @var DOMXPath 
+     */
+    protected $xpath;
+
+    /**
+     * Creates the parser
+     * 
+     * @param DOMNode $dom 
+     * @return void
+     */
+    public function __construct(DOMDocument $dom) {
+
+        $this->dom = $dom;
+
+        $this->xpath = new DOMXPath($dom);
+        $this->xpath->registerNameSpace('card',Sabre_CardDAV_Plugin::NS_CARDDAV);
+
+    }
+
+    /**
+     * Parses the request. 
+     * 
+     * @param DOMNode $dom 
+     * @return void
+     */
+    public function parse() {
+
+        $filterNode = null;
+        
+        $limit = $this->xpath->evaluate('number(/card:addressbook-query/card:limit/card:nresults)');
+        if (is_nan($limit)) $limit = null;
+
+        $filter = $this->xpath->query('/card:addressbook-query/card:filter');
+        if ($filter->length !== 1) {
+            throw new Sabre_DAV_Exception_BadRequest('Only one filter element is allowed');
+        }
+
+        $filter = $filter->item(0);
+        $test = $this->xpath->evaluate('string(@test)', $filter);
+        if (!$test) $test = self::TEST_ANYOF;
+        if ($test !== self::TEST_ANYOF && $test !== self::TEST_ALLOF) {
+            throw new Sabre_DAV_Exception_BadRequest('The test attribute must either hold "anyof" or "allof"');
+        }
+
+        $propFilters = array();
+
+        $propFilterNodes = $this->xpath->query('card:prop-filter', $filter);
+        for($ii=0; $ii < $propFilterNodes->length; $ii++) {
+
+            $propFilters[] = $this->parsePropFilterNode($propFilterNodes->item($ii));
+
+
+        }
+
+        $this->filters = $propFilters;
+        $this->limit = $limit;
+        $this->requestedProperties = array_keys(Sabre_DAV_XMLUtil::parseProperties($this->dom->firstChild));
+        $this->test = $test;
+
+    }
+
+    /**
+     * Parses the prop-filter xml element
+     * 
+     * @param DOMElement $propFilterNode 
+     * @return array 
+     */
+    protected function parsePropFilterNode(DOMElement $propFilterNode) {
+
+        $propFilter = array();
+        $propFilter['name'] = $propFilterNode->getAttribute('name');
+        $propFilter['test'] = $propFilterNode->getAttribute('test');
+        if (!$propFilter['test']) $propFilter['test'] = 'anyof';
+
+        $propFilter['is-not-defined'] = $this->xpath->query('card:is-not-defined', $propFilterNode)->length>0;
+
+        $paramFilterNodes = $this->xpath->query('card:param-filter', $propFilterNode);
+
+        $propFilter['param-filters'] = array();
+
+
+        for($ii=0;$ii<$paramFilterNodes->length;$ii++) {
+
+            $propFilter['param-filters'][] = $this->parseParamFilterNode($paramFilterNodes->item($ii));
+
+        }
+        $propFilter['text-matches'] = array();
+        $textMatchNodes = $this->xpath->query('card:text-match', $propFilterNode);
+
+        for($ii=0;$ii<$textMatchNodes->length;$ii++) {
+
+            $propFilter['text-matches'][] = $this->parseTextMatchNode($textMatchNodes->item($ii));
+
+        }
+
+        return $propFilter;
+
+    }
+
+    /**
+     * Parses the param-filter element 
+     * 
+     * @param DOMElement $paramFilterNode 
+     * @return array 
+     */
+    public function parseParamFilterNode(DOMElement $paramFilterNode) {
+        
+        $paramFilter = array();
+        $paramFilter['name'] = $paramFilterNode->getAttribute('name');
+        $paramFilter['is-not-defined'] = $this->xpath->query('card:is-not-defined', $paramFilterNode)->length>0;
+        $paramFilter['text-match'] = null;
+
+        $textMatch = $this->xpath->query('card:text-match', $paramFilterNode);
+        if ($textMatch->length>0) {
+            $paramFilter['text-match'] = $this->parseTextMatchNode($textMatch->item(0));
+        }
+
+        return $paramFilter; 
+
+    }
+
+    /**
+     * Text match
+     * 
+     * @param DOMElement $textMatchNode 
+     * @return void
+     */
+    public function parseTextMatchNode(DOMElement $textMatchNode) {
+
+        $matchType = $textMatchNode->getAttribute('match-type');
+        if (!$matchType) $matchType = 'contains';
+
+        if (!in_array($matchType, array('contains', 'equals', 'starts-with', 'ends-with'))) {
+            throw new Sabre_DAV_Exception_BadRequest('Unknown match-type: ' . $matchType);
+        }
+
+        $negateCondition = $textMatchNode->getAttribute('negate-condition');
+        $negateCondition = $negateCondition==='yes';
+        $collation = $textMatchNode->getAttribute('collation');
+        if (!$collation) $collation = 'i;unicode-casemap';
+
+        return array(
+            'negate-condition' => $negateCondition,
+            'collation' => $collation,
+            'match-type' => $matchType,
+            'value' => $textMatchNode->nodeValue
+        );
+          
+
+    } 
+
+}
diff --git a/lib/Sabre/CardDAV/AddressBookRoot.php b/lib/Sabre/CardDAV/AddressBookRoot.php
new file mode 100755 (executable)
index 0000000..d51f352
--- /dev/null
@@ -0,0 +1,78 @@
+<?php
+
+/**
+ * AddressBook rootnode 
+ *
+ * This object lists a collection of users, which can contain addressbooks.
+ *
+ * @package Sabre
+ * @subpackage CardDAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_CardDAV_AddressBookRoot extends Sabre_DAVACL_AbstractPrincipalCollection {
+
+    /**
+     * Principal Backend 
+     * 
+     * @var Sabre_DAVACL_IPrincipalBackend
+     */
+    protected $principalBackend;
+
+    /**
+     * CardDAV backend 
+     * 
+     * @var Sabre_CardDAV_Backend_Abstract 
+     */
+    protected $carddavBackend;
+
+    /**
+     * Constructor 
+     *
+     * This constructor needs both a principal and a carddav backend.
+     *
+     * By default this class will show a list of addressbook collections for 
+     * principals in the 'principals' collection. If your main principals are 
+     * actually located in a different path, use the $principalPrefix argument 
+     * to override this.
+     *
+     * @param Sabre_DAVACL_IPrincipalBackend $principalBackend 
+     * @param Sabre_CardDAV_Backend_Abstract $carddavBackend
+     * @param string $principalPrefix 
+     */
+    public function __construct(Sabre_DAVACL_IPrincipalBackend $principalBackend,Sabre_CardDAV_Backend_Abstract $carddavBackend, $principalPrefix = 'principals') {
+
+        $this->carddavBackend = $carddavBackend;
+        parent::__construct($principalBackend, $principalPrefix);
+
+    }
+
+    /**
+     * Returns the name of the node 
+     * 
+     * @return string 
+     */
+    public function getName() {
+
+        return Sabre_CardDAV_Plugin::ADDRESSBOOK_ROOT;
+
+    }
+
+    /**
+     * This method returns a node for a principal.
+     *
+     * The passed array contains principal information, and is guaranteed to
+     * at least contain a uri item. Other properties may or may not be
+     * supplied by the authentication backend.
+     * 
+     * @param array $principal 
+     * @return Sabre_DAV_INode 
+     */
+    public function getChildForPrincipal(array $principal) {
+
+        return new Sabre_CardDAV_UserAddressBooks($this->carddavBackend, $principal['uri']);
+
+    }
+
+}
diff --git a/lib/Sabre/CardDAV/Backend/Abstract.php b/lib/Sabre/CardDAV/Backend/Abstract.php
new file mode 100755 (executable)
index 0000000..8cbb11c
--- /dev/null
@@ -0,0 +1,121 @@
+<?php
+
+/**
+ * Abstract Backend class
+ *
+ * This class serves as a base-class for addressbook backends
+ *
+ * Note that there are references to 'addressBookId' scattered throughout the 
+ * class. The value of the addressBookId is completely up to you, it can be any 
+ * arbitrary value you can use as an unique identifier.
+ * 
+ * @package Sabre
+ * @subpackage CardDAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+abstract class Sabre_CardDAV_Backend_Abstract {
+
+    /**
+     * Returns the list of addressbooks for a specific user.
+     *
+     * Every addressbook should have the following properties:
+     *   id - an arbitrary unique id
+     *   uri - the 'basename' part of the url
+     *   principaluri - Same as the passed parameter
+     *
+     * Any additional clark-notation property may be passed besides this. Some 
+     * common ones are :
+     *   {DAV:}displayname
+     *   {urn:ietf:params:xml:ns:carddav}addressbook-description
+     *   {http://calendarserver.org/ns/}getctag
+     * 
+     * @param string $principalUri 
+     * @return array 
+     */
+    public abstract function getAddressBooksForUser($principalUri); 
+
+    /**
+     * Updates an addressbook's properties
+     *
+     * See Sabre_DAV_IProperties for a description of the mutations array, as 
+     * well as the return value. 
+     *
+     * @param mixed $addressBookId
+     * @param array $mutations
+     * @see Sabre_DAV_IProperties::updateProperties
+     * @return bool|array
+     */
+    public abstract function updateAddressBook($addressBookId, array $mutations); 
+
+    /**
+     * Creates a new address book 
+     *
+     * @param string $principalUri 
+     * @param string $url Just the 'basename' of the url. 
+     * @param array $properties 
+     * @return void
+     */
+    abstract public function createAddressBook($principalUri, $url, array $properties); 
+
+    /**
+     * Deletes an entire addressbook and all its contents
+     *
+     * @param mixed $addressBookId 
+     * @return void
+     */
+    abstract public function deleteAddressBook($addressBookId); 
+
+    /**
+     * Returns all cards for a specific addressbook id. 
+     *
+     * This method should return the following properties for each card:
+     *   * carddata - raw vcard data
+     *   * uri - Some unique url
+     *   * lastmodified - A unix timestamp
+
+     * @param mixed $addressbookId 
+     * @return array 
+     */
+    public abstract function getCards($addressbookId); 
+
+    /**
+     * Returns a specfic card
+     * 
+     * @param mixed $addressBookId 
+     * @param string $cardUri 
+     * @return void
+     */
+    public abstract function getCard($addressBookId, $cardUri); 
+
+    /**
+     * Creates a new card
+     * 
+     * @param mixed $addressBookId 
+     * @param string $cardUri 
+     * @param string $cardData 
+     * @return bool 
+     */
+    abstract public function createCard($addressBookId, $cardUri, $cardData); 
+
+    /**
+     * Updates a card
+     * 
+     * @param mixed $addressBookId 
+     * @param string $cardUri 
+     * @param string $cardData 
+     * @return bool 
+     */
+    abstract public function updateCard($addressBookId, $cardUri, $cardData); 
+
+    /**
+     * Deletes a card
+     * 
+     * @param mixed $addressBookId 
+     * @param string $cardUri 
+     * @return bool 
+     */
+    abstract public function deleteCard($addressBookId, $cardUri); 
+
+}
diff --git a/lib/Sabre/CardDAV/Backend/PDO.php b/lib/Sabre/CardDAV/Backend/PDO.php
new file mode 100755 (executable)
index 0000000..c1227ad
--- /dev/null
@@ -0,0 +1,277 @@
+<?php
+
+/**
+ * PDO CardDAV backend
+ *
+ * This CardDAV backend uses PDO to store addressbooks
+ * 
+ * @package Sabre
+ * @subpackage CardDAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_CardDAV_Backend_PDO extends Sabre_CardDAV_Backend_Abstract {
+
+    /**
+     * PDO connection 
+     * 
+     * @var PDO 
+     */
+    protected $pdo;
+
+    /**
+     * The PDO table name used to store addressbooks
+     */
+    protected $addressBooksTableName;
+
+    /**
+     * The PDO table name used to store cards
+     */
+    protected $cardsTableName;
+
+    /**
+     * Sets up the object 
+     * 
+     * @param PDO $pdo 
+     */
+    public function __construct(PDO $pdo, $addressBooksTableName = 'addressbooks', $cardsTableName = 'cards') {
+
+        $this->pdo = $pdo;
+        $this->addressBooksTableName = $addressBooksTableName;
+        $this->cardsTableName = $cardsTableName; 
+
+    }
+
+    /**
+     * Returns the list of addressbooks for a specific user. 
+     * 
+     * @param string $principalUri 
+     * @return array 
+     */
+    public function getAddressBooksForUser($principalUri) {
+
+        $stmt = $this->pdo->prepare('SELECT id, uri, displayname, principaluri, description, ctag FROM `'.$this->addressBooksTableName.'` WHERE principaluri = ?');
+        $result = $stmt->execute(array($principalUri));
+
+        $addressBooks = array();
+
+        foreach($stmt->fetchAll() as $row) {
+
+            $addressBooks[] = array(
+                'id'  => $row['id'],
+                'uri' => $row['uri'],
+                'principaluri' => $row['principaluri'],
+                '{DAV:}displayname' => $row['displayname'],
+                '{' . Sabre_CardDAV_Plugin::NS_CARDDAV . '}addressbook-description' => $row['description'],
+                '{http://calendarserver.org/ns/}getctag' => $row['ctag'],
+                '{' . Sabre_CardDAV_Plugin::NS_CARDDAV . '}supported-address-data' => 
+                    new Sabre_CardDAV_Property_SupportedAddressData(),
+            );
+
+        }
+
+        return $addressBooks;
+
+    }
+
+
+    /**
+     * Updates an addressbook's properties
+     *
+     * See Sabre_DAV_IProperties for a description of the mutations array, as 
+     * well as the return value. 
+     *
+     * @param mixed $addressBookId
+     * @param array $mutations
+     * @see Sabre_DAV_IProperties::updateProperties
+     * @return bool|array
+     */
+    public function updateAddressBook($addressBookId, array $mutations) {
+        
+        $updates = array();
+
+        foreach($mutations as $property=>$newValue) {
+
+            switch($property) {
+                case '{DAV:}displayname' :
+                    $updates['displayname'] = $newValue;
+                    break;
+                case '{' . Sabre_CardDAV_Plugin::NS_CARDDAV . '}addressbook-description' :
+                    $updates['description'] = $newValue;
+                    break;
+                default :
+                    // If any unsupported values were being updated, we must 
+                    // let the entire request fail.
+                    return false;
+            }
+
+        }
+
+        // No values are being updated?
+        if (!$updates) {
+            return false;
+        }
+
+        $query = 'UPDATE `' . $this->addressBooksTableName . '` SET ctag = ctag + 1 ';
+        foreach($updates as $key=>$value) {
+            $query.=', `' . $key . '` = :' . $key . ' ';
+        }
+        $query.=' WHERE id = :addressbookid';
+
+        $stmt = $this->pdo->prepare($query);
+        $updates['addressbookid'] = $addressBookId;
+
+        $stmt->execute($updates);
+
+        return true;
+
+    }
+
+    /**
+     * Creates a new address book 
+     *
+     * @param string $principalUri 
+     * @param string $url Just the 'basename' of the url. 
+     * @param array $properties 
+     * @return void
+     */
+    public function createAddressBook($principalUri, $url, array $properties) {
+
+        $values = array(
+            'displayname' => null,
+            'description' => null,
+            'principaluri' => $principalUri,
+            'uri' => $url,
+        );
+
+        foreach($properties as $property=>$newValue) {
+
+            switch($property) {
+                case '{DAV:}displayname' :
+                    $values['displayname'] = $newValue;
+                    break;
+                case '{' . Sabre_CardDAV_Plugin::NS_CARDDAV . '}addressbook-description' :
+                    $values['description'] = $newValue;
+                    break;
+                default :
+                    throw new Sabre_DAV_Exception_BadRequest('Unknown property: ' . $property);
+            }
+
+        }
+
+        $query = 'INSERT INTO `' . $this->addressBooksTableName . '` (uri, displayname, description, principaluri, ctag) VALUES (:uri, :displayname, :description, :principaluri, 1)';
+        $stmt = $this->pdo->prepare($query);
+        $stmt->execute($values);
+
+    }
+
+    /**
+     * Deletes an entire addressbook and all its contents
+     *
+     * @param int $addressBookId 
+     * @return void
+     */
+    public function deleteAddressBook($addressBookId) {
+
+        $stmt = $this->pdo->prepare('DELETE FROM `' . $this->cardsTableName . '` WHERE addressbookid = ?');
+        $stmt->execute(array($addressBookId));
+
+        $stmt = $this->pdo->prepare('DELETE FROM `' . $this->addressBooksTableName . '` WHERE id = ?');
+        $stmt->execute(array($addressBookId));
+
+    }
+
+    /**
+     * Returns all cards for a specific addressbook id. 
+     * 
+     * @param mixed $addressbookId 
+     * @return array 
+     */
+    public function getCards($addressbookId) {
+
+        $stmt = $this->pdo->prepare('SELECT id, carddata, uri, lastmodified FROM `' . $this->cardsTableName . '` WHERE addressbookid = ?');
+        $stmt->execute(array($addressbookId));
+
+        return $stmt->fetchAll(PDO::FETCH_ASSOC);
+
+    
+    }
+    /**
+     * Returns a specfic card
+     * 
+     * @param mixed $addressBookId 
+     * @param string $cardUri 
+     * @return array 
+     */
+    public function getCard($addressBookId, $cardUri) {
+
+        $stmt = $this->pdo->prepare('SELECT id, carddata, uri, lastmodified FROM `' . $this->cardsTableName . '` WHERE addressbookid = ? AND uri = ? LIMIT 1');
+        $stmt->execute(array($addressBookId, $cardUri));
+
+        $result = $stmt->fetchAll(PDO::FETCH_ASSOC);
+
+        return (count($result)>0?$result[0]:false);
+
+    }
+
+    /**
+     * Creates a new card
+     * 
+     * @param mixed $addressBookId 
+     * @param string $cardUri 
+     * @param string $cardData 
+     * @return bool 
+     */
+    public function createCard($addressBookId, $cardUri, $cardData) {
+
+        $stmt = $this->pdo->prepare('INSERT INTO `' . $this->cardsTableName . '` (carddata, uri, lastmodified, addressbookid) VALUES (?, ?, ?, ?)');
+
+        $result = $stmt->execute(array($cardData, $cardUri, time(), $addressBookId));
+
+        $stmt2 = $this->pdo->prepare('UPDATE `' . $this->addressBooksTableName . '` SET ctag = ctag + 1 WHERE id = ?');
+        $stmt2->execute(array($addressBookId));
+
+        return $result;
+
+    }
+
+    /**
+     * Updates a card
+     * 
+     * @param mixed $addressBookId 
+     * @param string $cardUri 
+     * @param string $cardData 
+     * @return bool 
+     */
+    public function updateCard($addressBookId, $cardUri, $cardData) {
+
+        $stmt = $this->pdo->prepare('UPDATE `' . $this->cardsTableName . '` SET carddata = ?, lastmodified = ? WHERE uri = ? AND addressbookid =?');
+        $result = $stmt->execute(array($cardData, time(), $cardUri, $addressBookId));
+
+        $stmt2 = $this->pdo->prepare('UPDATE `' . $this->addressBooksTableName . '` SET ctag = ctag + 1 WHERE id = ?');
+        $stmt2->execute(array($addressBookId));
+
+        return $stmt->rowCount()===1;
+
+    }
+
+    /**
+     * Deletes a card
+     * 
+     * @param mixed $addressBookId 
+     * @param string $cardUri 
+     * @return bool 
+     */
+    public function deleteCard($addressBookId, $cardUri) {
+
+        $stmt = $this->pdo->prepare('DELETE FROM `' . $this->cardsTableName . '` WHERE addressbookid = ? AND uri = ?');
+        $stmt->execute(array($addressBookId, $cardUri));
+
+        $stmt2 = $this->pdo->prepare('UPDATE `' . $this->addressBooksTableName . '` SET ctag = ctag + 1 WHERE id = ?');
+        $stmt2->execute(array($addressBookId));
+
+        return $stmt->rowCount()===1;
+
+    }
+}
diff --git a/lib/Sabre/CardDAV/Card.php b/lib/Sabre/CardDAV/Card.php
new file mode 100755 (executable)
index 0000000..b92469a
--- /dev/null
@@ -0,0 +1,220 @@
+<?php
+
+/**
+ * The Card object represents a single Card from an addressbook
+ * 
+ * @package Sabre
+ * @subpackage CardDAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_CardDAV_Card extends Sabre_DAV_File implements Sabre_CardDAV_ICard, Sabre_DAVACL_IACL {
+
+    /**
+     * CardDAV backend
+     * 
+     * @var Sabre_CardDAV_Backend_Abstract 
+     */
+    private $carddavBackend;
+
+    /**
+     * Array with information about this Card
+     * 
+     * @var array 
+     */
+    private $cardData;
+
+    /**
+     * Array with information about the containing addressbook 
+     * 
+     * @var array 
+     */
+    private $addressBookInfo;
+
+    /**
+     * Constructor 
+     * 
+     * @param Sabre_CardDAV_Backend_Abstract $carddavBackend 
+     * @param array $addressBookInfo
+     * @param array $cardData
+     */
+    public function __construct(Sabre_CardDAV_Backend_Abstract $carddavBackend,array $addressBookInfo,array $cardData) {
+
+        $this->carddavBackend = $carddavBackend;
+        $this->addressBookInfo = $addressBookInfo;
+        $this->cardData = $cardData;
+
+    }
+
+    /**
+     * Returns the uri for this object 
+     * 
+     * @return string 
+     */
+    public function getName() {
+
+        return $this->cardData['uri'];
+
+    }
+
+    /**
+     * Returns the VCard-formatted object 
+     * 
+     * @return string 
+     */
+    public function get() {
+
+        $cardData = $this->cardData['carddata'];
+        $s = fopen('php://temp','r+');
+        fwrite($s, $cardData);
+        rewind($s);
+        return $s;
+
+    }
+
+    /**
+     * Updates the VCard-formatted object 
+     * 
+     * @param string $cardData 
+     * @return void 
+     */
+    public function put($cardData) {
+
+        if (is_resource($cardData))
+            $cardData = stream_get_contents($cardData);
+
+        // Converting to UTF-8, if needed
+        $cardData = Sabre_DAV_StringUtil::ensureUTF8($cardData);
+
+        $this->carddavBackend->updateCard($this->addressBookInfo['id'],$this->cardData['uri'],$cardData);
+        $this->cardData['carddata'] = $cardData;
+
+    }
+
+    /**
+     * Deletes the card
+     * 
+     * @return void
+     */
+    public function delete() {
+
+        $this->carddavBackend->deleteCard($this->addressBookInfo['id'],$this->cardData['uri']);
+
+    }
+
+    /**
+     * Returns the mime content-type 
+     * 
+     * @return string 
+     */
+    public function getContentType() {
+
+        return 'text/x-vcard';
+
+    }
+
+    /**
+     * Returns an ETag for this object 
+     * 
+     * @return string 
+     */
+    public function getETag() {
+
+        return '"' . md5($this->cardData['carddata']) . '"';
+
+    }
+
+    /**
+     * Returns the last modification date as a unix timestamp
+     * 
+     * @return time 
+     */
+    public function getLastModified() {
+
+        return isset($this->cardData['lastmodified'])?$this->cardData['lastmodified']:null;
+
+    }
+
+    /**
+     * Returns the size of this object in bytes 
+     * 
+     * @return int
+     */
+    public function getSize() {
+
+        return strlen($this->cardData['carddata']);
+
+    }
+
+    /**
+     * Returns the owner principal
+     *
+     * This must be a url to a principal, or null if there's no owner 
+     * 
+     * @return string|null
+     */
+    public function getOwner() {
+
+        return $this->addressBookInfo['principaluri'];
+
+    }
+
+    /**
+     * Returns a group principal
+     *
+     * This must be a url to a principal, or null if there's no owner
+     * 
+     * @return string|null 
+     */
+    public function getGroup() {
+
+        return null;
+
+    }
+
+    /**
+     * Returns a list of ACE's for this node.
+     *
+     * Each ACE has the following properties:
+     *   * 'privilege', a string such as {DAV:}read or {DAV:}write. These are 
+     *     currently the only supported privileges
+     *   * 'principal', a url to the principal who owns the node
+     *   * 'protected' (optional), indicating that this ACE is not allowed to 
+     *      be updated. 
+     * 
+     * @return array 
+     */
+    public function getACL() {
+
+        return array(
+            array(
+                'privilege' => '{DAV:}read',
+                'principal' => $this->addressBookInfo['principaluri'],
+                'protected' => true,
+            ),
+            array(
+                'privilege' => '{DAV:}write',
+                'principal' => $this->addressBookInfo['principaluri'],
+                'protected' => true,
+            ),
+        );
+
+    }
+
+    /**
+     * Updates the ACL
+     *
+     * This method will receive a list of new ACE's. 
+     * 
+     * @param array $acl 
+     * @return void
+     */
+    public function setACL(array $acl) {
+
+        throw new Sabre_DAV_Exception_MethodNotAllowed('Changing ACL is not yet supported');
+
+    }
+
+}
+
diff --git a/lib/Sabre/CardDAV/IAddressBook.php b/lib/Sabre/CardDAV/IAddressBook.php
new file mode 100755 (executable)
index 0000000..977c016
--- /dev/null
@@ -0,0 +1,18 @@
+<?php
+
+/**
+ * AddressBook interface
+ *
+ * Implement this interface to allow a node to be recognized as an addressbook.
+ * 
+ * @package Sabre
+ * @subpackage CardDAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+interface Sabre_CardDAV_IAddressBook extends Sabre_DAV_ICollection {
+
+
+}
diff --git a/lib/Sabre/CardDAV/ICard.php b/lib/Sabre/CardDAV/ICard.php
new file mode 100755 (executable)
index 0000000..f98ca1f
--- /dev/null
@@ -0,0 +1,18 @@
+<?php
+
+/**
+ * Card interface 
+ *
+ * Extend the ICard interface to allow your custom nodes to be picked up as 
+ * 'Cards'. 
+ * 
+ * @package Sabre
+ * @subpackage CardDAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+interface Sabre_CardDAV_ICard extends Sabre_DAV_IFile { 
+
+}
+
diff --git a/lib/Sabre/CardDAV/IDirectory.php b/lib/Sabre/CardDAV/IDirectory.php
new file mode 100755 (executable)
index 0000000..6c89855
--- /dev/null
@@ -0,0 +1,21 @@
+<?php
+
+/**
+ * IDirectory interface
+ *
+ * Implement this interface to have an addressbook marked as a 'directory'. A 
+ * directory is an (often) global addressbook.
+ *
+ * A full description can be found in the IETF draft:
+ *   - draft-daboo-carddav-directory-gateway
+ *
+ * @package Sabre
+ * @subpackage CardDAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+interface Sabre_CardDAV_IDirectory extends Sabre_CardDAV_IAddressBook {
+
+
+}
diff --git a/lib/Sabre/CardDAV/Plugin.php b/lib/Sabre/CardDAV/Plugin.php
new file mode 100755 (executable)
index 0000000..2738b5b
--- /dev/null
@@ -0,0 +1,463 @@
+<?php
+
+/**
+ * CardDAV plugin
+ *
+ * The CardDAV plugin adds CardDAV functionality to the WebDAV server
+ *
+ * @package Sabre
+ * @subpackage CardDAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_CardDAV_Plugin extends Sabre_DAV_ServerPlugin {
+
+    /**
+     * Url to the addressbooks
+     */
+    const ADDRESSBOOK_ROOT = 'addressbooks';
+
+    /**
+     * xml namespace for CardDAV elements
+     */
+    const NS_CARDDAV = 'urn:ietf:params:xml:ns:carddav';
+
+    /**
+     * Add urls to this property to have them automatically exposed as 
+     * 'directories' to the user.
+     * 
+     * @var array
+     */
+    public $directories = array();
+
+    /**
+     * Server class 
+     *
+     * @var Sabre_DAV_Server 
+     */
+    protected $server;
+
+    /**
+     * Initializes the plugin 
+     *
+     * @param Sabre_DAV_Server $server 
+     * @return void 
+     */
+    public function initialize(Sabre_DAV_Server $server) {
+
+        /* Events */
+        $server->subscribeEvent('beforeGetProperties', array($this, 'beforeGetProperties'));
+        $server->subscribeEvent('report', array($this,'report'));
+
+        /* Namespaces */
+        $server->xmlNamespaces[self::NS_CARDDAV] = 'card';
+
+        /* Mapping Interfaces to {DAV:}resourcetype values */
+        $server->resourceTypeMapping['Sabre_CardDAV_IAddressBook'] = '{' . self::NS_CARDDAV . '}addressbook';
+        $server->resourceTypeMapping['Sabre_CardDAV_IDirectory'] = '{' . self::NS_CARDDAV . '}directory';
+        
+        /* Adding properties that may never be changed */
+        $server->protectedProperties[] = '{' . self::NS_CARDDAV . '}supported-address-data';
+        $server->protectedProperties[] = '{' . self::NS_CARDDAV . '}max-resource-size';
+
+
+        $this->server = $server;
+
+    }
+
+    /**
+     * Returns a list of supported features.
+     *
+     * This is used in the DAV: header in the OPTIONS and PROPFIND requests. 
+     *
+     * @return array
+     */
+    public function getFeatures() {
+
+        return array('addressbook');
+
+    }
+
+    /**
+     * Returns a list of reports this plugin supports.
+     *
+     * This will be used in the {DAV:}supported-report-set property.
+     * Note that you still need to subscribe to the 'report' event to actually 
+     * implement them 
+     *
+     * @param string $uri
+     * @return array 
+     */
+    public function getSupportedReportSet($uri) {
+
+        $node = $this->server->tree->getNodeForPath($uri);
+        if ($node instanceof Sabre_CardDAV_IAddressBook || $node instanceof Sabre_CardDAV_ICard) {
+            return array(
+                 '{' . self::NS_CARDDAV . '}addressbook-multiget',
+                 '{' . self::NS_CARDDAV . '}addressbook-query',
+            );
+        }
+        return array();
+
+    }
+
+
+    /**
+     * Adds all CardDAV-specific properties 
+     *
+     * @param string $path
+     * @param Sabre_DAV_INode $node 
+     * @param array $requestedProperties
+     * @param array $returnedProperties 
+     * @return void
+     */
+    public function beforeGetProperties($path, Sabre_DAV_INode $node, array &$requestedProperties, array &$returnedProperties) { 
+
+        if ($node instanceof Sabre_DAVACL_IPrincipal) {
+
+            // calendar-home-set property
+            $addHome = '{' . self::NS_CARDDAV . '}addressbook-home-set';
+            if (in_array($addHome,$requestedProperties)) {
+                $principalId = $node->getName(); 
+                $addressbookHomePath = self::ADDRESSBOOK_ROOT . '/' . $principalId . '/';
+                unset($requestedProperties[array_search($addHome, $requestedProperties)]);
+                $returnedProperties[200][$addHome] = new Sabre_DAV_Property_Href($addressbookHomePath);
+            }
+
+            $directories = '{' . self::NS_CARDDAV . '}directory-gateway';
+            if ($this->directories && in_array($directories, $requestedProperties)) {
+                unset($requestedProperties[array_search($directories, $requestedProperties)]);
+                $returnedProperties[200][$directories] = new Sabre_DAV_Property_HrefList($this->directories);
+            }
+
+        }
+
+        if ($node instanceof Sabre_CardDAV_ICard) {
+
+            // The address-data property is not supposed to be a 'real' 
+            // property, but in large chunks of the spec it does act as such. 
+            // Therefore we simply expose it as a property.
+            $addressDataProp = '{' . self::NS_CARDDAV . '}address-data';
+            if (in_array($addressDataProp, $requestedProperties)) {
+                unset($requestedProperties[$addressDataProp]);
+                $val = $node->get();
+                if (is_resource($val))
+                    $val = stream_get_contents($val);
+
+                // Taking out \r to not screw up the xml output
+                $returnedProperties[200][$addressDataProp] = str_replace("\r","", $val);
+
+            }
+        }
+
+    }
+
+    /**
+     * This functions handles REPORT requests specific to CardDAV 
+     *
+     * @param string $reportName 
+     * @param DOMNode $dom
+     * @return bool 
+     */
+    public function report($reportName,$dom) {
+
+        switch($reportName) { 
+            case '{'.self::NS_CARDDAV.'}addressbook-multiget' :
+                $this->addressbookMultiGetReport($dom);
+                return false;
+            case '{'.self::NS_CARDDAV.'}addressbook-query' :
+                $this->addressBookQueryReport($dom);
+                return false; 
+            default :
+                return;
+
+        }
+
+
+    }
+
+    /**
+     * This function handles the addressbook-multiget REPORT.
+     *
+     * This report is used by the client to fetch the content of a series
+     * of urls. Effectively avoiding a lot of redundant requests.
+     *
+     * @param DOMNode $dom
+     * @return void
+     */
+    public function addressbookMultiGetReport($dom) {
+
+        $properties = array_keys(Sabre_DAV_XMLUtil::parseProperties($dom->firstChild));
+
+        $hrefElems = $dom->getElementsByTagNameNS('urn:DAV','href');
+        $propertyList = array();
+
+        foreach($hrefElems as $elem) {
+
+            $uri = $this->server->calculateUri($elem->nodeValue);
+            list($propertyList[]) = $this->server->getPropertiesForPath($uri,$properties);
+
+        }
+
+        $this->server->httpResponse->sendStatus(207);
+        $this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8');
+        $this->server->httpResponse->sendBody($this->server->generateMultiStatus($propertyList));
+
+    }
+
+    /**
+     * This function handles the addressbook-query REPORT
+     *
+     * This report is used by the client to filter an addressbook based on a
+     * complex query.
+     *
+     * @param DOMNode $dom
+     * @return void
+     */
+    protected function addressbookQueryReport($dom) {
+
+        $query = new Sabre_CardDAV_AddressBookQueryParser($dom);
+        $query->parse();
+
+        $depth = $this->server->getHTTPDepth(0);
+
+        if ($depth==0) {
+            $candidateNodes = array(
+                $this->server->tree->getNodeForPath($this->server->getRequestUri())
+            );
+        } else {
+            $candidateNodes = $this->server->tree->getChildren($this->server->getRequestUri());
+        }
+
+        $validNodes = array();
+        foreach($candidateNodes as $node) {
+
+            if (!$node instanceof Sabre_CardDAV_ICard)
+                continue;
+
+            $blob = $node->get();
+            if (is_resource($blob)) {
+                $blob = stream_get_contents($blob);
+            }
+
+            if (!$this->validateFilters($blob, $query->filters, $query->test)) {
+                continue;
+            }
+
+            $validNodes[] = $node;
+
+            if ($query->limit && $query->limit <= count($validNodes)) {
+                // We hit the maximum number of items, we can stop now.
+                break;
+            }
+
+        }
+
+        $result = array();
+        foreach($validNodes as $validNode) {
+            if ($depth==0) { 
+                $href = $this->server->getRequestUri();
+            } else {
+                $href = $this->server->getRequestUri() . '/' . $validNode->getName();
+            }
+
+            list($result[]) = $this->server->getPropertiesForPath($href, $query->requestedProperties, 0);
+
+        }
+        $this->server->httpResponse->sendStatus(207);
+        $this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8');
+        $this->server->httpResponse->sendBody($this->server->generateMultiStatus($result));
+
+    }
+
+    /**
+     * Validates if a vcard makes it throught a list of filters.
+     * 
+     * @param string $vcardData 
+     * @param array $filters 
+     * @param string $test anyof or allof (which means OR or AND) 
+     * @return bool 
+     */
+    public function validateFilters($vcardData, array $filters, $test) {
+
+        $vcard = Sabre_VObject_Reader::read($vcardData);
+        
+        $success = true;
+
+        foreach($filters as $filter) {
+
+            $isDefined = isset($vcard->{$filter['name']});
+            if ($filter['is-not-defined']) {
+                if ($isDefined) {
+                    $success = false;
+                } else {
+                    $success = true;
+                }
+            } elseif ((!$filter['param-filters'] && !$filter['text-matches']) || !$isDefined) {
+
+                // We only need to check for existence
+                $success = $isDefined;
+                
+            } else {
+
+                $vProperties = $vcard->select($filter['name']); 
+
+                $results = array();
+                if ($filter['param-filters']) {
+                    $results[] = $this->validateParamFilters($vProperties, $filter['param-filters'], $filter['test']);
+                }
+                if ($filter['text-matches']) {
+                    $texts = array();
+                    foreach($vProperties as $vProperty)
+                        $texts[] = $vProperty->value;
+
+                    $results[] = $this->validateTextMatches($texts, $filter['text-matches'], $filter['test']);
+                }
+
+                if (count($results)===1) {
+                    $success = $results[0];
+                } else {
+                    if ($filter['test'] === 'anyof') {
+                        $success = $results[0] || $results[1];
+                    } else {
+                        $success = $results[0] && $results[1];
+                    }
+                }
+
+            } // else
+
+            // There are two conditions where we can already determine wether 
+            // or not this filter succeeds.
+            if ($test==='anyof' && $success) {
+                return true;
+            }
+            if ($test==='allof' && !$success) {
+                return false;
+            }
+
+        } // foreach
+
+        // If we got all the way here, it means we haven't been able to 
+        // determine early if the test failed or not.
+        //
+        // This implies for 'anyof' that the test failed, and for 'allof' that 
+        // we succeeded. Sounds weird, but makes sense.
+        return $test==='allof';
+
+    }
+
+    /**
+     * Validates if a param-filter can be applied to a specific property. 
+     * 
+     * @todo currently we're only validating the first parameter of the passed 
+     *       property. Any subsequence parameters with the same name are
+     *       ignored.
+     * @param Sabre_VObject_Property $vProperty 
+     * @param array $filters 
+     * @param string $test 
+     * @return bool 
+     */
+    protected function validateParamFilters(array $vProperties, array $filters, $test) {
+
+        $success = false;
+        foreach($filters as $filter) {
+
+            $isDefined = false;
+            foreach($vProperties as $vProperty) {
+                $isDefined = isset($vProperty[$filter['name']]);
+                if ($isDefined) break;
+            }
+
+            if ($filter['is-not-defined']) {
+                if ($isDefined) {
+                    $success = false;
+                } else {
+                    $success = true;
+                }
+
+            // If there's no text-match, we can just check for existence 
+            } elseif (!$filter['text-match'] || !$isDefined) {
+
+                $success = $isDefined;
+                
+            } else {
+
+                $texts = array();
+                $success = false;
+                foreach($vProperties as $vProperty) {
+                    // If we got all the way here, we'll need to validate the 
+                    // text-match filter.
+                    $success = Sabre_DAV_StringUtil::textMatch($vProperty[$filter['name']]->value, $filter['text-match']['value'], $filter['text-match']['collation'], $filter['text-match']['match-type']);
+                    if ($success) break;
+                }
+                if ($filter['text-match']['negate-condition']) {
+                    $success = !$success;
+                }
+
+            } // else
+
+            // There are two conditions where we can already determine wether 
+            // or not this filter succeeds.
+            if ($test==='anyof' && $success) {
+                return true;
+            }
+            if ($test==='allof' && !$success) {
+                return false;
+            }
+
+        } 
+
+        // If we got all the way here, it means we haven't been able to 
+        // determine early if the test failed or not.
+        //
+        // This implies for 'anyof' that the test failed, and for 'allof' that 
+        // we succeeded. Sounds weird, but makes sense.
+        return $test==='allof';
+
+    }
+
+    /**
+     * Validates if a text-filter can be applied to a specific property. 
+     * 
+     * @param array $texts
+     * @param array $filters 
+     * @param string $test 
+     * @return bool 
+     */
+    protected function validateTextMatches(array $texts, array $filters, $test) {
+
+        foreach($filters as $filter) {
+
+            $success = false;
+            foreach($texts as $haystack) {
+                $success = Sabre_DAV_StringUtil::textMatch($haystack, $filter['value'], $filter['collation'], $filter['match-type']);
+
+                // Breaking on the first match
+                if ($success) break;
+            }
+            if ($filter['negate-condition']) {
+                $success = !$success;
+            }
+            
+            if ($success && $test==='anyof')
+                return true;
+
+            if (!$success && $test=='allof')
+                return false;
+
+
+        }
+
+        // If we got all the way here, it means we haven't been able to 
+        // determine early if the test failed or not.
+        //
+        // This implies for 'anyof' that the test failed, and for 'allof' that 
+        // we succeeded. Sounds weird, but makes sense.
+        return $test==='allof';
+
+    }
+
+
+}
diff --git a/lib/Sabre/CardDAV/Property/SupportedAddressData.php b/lib/Sabre/CardDAV/Property/SupportedAddressData.php
new file mode 100755 (executable)
index 0000000..7853233
--- /dev/null
@@ -0,0 +1,69 @@
+<?php
+
+/**
+ * Supported-address-data property
+ *
+ * This property is a representation of the supported-address-data property
+ * in the CardDAV namespace. 
+ *
+ * @package Sabre
+ * @subpackage CardDAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_CardDAV_Property_SupportedAddressData extends Sabre_DAV_Property {
+
+    /**
+     * supported versions
+     * 
+     * @var array 
+     */
+    protected $supportedData = array();
+    
+    /**
+     * Creates the property 
+     * 
+     * @param array $components 
+     */
+    public function __construct(array $supportedData = null) {
+
+        if (is_null($supportedData)) {
+            $supportedData = array(
+                array('contentType' => 'text/vcard', 'version' => '3.0'),
+                array('contentType' => 'text/vcard', 'version' => '4.0'),
+            );
+        }
+
+       $this->supportedData = $supportedData; 
+
+    }
+    
+    /**
+     * Serializes the property in a DOMDocument 
+     * 
+     * @param Sabre_DAV_Server $server 
+     * @param DOMElement $node 
+     * @return void
+     */
+    public function serialize(Sabre_DAV_Server $server,DOMElement $node) {
+
+        $doc = $node->ownerDocument;
+
+        $prefix = 
+            isset($server->xmlNamespaces[Sabre_CardDAV_Plugin::NS_CARDDAV]) ?
+            $server->xmlNamespaces[Sabre_CardDAV_Plugin::NS_CARDDAV] :
+            'card';
+
+        foreach($this->supportedData as $supported) {
+
+            $caldata = $doc->createElementNS(Sabre_CardDAV_Plugin::NS_CARDDAV, $prefix . ':address-data-type');
+            $caldata->setAttribute('content-type',$supported['contentType']);
+            $caldata->setAttribute('version',$supported['version']);
+            $node->appendChild($caldata);
+
+        }
+
+    }
+
+}
diff --git a/lib/Sabre/CardDAV/UserAddressBooks.php b/lib/Sabre/CardDAV/UserAddressBooks.php
new file mode 100755 (executable)
index 0000000..5bdf58b
--- /dev/null
@@ -0,0 +1,240 @@
+<?php
+
+/**
+ * UserAddressBooks class
+ *
+ * The UserAddressBooks collection contains a list of addressbooks associated with a user
+ *
+ * @package Sabre
+ * @subpackage CardDAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_CardDAV_UserAddressBooks extends Sabre_DAV_Collection implements Sabre_DAV_IExtendedCollection, Sabre_DAVACL_IACL {
+
+    /**
+     * Principal uri
+     * 
+     * @var array 
+     */
+    protected $principalUri;
+
+    /**
+     * carddavBackend 
+     * 
+     * @var Sabre_CardDAV_Backend_Abstract 
+     */
+    protected $carddavBackend;
+
+    /**
+     * Constructor 
+     * 
+     * @param Sabre_CardDAV_Backend_Abstract $carddavBackend 
+     * @param string $principalUri 
+     */
+    public function __construct(Sabre_CardDAV_Backend_Abstract $carddavBackend, $principalUri) {
+
+        $this->carddavBackend = $carddavBackend;
+        $this->principalUri = $principalUri;
+       
+    }
+
+    /**
+     * Returns the name of this object 
+     * 
+     * @return string
+     */
+    public function getName() {
+      
+        list(,$name) = Sabre_DAV_URLUtil::splitPath($this->principalUri);
+        return $name; 
+
+    }
+
+    /**
+     * Updates the name of this object 
+     * 
+     * @param string $name 
+     * @return void
+     */
+    public function setName($name) {
+
+        throw new Sabre_DAV_Exception_MethodNotAllowed();
+
+    }
+
+    /**
+     * Deletes this object 
+     * 
+     * @return void
+     */
+    public function delete() {
+
+        throw new Sabre_DAV_Exception_MethodNotAllowed();
+
+    }
+
+    /**
+     * Returns the last modification date 
+     * 
+     * @return int 
+     */
+    public function getLastModified() {
+
+        return null; 
+
+    }
+
+    /**
+     * Creates a new file under this object.
+     *
+     * This is currently not allowed
+     * 
+     * @param string $filename 
+     * @param resource $data 
+     * @return void
+     */
+    public function createFile($filename, $data=null) {
+
+        throw new Sabre_DAV_Exception_MethodNotAllowed('Creating new files in this collection is not supported');
+
+    }
+
+    /**
+     * Creates a new directory under this object.
+     *
+     * This is currently not allowed.
+     * 
+     * @param string $filename 
+     * @return void
+     */
+    public function createDirectory($filename) {
+
+        throw new Sabre_DAV_Exception_MethodNotAllowed('Creating new collections in this collection is not supported');
+
+    }
+
+    /**
+     * Returns a single calendar, by name 
+     * 
+     * @param string $name
+     * @todo needs optimizing
+     * @return Sabre_CardDAV_AddressBook
+     */
+    public function getChild($name) {
+
+        foreach($this->getChildren() as $child) {
+            if ($name==$child->getName())
+                return $child;
+
+        }
+        throw new Sabre_DAV_Exception_FileNotFound('Addressbook with name \'' . $name . '\' could not be found');
+
+    }
+
+    /**
+     * Returns a list of addressbooks 
+     * 
+     * @return array 
+     */
+    public function getChildren() {
+
+        $addressbooks = $this->carddavBackend->getAddressbooksForUser($this->principalUri);
+        $objs = array();
+        foreach($addressbooks as $addressbook) {
+            $objs[] = new Sabre_CardDAV_AddressBook($this->carddavBackend, $addressbook);
+        }
+        return $objs;
+
+    }
+
+    /**
+     * Creates a new addressbook 
+     * 
+     * @param string $name
+     * @param array $resourceType 
+     * @param array $properties 
+     * @return void
+     */
+    public function createExtendedCollection($name, array $resourceType, array $properties) {
+
+        if (!in_array('{'.Sabre_CardDAV_Plugin::NS_CARDDAV.'}addressbook',$resourceType) || count($resourceType)!==2) {
+            throw new Sabre_DAV_Exception_InvalidResourceType('Unknown resourceType for this collection');
+        }
+        $this->carddavBackend->createAddressBook($this->principalUri, $name, $properties);
+
+    }
+
+    /**
+     * Returns the owner principal
+     *
+     * This must be a url to a principal, or null if there's no owner 
+     * 
+     * @return string|null
+     */
+    public function getOwner() {
+
+        return $this->principalUri;
+
+    }
+
+    /**
+     * Returns a group principal
+     *
+     * This must be a url to a principal, or null if there's no owner
+     * 
+     * @return string|null 
+     */
+    public function getGroup() {
+
+        return null;
+
+    }
+
+    /**
+     * Returns a list of ACE's for this node.
+     *
+     * Each ACE has the following properties:
+     *   * 'privilege', a string such as {DAV:}read or {DAV:}write. These are 
+     *     currently the only supported privileges
+     *   * 'principal', a url to the principal who owns the node
+     *   * 'protected' (optional), indicating that this ACE is not allowed to 
+     *      be updated. 
+     * 
+     * @return array 
+     */
+    public function getACL() {
+
+        return array(
+            array(
+                'privilege' => '{DAV:}read',
+                'principal' => $this->principalUri,
+                'protected' => true,
+            ),
+            array(
+                'privilege' => '{DAV:}write',
+                'principal' => $this->principalUri,
+                'protected' => true,
+            ),
+
+        );
+
+    }
+
+    /**
+     * Updates the ACL
+     *
+     * This method will receive a list of new ACE's. 
+     * 
+     * @param array $acl 
+     * @return void
+     */
+    public function setACL(array $acl) {
+
+        throw new Sabre_DAV_Exception_MethodNotAllowed('Changing ACL is not yet supported');
+
+    }
+
+
+}
diff --git a/lib/Sabre/CardDAV/Version.php b/lib/Sabre/CardDAV/Version.php
new file mode 100755 (executable)
index 0000000..2e1362b
--- /dev/null
@@ -0,0 +1,26 @@
+<?php
+
+/**
+ * Version Class
+ *
+ * This class contains the Sabre_CardDAV version information
+ * 
+ * @package Sabre
+ * @subpackage CardDAV 
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_CardDAV_Version {
+
+    /**
+     * Full version number
+     */
+    const VERSION = '1.5.3';
+
+    /**
+     * Stability : alpha, beta, stable
+     */
+    const STABILITY = 'stable';
+
+}
index e81ca00..e6e5b28 100755 (executable)
@@ -4,43 +4,40 @@
  *
  * This class can be used by authentication objects wishing to use HTTP Basic
  * Most of the digest logic is handled, implementors just need to worry about
- * the authenticateInternal and getUserInfo methods
+ * the validateUserPass method.
  *
  * @package Sabre
  * @subpackage DAV
- * @copyright Copyright (C) 2007-2010 Rooftop Solutions. All rights reserved.
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
  * @author James David Low (http://jameslow.com/)
  * @author Evert Pot (http://www.rooftopsolutions.nl/) 
  * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
  */
-abstract class Sabre_DAV_Auth_Backend_AbstractBasic extends Sabre_DAV_Auth_Backend_Abstract {
+abstract class Sabre_DAV_Auth_Backend_AbstractBasic implements Sabre_DAV_Auth_IBackend {
 
     /**
-     * This variable holds information about the currently
-     * logged in user.
+     * This variable holds the currently logged in username.
      *
-     * @var array|null
+     * @var string|null
      */
     protected $currentUser;
 
     /**
      * Validates a username and password
      *
-     * If the username and password were correct, this method must return
-     * an array with at least a 'uri' key.  
+     * This method should return true or false depending on if login
+     * succeeded.
      *
-     * If the credentials are incorrect, this method must return false.
-     *
-     * @return bool|array
+     * @return bool
      */
     abstract protected function validateUserPass($username, $password);
 
     /**
-     * Returns information about the currently logged in user.
+     * Returns information about the currently logged in username.
      *
      * If nobody is currently logged in, this method should return null.
      *
-     * @return array|null
+     * @return string|null
      */
     public function getCurrentUser() {
         return $this->currentUser;
@@ -76,7 +73,7 @@ abstract class Sabre_DAV_Auth_Backend_AbstractBasic extends Sabre_DAV_Auth_Backe
         if (!isset($userData['uri'])) {
             throw new Sabre_DAV_Exception('The returned array from validateUserPass must contain at a uri element');
         }
-        $this->currentUser = $userData;
+        $this->currentUser = $userpass[0];
         return true;
     }
 
index d8cdcb1..c5d20fa 100755 (executable)
@@ -5,38 +5,33 @@
  *
  * This class can be used by authentication objects wishing to use HTTP Digest
  * Most of the digest logic is handled, implementors just need to worry about 
- * the getUserInfo method 
+ * the getDigestHash method 
  *
  * @package Sabre
  * @subpackage DAV
- * @copyright Copyright (C) 2007-2010 Rooftop Solutions. All rights reserved.
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
  * @author Evert Pot (http://www.rooftopsolutions.nl/) 
  * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
  */
-abstract class Sabre_DAV_Auth_Backend_AbstractDigest extends Sabre_DAV_Auth_Backend_Abstract {
+abstract class Sabre_DAV_Auth_Backend_AbstractDigest implements Sabre_DAV_Auth_IBackend {
 
     /**
-     * This variable holds information about the currently
-     * logged in user.
+     * This variable holds the currently logged in username.
      * 
      * @var array|null
      */
     protected $currentUser;
 
     /**
-     * Returns a users information based on its username
+     * Returns a users digest hash based on the username and realm.
      *
-     * The returned struct must contain at least a uri 
-     * element (which can be identical to username) as well as a digestHash
-     * element.
-     *
-     * If the user was not known, false must be returned. 
+     * If the user was not known, null must be returned. 
      * 
      * @param string $realm
      * @param string $username 
-     * @return array 
+     * @return string|null 
      */
-    abstract public function getUserInfo($realm, $username);
+    abstract public function getDigestHash($realm,$username);
 
     /**
      * Authenticates the user based on the current request.
@@ -66,35 +61,31 @@ abstract class Sabre_DAV_Auth_Backend_AbstractDigest extends Sabre_DAV_Auth_Back
             throw new Sabre_DAV_Exception_NotAuthenticated('No digest authentication headers were found');
         }
 
-        $userData = $this->getUserInfo($realm, $username);
+        $hash = $this->getDigestHash($realm, $username);
         // If this was false, the user account didn't exist
-        if ($userData===false) {
+        if ($hash===false || is_null($hash)) {
             $digest->requireLogin();
             throw new Sabre_DAV_Exception_NotAuthenticated('The supplied username was not on file');
         }
-        if (!is_array($userData)) {
-            throw new Sabre_DAV_Exception('The returntype for getUserInfo must be either false or an array');
-        }
-
-        if (!isset($userData['uri']) || !isset($userData['digestHash'])) {
-            throw new Sabre_DAV_Exception('The returned array from getUserInfo must contain at least a uri and digestHash element');
+        if (!is_string($hash)) {
+            throw new Sabre_DAV_Exception('The returned value from getDigestHash must be a string or null');
         }
 
         // If this was false, the password or part of the hash was incorrect.
-        if (!$digest->validateA1($userData['digestHash'])) {
+        if (!$digest->validateA1($hash)) {
             $digest->requireLogin();
             throw new Sabre_DAV_Exception_NotAuthenticated('Incorrect username');
         }
 
-        $this->currentUser = $userData;
+        $this->currentUser = $username;
         return true;
 
     }
 
     /**
-     * Returns information about the currently logged in user
+     * Returns the currently logged in username
      * 
-     * @return array|null 
+     * @return string|null 
      */
     public function getCurrentUser() {
 
index 0b46270..a008e5d 100755 (executable)
@@ -3,18 +3,18 @@
 /**
  * Apache authenticator
  *
- * This authentication backend assumes that auhtentication has been
+ * This authentication backend assumes that authentication has been
  * conifgured in apache, rather than within SabreDAV.
  *
  * Make sure apache is properly configured for this to work.
  *
  * @package Sabre
  * @subpackage DAV
- * @copyright Copyright (C) 2007-2010 Rooftop Solutions. All rights reserved.
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
  * @author Evert Pot (http://www.rooftopsolutions.nl/) 
  * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
  */
-class Sabre_DAV_Auth_Backend_Apache extends Sabre_DAV_Auth_Backend_Abstract {
+class Sabre_DAV_Auth_Backend_Apache implements Sabre_DAV_Auth_IBackend {
 
     /**
      * Current apache user 
@@ -52,24 +52,7 @@ class Sabre_DAV_Auth_Backend_Apache extends Sabre_DAV_Auth_Backend_Abstract {
      */
     public function getCurrentUser() {
 
-        return array(
-            'uri' => 'principals/' . $this->remoteUser,
-        );
-
-    }
-
-    /**
-     * Returns the full list of users.
-     *
-     * This method must at least return a uri for each user.
-     *
-     * It is optional to implement this.
-     * 
-     * @return array 
-     */
-    public function getUsers() {
-
-        return array($this->getCurrentUser());
+        return $this->remoteUser;
 
     }
 
index 953c49e..3eb740c 100755 (executable)
@@ -7,7 +7,7 @@
  * 
  * @package Sabre
  * @subpackage DAV
- * @copyright Copyright (C) 2007-2010 Rooftop Solutions. All rights reserved.
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
  * @author Evert Pot (http://www.rooftopsolutions.nl/) 
  * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
  */
@@ -54,10 +54,7 @@ class Sabre_DAV_Auth_Backend_File extends Sabre_DAV_Auth_Backend_AbstractDigest
             if (!preg_match('/^[a-zA-Z0-9]{32}$/', $A1))
                 throw new Sabre_DAV_Exception('Malformed htdigest file. Invalid md5 hash');
                 
-            $this->users[$username] = array(
-                'digestHash' => $A1,
-                'uri'        => 'principals/' . $username
-            );
+            $this->users[$realm . ':' . $username] = $A1;
 
         }
 
@@ -70,32 +67,9 @@ class Sabre_DAV_Auth_Backend_File extends Sabre_DAV_Auth_Backend_AbstractDigest
      * @param string $username 
      * @return string 
      */
-    public function getUserInfo($realm, $username) {
+    public function getDigestHash($realm, $username) {
 
-        return isset($this->users[$username])?$this->users[$username]:false;
-
-    }
-
-
-    /**
-     * Returns the full list of users.
-     *
-     * This method must at least return a uri for each user.
-     * 
-     * @return array 
-     */
-    public function getUsers() {
-
-        $re = array();
-        foreach($this->users as $userName=>$A1) {
-
-            $re[] = array(
-                'uri'=>'principals/' . $userName
-            );
-
-        }
-
-        return $re;
+        return isset($this->users[$realm . ':' . $username])?$this->users[$realm . ':' . $username]:false;
 
     }
 
index c880858..7ac6f08 100755 (executable)
@@ -7,72 +7,59 @@
  * 
  * @package Sabre
  * @subpackage DAV
- * @copyright Copyright (C) 2007-2010 Rooftop Solutions. All rights reserved.
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
  * @author Evert Pot (http://www.rooftopsolutions.nl/) 
  * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
  */
 class Sabre_DAV_Auth_Backend_PDO extends Sabre_DAV_Auth_Backend_AbstractDigest {
 
-    private $pdo;
+    /**
+     * Reference to PDO connection 
+     * 
+     * @var PDO 
+     */
+    protected $pdo;
+
+    /**
+     * PDO table name we'll be using  
+     * 
+     * @var string
+     */
+    protected $tableName;
+
 
     /**
      * Creates the backend object. 
      *
      * If the filename argument is passed in, it will parse out the specified file fist.
      * 
-     * @param string $filename 
+     * @param string $filename
+     * @param string $tableName The PDO table name to use 
      * @return void
      */
-    public function __construct(PDO $pdo) {
+    public function __construct(PDO $pdo, $tableName = 'users') {
 
         $this->pdo = $pdo;
+        $this->tableName = $tableName;
 
     }
 
     /**
-     * Returns a users' information 
+     * Returns the digest hash for a user. 
      * 
      * @param string $realm 
      * @param string $username 
-     * @return string 
+     * @return string|null 
      */
-    public function getUserInfo($realm,$username) {
+    public function getDigestHash($realm,$username) {
 
-        $stmt = $this->pdo->prepare('SELECT username, digesta1, email FROM users WHERE username = ?');
+        $stmt = $this->pdo->prepare('SELECT username, digesta1 FROM `'.$this->tableName.'` WHERE username = ?');
         $stmt->execute(array($username));
         $result = $stmt->fetchAll();
 
-        if (!count($result)) return false;
-        $user = array(
-            'uri' => 'principals/' . $result[0]['username'],
-            'digestHash' => $result[0]['digesta1'],
-        );
-        if ($result[0]['email']) $user['{http://sabredav.org/ns}email-address'] = $result[0]['email'];
-        return $user;
-
-    }
-
-    /**
-     * Returns a list of all users
-     *
-     * @return array
-     */
-    public function getUsers() {
-
-        $result = $this->pdo->query('SELECT username, email FROM users')->fetchAll();
-        
-        $rv = array();
-        foreach($result as $user) {
-
-            $r = array(
-                'uri' => 'principals/' . $user['username'],
-            );
-            if ($user['email']) $r['{http://sabredav.org/ns}email-address'] = $user['email'];
-            $rv[] = $r;
-
-        }
+        if (!count($result)) return;
 
-        return $rv;
+        return $result[0]['digesta1'];
 
     }
 
index f1d07cc..d445659 100755 (executable)
@@ -7,8 +7,8 @@
  * 
  * @package Sabre
  * @subpackage DAV
- * @copyright Copyright (C) 2007-2010 Rooftop Solutions. All rights reserved.
- * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @copyright Copyright (C) 2012 Mario Matzulla. All rights reserved.
+ * @author Mario Matzulla (http://www.matzullas.de)
  * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
  */
 class Sabre_DAV_Auth_Backend_TYPO3 extends Sabre_DAV_Auth_Backend_AbstractBasic {
@@ -18,16 +18,26 @@ class Sabre_DAV_Auth_Backend_TYPO3 extends Sabre_DAV_Auth_Backend_AbstractBasic
     private $username;
 
        /**
+     * PDO table name we'll be using  
+     * 
+     * @var string
+     */
+    protected $tableName;
+
+
+    /**
      * Creates the backend object. 
      *
      * If the filename argument is passed in, it will parse out the specified file fist.
      * 
-     * @param string $filename 
+     * @param string $filename
+     * @param string $tableName The PDO table name to use 
      * @return void
      */
-    public function __construct(PDO $pdo) {
+    public function __construct(PDO $pdo, $tableName = 'users') {
 
         $this->pdo = $pdo;
+        $this->tableName = $tableName;
 
     }
     
@@ -148,7 +158,7 @@ class Sabre_DAV_Auth_Backend_TYPO3 extends Sabre_DAV_Auth_Backend_AbstractBasic
             $auth->requireLogin();
             throw new Sabre_DAV_Exception_NotAuthenticated('No basic authentication headers were found');
         }
-
+        
         // Authenticates the user
         if (!($userData = $this->validateUserPass($userpass[0],$userpass[1]))) {
             $auth->requireLogin();
@@ -157,7 +167,7 @@ class Sabre_DAV_Auth_Backend_TYPO3 extends Sabre_DAV_Auth_Backend_AbstractBasic
         if (!isset($userData['uri'])) {
             throw new Sabre_DAV_Exception('The returned array from validateUserPass must contain at a uri element');
         }
-        $this->currentUser = $userData;
+        $this->currentUser = $userpass[0];
         return true;
     }
 
diff --git a/lib/Sabre/DAV/Auth/IBackend.php b/lib/Sabre/DAV/Auth/IBackend.php
new file mode 100755 (executable)
index 0000000..4a83095
--- /dev/null
@@ -0,0 +1,34 @@
+<?php
+
+/**
+ * This is the base class for any authentication object.
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+interface Sabre_DAV_Auth_IBackend {
+
+    /**
+     * Authenticates the user based on the current request.
+     *
+     * If authentication is succesful, true must be returned.
+     * If authentication fails, an exception must be thrown.
+     *
+     * @return bool 
+     */
+    function authenticate(Sabre_DAV_Server $server,$realm); 
+
+    /**
+     * Returns information about the currently logged in username.
+     *
+     * If nobody is currently logged in, this method should return null.
+     * 
+     * @return string|null
+     */
+    function getCurrentUser();
+
+}
+
index cc4f25f..174448f 100755 (executable)
@@ -11,7 +11,7 @@
  * 
  * @package Sabre
  * @subpackage DAV
- * @copyright Copyright (C) 2007-2010 Rooftop Solutions. All rights reserved.
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
  * @author Evert Pot (http://www.rooftopsolutions.nl/) 
  * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
  */
@@ -45,7 +45,7 @@ class Sabre_DAV_Auth_Plugin extends Sabre_DAV_ServerPlugin {
      * @param string $realm 
      * @return void
      */
-    public function __construct(Sabre_DAV_Auth_Backend_Abstract $authBackend, $realm) {
+    public function __construct(Sabre_DAV_Auth_IBackend $authBackend, $realm) {
 
         $this->authBackend = $authBackend;
         $this->realm = $realm;
@@ -62,372 +62,49 @@ class Sabre_DAV_Auth_Plugin extends Sabre_DAV_ServerPlugin {
 
         $this->server = $server;
         $this->server->subscribeEvent('beforeMethod',array($this,'beforeMethod'),10);
-        $this->server->subscribeEvent('afterGetProperties',array($this,'afterGetProperties'));
-        $this->server->subscribeEvent('report',array($this,'report'));
 
     }
 
     /**
-     * This method intercepts calls to PROPFIND and similar lookups 
+     * Returns a plugin name.
      * 
-     * This is done to inject the current-user-principal if this is requested.
-     *
-     * @todo support for 'unauthenticated'
-     * @return void  
-     */
-    public function afterGetProperties($href, &$properties) {
-
-        if (array_key_exists('{DAV:}current-user-principal', $properties[404])) {
-            if ($ui = $this->authBackend->getCurrentUser()) {
-                $properties[200]['{DAV:}current-user-principal'] = new Sabre_DAV_Property_Principal(Sabre_DAV_Property_Principal::HREF, $ui['uri']);
-            } else {
-                $properties[200]['{DAV:}current-user-principal'] = new Sabre_DAV_Property_Principal(Sabre_DAV_Property_Principal::UNAUTHENTICATED);
-            }
-            unset($properties[404]['{DAV:}current-user-principal']);
-        }
-        if (array_key_exists('{DAV:}principal-collection-set', $properties[404])) {
-            $properties[200]['{DAV:}principal-collection-set'] = new Sabre_DAV_Property_Href('principals');
-            unset($properties[404]['{DAV:}principal-collection-set']);
-        }
-        if (array_key_exists('{DAV:}supported-report-set', $properties[200])) {
-            $properties[200]['{DAV:}supported-report-set']->addReport(array(
-                '{DAV:}expand-property',
-            ));
-        }
-
-
-    }
-
-    /**
-     * This method is called before any HTTP method and forces users to be authenticated
+     * Using this name other plugins will be able to access other plugins
+     * using Sabre_DAV_Server::getPlugin 
      * 
-     * @param string $method
-     * @throws Sabre_DAV_Exception_NotAuthenticated
-     * @return bool 
+     * @return string 
      */
-    public function beforeMethod($method, $uri) {
+    public function getPluginName() {
 
-        $this->authBackend->authenticate($this->server,$this->realm);
+        return 'auth';
 
     }
 
     /**
-     * This functions handles REPORT requests 
+     * Returns the current users' principal uri.
      * 
-     * @param string $reportName 
-     * @param DOMNode $dom 
-     * @return bool|null 
-     */
-    public function report($reportName,$dom) {
-
-        switch($reportName) { 
-            case '{DAV:}expand-property' :
-                $this->expandPropertyReport($dom);
-                return false;
-            case '{DAV:}principal-property-search' :
-                if ($this->server->getRequestUri()==='principals') {
-                    $this->principalPropertySearchReport($dom);
-                    return false;
-                }
-                break;
-            case '{DAV:}principal-search-property-set' :
-                if ($this->server->getRequestUri()==='principals') {
-                    $this->principalSearchPropertySetReport($dom);
-                    return false;
-                }
-                break;
-                 
-        }
-    
-    }
-
-    /**
-     * The expand-property report is defined in RFC3253 section 3-8. 
-     *
-     * This report is very similar to a standard PROPFIND. The difference is
-     * that it has the additional ability to look at properties containing a
-     * {DAV:}href element, follow that property and grab additional elements
-     * there.
-     *
-     * Other rfc's, such as ACL rely on this report, so it made sense to put
-     * it in this plugin.
-     *
-     * @param DOMElement $dom 
-     * @return void
-     */
-    protected function expandPropertyReport($dom) {
-
-        $requestedProperties = $this->parseExpandPropertyReportRequest($dom->firstChild->firstChild);
-        $depth = $this->server->getHTTPDepth(0);
-        $requestUri = $this->server->getRequestUri();
-
-        $result = $this->expandProperties($requestUri,$requestedProperties,$depth);
-
-        $dom = new DOMDocument('1.0','utf-8');
-        $dom->formatOutput = true;
-        $multiStatus = $dom->createElement('d:multistatus');
-        $dom->appendChild($multiStatus);
-
-        // Adding in default namespaces
-        foreach($this->server->xmlNamespaces as $namespace=>$prefix) {
-
-            $multiStatus->setAttribute('xmlns:' . $prefix,$namespace);
-
-        }
-
-        foreach($result as $entry) {
-
-            $entry->serialize($this->server,$multiStatus);
-
-        }
-
-        $xml = $dom->saveXML();
-        $this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8');
-        $this->server->httpResponse->sendStatus(207);
-        $this->server->httpResponse->sendBody($xml);
-
-        // Make sure the event chain is broken
-        return false;
-
-    }
-
-    /**
-     * This method is used by expandPropertyReport to parse
-     * out the entire HTTP request.
+     * If nobody is logged in, this will return null. 
      * 
-     * @param DOMElement $node 
-     * @return array 
+     * @return string|null 
      */
-    protected function parseExpandPropertyReportRequest($node) {
-
-        $requestedProperties = array();
-        do {
+    public function getCurrentUser() {
 
-            if (Sabre_DAV_XMLUtil::toClarkNotation($node)!=='{DAV:}property') continue;
-                
-            if ($node->firstChild) {
-                
-                $children = $this->parseExpandPropertyReportRequest($node->firstChild);
+        $userInfo = $this->authBackend->getCurrentUser();
+        if (!$userInfo) return null;
 
-            } else {
-
-                $children = array();
-
-            }
-
-            $namespace = $node->getAttribute('namespace');
-            if (!$namespace) $namespace = 'DAV:';
-
-            $propName = '{'.$namespace.'}' . $node->getAttribute('name');
-            $requestedProperties[$propName] = $children; 
-
-        } while ($node = $node->nextSibling);
-
-        return $requestedProperties;
+        return $userInfo;
 
     }
 
     /**
-     * This method expands all the properties and returns
-     * a list with property values
-     *
-     * @param array $path
-     * @param array $requestedProperties the list of required properties
-     * @param array $depth
+     * This method is called before any HTTP method and forces users to be authenticated
+     * 
+     * @param string $method
+     * @throws Sabre_DAV_Exception_NotAuthenticated
+     * @return bool 
      */
-    protected function expandProperties($path,array $requestedProperties,$depth) { 
-
-        $foundProperties = $this->server->getPropertiesForPath($path,array_keys($requestedProperties),$depth);
-
-        $result = array();
-
-        foreach($foundProperties as $node) {
-
-            foreach($requestedProperties as $propertyName=>$childRequestedProperties) {
-
-                // We're only traversing if sub-properties were requested
-                if(count($childRequestedProperties)===0) continue;
-                
-                // We only have to do the expansion if the property was found
-                // and it contains an href element.
-                if (!array_key_exists($propertyName,$node[200])) continue;
-                if (!($node[200][$propertyName] instanceof Sabre_DAV_Property_IHref)) continue;
-
-                $href = $node[200][$propertyName]->getHref();
-                list($node[200][$propertyName]) = $this->expandProperties($href,$childRequestedProperties,0);
-
-            }
-            $result[] = new Sabre_DAV_Property_Response($path, $node);
-
-        }
-
-        return $result;
-
-    }
-
-    protected function principalSearchPropertySetReport(DOMDocument $dom) {
-
-        $searchProperties = array(
-            '{DAV:}displayname' => 'display name'
-
-        );
-
-        $httpDepth = $this->server->getHTTPDepth(0);
-        if ($httpDepth!==0) {
-            throw new Sabre_DAV_Exception_BadRequest('This report is only defined when Depth: 0');
-        }
-        
-        if ($dom->firstChild->hasChildNodes()) 
-            throw new Sabre_DAV_Exception_BadRequest('The principal-search-property-set report element is not allowed to have child elements'); 
-
-        $dom = new DOMDocument('1.0','utf-8');
-        $dom->formatOutput = true;
-        $root = $dom->createElement('d:principal-search-property-set');
-        $dom->appendChild($root);
-        // Adding in default namespaces
-        foreach($this->server->xmlNamespaces as $namespace=>$prefix) {
-
-            $root->setAttribute('xmlns:' . $prefix,$namespace);
-
-        }
-
-        $nsList = $this->server->xmlNamespaces; 
-
-        foreach($searchProperties as $propertyName=>$description) {
-
-            $psp = $dom->createElement('d:principal-search-property');
-            $root->appendChild($psp);
-
-            $prop = $dom->createElement('d:prop');
-            $psp->appendChild($prop);
-  
-            $propName = null;
-            preg_match('/^{([^}]*)}(.*)$/',$propertyName,$propName);
-
-            //if (!isset($nsList[$propName[1]])) {
-            //    $nsList[$propName[1]] = 'x' . count($nsList);
-            //}
-
-            // If the namespace was defined in the top-level xml namespaces, it means 
-            // there was already a namespace declaration, and we don't have to worry about it.
-            //if (isset($server->xmlNamespaces[$propName[1]])) {
-                $currentProperty = $dom->createElement($nsList[$propName[1]] . ':' . $propName[2]);
-            //} else {
-            //    $currentProperty = $dom->createElementNS($propName[1],$nsList[$propName[1]].':' . $propName[2]);
-            //}
-            $prop->appendChild($currentProperty);
-
-            $descriptionElem = $dom->createElement('d:description');
-            $descriptionElem->setAttribute('xml:lang','en');
-            $descriptionElem->appendChild($dom->createTextNode($description));
-            $psp->appendChild($descriptionElem);
-
-
-        }
-
-        $this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8');
-        $this->server->httpResponse->sendStatus(200);
-        $this->server->httpResponse->sendBody($dom->saveXML());
-
-    }
-
-    protected function principalPropertySearchReport($dom) {
-
-        $searchableProperties = array(
-            '{DAV:}displayname' => 'display name'
-
-        );
-
-        list($searchProperties, $requestedProperties) = $this->parsePrincipalPropertySearchReportRequest($dom);
-
-        $uri = $this->server->getRequestUri();
-        
-        $result = array();
-
-        $lookupResults = $this->server->getPropertiesForPath($uri, array_keys($searchProperties), 1);
-
-        // The first item in the results is the parent, so we get rid of it.
-        array_shift($lookupResults);
-
-        $matches = array();
-
-        foreach($lookupResults as $lookupResult) {
-
-            foreach($searchProperties as $searchProperty=>$searchValue) {
-                if (!isset($searchableProperties[$searchProperty])) {
-                    throw new Sabre_DAV_Exception_BadRequest('Searching for ' . $searchProperty . ' is not supported');
-                }
-                
-                if (isset($lookupResult[200][$searchProperty]) &&
-                    mb_stripos($lookupResult[200][$searchProperty], $searchValue, 0, 'UTF-8')!==false) {
-                        $matches[] = $lookupResult['href'];
-                }
-
-            }
-
-        }
-
-        $matchProperties = array();
-
-        foreach($matches as $match) {
-            
-           list($result) = $this->server->getPropertiesForPath($match, $requestedProperties, 0);
-           $matchProperties[] = $result;
-
-        }
-
-        $xml = $this->server->generateMultiStatus($matchProperties);
-        $this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8');
-        $this->server->httpResponse->sendStatus(207);
-        $this->server->httpResponse->sendBody($xml);
-
-    }
-
-    protected function parsePrincipalPropertySearchReportRequest($dom) {
-
-        $httpDepth = $this->server->getHTTPDepth(0);
-        if ($httpDepth!==0) {
-            throw new Sabre_DAV_Exception_BadRequest('This report is only defined when Depth: 0');
-        }
-
-        $searchProperties = array();
-
-        // Parsing the search request
-        foreach($dom->firstChild->childNodes as $searchNode) {
-
-            if (Sabre_DAV_XMLUtil::toClarkNotation($searchNode)!=='{DAV:}property-search')
-                continue;
-
-            $propertyName = null;
-            $propertyValue = null;
-
-            foreach($searchNode->childNodes as $childNode) {
-
-                switch(Sabre_DAV_XMLUtil::toClarkNotation($childNode)) {
-
-                    case '{DAV:}prop' :
-                        $property = Sabre_DAV_XMLUtil::parseProperties($searchNode);
-                        reset($property); 
-                        $propertyName = key($property);
-                        break;
-
-                    case '{DAV:}match' :
-                        $propertyValue = $childNode->textContent;
-                        break;
-
-                }
-
-
-            }
-
-            if (is_null($propertyName) || is_null($propertyValue))
-                throw new Sabre_DAV_Exception_BadRequest('Invalid search request. propertyname: ' . $propertyName . '. propertvvalue: ' . $propertyValue);
-
-            $searchProperties[$propertyName] = $propertyValue;
-
-        }
+    public function beforeMethod($method, $uri) {
 
-        return array($searchProperties, array_keys(Sabre_DAV_XMLUtil::parseProperties($dom->firstChild)));
+        $this->authBackend->authenticate($this->server,$this->realm);
 
     }
 
index 430ebde..f1fa404 100755 (executable)
@@ -13,7 +13,7 @@
  * 
  * @package Sabre
  * @subpackage DAV
- * @copyright Copyright (C) 2007-2010 Rooftop Solutions. All rights reserved.
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
  * @author Evert Pot (http://www.rooftopsolutions.nl/) 
  * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
  */
@@ -88,7 +88,7 @@ class Sabre_DAV_Browser_GuessContentType extends Sabre_DAV_ServerPlugin {
     protected function getContentType($fileName) {
 
         // Just grabbing the extension
-        $extension = substr($fileName,strrpos($fileName,'.')+1);
+        $extension = strtolower(substr($fileName,strrpos($fileName,'.')+1));
         if (isset($this->extensionMap[$extension]))
             return $this->extensionMap[$extension];
 
index df60ae4..1e0c3ba 100755 (executable)
@@ -8,7 +8,7 @@
  * 
  * @package Sabre
  * @subpackage DAV
- * @copyright Copyright (C) 2007-2010 Rooftop Solutions. All rights reserved.
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
  * @author Evert Pot (http://www.rooftopsolutions.nl/)
  * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
  */
index f718b4f..61c4dcb 100755 (executable)
@@ -11,7 +11,7 @@
  * 
  * @package Sabre
  * @subpackage DAV
- * @copyright Copyright (C) 2007-2010 Rooftop Solutions. All rights reserved.
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
  * @author Evert Pot (http://www.rooftopsolutions.nl/)
  * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
  */
@@ -68,9 +68,16 @@ class Sabre_DAV_Browser_Plugin extends Sabre_DAV_ServerPlugin {
     public function httpGetInterceptor($method, $uri) {
 
         if ($method!='GET') return true;
-        
-        $node = $this->server->tree->getNodeForPath($uri);
-        if ($node instanceof Sabre_DAV_IFile) return true;
+
+        try { 
+            $node = $this->server->tree->getNodeForPath($uri);
+        } catch (Sabre_DAV_Exception_FileNotFound $e) {
+            // We're simply stopping when the file isn't found to not interfere 
+            // with other plugins.
+            return;
+        }
+        if ($node instanceof Sabre_DAV_IFile) 
+            return;
 
         $this->server->httpResponse->sendStatus(200);
         $this->server->httpResponse->setHeader('Content-Type','text/html; charset=utf-8');
@@ -94,7 +101,7 @@ class Sabre_DAV_Browser_Plugin extends Sabre_DAV_ServerPlugin {
     public function httpPOSTHandler($method, $uri) {
 
         if ($method!='POST') return true;
-        if (isset($_POST['action'])) switch($_POST['action']) {
+        if (isset($_POST['sabreAction'])) switch($_POST['sabreAction']) {
 
             case 'mkcol' :
                 if (isset($_POST['name']) && trim($_POST['name'])) {
@@ -165,6 +172,23 @@ class Sabre_DAV_Browser_Plugin extends Sabre_DAV_ServerPlugin {
         '{DAV:}getlastmodified',
     ),1);
 
+    $parent = $this->server->tree->getNodeForPath($path);
+
+
+    if ($path) {
+
+        list($parentUri) = Sabre_DAV_URLUtil::splitPath($path);
+        $fullPath = Sabre_DAV_URLUtil::encodePath($this->server->getBaseUri() . $parentUri);
+
+        $html.= "<tr>
+<td><a href=\"{$fullPath}\">..</a></td>
+<td>[parent]</td>
+<td></td>
+<td></td>
+</tr>";
+
+    }
+
     foreach($files as $k=>$file) {
 
         // This is the current directory, we can skip it
@@ -174,20 +198,33 @@ class Sabre_DAV_Browser_Plugin extends Sabre_DAV_ServerPlugin {
 
         $type = null;
 
+
         if (isset($file[200]['{DAV:}resourcetype'])) {
             $type = $file[200]['{DAV:}resourcetype']->getValue();
 
             // resourcetype can have multiple values
-            if (is_array($type)) {
-                $type = implode(', ', $type);
-            }
+            if (!is_array($type)) $type = array($type);
+
+            foreach($type as $k=>$v) { 
+
+                // Some name mapping is preferred 
+                switch($v) {
+                    case '{DAV:}collection' :
+                        $type[$k] = 'Collection';
+                        break;
+                    case '{DAV:}principal' :
+                        $type[$k] = 'Principal';
+                        break;
+                    case '{urn:ietf:params:xml:ns:carddav}addressbook' :
+                        $type[$k] = 'Addressbook';
+                        break;
+                    case '{urn:ietf:params:xml:ns:caldav}calendar' :
+                        $type[$k] = 'Calendar';
+                        break;
+                }
 
-            // Some name mapping is preferred 
-            switch($type) {
-                case '{DAV:}collection' :
-                    $type = 'Collection';
-                    break;
             }
+            $type = implode(', ', $type);
         }
 
         // If no resourcetype was found, we attempt to use
@@ -219,16 +256,16 @@ class Sabre_DAV_Browser_Plugin extends Sabre_DAV_ServerPlugin {
 
   $html.= "<tr><td colspan=\"4\"><hr /></td></tr>";
 
-  if ($this->enablePost) {
+  if ($this->enablePost && $parent instanceof Sabre_DAV_ICollection) {
       $html.= '<tr><td><form method="post" action="">
             <h3>Create new folder</h3>
-            <input type="hidden" name="action" value="mkcol" />
+            <input type="hidden" name="sabreAction" value="mkcol" />
             Name: <input type="text" name="name" /><br />
             <input type="submit" value="create" />
             </form>
             <form method="post" action="" enctype="multipart/form-data">
             <h3>Upload file</h3>
-            <input type="hidden" name="action" value="put" />
+            <input type="hidden" name="sabreAction" value="put" />
             Name (optional): <input type="text" name="name" /><br />
             File: <input type="file" name="file" /><br />
             <input type="submit" value="upload" />
@@ -237,7 +274,7 @@ class Sabre_DAV_Browser_Plugin extends Sabre_DAV_ServerPlugin {
   }
 
   $html.= "</table>
-  <address>Generated by SabreDAV " . Sabre_DAV_Version::VERSION ."-". Sabre_DAV_Version::STABILITY . " (c)2007-2010 <a href=\"http://code.google.com/p/sabredav/\">http://code.google.com/p/sabredav/</a></address>
+  <address>Generated by SabreDAV " . Sabre_DAV_Version::VERSION ."-". Sabre_DAV_Version::STABILITY . " (c)2007-2012 <a href=\"http://code.google.com/p/sabredav/\">http://code.google.com/p/sabredav/</a></address>
 </body>
 </html>";
 
diff --git a/lib/Sabre/DAV/Client.php b/lib/Sabre/DAV/Client.php
new file mode 100755 (executable)
index 0000000..f300233
--- /dev/null
@@ -0,0 +1,431 @@
+<?php
+
+/**
+ * SabreDAV DAV client
+ *
+ * This client wraps around Curl to provide a convenient API to a WebDAV 
+ * server.
+ *
+ * NOTE: This class is experimental, it's api will likely change in the future.
+ * 
+ * @package Sabre
+ * @subpackage DAVClient
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_Client {
+
+    public $propertyMap = array();
+
+    protected $baseUri;
+    protected $userName;
+    protected $password;
+    protected $proxy;
+
+    /**
+     * Constructor
+     *
+     * Settings are provided through the 'settings' argument. The following 
+     * settings are supported:
+     *
+     *   * baseUri
+     *   * userName (optional)
+     *   * password (optional)
+     *   * proxy (optional)
+     * 
+     * @param array $settings 
+     */
+    public function __construct(array $settings) {
+
+        if (!isset($settings['baseUri'])) {
+            throw new InvalidArgumentException('A baseUri must be provided');
+        }
+        
+        $validSettings = array(
+            'baseUri',
+            'userName',
+            'password',
+            'proxy'
+        );
+
+        foreach($validSettings as $validSetting) {
+            if (isset($settings[$validSetting])) {
+                $this->$validSetting = $settings[$validSetting];
+            }
+        }
+
+        $this->propertyMap['{DAV:}resourcetype'] = 'Sabre_DAV_Property_ResourceType';
+
+    }
+
+    /**
+     * Does a PROPFIND request
+     *
+     * The list of requested properties must be specified as an array, in clark 
+     * notation. 
+     *
+     * The returned array will contain a list of filenames as keys, and 
+     * properties as values.
+     *
+     * The properties array will contain the list of properties. Only properties 
+     * that are actually returned from the server (without error) will be 
+     * returned, anything else is discarded.
+     *
+     * Depth should be either 0 or 1. A depth of 1 will cause a request to be 
+     * made to the server to also return all child resources.
+     *
+     * @param string $url 
+     * @param array $properties 
+     * @param int $depth 
+     * @return array 
+     */
+    public function propFind($url, array $properties, $depth = 0) {
+
+        $body = '<?xml version="1.0"?>' . "\n";
+        $body.= '<d:propfind xmlns:d="DAV:">' . "\n";
+        $body.= '  <d:prop>' . "\n";
+
+        foreach($properties as $property) {
+
+            list(
+                $namespace,
+                $elementName
+            ) = Sabre_DAV_XMLUtil::parseClarkNotation($property);
+
+            if ($namespace === 'DAV:') {
+                $body.='    <d:' . $elementName . ' />' . "\n";
+            } else {
+                $body.="    <x:" . $elementName . " xmlns:x=\"" . $namespace . "\"/>\n";
+            }
+
+        }
+
+        $body.= '  </d:prop>' . "\n";
+        $body.= '</d:propfind>';
+
+        $response = $this->request('PROPFIND', $url, $body, array(
+            'Depth' => $depth,
+            'Content-Type' => 'application/xml'
+        ));
+
+        $result = $this->parseMultiStatus($response['body']);
+
+        // If depth was 0, we only return the top item
+        if ($depth===0) {
+            reset($result);
+            $result = current($result);
+            return $result[200];
+        }
+
+        $newResult = array();
+        foreach($result as $href => $statusList) {
+
+            $newResult[$href] = $statusList[200];
+
+        }
+
+        return $newResult;
+
+    }
+
+    /**
+     * Updates a list of properties on the server
+     *
+     * The list of properties must have clark-notation properties for the keys, 
+     * and the actual (string) value for the value. If the value is null, an 
+     * attempt is made to delete the property. 
+     *
+     * @todo Must be building the request using the DOM, and does not yet 
+     *       support complex properties. 
+     * @param string $url 
+     * @param array $properties 
+     * @return void
+     */
+    public function propPatch($url, array $properties) {
+
+        $body = '<?xml version="1.0"?>' . "\n";
+        $body.= '<d:propertyupdate xmlns:d="DAV:">' . "\n";
+
+        foreach($properties as $propName => $propValue) {
+
+            list(
+                $namespace,
+                $elementName
+            ) = Sabre_DAV_XMLUtil::parseClarkNotation($propName);
+
+            if ($propValue === null) {
+
+                $body.="<d:remove><d:prop>\n";
+
+                if ($namespace === 'DAV:') {
+                    $body.='    <d:' . $elementName . ' />' . "\n";
+                } else {
+                    $body.="    <x:" . $elementName . " xmlns:x=\"" . $namespace . "\"/>\n";
+                }
+
+                $body.="</d:prop></d:remove>\n";
+
+            } else {
+
+                $body.="<d:set><d:prop>\n";
+                if ($namespace === 'DAV:') {
+                    $body.='    <d:' . $elementName . '>';
+                } else {
+                    $body.="    <x:" . $elementName . " xmlns:x=\"" . $namespace . "\">";
+                }
+                // Shitty.. i know
+                $body.=htmlspecialchars($propValue, ENT_NOQUOTES, 'UTF-8'); 
+                if ($namespace === 'DAV:') {
+                    $body.='</d:' . $elementName . '>' . "\n";
+                } else {
+                    $body.="</x:" . $elementName . ">\n";
+                }
+                $body.="</d:prop></d:set>\n";
+
+            }
+
+        }
+
+        $body.= '</d:propertyupdate>';
+
+        $response = $this->request('PROPPATCH', $url, $body, array(
+            'Content-Type' => 'application/xml'
+        ));
+
+    }
+
+    /**
+     * Performs an HTTP options request
+     *
+     * This method returns all the features from the 'DAV:' header as an array. 
+     * If there was no DAV header, or no contents this method will return an 
+     * empty array. 
+     * 
+     * @return array 
+     */
+    public function options() {
+
+        $result = $this->request('OPTIONS');
+        if (!isset($result['headers']['dav'])) {
+            return array();
+        }
+
+        $features = explode(',', $result['headers']['dav']);
+        foreach($features as &$v) {
+            $v = trim($v);
+        }
+        return $features;
+
+    }
+
+    /**
+     * Performs an actual HTTP request, and returns the result.
+     *
+     * If the specified url is relative, it will be expanded based on the base 
+     * url.
+     *
+     * The returned array contains 3 keys:
+     *   * body - the response body
+     *   * httpCode - a HTTP code (200, 404, etc)
+     *   * headers - a list of response http headers. The header names have 
+     *     been lowercased.
+     *
+     * @param string $method 
+     * @param string $url 
+     * @param string $body 
+     * @param array $headers 
+     * @return array 
+     */
+    public function request($method, $url = '', $body = null, $headers = array()) {
+
+        $url = $this->getAbsoluteUrl($url);
+
+        $curlSettings = array(
+            CURLOPT_RETURNTRANSFER => true,
+            CURLOPT_CUSTOMREQUEST => $method,
+            CURLOPT_POSTFIELDS => $body,
+            // Return headers as part of the response
+            CURLOPT_HEADER => true
+        );
+
+        // Adding HTTP headers
+        $nHeaders = array(); 
+        foreach($headers as $key=>$value) {
+
+            $nHeaders[] = $key . ': ' . $value;
+
+        }
+        $curlSettings[CURLOPT_HTTPHEADER] = $nHeaders;
+
+        if ($this->proxy) {
+            $curlSettings[CURLOPT_PROXY] = $this->proxy;
+        }
+
+        if ($this->userName) {
+            $curlSettings[CURLOPT_HTTPAUTH] = CURLAUTH_BASIC | CURLAUTH_DIGEST;
+            $curlSettings[CURLOPT_USERPWD] = $this->userName . ':' . $this->password;
+        }
+
+        list(
+            $response,
+            $curlInfo,
+            $curlErrNo,
+            $curlError
+        ) = $this->curlRequest($url, $curlSettings);
+
+        $headerBlob = substr($response, 0, $curlInfo['header_size']);
+        $response = substr($response, $curlInfo['header_size']);
+
+        // In the case of 100 Continue, or redirects we'll have multiple lists 
+        // of headers for each separate HTTP response. We can easily split this 
+        // because they are separated by \r\n\r\n
+        $headerBlob = explode("\r\n\r\n", trim($headerBlob, "\r\n"));
+        
+        // We only care about the last set of headers
+        $headerBlob = $headerBlob[count($headerBlob)-1];
+
+        // Splitting headers
+        $headerBlob = explode("\r\n", $headerBlob);
+        
+        $headers = array();
+        foreach($headerBlob as $header) {
+            $parts = explode(':', $header, 2);
+            if (count($parts)==2) {
+                $headers[strtolower(trim($parts[0]))] = trim($parts[1]);
+            }
+        }
+
+        $response = array(
+            'body' => $response,
+            'statusCode' => $curlInfo['http_code'],
+            'headers' => $headers
+        );
+
+        if ($curlErrNo) {
+            throw new Sabre_DAV_Exception('[CURL] Error while making request: ' . $curlError . ' (error code: ' . $curlErrNo . ')');
+        } 
+
+        if ($response['statusCode']>=400) {
+            throw new Sabre_DAV_Exception('HTTP error response. (errorcode ' . $response['statusCode'] . ')');
+        }
+
+        return $response;
+
+    }
+
+    /**
+     * Wrapper for all curl functions.
+     *
+     * The only reason this was split out in a separate method, is so it 
+     * becomes easier to unittest. 
+     *