Update to sabre dav version 1.5.6 and changes for TYPO3 6.2
authormario_matzulla <mario_matzulla@735d13b6-9817-0410-8766-e36946ffe9aa>
Tue, 3 Feb 2015 11:13:20 +0000 (11:13 +0000)
committermario_matzulla <mario_matzulla@735d13b6-9817-0410-8766-e36946ffe9aa>
Tue, 3 Feb 2015 11:13:20 +0000 (11:13 +0000)
git-svn-id: https://svn.typo3.org/TYPO3v4/Extensions/caldav/trunk@88318 735d13b6-9817-0410-8766-e36946ffe9aa

77 files changed:
.htaccess [new file with mode: 0644]
ChangeLog
_htaccess [deleted file]
caldav.php
ext_conf_template.txt [new file with mode: 0644]
ext_emconf.php
ext_icon.gif
ext_tables.php
hooks/class.tx_caldav_tcemain_processdatamap.php
lib/ChangeLog [new file with mode: 0644]
lib/Sabre/CalDAV/Backend/TYPO3.php
lib/Sabre/CalDAV/CalendarRootNode.php
lib/Sabre/CalDAV/Principal/Collection.php [new file with mode: 0644]
lib/Sabre/CalDAV/Principal/ProxyRead.php [new file with mode: 0644]
lib/Sabre/CalDAV/Principal/ProxyWrite.php [new file with mode: 0644]
lib/Sabre/CalDAV/Principal/User.php [new file with mode: 0644]
lib/Sabre/CalDAV/UserCalendars.php
lib/Sabre/CalDAV/Version.php
lib/Sabre/CardDAV/AddressBook.php [new file with mode: 0644]
lib/Sabre/CardDAV/AddressBookQueryParser.php [new file with mode: 0644]
lib/Sabre/CardDAV/AddressBookRoot.php [new file with mode: 0644]
lib/Sabre/CardDAV/Backend/Abstract.php [new file with mode: 0644]
lib/Sabre/CardDAV/Backend/PDO.php [new file with mode: 0644]
lib/Sabre/CardDAV/Card.php [new file with mode: 0644]
lib/Sabre/CardDAV/IAddressBook.php [new file with mode: 0644]
lib/Sabre/CardDAV/ICard.php [new file with mode: 0644]
lib/Sabre/CardDAV/IDirectory.php [new file with mode: 0644]
lib/Sabre/CardDAV/Plugin.php [new file with mode: 0644]
lib/Sabre/CardDAV/Property/SupportedAddressData.php [new file with mode: 0644]
lib/Sabre/CardDAV/UserAddressBooks.php [new file with mode: 0644]
lib/Sabre/CardDAV/Version.php [new file with mode: 0644]
lib/Sabre/DAV/Auth/Backend/Abstract.php [deleted file]
lib/Sabre/DAV/Auth/Backend/TYPO3.php
lib/Sabre/DAV/Auth/Principal.php [deleted file]
lib/Sabre/DAV/Auth/PrincipalCollection.php [deleted file]
lib/Sabre/DAV/ObjectTree.php
lib/Sabre/DAV/Property/Principal.php [deleted file]
lib/Sabre/DAV/Property/ResourceType.php
lib/Sabre/DAV/Server.php
lib/Sabre/DAVACL/AbstractPrincipalCollection.php [new file with mode: 0644]
lib/Sabre/DAVACL/Exception/AceConflict.php [new file with mode: 0644]
lib/Sabre/DAVACL/Exception/NeedPrivileges.php [new file with mode: 0644]
lib/Sabre/DAVACL/Exception/NoAbstract.php [new file with mode: 0644]
lib/Sabre/DAVACL/Exception/NotRecognizedPrincipal.php [new file with mode: 0644]
lib/Sabre/DAVACL/Exception/NotSupportedPrivilege.php [new file with mode: 0644]
lib/Sabre/DAVACL/IACL.php [new file with mode: 0644]
lib/Sabre/DAVACL/IPrincipal.php [new file with mode: 0644]
lib/Sabre/DAVACL/IPrincipalBackend.php [new file with mode: 0644]
lib/Sabre/DAVACL/Plugin.php [new file with mode: 0644]
lib/Sabre/DAVACL/Principal.php [new file with mode: 0644]
lib/Sabre/DAVACL/PrincipalBackend/PDO.php [new file with mode: 0644]
lib/Sabre/DAVACL/PrincipalBackend/TYPO3.php [new file with mode: 0644]
lib/Sabre/DAVACL/PrincipalCollection.php [new file with mode: 0644]
lib/Sabre/DAVACL/Property/Acl.php [new file with mode: 0644]
lib/Sabre/DAVACL/Property/CurrentUserPrivilegeSet.php [new file with mode: 0644]
lib/Sabre/DAVACL/Property/Principal.php [new file with mode: 0644]
lib/Sabre/DAVACL/Property/SupportedPrivilegeSet.php [new file with mode: 0644]
lib/Sabre/DAVACL/Version.php [new file with mode: 0644]
lib/Sabre/VObject/Component.php [new file with mode: 0644]
lib/Sabre/VObject/Element.php [new file with mode: 0644]
lib/Sabre/VObject/Element/DateTime.php [new file with mode: 0644]
lib/Sabre/VObject/Element/MultiDateTime.php [new file with mode: 0644]
lib/Sabre/VObject/ElementList.php [new file with mode: 0644]
lib/Sabre/VObject/Node.php [new file with mode: 0644]
lib/Sabre/VObject/Parameter.php [new file with mode: 0644]
lib/Sabre/VObject/ParseException.php [new file with mode: 0644]
lib/Sabre/VObject/Property.php [new file with mode: 0644]
lib/Sabre/VObject/Reader.php [new file with mode: 0644]
lib/Sabre/VObject/Version.php [new file with mode: 0644]
lib/Sabre/VObject/includes.php [new file with mode: 0644]
mod1/class.tx_cal_ics_generator.php [new file with mode: 0644]
mod1/clear.gif [new file with mode: 0644]
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: 0644]

diff --git a/.htaccess b/.htaccess
new file mode 100644 (file)
index 0000000..0644faa
--- /dev/null
+++ b/.htaccess
@@ -0,0 +1,4 @@
+<IfModule mod_rewrite.c>
+  RewriteEngine on
+  RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization},L]
+</IfModule> 
\ No newline at end of file
index df57f26..5b87d83 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
-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. 
+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.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/_htaccess b/_htaccess
deleted file mode 100755 (executable)
index 0644faa..0000000
--- a/_htaccess
+++ /dev/null
@@ -1,4 +0,0 @@
-<IfModule mod_rewrite.c>
-  RewriteEngine on
-  RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization},L]
-</IfModule> 
\ No newline at end of file
index 41f482e..9caacda 100755 (executable)
@@ -1,5 +1,8 @@
 <?php
-
+ini_set("display_errors", 1);
+ini_set("track_errors", 1);
+ini_set("html_errors", 1);
+error_reporting(E_ALL);
 // *******************************
 // Checking PHP version
 // *******************************
@@ -18,15 +21,16 @@ $TYPO3_MISC['microtime_start'] = microtime(true);
 define('TYPO3_OS', stristr(PHP_OS,'win')&&!stristr(PHP_OS,'darwin')?'WIN':'');
 define('TYPO3_MODE','FE');
 
-if(!defined('PATH_thisScript')) {
-#      define('PATH_thisScript', str_replace('//', '/', str_replace('\\', '/',
-#              (PHP_SAPI == 'fpm-fcgi' || PHP_SAPI == 'cgi' || PHP_SAPI == 'isapi' || PHP_SAPI == 'cgi-fcgi') &&
-#              ($_SERVER['ORIG_PATH_TRANSLATED'] ? $_SERVER['ORIG_PATH_TRANSLATED'] : $_SERVER['PATH_TRANSLATED']) ?
-#              ($_SERVER['ORIG_PATH_TRANSLATED'] ? $_SERVER['ORIG_PATH_TRANSLATED'] : $_SERVER['PATH_TRANSLATED']) :
-#              ($_SERVER['ORIG_SCRIPT_FILENAME'] ? $_SERVER['ORIG_SCRIPT_FILENAME'] : $_SERVER['SCRIPT_FILENAME']))));
-       define('PATH_thisScript',str_replace('//', '/', str_replace('\\', '/',dirname($_SERVER['SCRIPT_FILENAME']))));
-}
-if (!defined('PATH_site'))                     define('PATH_site', str_replace('typo3conf/ext/caldav','',PATH_thisScript));
+#if(!defined('PATH_thisScript')) {
+##     define('PATH_thisScript', str_replace('//', '/', str_replace('\\', '/',
+##             (PHP_SAPI == 'fpm-fcgi' || PHP_SAPI == 'cgi' || PHP_SAPI == 'isapi' || PHP_SAPI == 'cgi-fcgi') &&
+##             ($_SERVER['ORIG_PATH_TRANSLATED'] ? $_SERVER['ORIG_PATH_TRANSLATED'] : $_SERVER['PATH_TRANSLATED']) ?
+##             ($_SERVER['ORIG_PATH_TRANSLATED'] ? $_SERVER['ORIG_PATH_TRANSLATED'] : $_SERVER['PATH_TRANSLATED']) :
+##             ($_SERVER['ORIG_SCRIPT_FILENAME'] ? $_SERVER['ORIG_SCRIPT_FILENAME'] : $_SERVER['SCRIPT_FILENAME']))));
+#      define('PATH_thisScript',str_replace('//', '/', str_replace('\\', '/',dirname($_SERVER['SCRIPT_FILENAME']))));
+$PATH_thisScript = str_replace('//', '/', str_replace('\\', '/',dirname($_SERVER['SCRIPT_FILENAME'])));
+#}
+if (!defined('PATH_site'))                     define('PATH_site', str_replace('typo3conf/ext/caldav','',$PATH_thisScript));
 if (!defined('PATH_t3lib'))            define('PATH_t3lib', PATH_site.'t3lib/');
 
 define('TYPO3_mainDir', 'typo3/');             // This is the directory of the backend administration for the sites of this TYPO3 installation.
@@ -42,6 +46,8 @@ if (!defined('PATH_tslib')) {
 }
 $TYPO3_AJAX = false;
 
+define('PATH_thisScript',str_replace('//', '/', str_replace('\\', '/',dirname($_SERVER['SCRIPT_FILENAME']))).'/caldav.php');
+
 if (!@is_dir(PATH_typo3conf))  die('Cannot find configuration. This file is probably executed from the wrong location.');
 
 // *********************
@@ -55,82 +61,54 @@ unset($error);
 // *********************
 ob_start();
 
-// *********************
+require __DIR__ . '/../../../typo3/sysext/core/Classes/Core/Bootstrap.php';
+\TYPO3\CMS\Core\Core\Bootstrap::getInstance()
+->baseSetup('')
+->redirectToInstallerIfEssentialConfigurationDoesNotExist();
+
+\TYPO3\CMS\Core\Core\Bootstrap::getInstance()
+       ->startOutputBuffering()
+       ->loadConfigurationAndInitialize()
+       ->loadTypo3LoadedExtAndExtLocalconf(TRUE)
+       ->applyAdditionalConfigurationSettings();
+
 // Timetracking started
-// *********************
-if ($_COOKIE['be_typo_user']) {
-       require_once(PATH_t3lib.'class.t3lib_timetrack.php');
-       $TT = new t3lib_timeTrack;
+$configuredCookieName = trim($GLOBALS['TYPO3_CONF_VARS']['BE']['cookieName']);
+if (empty($configuredCookieName)) {
+       $configuredCookieName = 'be_typo_user';
+}
+if ($_COOKIE[$configuredCookieName]) {
+       $TT = new \TYPO3\CMS\Core\TimeTracker\TimeTracker();
 } else {
-       require_once(PATH_t3lib.'class.t3lib_timetracknull.php');
-       $TT = new t3lib_timeTrackNull;
+       $TT = new \TYPO3\CMS\Core\TimeTracker\NullTimeTracker();
 }
 
 $TT->start();
 $TT->push('','Script start');
 
+\TYPO3\CMS\Core\Core\Bootstrap::getInstance()->initializeTypo3DbGlobal();
+
+/** @var $TSFE \TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController */
+$TSFE = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(
+               'TYPO3\\CMS\\Frontend\\Controller\\TypoScriptFrontendController',
+               $TYPO3_CONF_VARS,
+               \TYPO3\CMS\Core\Utility\GeneralUtility::_GP('id'),
+               \TYPO3\CMS\Core\Utility\GeneralUtility::_GP('type'),
+               \TYPO3\CMS\Core\Utility\GeneralUtility::_GP('no_cache'),
+               \TYPO3\CMS\Core\Utility\GeneralUtility::_GP('cHash'),
+               \TYPO3\CMS\Core\Utility\GeneralUtility::_GP('jumpurl'),
+               \TYPO3\CMS\Core\Utility\GeneralUtility::_GP('MP'),
+               \TYPO3\CMS\Core\Utility\GeneralUtility::_GP('RDCT')
+);
 
-// *********************
-// Mandatory libraries included
-// *********************
-$TT->push('Include class t3lib_db, t3lib_div, t3lib_extmgm','');
-       require_once(PATH_t3lib.'class.t3lib_div.php');
-       require_once(PATH_t3lib.'class.t3lib_extmgm.php');
-$TT->pull();
-
-require(PATH_t3lib.'config_default.php');
-
-if (!defined ('TYPO3_db'))     die ('The configuration file was not included.');       // the name of the TYPO3 database is stored in this constant. Here the inclusion of the config-file is verified by checking if this var is set.
-if (!t3lib_extMgm::isLoaded('cms'))    die('<strong>Error:</strong> The main frontend extension "cms" was not loaded. Enable it in the extension manager in the backend.');
-
-if (!defined('PATH_tslib')) {
-       define('PATH_tslib', t3lib_extMgm::extPath('cms').'tslib/');
-}
-
-
-
-
-// *********************
-// Error & Exception handling
-// *********************
-if ($TYPO3_CONF_VARS['SC_OPTIONS']['errors']['exceptionHandler'] !== '') {
-       $TT->push('Register Exceptionhandler', '');
-       if ($TYPO3_CONF_VARS['SYS']['errorHandler'] !== '') {
-                       // register an error handler for the given errorHandlerErrors
-               $errorHandler = t3lib_div::makeInstance($TYPO3_CONF_VARS['SYS']['errorHandler'], $TYPO3_CONF_VARS['SYS']['errorHandlerErrors']);
-                       // set errors which will be converted in an exception
-               $errorHandler->setExceptionalErrors($TYPO3_CONF_VARS['SC_OPTIONS']['errors']['exceptionalErrors']);
-       }
-       $exceptionHandler = t3lib_div::makeInstance($TYPO3_CONF_VARS['SC_OPTIONS']['errors']['exceptionHandler']);
-       $TT->pull();
-}
 
-$TYPO3_DB = t3lib_div::makeInstance('t3lib_DB');
-$TYPO3_DB->debugOutput = $TYPO3_CONF_VARS['SYS']['sqlDebug'];
+$TSFE->connectToDB();
+$TSFE->sendRedirect();
 
-$CLIENT = t3lib_div::clientInfo();                             // Set to the browser: net / msie if 4+ browsers
 $TT->pull();
 
-require_once (t3lib_extMgm::extPath('cal').'controller/class.tx_cal_tsfe.php');
-// ***********************************
-// Create $TSFE object (TSFE = TypoScript Front End)
-// Connecting to database
-// ***********************************
-$TSFE = t3lib_div::makeInstance('tslib_fe',
-       $TYPO3_CONF_VARS,
-       t3lib_div::_GP('id'),
-       t3lib_div::_GP('type'),
-       t3lib_div::_GP('no_cache'),
-       t3lib_div::_GP('cHash'),
-       t3lib_div::_GP('jumpurl'),
-       t3lib_div::_GP('MP'),
-       t3lib_div::_GP('RDCT')
-);
-       // Initialize FE user object:
-$feUserObj = tslib_eidtools::initFeUser();
-tslib_eidtools::connectDB();
-require_once(t3lib_extMgm::extPath('simulatestatic').'class.tx_simulatestatic.php');
-require_once(t3lib_extMgm::extPath('statictemplates').'class.tx_statictemplates.php');
+#require_once(t3lib_extMgm::extPath('simulatestatic').'class.tx_simulatestatic.php');
+#require_once(t3lib_extMgm::extPath('statictemplates').'class.tx_statictemplates.php');
 require_once(t3lib_extMgm::extPath('cal').'model/class.tx_cal_date.php');
 require_once(t3lib_extMgm::extPath('cal').'service/class.tx_cal_icalendar_service.php');
 /*
@@ -146,8 +124,12 @@ This server features CalDAV and ACL support
 // If you want to run the SabreDAV server in a custom location (using mod_rewrite for instance)
 // You can override the baseUri here.
 // $baseUri = '/';
+if (isset($GLOBALS['TYPO3_CONF_VARS']['DB']['port'])) {
+       $typo_db_host = TYPO3_db_host.';port='.$GLOBALS['TYPO3_CONF_VARS']['DB']['port'];
+} else {
+       $typo_db_host = str_replace(':',';port=',TYPO3_db_host);
+}
 
-$typo_db_host = str_replace(':',';port=',TYPO3_db_host);
 
 /* Database */
 //$pdo = new PDO('sqlite:data/db.sqlite');
diff --git a/ext_conf_template.txt b/ext_conf_template.txt
new file mode 100644 (file)
index 0000000..5530023
--- /dev/null
@@ -0,0 +1,2 @@
+# cat=basic/enable/10; type=text; label= Configure the PIDs containing the fe_users for authentication.
+pids = 3,198
\ No newline at end of file
index 808a0f6..c2cb7d1 100755 (executable)
@@ -1,47 +1,35 @@
 <?php
 
-########################################################################
-# Extension Manager/Repository config file for ext "caldav".
-#
-# Auto generated 01-07-2012 14:16
-#
-# Manual updates:
-# Only the data in the array - everything else is removed by next
-# writing. "version" and "dependencies" must not be touched!
-########################################################################
+/**
+ * *************************************************************
+ * Extension Manager/Repository config file for ext "caldav".
+ *
+ * Auto generated 01-07-2012 14:16
+ *
+ * Manual updates:
+ * Only the data in the array - everything else is removed by next
+ * writing. "version" and "dependencies" must not be touched!
+ * *************************************************************
+ */
 
 $EM_CONF[$_EXTKEY] = array(
        'title' => 'CalDAV',
        'description' => 'Extends Calendar Base with CalDAV functionality.',
        'category' => 'plugin',
+       'shy' => 0,
+       'version' => '1.0.0',
+       'loadOrder' => '',
+       'state' => 'stable',
+       'uploadfolder' => 0,
+       'clearCacheOnLoad' => 1,
        'author' => 'Mario Matzulla',
        'author_email' => 'mario@matzullas.de',
        'shy' => '',
-       'dependencies' => 'cal',
-       'conflicts' => '',
-       'priority' => '',
-       'module' => '',
-       'state' => 'beta',
-       'internal' => '',
-       'uploadfolder' => 0,
-       'createDirs' => '',
-       'modify_tables' => '',
-       'clearCacheOnLoad' => 1,
-       'lockType' => '',
-       'author_company' => '',
-       'version' => '1.0.0',
-       'constraints' => array(
-               'depends' => array(
-                       'cal' => '1.4.0',
-               ),
-               'conflicts' => array(
-               ),
-               'suggests' => array(
+       'constraints' => array (
+               'depends' => array (
+                       'cal' => '1.7.0-' 
                ),
-       ),
-       '_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(
-       ),
+       )
 );
 
 ?>
\ No newline at end of file
index 2e3322d..1891519 100755 (executable)
Binary files a/ext_icon.gif and b/ext_icon.gif differ
index 740b4fb..d1bc055 100755 (executable)
@@ -7,4 +7,5 @@ if (TYPO3_MODE=="BE")   {
        t3lib_extMgm::addModule("tools","calicsgenerator","",t3lib_extMgm::extPath($_EXTKEY)."mod1/");
 }
 
+
 ?>
\ No newline at end of file
index bd951c7..cccb24b 100755 (executable)
@@ -29,7 +29,7 @@
  ***************************************************************/
 
 define('ICALENDAR_PATH',       t3lib_extMgm::extPath('cal').'model/class.tx_model_iCalendar.php');
-require_once(t3lib_extMgm::extPath('cal').'controller/class.tx_cal_functions.php');
+#require_once(t3lib_extMgm::extPath('cal').'controller/class.tx_cal_functions.php');
 
 /**
  * This hook extends the tcemain class.
@@ -65,7 +65,7 @@ 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->modelObj->findEvent($event['uid'], 'tx_cal_phpicalendar', '', false, false, false, true, true);
+                                               $eventObject = $tx_cal_api->modelObj->findEvent($event['uid'], 'tx_cal_phpicalendar', $tx_cal_api->conf['pidList'], false, false, false, true, true);
 
                                                if ($eventObject->conf['view.']['event.']['phpicalendarEventTemplate']) {
                                                        $oldPath = &$eventObject->conf['view.']['event.']['phpicalendarEventTemplate'];
@@ -76,6 +76,7 @@ class tx_caldav_tcemain_processdatamap {
                                                $eventObject->conf['view'] = 'single_ics';
                                                $extPath=t3lib_extMgm::extPath('cal');
                                                
+                                               $oldPath = 'EXT:cal/standard_template/event_model.tmpl';
                                                $oldPath = str_replace('EXT:cal/', $extPath, $oldPath);
                                                //$oldPath = str_replace(PATH_site, '', $oldPath);
                                                $eventObject->conf['view.']['event.']['phpicalendarEventTemplate'] = $oldPath;
diff --git a/lib/ChangeLog b/lib/ChangeLog
new file mode 100644 (file)
index 0000000..df57f26
--- /dev/null
@@ -0,0 +1,440 @@
+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
index c3e5268..bcafef9 100755 (executable)
@@ -81,7 +81,6 @@ class Sabre_CalDAV_Backend_TYPO3 extends Sabre_CalDAV_Backend_Abstract {
         * @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)));
@@ -303,7 +302,6 @@ class Sabre_CalDAV_Backend_TYPO3 extends Sabre_CalDAV_Backend_Abstract {
         * @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();
@@ -451,7 +449,7 @@ class Sabre_CalDAV_Backend_TYPO3 extends Sabre_CalDAV_Backend_Abstract {
                                $stmt->execute(array($component->getAttribute('UID'),$objectUri));
                        }
                }
-               $service->insertCalEventsIntoDB($components->_components, $calendarId, $calendarRow['pid'], 0, 0, false);
+               $service->insertCalEventsIntoDB($components->_components, $calendarId, $calendarRow['pid'], 0, 0);
                $this->clearCache($calendarRow['pid']);
        }
 
@@ -464,26 +462,26 @@ class Sabre_CalDAV_Backend_TYPO3 extends Sabre_CalDAV_Backend_Abstract {
                        $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);
+//             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.'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');
+//             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);
+//             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 = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\DataHandling\\DataHandler');
                $tce->clear_cacheCmd($pageIDForPlugin);  // ID of the page for which to clear the cache
        }
 }
\ No newline at end of file
index 0f7dca7..a3b16bf 100755 (executable)
@@ -67,7 +67,6 @@ class Sabre_CalDAV_CalendarRootNode extends Sabre_DAVACL_AbstractPrincipalCollec
      * @return Sabre_DAV_INode 
      */
     public function getChildForPrincipal(array $principal) {
-
         return new Sabre_CalDAV_UserCalendars($this->principalBackend, $this->caldavBackend, $principal['uri']);
 
     }
diff --git a/lib/Sabre/CalDAV/Principal/Collection.php b/lib/Sabre/CalDAV/Principal/Collection.php
new file mode 100644 (file)
index 0000000..5922af5
--- /dev/null
@@ -0,0 +1,30 @@
+<?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 100644 (file)
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 100644 (file)
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 100644 (file)
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 3c60971..08585dd 100755 (executable)
@@ -40,7 +40,6 @@ class Sabre_CalDAV_UserCalendars implements Sabre_DAV_IExtendedCollection, Sabre
      * @param mixed $userUri 
      */
     public function __construct(Sabre_DAVACL_IPrincipalBackend $principalBackend, Sabre_CalDAV_Backend_Abstract $caldavBackend, $userUri) {
-
         $this->principalBackend = $principalBackend;
         $this->caldavBackend = $caldavBackend;
         $this->principalInfo = $principalBackend->getPrincipalByPath($userUri);
index 2647f15..6f6fed9 100755 (executable)
@@ -14,7 +14,7 @@ class Sabre_CalDAV_Version {
     /**
      * Full version number
      */
-    const VERSION = '1.5.5';
+    const VERSION = '1.5.6';
 
     /**
      * Stability : alpha, beta, stable
diff --git a/lib/Sabre/CardDAV/AddressBook.php b/lib/Sabre/CardDAV/AddressBook.php
new file mode 100644 (file)
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 100644 (file)
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 100644 (file)
index 0000000..cf61643
--- /dev/null
@@ -0,0 +1,77 @@
+<?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 100644 (file)
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 100644 (file)
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 100644 (file)
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 100644 (file)
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 100644 (file)
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 100644 (file)
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 100644 (file)
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 100644 (file)
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 100644 (file)
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 100644 (file)
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';
+
+}
diff --git a/lib/Sabre/DAV/Auth/Backend/Abstract.php b/lib/Sabre/DAV/Auth/Backend/Abstract.php
deleted file mode 100755 (executable)
index 79383fa..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-<?php
-
-/**
- * This is the base class for any authentication object.
- *
- * @package Sabre
- * @subpackage DAV
- * @copyright Copyright (C) 2007-2010 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_Abstract {
-
-    /**
-     * 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 
-     */
-    abstract public function authenticate(Sabre_DAV_Server $server,$realm); 
-
-    /**
-     * Returns information about the currently logged in user.
-     *
-     * If nobody is currently logged in, this method should return null.
-     * 
-     * @return array|null
-     */
-    abstract public function getCurrentUser();
-
-    /**
-     * 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();
-
-    }
-
-}
-
index d445659..74317fc 100755 (executable)
@@ -57,16 +57,16 @@ class Sabre_DAV_Auth_Backend_TYPO3 extends Sabre_DAV_Auth_Backend_AbstractBasic
                $formfield_uident = 'pass';                     // formfield with password
                $formfield_chalvalue = 'challenge';             // formfield with a unique value which is used to encrypt the password and username
                $formfield_status = 'logintype';
-       $_POST['logintype'] = 'login';
-               $_POST['user'] = $username;
-               $_POST['pass'] = $password;
-               $_POST['challenge'] = '';
+       $_GET['logintype'] = 'login';
+               $_GET['user'] = $username;
+               $_GET['pass'] = $password;
+               $_GET['challenge'] = '';
                $confArr = unserialize($TYPO3_CONF_VARS['EXT']['extConf']['caldav']);
-               $_POST['pid'] = $confArr['pids'];
+               $_GET['pid'] = $confArr['pids'];
                $TYPO3_CONF_VARS['FE']['loginSecurityLevel']='normal';          
-               require_once (PATH_tslib.'/class.tslib_eidtools.php');
+               //require_once (PATH_tslib.'/class.tslib_eidtools.php');
 
-               $feUserObj = tslib_eidtools::initFeUser();
+               $feUserObj = \TYPO3\CMS\Frontend\Utility\EidUtility::initFeUser();
 
        if (is_array($feUserObj->user) && $feUserObj->user['uid'] && $feUserObj->user['is_online']){
                $this->username = $username;
diff --git a/lib/Sabre/DAV/Auth/Principal.php b/lib/Sabre/DAV/Auth/Principal.php
deleted file mode 100755 (executable)
index a69ce6f..0000000
+++ /dev/null
@@ -1,147 +0,0 @@
-<?php
-
-/**
- * Principal class
- *
- * This class represents a user in the directory tree.
- * Many WebDAV specs require a user to show up in the directory 
- * structure. The principal is defined in RFC 3744.
- * 
- * @package Sabre
- * @subpackage DAV
- * @copyright Copyright (C) 2007-2010 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_Principal extends Sabre_DAV_Node implements Sabre_DAV_IProperties {
-
-    /**
-     * Full uri for this principal resource 
-     * 
-     * @var string 
-     */
-    protected $principalUri;
-
-    /**
-     * Struct with principal information.
-     *
-     * @var array 
-     */
-    protected $principalProperties;
-
-    /**
-     * Creates the principal object 
-     *
-     * @param string $principalUri Full uri to the principal resource
-     * @param array $principalProperties
-     */
-    public function __construct($principalUri,array $principalProperties = array()) {
-
-        $this->principalUri = $principalUri;
-        $this->principalProperties = $principalProperties;
-
-    }
-
-    /**
-     * Returns the name of the element 
-     * 
-     * @return void
-     */
-    public function getName() {
-
-        list(, $name) = Sabre_DAV_URLUtil::splitPath($this->principalUri);
-        return $name;
-
-    }
-
-    /**
-     * Returns the name of the user 
-     * 
-     * @return void
-     */
-    public function getDisplayName() {
-
-        if (isset($this->principalProperties['{DAV:}displayname'])) {
-            return $this->principalProperties['{DAV:}displayname'];
-        } else {
-            return $this->getName();
-        }
-
-    }
-
-    /**
-     * Returns a list of properties 
-     * 
-     * @param array $requestedProperties 
-     * @return void
-     */
-    public function getProperties($requestedProperties) {
-
-        if (!count($requestedProperties)) {
-           
-            // If all properties were requested
-            // we will only returns properties from this list
-            $requestedProperties = array(
-                '{DAV:}resourcetype',
-                '{DAV:}displayname',
-            );
-
-        }
-
-        // We need to always return the resourcetype
-        // This is a bug in the core server, but it is easier to do it this way for now
-        $newProperties = array(
-            '{DAV:}resourcetype' => new Sabre_DAV_Property_ResourceType('{DAV:}principal')
-        );
-        foreach($requestedProperties as $propName) switch($propName) {
-            
-            case '{DAV:}alternate-URI-set' :
-                if (isset($this->principalProperties['{http://sabredav.org/ns}email-address'])) {
-                    $href = 'mailto:' . $this->principalProperties['{http://sabredav.org/ns}email-address'];
-                    $newProperties[$propName] = new Sabre_DAV_Property_Href($href);
-                } else {
-                    $newProperties[$propName] = null;
-                }
-                break;
-            case '{DAV:}group-member-set' :
-            case '{DAV:}group-membership' :
-                $newProperties[$propName] = null;
-                break;
-
-            case '{DAV:}principal-URL' :
-                $newProperties[$propName] = new Sabre_DAV_Property_Href($this->principalUri);
-                break;
-
-            case '{DAV:}displayname' :
-                $newProperties[$propName] = $this->getDisplayName();
-                break;
-
-            default :
-                if (isset($this->principalProperties[$propName])) {
-                    $newProperties[$propName] = $this->principalProperties[$propName];
-                }
-                break;
-
-        }
-
-        return $newProperties;
-        
-
-    }
-
-    /**
-     * Updates this principals properties.
-     *
-     * Currently this is not supported
-     * 
-     * @param array $properties
-     * @see Sabre_DAV_IProperties::updateProperties
-     * @return bool|array 
-     */
-    public function updateProperties($properties) {
-
-        return false;
-
-    }
-
-}
diff --git a/lib/Sabre/DAV/Auth/PrincipalCollection.php b/lib/Sabre/DAV/Auth/PrincipalCollection.php
deleted file mode 100755 (executable)
index 75cf3af..0000000
+++ /dev/null
@@ -1,74 +0,0 @@
-<?php
-
-/**
- * Principals Collection
- *
- * This collection represents a list of users. It uses
- * Sabre_DAV_Auth_Backend to determine which users are available on the list.
- *
- * The users are instances of Sabre_DAV_Auth_Principal
- * 
- * @package Sabre
- * @subpackage DAV
- * @copyright Copyright (C) 2007-2010 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_PrincipalCollection extends Sabre_DAV_Directory {
-
-    /**
-     * The name of this object. It is not adviced to change this.
-     * The plugins that depend on the principals collection to exist need to 
-     * be have a common name to find it.
-     */
-    const NODENAME = 'principals';
-
-    /**
-     * Authentication backend 
-     * 
-     * @var Sabre_DAV_Auth_Backend 
-     */
-    protected $authBackend;
-
-    /**
-     * Creates the object 
-     * 
-     * @param Sabre_DAV_Auth_Backend_Abstract $authBackend 
-     */
-    public function __construct(Sabre_DAV_Auth_Backend_Abstract $authBackend) {
-
-        $this->authBackend = $authBackend;
-
-    }
-
-    /**
-     * Returns the name of this collection. 
-     * 
-     * @return string 
-     */
-    public function getName() {
-
-        return self::NODENAME; 
-
-    }
-
-    /**
-     * Retursn the list of users 
-     * 
-     * @return void
-     */
-    public function getChildren() {
-
-        $children = array();
-        foreach($this->authBackend->getUsers() as $principalInfo) {
-
-            $principalUri = $principalInfo['uri'] . '/';
-            $children[] = new Sabre_DAV_Auth_Principal($principalUri,$principalInfo);
-
-
-        }
-        return $children; 
-
-    }
-
-}
index 71558e8..2d94028 100755 (executable)
@@ -49,11 +49,12 @@ class Sabre_DAV_ObjectTree extends Sabre_DAV_Tree {
      */
     public function getNodeForPath($path) {
 
-        $path = trim($path,'/');
+       $path = trim($path,'/');
         if (isset($this->cache[$path])) return $this->cache[$path];
 
         //if (!$path || $path=='.') return $this->rootNode;
         $currentNode = $this->rootNode;
+
         $i=0;
         // We're splitting up the path variable into folder/subfolder components and traverse to the correct node.. 
         foreach(explode('/',$path) as $pathPart) {
@@ -65,8 +66,7 @@ class Sabre_DAV_ObjectTree extends Sabre_DAV_Tree {
                die();
                 throw new Sabre_DAV_Exception_FileNotFound('Could not find node at path: ' . $path);
             }
-            $currentNode = $currentNode->getChild($pathPart); 
-
+            $currentNode = $currentNode->getChild($pathPart);
         }
 
         $this->cache[$path] = $currentNode;
@@ -108,7 +108,6 @@ class Sabre_DAV_ObjectTree extends Sabre_DAV_Tree {
      * @return array 
      */
     public function getChildren($path) {
-
         $node = $this->getNodeForPath($path);
         $children = $node->getChildren();
         foreach($children as $child) {
diff --git a/lib/Sabre/DAV/Property/Principal.php b/lib/Sabre/DAV/Property/Principal.php
deleted file mode 100755 (executable)
index df3f784..0000000
+++ /dev/null
@@ -1,126 +0,0 @@
-<?php
-
-/**
- * Principal property
- *
- * The principal property represents a principal from RFC3744 (ACL).
- * The property can be used to specify a principal or pseudo principals. 
- *
- * @package Sabre
- * @subpackage DAV
- * @copyright Copyright (C) 2007-2010 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_Property_Principal extends Sabre_DAV_Property implements Sabre_DAV_Property_IHref {
-
-    /**
-     * To specify a not-logged-in user, use the UNAUTHENTICTED principal
-     */
-    const UNAUTHENTICATED = 1;
-
-    /**
-     * To specify any principal that is logged in, use AUTHENTICATED
-     */
-    const AUTHENTICATED = 2;
-
-    /**
-     * Specific princpals can be specified with the HREF
-     */
-    const HREF = 3;
-
-    /**
-     * Principal-type
-     *
-     * Must be one of the UNAUTHENTICATED, AUTHENTICATED or HREF constants.
-     * 
-     * @var int 
-     */
-    private $type;
-
-    /**
-     * Url to principal
-     *
-     * This value is only used for the HREF principal type.
-     * 
-     * @var string 
-     */
-    private $href;
-
-    /**
-     * Creates the property.
-     *
-     * The 'type' argument must be one of the type constants defined in this class.
-     *
-     * 'href' is only required for the HREF type.
-     * 
-     * @param int $type 
-     * @param string $href 
-     * @return void
-     */
-    public function __construct($type, $href = null) {
-
-        $this->type = $type;
-
-        if ($type===self::HREF && is_null($href)) {
-            throw new Sabre_DAV_Exception('The href argument must be specified for the HREF principal type.');
-        }
-        $this->href = $href;
-
-    }
-
-    /**
-     * Returns the principal type 
-     * 
-     * @return int 
-     */
-    public function getType() {
-
-        return $this->type;
-
-    }
-
-    /**
-     * Returns the principal uri. 
-     * 
-     * @return string
-     */
-    public function getHref() {
-
-        return $this->href;
-
-    }
-
-    /**
-     * Serializes the property into a DOMElement. 
-     * 
-     * @param Sabre_DAV_Server $server 
-     * @param DOMElement $node 
-     * @return void
-     */
-    public function serialize(Sabre_DAV_Server $server, DOMElement $node) {
-
-        $prefix = $server->xmlNamespaces['DAV:'];
-        switch($this->type) {
-
-            case self::UNAUTHENTICATED :
-                $node->appendChild(
-                    $node->ownerDocument->createElement($prefix . ':unauthenticated')
-                );
-                break;
-            case self::AUTHENTICATED :
-                $node->appendChild(
-                    $node->ownerDocument->createElement($prefix . ':authenticated')
-                );
-                break;
-            case self::HREF :
-                $href = $node->ownerDocument->createElement($prefix . ':href');
-                $href->nodeValue = $server->getBaseUri() . $this->href;
-                $node->appendChild($href);
-                break;
-
-        }
-
-    }
-
-}
index 54b4ea6..c2de051 100755 (executable)
@@ -15,7 +15,7 @@
 class Sabre_DAV_Property_ResourceType extends Sabre_DAV_Property {
 
     /**
-     * resourceType 
+     * resourceType a
      * 
      * @var array
      */
index 85be9d2..c6efa24 100755 (executable)
@@ -127,7 +127,7 @@ class Sabre_DAV_Server {
      *
      * @var bool 
      */
-    public $debugExceptions = false;
+    public $debugExceptions = true;
 
     /**
      * This property allows you to automatically add the 'resourcetype' value 
@@ -1278,7 +1278,6 @@ class Sabre_DAV_Server {
         if ($depth!=0) $depth = 1;
 
         $returnPropertyList = array();
-        
         $parentNode = $this->tree->getNodeForPath($path);
         $nodes = array(
             $path => $parentNode
diff --git a/lib/Sabre/DAVACL/AbstractPrincipalCollection.php b/lib/Sabre/DAVACL/AbstractPrincipalCollection.php
new file mode 100644 (file)
index 0000000..556cc40
--- /dev/null
@@ -0,0 +1,120 @@
+<?php
+
+/**
+ * Principals Collection
+ *
+ * This is a helper class that easily allows you to create a collection that 
+ * has a childnode for every principal.
+ * 
+ * To use this class, simply implement the getChildForPrincipal method. 
+ *
+ * @package Sabre
+ * @subpackage DAVACL
+ * @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_DAVACL_AbstractPrincipalCollection extends Sabre_DAV_Collection  {
+
+    /**
+     * Node or 'directory' name. 
+     * 
+     * @var string 
+     */
+    protected $path;
+
+    /**
+     * Principal backend 
+     * 
+     * @var Sabre_DAVACL_IPrincipalBackend 
+     */
+    protected $principalBackend;
+
+    /**
+     * If this value is set to true, it effectively disables listing of users
+     * it still allows user to find other users if they have an exact url. 
+     * 
+     * @var bool 
+     */
+    public $disableListing = false;
+
+    /**
+     * Creates the object
+     *
+     * This object must be passed the principal backend. This object will 
+     * filter all principals from a specfied prefix ($principalPrefix). The 
+     * default is 'principals', if your principals are stored in a different 
+     * collection, override $principalPrefix
+     * 
+     * 
+     * @param Sabre_DAVACL_IPrincipalBackend $principalBackend 
+     * @param string $principalPrefix
+     * @param string $nodeName
+     */
+    public function __construct(Sabre_DAVACL_IPrincipalBackend $principalBackend, $principalPrefix = 'principals') {
+
+        $this->principalPrefix = $principalPrefix;
+        $this->principalBackend = $principalBackend;
+
+    }
+
+    /**
+     * 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 $principalInfo 
+     * @return Sabre_DAVACL_IPrincipal
+     */
+    abstract function getChildForPrincipal(array $principalInfo);
+
+    /**
+     * Returns the name of this collection. 
+     * 
+     * @return string 
+     */
+    public function getName() {
+
+        list(,$name) = Sabre_DAV_URLUtil::splitPath($this->principalPrefix);
+        return $name; 
+
+    }
+
+    /**
+     * Return the list of users 
+     * 
+     * @return void
+     */
+    public function getChildren() {
+        if ($this->disableListing)
+            throw new Sabre_DAV_Exception_MethodNotAllowed('Listing members of this collection is disabled');
+
+        $children = array();
+        foreach($this->principalBackend->getPrincipalsByPrefix($this->principalPrefix) as $principalInfo) {
+
+            $children[] = $this->getChildForPrincipal($principalInfo);
+
+
+        }
+        return $children; 
+
+    }
+
+    /**
+     * Returns a child object, by its name.
+     * 
+     * @param string $name
+     * @throws Sabre_DAV_Exception_FileNotFound
+     * @return Sabre_DAV_IPrincipal
+     */
+    public function getChild($name) {
+        $principalInfo = $this->principalBackend->getPrincipalByPath($this->principalPrefix . '/' . $name);
+        if (!$principalInfo) throw new Sabre_DAV_Exception_FileNotFound('Principal with name ' . $name . ' not found');
+        $return = $this->getChildForPrincipal($principalInfo);
+        return $return;
+
+    }
+
+}
diff --git a/lib/Sabre/DAVACL/Exception/AceConflict.php b/lib/Sabre/DAVACL/Exception/AceConflict.php
new file mode 100644 (file)
index 0000000..e898b8b
--- /dev/null
@@ -0,0 +1,34 @@
+<?php
+
+/**
+ * Sabre_DAVACL_Exception_AceConflict 
+ * 
+ * @package Sabre
+ * @subpackage DAVACL
+ * @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_DAVACL_Exception_AceConflict extends Sabre_DAV_Exception_Conflict {
+
+    /**
+     * Adds in extra information in the xml response.
+     *
+     * This method adds the {DAV:}no-ace-conflict element as defined in rfc3744
+     * 
+     * @param Sabre_DAV_Server $server 
+     * @param DOMElement $errorNode 
+     * @return void
+     */
+    public function serialize(Sabre_DAV_Server $server,DOMElement $errorNode) {
+        
+        $doc = $errorNode->ownerDocument;
+        
+        $np = $doc->createElementNS('DAV:','d:no-ace-conflict');
+        $errorNode->appendChild($np);
+
+    }
+
+}
+
+?>
diff --git a/lib/Sabre/DAVACL/Exception/NeedPrivileges.php b/lib/Sabre/DAVACL/Exception/NeedPrivileges.php
new file mode 100644 (file)
index 0000000..8bc7b55
--- /dev/null
@@ -0,0 +1,80 @@
+<?php
+
+/**
+ * NeedPrivileges 
+ *
+ * The 403-need privileges is thrown when a user didn't have the appropriate
+ * permissions to perform an operation
+ * 
+ * @package Sabre
+ * @subpackage DAVACL
+ * @version $Id$
+ * @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_DAVACL_Exception_NeedPrivileges extends Sabre_DAV_Exception_Forbidden {
+
+    /**
+     * The relevant uri 
+     * 
+     * @var string 
+     */
+    protected $uri;
+
+    /**
+     * The privileges the user didn't have. 
+     * 
+     * @var array 
+     */
+    protected $privileges;
+
+    /**
+     * Constructor 
+     * 
+     * @param string $uri 
+     * @param array $privileges 
+     */
+    public function __construct($uri,array $privileges) {
+
+        $this->uri = $uri;
+        $this->privileges = $privileges;
+
+    }
+
+    /**
+     * Adds in extra information in the xml response.
+     *
+     * This method adds the {DAV:}need-privileges element as defined in rfc3744
+     * 
+     * @param Sabre_DAV_Server $server 
+     * @param DOMElement $errorNode 
+     * @return void
+     */
+    public function serialize(Sabre_DAV_Server $server,DOMElement $errorNode) {
+        
+        $doc = $errorNode->ownerDocument;
+        
+        $np = $doc->createElementNS('DAV:','d:need-privileges');
+        $errorNode->appendChild($np);
+
+        foreach($this->privileges as $privilege) {
+
+            $resource = $doc->createElementNS('DAV:','d:resource');
+            $np->appendChild($resource);
+
+            $resource->appendChild($doc->createElementNS('DAV:','d:href',$server->getBaseUri() . $this->uri));
+
+            $priv = $doc->createElementNS('DAV:','d:privilege');
+            $resource->appendChild($priv);
+
+            preg_match('/^{([^}]*)}(.*)$/',$privilege,$privilegeParts);
+            $priv->appendChild($doc->createElementNS($privilegeParts[1],'d:' . $privilegeParts[2]));
+
+
+        }
+
+    }
+
+}
+
diff --git a/lib/Sabre/DAVACL/Exception/NoAbstract.php b/lib/Sabre/DAVACL/Exception/NoAbstract.php
new file mode 100644 (file)
index 0000000..9bf1967
--- /dev/null
@@ -0,0 +1,34 @@
+<?php
+
+/**
+ * Sabre_DAVACL_Exception_NoAbstract
+ * 
+ * @package Sabre
+ * @subpackage DAVACL
+ * @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_DAVACL_Exception_NoAbstract extends Sabre_DAV_Exception_PreconditionFailed {
+
+    /**
+     * Adds in extra information in the xml response.
+     *
+     * This method adds the {DAV:}no-abstract element as defined in rfc3744
+     * 
+     * @param Sabre_DAV_Server $server 
+     * @param DOMElement $errorNode 
+     * @return void
+     */
+    public function serialize(Sabre_DAV_Server $server,DOMElement $errorNode) {
+        
+        $doc = $errorNode->ownerDocument;
+        
+        $np = $doc->createElementNS('DAV:','d:no-abstract');
+        $errorNode->appendChild($np);
+
+    }
+
+}
+
+?>
diff --git a/lib/Sabre/DAVACL/Exception/NotRecognizedPrincipal.php b/lib/Sabre/DAVACL/Exception/NotRecognizedPrincipal.php
new file mode 100644 (file)
index 0000000..b8ab5cf
--- /dev/null
@@ -0,0 +1,34 @@
+<?php
+
+/**
+ * Sabre_DAVACL_Exception_NotRecognizedPrincipal
+ * 
+ * @package Sabre
+ * @subpackage DAVACL
+ * @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_DAVACL_Exception_NotRecognizedPrincipal extends Sabre_DAV_Exception_PreconditionFailed {
+
+    /**
+     * Adds in extra information in the xml response.
+     *
+     * This method adds the {DAV:}recognized-principal element as defined in rfc3744
+     * 
+     * @param Sabre_DAV_Server $server 
+     * @param DOMElement $errorNode 
+     * @return void
+     */
+    public function serialize(Sabre_DAV_Server $server,DOMElement $errorNode) {
+        
+        $doc = $errorNode->ownerDocument;
+        
+        $np = $doc->createElementNS('DAV:','d:recognized-principal');
+        $errorNode->appendChild($np);
+
+    }
+
+}
+
+?>
diff --git a/lib/Sabre/DAVACL/Exception/NotSupportedPrivilege.php b/lib/Sabre/DAVACL/Exception/NotSupportedPrivilege.php
new file mode 100644 (file)
index 0000000..7257dd1
--- /dev/null
@@ -0,0 +1,34 @@
+<?php
+
+/**
+ * Sabre_DAVACL_Exception_NotSupportedPrivilege
+ * 
+ * @package Sabre
+ * @subpackage DAVACL
+ * @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_DAVACL_Exception_NotSupportedPrivilege extends Sabre_DAV_Exception_PreconditionFailed {
+
+    /**
+     * Adds in extra information in the xml response.
+     *
+     * This method adds the {DAV:}not-supported-privilege element as defined in rfc3744
+     * 
+     * @param Sabre_DAV_Server $server 
+     * @param DOMElement $errorNode 
+     * @return void
+     */
+    public function serialize(Sabre_DAV_Server $server,DOMElement $errorNode) {
+        
+        $doc = $errorNode->ownerDocument;
+        
+        $np = $doc->createElementNS('DAV:','d:not-supported-privilege');
+        $errorNode->appendChild($np);
+
+    }
+
+}
+
+?>
diff --git a/lib/Sabre/DAVACL/IACL.php b/lib/Sabre/DAVACL/IACL.php
new file mode 100644 (file)
index 0000000..e53d310
--- /dev/null
@@ -0,0 +1,58 @@
+<?php
+
+/**
+ * ACL-enabled node
+ *
+ * If you want to add WebDAV ACL to a node, you must implement this class
+ * 
+ * @package Sabre
+ * @subpackage DAVACL
+ * @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_DAVACL_IACL extends Sabre_DAV_INode {
+
+    /**
+     * Returns the owner principal
+     *
+     * This must be a url to a principal, or null if there's no owner 
+     * 
+     * @return string|null
+     */
+    function getOwner();
+
+    /**
+     * Returns a group principal
+     *
+     * This must be a url to a principal, or null if there's no owner
+     * 
+     * @return string|null 
+     */
+    function getGroup();
+
+    /**
+     * 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 
+     */
+    function getACL();
+
+    /**
+     * Updates the ACL
+     *
+     * This method will receive a list of new ACE's. 
+     * 
+     * @param array $acl 
+     * @return void
+     */
+    function setACL(array $acl);
+
+}
diff --git a/lib/Sabre/DAVACL/IPrincipal.php b/lib/Sabre/DAVACL/IPrincipal.php
new file mode 100644 (file)
index 0000000..34b0fcc
--- /dev/null
@@ -0,0 +1,75 @@
+<?php
+
+/**
+ * IPrincipal interface
+ * 
+ * Implement this interface to define your own principals
+ * 
+ * @package Sabre
+ * @subpackage DAVACL
+ * @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_DAVACL_IPrincipal extends Sabre_DAV_INode {
+
+    /**
+     * Returns a list of altenative urls for a principal
+     * 
+     * This can for example be an email address, or ldap url.
+     * 
+     * @return array 
+     */
+    function getAlternateUriSet();
+
+    /**
+     * Returns the full principal url 
+     * 
+     * @return string 
+     */
+    function getPrincipalUrl();
+
+    /**
+     * 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
+     */
+    function getGroupMemberSet();
+
+    /**
+     * 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 
+     */
+    function getGroupMembership();
+
+    /**
+     * 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 
+     */
+    function setGroupMemberSet(array $principals);
+
+    /**
+     * Returns the displayname
+     *
+     * This should be a human readable name for the principal.
+     * If none is available, return the nodename. 
+     * 
+     * @return string 
+     */
+    function getDisplayName();
+
+}
diff --git a/lib/Sabre/DAVACL/IPrincipalBackend.php b/lib/Sabre/DAVACL/IPrincipalBackend.php
new file mode 100644 (file)
index 0000000..ec8529f
--- /dev/null
@@ -0,0 +1,73 @@
+<?php
+
+/**
+ * Implement this interface to create your own principal backends.
+ *
+ * Creating backends for principals is entirely optional. You can also 
+ * implement Sabre_DAVACL_IPrincipal directly. This interface is used solely by 
+ * Sabre_DAVACL_AbstractPrincipalCollection.
+ *
+ * @package Sabre
+ * @subpackage DAVACL
+ * @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_DAVACL_IPrincipalBackend {
+
+    /**
+     * Returns a list of principals based on a prefix.
+     *
+     * This prefix will often contain something like 'principals'. You are only 
+     * expected to return principals that are in this base path.
+     *
+     * You are expected to return at least a 'uri' for every user, you can 
+     * return any additional properties if you wish so. Common properties are:
+     *   {DAV:}displayname 
+     *   {http://sabredav.org/ns}email-address - This is a custom SabreDAV 
+     *     field that's actualy injected in a number of other properties. If
+     *     you have an email address, use this property.
+     * 
+     * @param string $prefixPath 
+     * @return array 
+     */
+    function getPrincipalsByPrefix($prefixPath);
+
+    /**
+     * Returns a specific principal, specified by it's path.
+     * The returned structure should be the exact same as from 
+     * getPrincipalsByPrefix. 
+     * 
+     * @param string $path 
+     * @return array 
+     */
+    function getPrincipalByPath($path);
+
+    /**
+     * Returns the list of members for a group-principal 
+     * 
+     * @param string $principal 
+     * @return array 
+     */
+    function getGroupMemberSet($principal);
+
+    /**
+     * Returns the list of groups a principal is a member of 
+     * 
+     * @param string $principal 
+     * @return array 
+     */
+    function getGroupMembership($principal);
+
+    /**
+     * Updates the list of group members for a group principal.
+     *
+     * The principals should be passed as a list of uri's. 
+     * 
+     * @param string $principal 
+     * @param array $members 
+     * @return void
+     */
+    function setGroupMemberSet($principal, array $members); 
+
+}
diff --git a/lib/Sabre/DAVACL/Plugin.php b/lib/Sabre/DAVACL/Plugin.php
new file mode 100644 (file)
index 0000000..4ea72c3
--- /dev/null
@@ -0,0 +1,1240 @@
+<?php
+
+/**
+ * SabreDAV ACL Plugin
+ *
+ * This plugin provides funcitonality to enforce ACL permissions.
+ * ACL is defined in RFC3744.
+ *
+ * In addition it also provides support for the {DAV:}current-user-principal 
+ * property, defined in RFC5397 and the {DAV:}expand-property report, as 
+ * defined in RFC3253. 
+ * 
+ * @package Sabre
+ * @subpackage DAVACL
+ * @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_DAVACL_Plugin extends Sabre_DAV_ServerPlugin {
+
+    /**
+     * Recursion constants
+     *
+     * This only checks the base node
+     */
+    const R_PARENT = 1;
+
+    /**
+     * Recursion constants
+     *
+     * This checks every node in the tree
+     */
+    const R_RECURSIVE = 2;
+
+    /**
+     * Recursion constants
+     *
+     * This checks every parentnode in the tree, but not leaf-nodes.
+     */
+    const R_RECURSIVEPARENTS = 3;
+
+    /**
+     * Reference to server object. 
+     * 
+     * @var Sabre_DAV_Server 
+     */
+    protected $server;
+
+    /**
+     * List of urls containing principal collections.
+     * Modify this if your principals are located elsewhere. 
+     * 
+     * @var array
+     */
+    public $principalCollectionSet = array(
+        'principals',
+    );
+
+    /**
+     * By default ACL is only enforced for nodes that have ACL support (the 
+     * ones that implement Sabre_DAVACL_IACL). For any other node, access is 
+     * always granted.
+     *
+     * To override this behaviour you can turn this setting off. This is useful 
+     * if you plan to fully support ACL in the entire tree.
+     *
+     * @var bool 
+     */
+    public $allowAccessToNodesWithoutACL = true;
+
+    /**
+     * By default nodes that are inaccessible by the user, can still be seen
+     * in directory listings (PROPFIND on parent with Depth: 1)
+     *
+     * In certain cases it's desirable to hide inaccessible nodes. Setting this 
+     * to true will cause these nodes to be hidden from directory listings.
+     * 
+     * @var bool 
+     */
+    public $hideNodesFromListings = false;
+
+    /**
+     * This string is prepended to the username of the currently logged in 
+     * user. This allows the plugin to determine the principal path based on 
+     * the username.
+     * 
+     * @var string
+     */
+    public $defaultUsernamePath = 'principals';
+
+    /**
+     * Returns a list of features added by this plugin.
+     *
+     * This list is used in the response of a HTTP OPTIONS request.
+     * 
+     * @return array 
+     */
+    public function getFeatures() {
+
+        return array('access-control');
+
+    }
+
+    /**
+     * Returns a list of available methods for a given url 
+     * 
+     * @param string $uri 
+     * @return array 
+     */
+    public function getMethods($uri) {
+
+        return array('ACL');
+
+    }
+
+    /**
+     * 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 'acl';
+
+    }
+
+    /**
+     * 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) {
+
+        return array(
+            '{DAV:}expand-property',
+            '{DAV:}principal-property-search',
+            '{DAV:}principal-search-property-set', 
+        );
+
+    }
+
+
+    /**
+     * Checks if the current user has the specified privilege(s). 
+     * 
+     * You can specify a single privilege, or a list of privileges.
+     * This method will throw an exception if the privilege is not available
+     * and return true otherwise.
+     *
+     * @param string $uri
+     * @param array|string $privileges
+     * @param bool $throwExceptions if set to false, this method won't through exceptions. 
+     * @throws Sabre_DAVACL_Exception_NeedPrivileges
+     * @return bool 
+     */
+    public function checkPrivileges($uri,$privileges,$recursion = self::R_PARENT, $throwExceptions = true) {
+
+        if (!is_array($privileges)) $privileges = array($privileges);
+
+        $acl = $this->getCurrentUserPrivilegeSet($uri);
+
+        if (is_null($acl)) {
+            if ($this->allowAccessToNodesWithoutACL) {
+                return true;
+            } else {
+                if ($throwExceptions) 
+                    throw new Sabre_DAVACL_Exception_NeedPrivileges($uri,$privileges);
+                else
+                    return false;
+
+            }
+        }
+
+        $failed = array();
+        foreach($privileges as $priv) {
+
+            if (!in_array($priv, $acl)) {
+                $failed[] = $priv;
+            }
+
+        }
+
+        if ($failed) {
+            if ($throwExceptions) 
+                throw new Sabre_DAVACL_Exception_NeedPrivileges($uri,$failed);
+            else
+                return false;
+        }
+        return true;
+
+    }
+
+    /**
+     * Returns the standard users' principal.
+     *
+     * This is one authorative principal url for the current user.
+     * This method will return null if the user wasn't logged in. 
+     * 
+     * @return string|null 
+     */
+    public function getCurrentUserPrincipal() {
+
+        $authPlugin = $this->server->getPlugin('auth');
+        if (is_null($authPlugin)) return null;
+
+        $userName = $authPlugin->getCurrentUser();
+        if (!$userName) return null;
+
+        return $this->defaultUsernamePath . '/' . $userName;
+
+    }
+
+    /**
+     * Returns a list of principals that's associated to the current
+     * user, either directly or through group membership. 
+     * 
+     * @return array 
+     */
+    public function getCurrentUserPrincipals() {
+
+        $currentUser = $this->getCurrentUserPrincipal();
+
+        if (is_null($currentUser)) return array();
+
+        $check = array($currentUser);
+        $principals = array($currentUser);
+
+        while(count($check)) {
+
+            $principal = array_shift($check);
+
+            $node = $this->server->tree->getNodeForPath($principal);
+
+            if ($node instanceof Sabre_DAVACL_IPrincipal) {
+
+                foreach($node->getGroupMembership() as $groupMember) {
+
+                    if (!in_array($groupMember, $principals)) {
+
+                        $check[] = $groupMember;
+                        $principals[] = $groupMember;
+
+                    }
+
+                }
+
+            }
+
+        }
+
+        return $principals;
+
+    }
+
+    /**
+     * Returns the supported privilege structure for this ACL plugin.
+     *
+     * See RFC3744 for more details. Currently we default on a simple,
+     * standard structure. 
+     * 
+     * @return array 
+     */
+    public function getSupportedPrivilegeSet() {
+
+        return array(
+            'privilege'  => '{DAV:}all',
+            'abstract'   => true,
+            'aggregates' => array(
+                array(
+                    'privilege'  => '{DAV:}read',
+                    'aggregates' => array(
+                        array(
+                            'privilege' => '{DAV:}read-acl',
+                            'abstract'  => true,
+                        ),
+                        array(
+                            'privilege' => '{DAV:}read-current-user-privilege-set',
+                            'abstract'  => true,
+                        ),
+                    ),
+                ), // {DAV:}read
+                array(
+                    'privilege'  => '{DAV:}write',
+                    'aggregates' => array(
+                        array(
+                            'privilege' => '{DAV:}write-acl',
+                            'abstract'  => true,
+                        ),
+                        array(
+                            'privilege' => '{DAV:}write-properties',
+                            'abstract'  => true,
+                        ),
+                        array(
+                            'privilege' => '{DAV:}write-content',
+                            'abstract'  => true,
+                        ),
+                        array(
+                            'privilege' => '{DAV:}bind',
+                            'abstract'  => true,
+                        ),
+                        array(
+                            'privilege' => '{DAV:}unbind',
+                            'abstract'  => true,
+                        ),
+                        array(
+                            'privilege' => '{DAV:}unlock',
+                            'abstract'  => true,
+                        ),
+                    ),
+                ), // {DAV:}write
+            ), 
+        ); // {DAV:}all
+
+    }
+
+    /**
+     * Returns the supported privilege set as a flat list
+     *
+     * This is much easier to parse.
+     *
+     * The returned list will be index by privilege name.
+     * The value is a struct containing the following properties:
+     *   - aggregates
+     *   - abstract
+     *   - concrete
+     * 
+     * @return array 
+     */
+    final public function getFlatPrivilegeSet() {
+
+        $privs = $this->getSupportedPrivilegeSet();
+
+        $flat = array();
+        $this->getFPSTraverse($privs, null, $flat);
+
+        return $flat;
+
+    }
+
+    /**
+     * Traverses the privilege set tree for reordering
+     *
+     * This function is solely used by getFlatPrivilegeSet, and would have been 
+     * a closure if it wasn't for the fact I need to support PHP 5.2.
+     * 
+     * @return void
+     */
+    final private function getFPSTraverse($priv, $concrete, &$flat) {
+
+        $myPriv = array(
+            'privilege' => $priv['privilege'],
+            'abstract' => isset($priv['abstract']) && $priv['abstract'],
+            'aggregates' => array(),
+            'concrete' => isset($priv['abstract']) && $priv['abstract']?$concrete:$priv['privilege'],
+        );
+
+        if (isset($priv['aggregates']))
+            foreach($priv['aggregates'] as $subPriv) $myPriv['aggregates'][] = $subPriv['privilege'];
+
+        $flat[$priv['privilege']] = $myPriv;
+
+        if (isset($priv['aggregates'])) {
+
+            foreach($priv['aggregates'] as $subPriv) {
+            
+                $this->getFPSTraverse($subPriv, $myPriv['concrete'], $flat);
+
+            }
+
+        }
+
+    }
+
+    /**
+     * Returns the full ACL list.
+     *
+     * Either a uri or a Sabre_DAV_INode may be passed.
+     *
+     * null will be returned if the node doesn't support ACLs. 
+     * 
+     * @param string|Sabre_DAV_INode $node
+     * @return array
+     */
+    public function getACL($node) {
+
+        if (is_string($node)) {
+            $node = $this->server->tree->getNodeForPath($node);
+        }
+        if ($node instanceof Sabre_DAVACL_IACL) {
+            return $node->getACL();
+        }
+        return null; 
+
+    }
+
+    /**
+     * Returns a list of privileges the current user has
+     * on a particular node.
+     *
+     * Either a uri or a Sabre_DAV_INode may be passed.
+     *
+     * null will be returned if the node doesn't support ACLs. 
+     * 
+     * @param string|Sabre_DAV_INode $node 
+     * @return array 
+     */
+    public function getCurrentUserPrivilegeSet($node) {
+
+        if (is_string($node)) {
+            $node = $this->server->tree->getNodeForPath($node);
+        }
+
+        $acl = $this->getACL($node);
+
+        if (is_null($acl)) return null;
+
+        $principals = $this->getCurrentUserPrincipals();
+
+        $collected = array();
+
+        foreach($acl as $ace) {
+            if (in_array($ace['principal'], $principals)) {
+                $collected[] = $ace;
+            }
+
+        }
+
+        // Now we deduct all aggregated privileges.
+        $flat = $this->getFlatPrivilegeSet();
+
+        $collected2 = array();
+        foreach($collected as $privilege) {
+
+            $collected2[] = $privilege['privilege'];
+            foreach($flat[$privilege['privilege']]['aggregates'] as $subPriv) {
+                if (!in_array($subPriv, $collected2)) 
+                    $collected2[] = $subPriv;
+            }
+
+        }
+
+        return $collected2;
+
+    }
+
+    /**
+     * Sets up the plugin
+     *
+     * This method is automatically called by the server class.
+     * 
+     * @param Sabre_DAV_Server $server 
+     * @return void
+     */
+    public function initialize(Sabre_DAV_Server $server) {
+
+        $this->server = $server;
+        $server->subscribeEvent('beforeGetProperties',array($this,'beforeGetProperties'));
+
+        $server->subscribeEvent('beforeMethod', array($this,'beforeMethod'),20);
+        $server->subscribeEvent('beforeBind', array($this,'beforeBind'),20);
+        $server->subscribeEvent('beforeUnbind', array($this,'beforeUnbind'),20);
+        $server->subscribeEvent('updateProperties',array($this,'updateProperties'));
+        $server->subscribeEvent('beforeUnlock', array($this,'beforeUnlock'),20);
+        $server->subscribeEvent('report',array($this,'report'));
+        $server->subscribeEvent('unknownMethod', array($this, 'unknownMethod'));
+
+        array_push($server->protectedProperties,
+            '{DAV:}alternate-URI-set',
+            '{DAV:}principal-URL',
+            '{DAV:}group-membership',
+            '{DAV:}principal-collection-set',
+            '{DAV:}current-user-principal',
+            '{DAV:}supported-privilege-set',
+            '{DAV:}current-user-privilege-set',
+            '{DAV:}acl',
+            '{DAV:}acl-restrictions',
+            '{DAV:}inherited-acl-set',
+            '{DAV:}owner',
+            '{DAV:}group'
+        );
+
+        // Automatically mapping nodes implementing IPrincipal to the 
+        // {DAV:}principal resourcetype.
+        $server->resourceTypeMapping['Sabre_DAVACL_IPrincipal'] = '{DAV:}principal';
+
+        // Mapping the group-member-set property to the HrefList property 
+        // class.
+        $server->propertyMap['{DAV:}group-member-set'] = 'Sabre_DAV_Property_HrefList';
+
+    }
+
+
+    /* {{{ Event handlers */
+
+    /**
+     * Triggered before any method is handled 
+     * 
+     * @param string $method 
+     * @param string $uri 
+     * @return void
+     */
+    public function beforeMethod($method, $uri) {
+
+        $exists = $this->server->tree->nodeExists($uri);
+
+        // If the node doesn't exists, none of these checks apply
+        if (!$exists) return;
+
+        switch($method) {
+
+            case 'GET' :
+            case 'HEAD' :
+            case 'OPTIONS' :
+                // For these 3 we only need to know if the node is readable.
+                $this->checkPrivileges($uri,'{DAV:}read');
+                break;
+
+            case 'PUT' :
+            case 'LOCK' :
+            case 'UNLOCK' : 
+                // This method requires the write-content priv if the node 
+                // already exists, and bind on the parent if the node is being 
+                // created. 
+                // The bind privilege is handled in the beforeBind event. 
+                $this->checkPrivileges($uri,'{DAV:}write-content');
+                break;
+            
+
+            case 'PROPPATCH' :
+                $this->checkPrivileges($uri,'{DAV:}write-properties');
+                break;
+
+            case 'ACL' :
+                $this->checkPrivileges($uri,'{DAV:}write-acl');
+                break;
+
+            case 'COPY' :
+            case 'MOVE' :
+                // Copy requires read privileges on the entire source tree.
+                // If the target exists write-content normally needs to be 
+                // checked, however, we're deleting the node beforehand and 
+                // creating a new one after, so this is handled by the 
+                // beforeUnbind event.
+                // 
+                // The creation of the new node is handled by the beforeBind 
+                // event.
+                //
+                // If MOVE is used beforeUnbind will also be used to check if 
+                // the sourcenode can be deleted. 
+                $this->checkPrivileges($uri,'{DAV:}read',self::R_RECURSIVE);
+
+                break;
+
+        }
+
+    }
+
+    /**
+     * Triggered before a new node is created.
+     * 
+     * This allows us to check permissions for any operation that creates a
+     * new node, such as PUT, MKCOL, MKCALENDAR, LOCK, COPY and MOVE.
+     * 
+     * @param string $uri 
+     * @return void
+     */
+    public function beforeBind($uri) {
+
+        list($parentUri,$nodeName) = Sabre_DAV_URLUtil::splitPath($uri);
+        $this->checkPrivileges($parentUri,'{DAV:}bind');
+
+    }
+
+    /**
+     * Triggered before a node is deleted 
+     * 
+     * This allows us to check permissions for any operation that will delete 
+     * an existing node. 
+     * 
+     * @param string $uri 
+     * @return void
+     */
+    public function beforeUnbind($uri) {
+
+        list($parentUri,$nodeName) = Sabre_DAV_URLUtil::splitPath($uri);
+        $this->checkPrivileges($parentUri,'{DAV:}unbind',self::R_RECURSIVEPARENTS);
+
+    }
+
+    /**
+     * Triggered before a node is unlocked. 
+     * 
+     * @param string $uri 
+     * @param Sabre_DAV_Locks_LockInfo $lock
+     * @TODO: not yet implemented 
+     * @return void
+     */
+    public function beforeUnlock($uri, Sabre_DAV_Locks_LockInfo $lock) {
+           
+
+    }
+
+    /**
+     * Triggered before properties are looked up in specific nodes. 
+     * 
+     * @param string $uri 
+     * @param Sabre_DAV_INode $node 
+     * @param array $requestedProperties 
+     * @param array $returnedProperties
+     * @TODO really should be broken into multiple methods, or even a class. 
+     * @return void
+     */
+    public function beforeGetProperties($uri, Sabre_DAV_INode $node, &$requestedProperties, &$returnedProperties) {
+
+        // Checking the read permission
+        if (!$this->checkPrivileges($uri,'{DAV:}read',self::R_PARENT,false)) {
+
+            // User is not allowed to read properties
+            if ($this->hideNodesFromListings) {
+                return false;
+            }
+
+            // Marking all requested properties as '403'.
+            foreach($requestedProperties as $key=>$requestedProperty) {
+                unset($requestedProperties[$key]);
+                $returnedProperties[403][$requestedProperty] = null;
+            }
+            return;
+
+        } 
+
+        /* Adding principal properties */
+        if ($node instanceof Sabre_DAVACL_IPrincipal) {
+
+            if (false !== ($index = array_search('{DAV:}alternate-URI-set', $requestedProperties))) {
+
+                unset($requestedProperties[$index]);
+                $returnedProperties[200]['{DAV:}alternate-URI-set'] = new Sabre_DAV_Property_HrefList($node->getAlternateUriSet());
+
+            }
+            if (false !== ($index = array_search('{DAV:}principal-URL', $requestedProperties))) {
+
+                unset($requestedProperties[$index]);
+                $returnedProperties[200]['{DAV:}principal-URL'] = new Sabre_DAV_Property_Href($node->getPrincipalUrl() . '/');
+
+            }
+            if (false !== ($index = array_search('{DAV:}group-member-set', $requestedProperties))) {
+
+                unset($requestedProperties[$index]);
+                $returnedProperties[200]['{DAV:}group-member-set'] = new Sabre_DAV_Property_HrefList($node->getGroupMemberSet());
+
+            }
+            if (false !== ($index = array_search('{DAV:}group-membership', $requestedProperties))) {
+
+                unset($requestedProperties[$index]);
+                $returnedProperties[200]['{DAV:}group-membership'] = new Sabre_DAV_Property_HrefList($node->getGroupMembership());
+
+            }
+
+            if (false !== ($index = array_search('{DAV:}displayname', $requestedProperties))) {
+
+                $returnedProperties[200]['{DAV:}displayname'] = $node->getDisplayName();
+
+            }
+
+        }
+        if (false !== ($index = array_search('{DAV:}principal-collection-set', $requestedProperties))) {
+
+            unset($requestedProperties[$index]);
+            $val = $this->principalCollectionSet;
+            // Ensuring all collections end with a slash
+            foreach($val as $k=>$v) $val[$k] = $v . '/';
+            $returnedProperties[200]['{DAV:}principal-collection-set'] = new Sabre_DAV_Property_HrefList($val);
+
+        }
+        if (false !== ($index = array_search('{DAV:}current-user-principal', $requestedProperties))) {
+
+            unset($requestedProperties[$index]);
+            if ($url = $this->getCurrentUserPrincipal()) {
+                $returnedProperties[200]['{DAV:}current-user-principal'] = new Sabre_DAVACL_Property_Principal(Sabre_DAVACL_Property_Principal::HREF, $url . '/');
+            } else {
+                $returnedProperties[200]['{DAV:}current-user-principal'] = new Sabre_DAVACL_Property_Principal(Sabre_DAVACL_Property_Principal::UNAUTHENTICATED);
+            }
+
+        }
+        if (false !== ($index = array_search('{DAV:}supported-privilege-set', $requestedProperties))) {
+
+            unset($requestedProperties[$index]);
+            $returnedProperties[200]['{DAV:}supported-privilege-set'] = new Sabre_DAVACL_Property_SupportedPrivilegeSet($this->getSupportedPrivilegeSet());
+
+        }
+        if (false !== ($index = array_search('{DAV:}current-user-privilege-set', $requestedProperties))) {
+
+            if (!$this->checkPrivileges($uri, '{DAV:}read-current-user-privilege-set', self::R_PARENT, false)) {
+                $returnedProperties[403]['{DAV:}current-user-privilege-set'] = null;
+                unset($requestedProperties[$index]);
+            } else {
+                $val = $this->getCurrentUserPrivilegeSet($node);
+                if (!is_null($val)) {
+                    unset($requestedProperties[$index]);
+                    $returnedProperties[200]['{DAV:}current-user-privilege-set'] = new Sabre_DAVACL_Property_CurrentUserPrivilegeSet($val);
+                }
+            }
+
+        }
+
+        /* The ACL property contains all the permissions */
+        if (false !== ($index = array_search('{DAV:}acl', $requestedProperties))) {
+
+            if (!$this->checkPrivileges($uri, '{DAV:}read-acl', self::R_PARENT, false)) {
+
+                unset($requestedProperties[$index]);
+                $returnedProperties[403]['{DAV:}acl'] = null;
+
+            } else {
+
+                $acl = $this->getACL($node);
+                if (!is_null($acl)) {
+                    unset($requestedProperties[$index]);
+                    $returnedProperties[200]['{DAV:}acl'] = new Sabre_DAVACL_Property_Acl($this->getACL($node));
+                }
+
+            }
+
+        }
+
+    }
+
+    /**
+     * This method intercepts PROPPATCH methods and make sure the 
+     * group-member-set is updated correctly. 
+     * 
+     * @param array $propertyDelta 
+     * @param array $result 
+     * @param Sabre_DAV_INode $node 
+     * @return void
+     */
+    public function updateProperties(&$propertyDelta, &$result, Sabre_DAV_INode $node) {
+
+        if (!array_key_exists('{DAV:}group-member-set', $propertyDelta))
+            return;
+
+        if (is_null($propertyDelta['{DAV:}group-member-set'])) {
+            $memberSet = array();
+        } elseif ($propertyDelta['{DAV:}group-member-set'] instanceof Sabre_DAV_Property_HrefList) {
+            $memberSet = $propertyDelta['{DAV:}group-member-set']->getHrefs();
+        } else {
+            throw new Sabre_DAV_Exception('The group-member-set property MUST be an instance of Sabre_DAV_Property_HrefList or null');
+        }
+
+        if (!($node instanceof Sabre_DAVACL_IPrincipal)) {
+            $result[403]['{DAV:}group-member-set'] = null;
+            unset($propertyDelta['{DAV:}group-member-set']);
+
+            // Returning false will stop the updateProperties process
+            return false;
+        }
+
+        $node->setGroupMemberSet($memberSet);
+        
+        $result[200]['{DAV:}group-member-set'] = null;
+        unset($propertyDelta['{DAV:}group-member-set']);
+
+    }
+
+    /**
+     * This method handels HTTP REPORT requests 
+     * 
+     * @param string $reportName 
+     * @param DOMNode $dom 
+     * @return void
+     */
+    public function report($reportName, $dom) {
+
+        switch($reportName) {
+
+            case '{DAV:}principal-property-search' :
+                $this->principalPropertySearchReport($dom);
+                return false;
+            case '{DAV:}principal-search-property-set' :
+                $this->principalSearchPropertySetReport($dom);
+                return false; 
+            case '{DAV:}expand-property' :
+                $this->expandPropertyReport($dom);
+                return false;
+
+        }
+
+    }
+
+    /**
+     * This event is triggered for any HTTP method that is not known by the 
+     * webserver. 
+     *
+     * @param string $method 
+     * @param string $uri 
+     * @return void
+     */
+    public function unknownMethod($method, $uri) {
+
+        if ($method!=='ACL') return;
+
+        $this->httpACL($uri);
+        return false;
+
+    }
+
+    /**
+     * This method is responsible for handling the 'ACL' event.
+     *
+     * @param string $uri
+     * @return void
+     */
+    public function httpACL($uri) { 
+
+        $body = $this->server->httpRequest->getBody(true);
+        $dom = Sabre_DAV_XMLUtil::loadDOMDocument($body);
+
+        $newAcl = 
+            Sabre_DAVACL_Property_Acl::unserialize($dom->firstChild)
+            ->getPrivileges();
+
+        // Normalizing urls
+        foreach($newAcl as $k=>$newAce) {
+            $newAcl[$k]['principal'] = $this->server->calculateUri($newAce['principal']);
+        }
+
+        $node = $this->server->tree->getNodeForPath($uri);
+
+        if (!($node instanceof Sabre_DAVACL_IACL)) {
+            throw new Sabre_DAV_Exception_MethodNotAllowed('This node does not support the ACL method');
+        }
+
+        $oldAcl = $this->getACL($node);
+
+        $supportedPrivileges = $this->getFlatPrivilegeSet(); 
+
+        /* Checking if protected principals from the existing principal set are 
+           not overwritten. */
+        foreach($oldAcl as $k=>$oldAce) {
+
+            if (!isset($oldAce['protected']) || !$oldAce['protected']) continue; 
+
+            $found = false;
+            foreach($newAcl as $newAce) {
+                if (
+                    $newAce['privilege'] === $oldAce['privilege'] &&
+                    $newAce['principal'] === $oldAce['principal'] &&
+                    $newAce['protected']
+                ) 
+                $found = true;
+            }
+
+            if (!$found) 
+                throw new Sabre_DAVACL_Exception_AceConflict('This resource contained a protected {DAV:}ace, but this privilege did not occur in the ACL request');
+
+        }
+
+        foreach($newAcl as $k=>$newAce) {
+
+            // Do we recognize the privilege
+            if (!isset($supportedPrivileges[$newAce['privilege']])) {
+                throw new Sabre_DAVACL_Exception_NotSupportedPrivilege('The privilege you specified (' . $newAce['privilege'] . ') is not recognized by this server');
+            }
+
+            if ($supportedPrivileges[$newAce['privilege']]['abstract']) {
+                throw new Sabre_DAVACL_Exception_NoAbstract('The privilege you specified (' . $newAce['privilege'] . ') is an abstract privilege');
+            }
+
+            // Looking up the principal
+            try {
+                $principal = $this->server->tree->getNodeForPath($newAce['principal']);
+            } catch (Sabre_DAV_Exception_FileNotFound $e) {
+                throw new Sabre_DAVACL_Exception_NotRecognizedPrincipal('The specified principal (' . $newAce['principal'] . ') does not exist');
+            }
+            if (!($principal instanceof Sabre_DAVACL_IPrincipal)) {
+                throw new Sabre_DAVACL_Exception_NotRecognizedPrincipal('The specified uri (' . $newAce['principal'] . ') is not a principal');
+            } 
+
+        }
+        $node->setACL($newAcl);
+
+    }
+
+    /* }}} */
+
+    /* Reports {{{ */
+
+    /**
+     * 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 $response) {
+            $response->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);
+
+    }
+
+    /**
+     * This method is used by expandPropertyReport to parse
+     * out the entire HTTP request.
+     * 
+     * @param DOMElement $node 
+     * @return array 
+     */
+    protected function parseExpandPropertyReportRequest($node) {
+
+        $requestedProperties = array();
+        do {
+
+            if (Sabre_DAV_XMLUtil::toClarkNotation($node)!=='{DAV:}property') continue;
+                
+            if ($node->firstChild) {
+                
+                $children = $this->parseExpandPropertyReportRequest($node->firstChild);
+
+            } 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;
+
+    }
+
+    /**
+     * 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
+     */
+    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) {
+                    $hrefs = array($node[200][$propertyName]->getHref());
+                } elseif ($node[200][$propertyName] instanceof Sabre_DAV_Property_HrefList) {
+                    $hrefs = $node[200][$propertyName]->getHrefs();
+                }
+
+                $childProps = array();
+                foreach($hrefs as $href) {
+                    $childProps = array_merge($childProps, $this->expandProperties($href,$childRequestedProperties,0));
+                }
+                $node[200][$propertyName] = new Sabre_DAV_Property_ResponseList($childProps);
+
+            }
+            $result[] = new Sabre_DAV_Property_Response($path, $node);
+
+        }
+
+        return $result;
+
+    }
+
+    /**
+     * principalSearchPropertySetReport
+     *
+     * This method responsible for handing the 
+     * {DAV:}principal-search-property-set report. This report returns a list
+     * of properties the client may search on, using the
+     * {DAV:}principal-property-search report.
+     * 
+     * @param DOMDocument $dom 
+     * @return void
+     */
+    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);
+
+            $currentProperty = $dom->createElement($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());
+
+    }
+
+    /**
+     * principalPropertySearchReport
+     *
+     * This method is reponsible for handing the 
+     * {DAV:}principal-property-search report. This report can be used for 
+     * clients to search for groups of principals, based on the value of one
+     * or more properties.
+     * 
+     * @param DOMDocument $dom 
+     * @return void
+     */
+    protected function principalPropertySearchReport(DOMDocument $dom) {
+
+        $searchableProperties = array(
+            '{DAV:}displayname' => 'display name'
+
+        );
+
+        list($searchProperties, $requestedProperties, $applyToPrincipalCollectionSet) = $this->parsePrincipalPropertySearchReportRequest($dom);
+
+        $result = array();
+
+        if ($applyToPrincipalCollectionSet) {
+            $uris = array();
+        } else {
+            $uris = array($this->server->getRequestUri());
+        }
+
+        $lookupResults = array();
+        foreach($uris as $uri) {
+
+            $p = array_keys($searchProperties);
+            $p[] = '{DAV:}resourcetype';
+            $r = $this->server->getPropertiesForPath($uri, $p, 1);
+
+            // The first item in the results is the parent, so we get rid of it.
+            array_shift($r);
+            $lookupResults = array_merge($lookupResults, $r);
+        } 
+
+        $matches = array();
+
+        foreach($lookupResults as $lookupResult) {
+
+            // We're only looking for principals 
+            if (!isset($lookupResult[200]['{DAV:}resourcetype']) || 
+                (!($lookupResult[200]['{DAV:}resourcetype'] instanceof Sabre_DAV_Property_ResourceType)) ||
+                !$lookupResult[200]['{DAV:}resourcetype']->is('{DAV:}principal')) continue;
+
+            foreach($searchProperties as $searchProperty=>$searchValue) {
+                if (!isset($searchableProperties[$searchProperty])) {
+                    // If a property is not 'searchable', the spec dictates 
+                    // this is not a match. 
+                    continue;
+                }
+
+                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);
+
+    }
+
+    /**
+     * parsePrincipalPropertySearchReportRequest
+     *
+     * This method parses the request body from a
+     * {DAV:}principal-property-search report.
+     *
+     * This method returns an array with two elements:
+     *  1. an array with properties to search on, and their values
+     *  2. a list of propertyvalues that should be returned for the request.
+     * 
+     * @param DOMDocument $dom 
+     * @return array 
+     */
+    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();
+
+        $applyToPrincipalCollectionSet = false;
+
+        // Parsing the search request
+        foreach($dom->firstChild->childNodes as $searchNode) {
+
+            if (Sabre_DAV_XMLUtil::toClarkNotation($searchNode) == '{DAV:}apply-to-principal-collection-set')
+                $applyToPrincipalCollectionSet = true;
+
+            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;
+
+        }
+
+        return array($searchProperties, array_keys(Sabre_DAV_XMLUtil::parseProperties($dom->firstChild)), $applyToPrincipalCollectionSet);
+
+    }
+
+
+    /* }}} */
+
+}
diff --git a/lib/Sabre/DAVACL/Principal.php b/lib/Sabre/DAVACL/Principal.php
new file mode 100644 (file)
index 0000000..9b13f20
--- /dev/null
@@ -0,0 +1,263 @@
+<?php
+
+/**
+ * Principal class
+ *
+ * This class is a representation of a simple principal
+ * 
+ * Many WebDAV specs require a user to show up in the directory 
+ * structure. 
+ *
+ * This principal also has basic ACL settings, only allowing the principal
+ * access it's own principal. 
+ * 
+ * @package Sabre
+ * @subpackage DAVACL
+ * @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_DAVACL_Principal extends Sabre_DAV_Node implements Sabre_DAVACL_IPrincipal, Sabre_DAV_IProperties, Sabre_DAVACL_IACL {
+
+    /**
+     * Struct with principal information.
+     *
+     * @var array 
+     */
+    protected $principalProperties;
+
+    /**
+     * Principal backend 
+     * 
+     * @var Sabre_DAVACL_IPrincipalBackend 
+     */
+    protected $principalBackend;
+
+    /**
+     * Creates the principal object 
+     *
+     * @param array $principalProperties
+     */
+    public function __construct(Sabre_DAVACL_IPrincipalBackend $principalBackend, array $principalProperties = array()) {
+
+        if (!isset($principalProperties['uri'])) {
+            throw new Sabre_DAV_Exception('The principal properties must at least contain the \'uri\' key');
+        }
+        $this->principalBackend = $principalBackend;
+        $this->principalProperties = $principalProperties;
+
+    }
+
+    /**
+     * Returns the full principal url 
+     * 
+     * @return string 
+     */
+    public function getPrincipalUrl() {
+
+        return $this->principalProperties['uri'];
+
+    } 
+
+    /**
+     * 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() {
+
+        $uris = array();
+        if (isset($this->principalProperties['{DAV:}alternate-URI-set'])) {
+
+            $uris = $this->principalProperties['{DAV:}alternate-URI-set'];
+
+        }
+
+        if (isset($this->principalProperties['{http://sabredav.org/ns}email-address'])) {
+            $uris[] = 'mailto:' . $this->principalProperties['{http://sabredav.org/ns}email-address'];
+        }
+
+        return array_unique($uris); 
+
+    }
+
+    /**
+     * 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->principalProperties['uri']);
+
+    }
+
+    /**
+     * 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->principalProperties['uri']);
+
+    }
+
+
+    /**
+     * 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 $groupMembers) {
+
+        $this->principalBackend->setGroupMemberSet($this->principalProperties['uri'], $groupMembers);
+
+    }
+
+
+    /**
+     * Returns this principals name.
+     * 
+     * @return string 
+     */
+    public function getName() {
+
+        $uri = $this->principalProperties['uri'];
+        list(, $name) = Sabre_DAV_URLUtil::splitPath($uri);
+
+        return $name;
+
+    }
+
+    /**
+     * Returns the name of the user 
+     * 
+     * @return void
+     */
+    public function getDisplayName() {
+
+        if (isset($this->principalProperties['{DAV:}displayname'])) {
+            return $this->principalProperties['{DAV:}displayname'];
+        } else {
+            return $this->getName();
+        }
+
+    }
+
+    /**
+     * Returns a list of properties 
+     * 
+     * @param array $requestedProperties 
+     * @return void
+     */
+    public function getProperties($requestedProperties) {
+
+        $newProperties = array();
+        foreach($requestedProperties as $propName) {
+            
+            if (isset($this->principalProperties[$propName])) {
+                $newProperties[$propName] = $this->principalProperties[$propName];
+            }
+
+        }
+
+        return $newProperties;
+        
+    }
+
+    /**
+     * Updates this principals properties.
+     *
+     * Currently this is not supported
+     * 
+     * @param array $properties
+     * @see Sabre_DAV_IProperties::updateProperties
+     * @return bool|array 
+     */
+    public function updateProperties($properties) {
+
+        return false;
+
+    }
+
+    /**
+     * 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->principalProperties['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->principalProperties['uri'],
+                '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('Updating ACLs is not allowed here');
+
+    }
+
+}
diff --git a/lib/Sabre/DAVACL/PrincipalBackend/PDO.php b/lib/Sabre/DAVACL/PrincipalBackend/PDO.php
new file mode 100644 (file)
index 0000000..5eed45b
--- /dev/null
@@ -0,0 +1,206 @@
+<?php
+
+/**
+ * PDO principal backend
+ *
+ * This is a simple principal backend that maps exactly to the users table, as 
+ * used by Sabre_DAV_Auth_Backend_PDO.
+ *
+ * It assumes all principals are in a single collection. The default collection 
+ * is 'principals/', but this can be overriden.
+ *
+ * @package Sabre
+ * @subpackage DAVACL
+ * @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_DAVACL_PrincipalBackend_PDO implements Sabre_DAVACL_IPrincipalBackend {
+
+    /**
+     * pdo 
+     * 
+     * @var PDO 
+     */
+    protected $pdo;
+
+    /**
+     * PDO table name for 'principals' 
+     * 
+     * @var string 
+     */
+    protected $tableName;
+
+    /**
+     * PDO table name for 'group members' 
+     * 
+     * @var string 
+     */
+    protected $groupMembersTableName;
+
+    /**
+     * Sets up the backend.
+     * 
+     * @param PDO $pdo
+     * @param string $tableName 
+     */
+    public function __construct(PDO $pdo, $tableName = 'principals', $groupMembersTableName = 'groupmembers') {
+
+        $this->pdo = $pdo;
+        $this->tableName = $tableName;
+        $this->groupMembersTableName = $groupMembersTableName;
+
+    } 
+
+
+    /**
+     * Returns a list of principals based on a prefix.
+     *
+     * This prefix will often contain something like 'principals'. You are only 
+     * expected to return principals that are in this base path.
+     *
+     * You are expected to return at least a 'uri' for every user, you can 
+     * return any additional properties if you wish so. Common properties are:
+     *   {DAV:}displayname 
+     *   {http://sabredav.org/ns}email-address - This is a custom SabreDAV 
+     *     field that's actualy injected in a number of other properties. If
+     *     you have an email address, use this property.
+     * 
+     * @param string $prefixPath 
+     * @return array 
+     */
+    public function getPrincipalsByPrefix($prefixPath) {
+        $result = $this->pdo->query('SELECT uri, email, displayname FROM `'. $this->tableName . '`');
+
+        $principals = array();
+
+        while($row = $result->fetch(PDO::FETCH_ASSOC)) {
+
+            // Checking if the principal is in the prefix
+            list($rowPrefix) = Sabre_DAV_URLUtil::splitPath($row['uri']);
+            if ($rowPrefix !== $prefixPath) continue;
+
+            $principals[] = array(
+                'uri' => $row['uri'],
+                '{DAV:}displayname' => $row['displayname']?$row['displayname']:basename($row['uri']),
+                '{http://sabredav.org/ns}email-address' => $row['email'],
+            );
+
+        }
+
+        return $principals;
+
+    }
+
+    /**
+     * Returns a specific principal, specified by it's path.
+     * The returned structure should be the exact same as from 
+     * getPrincipalsByPrefix. 
+     * 
+     * @param string $path 
+     * @return array 
+     */
+    public function getPrincipalByPath($path) {
+
+        $stmt = $this->pdo->prepare('SELECT id, uri, email, displayname FROM `'.$this->tableName.'` WHERE uri = ?');
+        $stmt->execute(array($path));
+
+        $users = array();
+
+        $row = $stmt->fetch(PDO::FETCH_ASSOC);
+        if (!$row) return;
+
+        return array(
+            'id'  => $row['id'],
+            'uri' => $row['uri'],
+            '{DAV:}displayname' => $row['displayname']?$row['displayname']:basename($row['uri']),
+            '{http://sabredav.org/ns}email-address' => $row['email'],
+        );
+
+    }
+
+    /**
+     * Returns the list of members for a group-principal 
+     * 
+     * @param string $principal 
+     * @return array 
+     */
+    public function getGroupMemberSet($principal) {
+
+        $principal = $this->getPrincipalByPath($principal);
+        if (!$principal) throw new Sabre_DAV_Exception('Principal not found');
+
+        $stmt = $this->pdo->prepare('SELECT principals.uri as uri FROM `'.$this->groupMembersTableName.'` AS groupmembers LEFT JOIN `'.$this->tableName.'` AS principals ON groupmembers.member_id = principals.id WHERE groupmembers.principal_id = ?');
+        $stmt->execute(array($principal['id']));
+
+        $result = array();
+        while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+            $result[] = $row['uri'];
+        }
+        return $result;
+    
+    }
+
+    /**
+     * Returns the list of groups a principal is a member of 
+     * 
+     * @param string $principal 
+     * @return array 
+     */
+    public function getGroupMembership($principal) {
+
+        $principal = $this->getPrincipalByPath($principal);
+        if (!$principal) throw new Sabre_DAV_Exception('Principal not found');
+
+        $stmt = $this->pdo->prepare('SELECT principals.uri as uri FROM `'.$this->groupMembersTableName.'` AS groupmembers LEFT JOIN `'.$this->tableName.'` AS principals ON groupmembers.principal_id = principals.id WHERE groupmembers.member_id = ?');
+        $stmt->execute(array($principal['id']));
+
+        $result = array();
+        while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+            $result[] = $row['uri'];
+        }
+        return $result;
+
+    }
+
+    /**
+     * Updates the list of group members for a group principal.
+     *
+     * The principals should be passed as a list of uri's. 
+     * 
+     * @param string $principal 
+     * @param array $members 
+     * @return void
+     */
+    public function setGroupMemberSet($principal, array $members) {
+
+        // Grabbing the list of principal id's.
+        $stmt = $this->pdo->prepare('SELECT id, uri FROM `'.$this->tableName.'` WHERE uri IN (? ' . str_repeat(', ? ', count($members)) . ');');
+        $stmt->execute(array_merge(array($principal), $members));
+
+        $memberIds = array();
+        $principalId = null;
+
+        while($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+            if ($row['uri'] == $principal) {
+                $principalId = $row['id'];
+            } else {
+                $memberIds[] = $row['id'];
+            }
+        }
+        if (!$principalId) throw new Sabre_DAV_Exception('Principal not found');
+
+        // Wiping out old members
+        $stmt = $this->pdo->prepare('DELETE FROM `'.$this->groupMembersTableName.'` WHERE principal_id = ?;');
+        $stmt->execute(array($principalId));
+
+        foreach($memberIds as $memberId) {
+
+            $stmt = $this->pdo->prepare('INSERT INTO `'.$this->groupMembersTableName.'` (principal_id, member_id) VALUES (?, ?);');
+            $stmt->execute(array($principalId, $memberId));
+
+        }
+
+    }
+
+}
diff --git a/lib/Sabre/DAVACL/PrincipalBackend/TYPO3.php b/lib/Sabre/DAVACL/PrincipalBackend/TYPO3.php
new file mode 100644 (file)
index 0000000..8262cf2
--- /dev/null
@@ -0,0 +1,224 @@
+<?php
+
+/**
+ * PDO principal backend
+ *
+ * This is a simple principal backend that maps exactly to the users table, as 
+ * used by Sabre_DAV_Auth_Backend_PDO.
+ *
+ * It assumes all principals are in a single collection. The default collection 
+ * is 'principals/', but this can be overriden.
+ *
+ * @package Sabre
+ * @subpackage DAVACL
+ * @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_DAVACL_PrincipalBackend_TYPO3 implements Sabre_DAVACL_IPrincipalBackend {
+
+    /**
+     * pdo 
+     * 
+     * @var PDO 
+     */
+    protected $pdo;
+
+    /**
+     * PDO table name for 'principals' 
+     * 
+     * @var string 
+     */
+    protected $tableName;
+
+    /**
+     * PDO table name for 'group members' 
+     * 
+     * @var string 
+     */
+    protected $groupMembersTableName;
+
+    /**
+     * Sets up the backend.
+     * 
+     * @param PDO $pdo
+     * @param string $tableName 
+     */
+    public function __construct(PDO $pdo, $tableName = 'fe_users', $groupMembersTableName = 'fe_groups') {
+
+        $this->pdo = $pdo;
+        $this->tableName = $tableName;
+        $this->groupMembersTableName = $groupMembersTableName;
+
+    } 
+
+
+    /**
+     * Returns a list of principals based on a prefix.
+     *
+     * This prefix will often contain something like 'principals'. You are only 
+     * expected to return principals that are in this base path.
+     *
+     * You are expected to return at least a 'uri' for every user, you can 
+     * return any additional properties if you wish so. Common properties are:
+     *   {DAV:}displayname 
+     *   {http://sabredav.org/ns}email-address - This is a custom SabreDAV 
+     *     field that's actualy injected in a number of other properties. If
+     *     you have an email address, use this property.
+     * 
+     * @param string $prefixPath 
+     * @return array 
+     */
+    public function getPrincipalsByPrefix($prefixPath) {
+        $result = $this->pdo->query('SELECT uri, email, displayname FROM `'. $this->tableName . '`');
+
+        $principals = array();
+
+        while($row = $result->fetch(PDO::FETCH_ASSOC)) {
+
+            // Checking if the principal is in the prefix
+            list($rowPrefix) = Sabre_DAV_URLUtil::splitPath($row['uri']);
+            if ($rowPrefix !== $prefixPath) continue;
+
+            $principals[] = array(
+                'uri' => $row['uri'],
+                '{DAV:}displayname' => $row['displayname']?$row['displayname']:basename($row['uri']),
+                '{http://sabredav.org/ns}email-address' => $row['email'],
+            );
+
+        }
+
+        return $principals;
+
+    }
+
+    /**
+     * Returns a specific principal, specified by it's path.
+     * The returned structure should be the exact same as from 
+     * getPrincipalsByPrefix. 
+     * 
+     * @param string $path 
+     * @return array 
+     */
+    public function getPrincipalByPath($path) {
+       $pathParts = t3lib_div::trimExplode('/',$path);
+       $name = $pathParts[1];
+        $stmt = $this->pdo->prepare('SELECT uid, username, email, name FROM `'.$this->tableName.'` WHERE username = ?');
+        $stmt->execute(array($name));
+
+        $users = array();
+
+        $row = $stmt->fetch(PDO::FETCH_ASSOC);
+        
+        if (!$row) return;
+        $return = array(
+            'id'  => $row['uid'],
+            'uri' => 'principals/'.$row['username'],
+            '{DAV:}displayname' => $row['name']?$row['name']:basename($row['username']),
+            '{http://sabredav.org/ns}email-address' => $row['email'],
+        );
+               return $return;
+
+    }
+
+    /**
+     * Returns the list of members for a group-principal 
+     * 
+     * @param string $principal 
+     * @return array 
+     */
+    public function getGroupMemberSet($principal) {
+        $principal = $this->getPrincipalByPath($principal);
+        if (!$principal) throw new Sabre_DAV_Exception('Principal not found');
+        
+        $stmt = $this->pdo->prepare("SELECT uid, tx_cal_calendar FROM fe_users WHERE uid = ? AND deleted=0");\r
+        $stmt->execute(array($principal['id']));
+        
+        $calendarIds = '';\r
+        while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {\r
+               $calendarIds = $row['tx_cal_calendar'];
+        }
+       
+        $stmt = $this->pdo->prepare("SELECT * FROM tx_cal_calendar WHERE uid in (?)");\r
+        $stmt->execute(array($calendarIds));
+
+        $result = array();
+        while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+            $result[] = $principal['uri'].'/'.$row['title'];
+
+        }
+        return $result;
+    
+    }
+
+    /**
+     * Returns the list of groups a principal is a member of 
+     * 
+     * @param string $principal 
+     * @return array 
+     */
+    public function getGroupMembership($principal) {
+        $principal = $this->getPrincipalByPath($principal);
+        if (!$principal) throw new Sabre_DAV_Exception('Principal not found');
+        
+        $stmt = $this->pdo->prepare("SELECT uid, tx_cal_calendar FROM fe_users WHERE uid = ? AND deleted=0");
+        $stmt->execute(array($principal['id']));
+        
+        $calendarIds = '';
+        while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+               $calendarIds = $row['tx_cal_calendar'];
+        }
+       
+        $stmt = $this->pdo->prepare("SELECT * FROM tx_cal_calendar WHERE uid in (?)");
+        $stmt->execute(array($calendarIds));
+
+        $result = array();
+        while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+            $result[] = $principal['uri'];
+
+        }
+        return $result;
+    }
+
+    /**
+     * Updates the list of group members for a group principal.
+     *
+     * The principals should be passed as a list of uri's. 
+     * 
+     * @param string $principal 
+     * @param array $members 
+     * @return void
+     */
+    public function setGroupMemberSet($principal, array $members) {
+               //TODO: implement this
+               return;
+        // Grabbing the list of principal id's.
+        $stmt = $this->pdo->prepare('SELECT id, uri FROM `'.$this->tableName.'` WHERE uri IN (? ' . str_repeat(', ? ', count($members)) . ');');
+        $stmt->execute(array_merge(array($principal), $members));
+
+        $memberIds = array();
+        $principalId = null;
+
+        while($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+            if ($row['uri'] == $principal) {
+                $principalId = $row['id'];
+            } else {
+                $memberIds[] = $row['id'];
+            }
+        }
+        if (!$principalId) throw new Sabre_DAV_Exception('Principal not found');
+
+        // Wiping out old members
+        $stmt = $this->pdo->prepare('DELETE FROM `'.$this->groupMembersTableName.'` WHERE principal_id = ?;');
+        $stmt->execute(array($principalId));
+
+        foreach($memberIds as $memberId) {
+
+            $stmt = $this->pdo->prepare('INSERT INTO `'.$this->groupMembersTableName.'` (principal_id, member_id) VALUES (?, ?);');
+            $stmt->execute(array($principalId, $memberId));
+
+        }
+
+    }
+
+}
diff --git a/lib/Sabre/DAVACL/PrincipalCollection.php b/lib/Sabre/DAVACL/PrincipalCollection.php
new file mode 100644 (file)
index 0000000..ab3fbb3
--- /dev/null
@@ -0,0 +1,34 @@
+<?php
+
+/**
+ * Principals Collection
+ *
+ * This collection represents a list of users. It uses
+ * Sabre_DAV_Auth_Backend to determine which users are available on the list.
+ *
+ * The users are instances of Sabre_DAV_Auth_Principal
+ * 
+ * @package Sabre
+ * @subpackage DAVACL
+ * @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_DAVACL_PrincipalCollection extends Sabre_DAVACL_AbstractPrincipalCollection {
+
+    /**
+     * 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_DAVACL_Principal($this->principalBackend, $principal);
+
+    }
+
+}
diff --git a/lib/Sabre/DAVACL/Property/Acl.php b/lib/Sabre/DAVACL/Property/Acl.php
new file mode 100644 (file)
index 0000000..41f0acc
--- /dev/null
@@ -0,0 +1,186 @@
+<?php
+
+/**
+ * This class represents the {DAV:}acl property 
+ * 
+ * @package Sabre
+ * @subpackage DAVACL
+ * @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_DAVACL_Property_Acl extends Sabre_DAV_Property {
+
+    /**
+     * List of privileges 
+     * 
+     * @var array 
+     */
+    private $privileges;
+
+    /**
+     * Wether or not the server base url is required to be prefixed when 
+     * serializing the property. 
+     * 
+     * @var boolean 
+     */
+    private $prefixBaseUrl;
+
+    /**
+     * Constructor
+     *
+     * This object requires a structure similar to the return value from 
+     * Sabre_DAVACL_Plugin::getACL().
+     *
+     * Each privilege is a an array with at least a 'privilege' property, and a 
+     * 'principal' property. A privilege may have a 'protected' property as 
+     * well. 
+     *
+     * The prefixBaseUrl should be set to false, if the supplied principal urls 
+     * are already full urls. If this is kept to true, the servers base url 
+     * will automatically be prefixed. 
+     *
+     * @param bool $prefixBaseUrl 
+     * @param array $privileges 
+     */
+    public function __construct(array $privileges, $prefixBaseUrl = true) {
+
+        $this->privileges = $privileges;
+        $this->prefixBaseUrl = $prefixBaseUrl;
+
+    }
+
+    /**
+     * Returns the list of privileges for this property 
+     * 
+     * @return array 
+     */
+    public function getPrivileges() {
+
+        return $this->privileges;
+
+    }
+
+    /**
+     * Serializes the property into a DOMElement 
+     * 
+     * @param Sabre_DAV_Server $server 
+     * @param DOMElement $node 
+     * @return void
+     */
+    public function serialize(Sabre_DAV_Server $server,DOMElement $node) {
+
+        $doc = $node->ownerDocument;
+        foreach($this->privileges as $ace) {
+
+            $this->serializeAce($doc, $node, $ace, $server);
+
+        }
+
+    }
+
+    /**
+     * Unserializes the {DAV:}acl xml element. 
+     * 
+     * @param DOMElement $dom 
+     * @return Sabre_DAVACL_Property_Acl 
+     */
+    static public function unserialize(DOMElement $dom) {
+
+        $privileges = array();
+        $xaces = $dom->getElementsByTagNameNS('urn:DAV','ace');
+        for($ii=0; $ii < $xaces->length; $ii++) {
+
+            $xace = $xaces->item($ii);
+            $principal = $xace->getElementsByTagNameNS('urn:DAV','principal');
+            if ($principal->length !== 1) {
+                throw new Sabre_DAV_Exception_BadRequest('Each {DAV:}ace element must have one {DAV:}principal element');
+            }
+            $principal = Sabre_DAVACL_Property_Principal::unserialize($principal->item(0));
+
+            if ($principal->getType()!==Sabre_DAVACL_Property_Principal::HREF) {
+                throw new Sabre_DAV_Exception_NotImplemented('Currently only uri based principals are support, {DAV:}all, {DAV:}unauthenticated and {DAV:}authenticated are not implemented yet');
+            }
+
+            $principal = $principal->getHref();
+            $protected = false;
+
+            if ($xace->getElementsByTagNameNS('urn:DAV','protected')->length > 0) {
+                $protected = true;
+            }
+
+            $grants = $xace->getElementsByTagNameNS('urn:DAV','grant');
+            if ($grants->length < 1) {
+                throw new Sabre_DAV_Exception_NotImplemented('Every {DAV:}ace element must have a {DAV:}grant element. {DAV:}deny is not yet supported');
+            }
+            $grant = $grants->item(0);
+
+            $xprivs = $grant->getElementsByTagNameNS('urn:DAV','privilege');
+            for($jj=0; $jj<$xprivs->length; $jj++) {
+
+                $xpriv = $xprivs->item($jj);
+
+                $privilegeName = null;
+
+                for ($kk=0;$kk<$xpriv->childNodes->length;$kk++) {
+
+                    $childNode = $xpriv->childNodes->item($kk);
+                    if ($t = Sabre_DAV_XMLUtil::toClarkNotation($childNode)) {
+                        $privilegeName = $t;
+                        break;
+                    }
+                }
+                if (is_null($privilegeName)) {
+                    throw new Sabre_DAV_Exception_BadRequest('{DAV:}privilege elements must have a privilege element contained within them.');
+                }
+
+                $privileges[] = array(
+                    'principal' => $principal,
+                    'protected' => $protected,
+                    'privilege' => $privilegeName,
+                );
+
+            } 
+
+        }
+
+        return new self($privileges);
+
+    }
+
+    /**
+     * Serializes a single access control entry. 
+     * 
+     * @param DOMDocument $doc 
+     * @param DOMElement $node 
+     * @param array $ace
+     * @param Sabre_DAV_Server $server 
+     * @return void
+     */
+    private function serializeAce($doc,$node,$ace, $server) {
+
+        $xace  = $doc->createElementNS('DAV:','d:ace');
+        $node->appendChild($xace);
+
+        $principal = $doc->createElementNS('DAV:','d:principal');
+        $xace->appendChild($principal);
+        $principal->appendChild($doc->createElementNS('DAV:','d:href',($this->prefixBaseUrl?$server->getBaseUri():'') . $ace['principal'] . '/'));
+
+        $grant = $doc->createElementNS('DAV:','d:grant');
+        $xace->appendChild($grant);
+
+        $privParts = null;
+
+        preg_match('/^{([^}]*)}(.*)$/',$ace['privilege'],$privParts);
+
+        $xprivilege = $doc->createElementNS('DAV:','d:privilege');
+        $grant->appendChild($xprivilege);
+
+        $xprivilege->appendChild($doc->createElementNS($privParts[1],'d:'.$privParts[2]));
+
+        if (isset($ace['protected']) && $ace['protected'])
+            $xace->appendChild($doc->createElement('d:protected'));
+
+    }
+
+}
diff --git a/lib/Sabre/DAVACL/Property/CurrentUserPrivilegeSet.php b/lib/Sabre/DAVACL/Property/CurrentUserPrivilegeSet.php
new file mode 100644 (file)
index 0000000..8b17a9b
--- /dev/null
@@ -0,0 +1,75 @@
+<?php
+
+/**
+ * CurrentUserPrivilegeSet
+ * 
+ * This class represents the current-user-privilege-set property. When 
+ * requested, it contain all the privileges a user has on a specific node. 
+ * 
+ * @package Sabre
+ * @subpackage DAVACL
+ * @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_DAVACL_Property_CurrentUserPrivilegeSet extends Sabre_DAV_Property {
+
+    /**
+     * List of privileges 
+     * 
+     * @var array 
+     */
+    private $privileges;
+
+    /**
+     * Creates the object
+     *
+     * Pass the privileges in clark-notation 
+     * 
+     * @param array $privileges 
+     */
+    public function __construct(array $privileges) {
+
+        $this->privileges = $privileges;
+
+    }
+
+    /**
+     * Serializes the property in the DOM 
+     * 
+     * @param Sabre_DAV_Server $server 
+     * @param DOMElement $node 
+     * @return void
+     */
+    public function serialize(Sabre_DAV_Server $server,DOMElement $node) {
+
+        $doc = $node->ownerDocument;
+        foreach($this->privileges as $privName) {
+
+            $this->serializePriv($doc,$node,$privName);
+
+        }
+
+    }
+
+    /**
+     * Serializes one privilege 
+     * 
+     * @param DOMDocument $doc 
+     * @param DOMElement $node 
+     * @param string $privName 
+     * @return void
+     */
+    protected function serializePriv($doc,$node,$privName) {
+
+        $xp  = $doc->createElementNS('DAV:','d:privilege');
+        $node->appendChild($xp);
+
+        $privParts = null;
+        preg_match('/^{([^}]*)}(.*)$/',$privName,$privParts);
+
+        $xp->appendChild($doc->createElementNS($privParts[1],'d:'.$privParts[2]));
+
+    }
+
+}
diff --git a/lib/Sabre/DAVACL/Property/Principal.php b/lib/Sabre/DAVACL/Property/Principal.php
new file mode 100644 (file)
index 0000000..b035116
--- /dev/null
@@ -0,0 +1,154 @@
+<?php
+
+/**
+ * Principal property
+ *
+ * The principal property represents a principal from RFC3744 (ACL).
+ * The property can be used to specify a principal or pseudo principals. 
+ *
+ * @package Sabre
+ * @subpackage DAVACL
+ * @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_DAVACL_Property_Principal extends Sabre_DAV_Property implements Sabre_DAV_Property_IHref {
+
+    /**
+     * To specify a not-logged-in user, use the UNAUTHENTICTED principal
+     */
+    const UNAUTHENTICATED = 1;
+
+    /**
+     * To specify any principal that is logged in, use AUTHENTICATED
+     */
+    const AUTHENTICATED = 2;
+
+    /**
+     * Specific princpals can be specified with the HREF
+     */
+    const HREF = 3;
+
+    /**
+     * Principal-type
+     *
+     * Must be one of the UNAUTHENTICATED, AUTHENTICATED or HREF constants.
+     * 
+     * @var int 
+     */
+    private $type;
+
+    /**
+     * Url to principal
+     *
+     * This value is only used for the HREF principal type.
+     * 
+     * @var string 
+     */
+    private $href;
+
+    /**
+     * Creates the property.
+     *
+     * The 'type' argument must be one of the type constants defined in this class.
+     *
+     * 'href' is only required for the HREF type.
+     * 
+     * @param int $type 
+     * @param string $href 
+     * @return void
+     */
+    public function __construct($type, $href = null) {
+
+        $this->type = $type;
+
+        if ($type===self::HREF && is_null($href)) {
+            throw new Sabre_DAV_Exception('The href argument must be specified for the HREF principal type.');
+        }
+        $this->href = $href;
+
+    }
+
+    /**
+     * Returns the principal type 
+     * 
+     * @return int 
+     */
+    public function getType() {
+
+        return $this->type;
+
+    }
+
+    /**
+     * Returns the principal uri. 
+     * 
+     * @return string
+     */
+    public function getHref() {
+
+        return $this->href;
+
+    }
+
+    /**
+     * Serializes the property into a DOMElement. 
+     * 
+     * @param Sabre_DAV_Server $server 
+     * @param DOMElement $node 
+     * @return void
+     */
+    public function serialize(Sabre_DAV_Server $server, DOMElement $node) {
+
+        $prefix = $server->xmlNamespaces['DAV:'];
+        switch($this->type) {
+
+            case self::UNAUTHENTICATED :
+                $node->appendChild(
+                    $node->ownerDocument->createElement($prefix . ':unauthenticated')
+                );
+                break;
+            case self::AUTHENTICATED :
+                $node->appendChild(
+                    $node->ownerDocument->createElement($prefix . ':authenticated')
+                );
+                break;
+            case self::HREF :
+                $href = $node->ownerDocument->createElement($prefix . ':href');
+                $href->nodeValue = $server->getBaseUri() . $this->href;
+                $node->appendChild($href);
+                break;
+
+        }
+
+    }
+
+    /**
+     * Deserializes a DOM element into a property object. 
+     * 
+     * @param DOMElement $dom 
+     * @return Sabre_DAV_Property_Principal 
+     */
+    static public function unserialize(DOMElement $dom) {
+
+        $parent = $dom->firstChild;
+        while(!Sabre_DAV_XMLUtil::toClarkNotation($parent)) {
+            $parent = $parent->nextSibling;
+        }
+
+        switch(Sabre_DAV_XMLUtil::toClarkNotation($parent)) {
+
+            case '{DAV:}unauthenticated' :
+                return new self(self::UNAUTHENTICATED);
+            case '{DAV:}authenticated' :
+                return new self(self::AUTHENTICATED);
+            case '{DAV:}href':
+                return new self(self::HREF, $parent->textContent);
+            default :
+                throw new Sabre_DAV_Exception_BadRequest('Unexpected element (' . Sabre_DAV_XMLUtil::toClarkNotation($parent) . '). Could not deserialize');
+
+        }
+
+    }
+
+}
diff --git a/lib/Sabre/DAVACL/Property/SupportedPrivilegeSet.php b/lib/Sabre/DAVACL/Property/SupportedPrivilegeSet.php
new file mode 100644 (file)
index 0000000..2eab5af
--- /dev/null
@@ -0,0 +1,92 @@
+<?php
+
+/**
+ * SupportedPrivilegeSet property
+ *
+ * This property encodes the {DAV:}supported-privilege-set property, as defined 
+ * in rfc3744. Please consult the rfc for details about it's structure.
+ *
+ * This class expects a structure like the one given from 
+ * Sabre_DAVACL_Plugin::getSupportedPrivilegeSet as the argument in its 
+ * constructor.
+ *   
+ * @package Sabre
+ * @subpackage DAVACL
+ * @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_DAVACL_Property_SupportedPrivilegeSet extends Sabre_DAV_Property {
+
+    /**
+     * privileges 
+     * 
+     * @var array 
+     */
+    private $privileges;
+
+    /**
+     * Constructor 
+     * 
+     * @param array $privileges 
+     */
+    public function __construct(array $privileges) {
+
+        $this->privileges = $privileges;
+
+    }
+
+    /**
+     * Serializes the property into a domdocument. 
+     * 
+     * @param Sabre_DAV_Server $server 
+     * @param DOMElement $node 
+     * @return void
+     */
+    public function serialize(Sabre_DAV_Server $server,DOMElement $node) {
+
+        $doc = $node->ownerDocument;
+        $this->serializePriv($doc, $node, $this->privileges);
+
+    }
+
+    /**
+     * Serializes a property
+     *
+     * This is a recursive function. 
+     * 
+     * @param DOMDocument $doc 
+     * @param DOMElement $node 
+     * @param array $privilege 
+     * @return void
+     */
+    private function serializePriv($doc,$node,$privilege) {
+
+        $xsp = $doc->createElementNS('DAV:','d:supported-privilege');
+        $node->appendChild($xsp);
+
+        $xp  = $doc->createElementNS('DAV:','d:privilege');
+        $xsp->appendChild($xp);
+
+        $privParts = null;
+        preg_match('/^{([^}]*)}(.*)$/',$privilege['privilege'],$privParts);
+
+        $xp->appendChild($doc->createElementNS($privParts[1],'d:'.$privParts[2]));
+
+        if (isset($privilege['abstract']) && $privilege['abstract']) {
+            $xsp->appendChild($doc->createElementNS('DAV:','d:abstract'));
+        }
+
+        if (isset($privilege['description'])) {
+            $xsp->appendChild($doc->createElementNS('DAV:','d:description',$privilege['description']));
+        }
+
+        if (isset($privilege['aggregates'])) { 
+            foreach($privilege['aggregates'] as $subPrivilege) {
+                $this->serializePriv($doc,$xsp,$subPrivilege);
+            }
+        }
+
+    }
+
+}
diff --git a/lib/Sabre/DAVACL/Version.php b/lib/Sabre/DAVACL/Version.php
new file mode 100644 (file)
index 0000000..0a7f585
--- /dev/null
@@ -0,0 +1,24 @@
+<?php
+
+/**
+ * This class contains the SabreDAV version constants.
+ * 
+ * @package Sabre
+ * @subpackage DAVACL
+ * @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_DAVACL_Version {
+
+    /**
+     * Full version number
+     */
+    const VERSION = '1.5.2';
+
+    /**
+     * Stability : alpha, beta, stable
+     */
+    const STABILITY = 'stable';
+
+}
diff --git a/lib/Sabre/VObject/Component.php b/lib/Sabre/VObject/Component.php
new file mode 100644 (file)
index 0000000..7aad23a
--- /dev/null
@@ -0,0 +1,254 @@
+<?php
+
+/**
+ * VObject Component
+ *
+ * This class represents a VCALENDAR/VCARD component. A component is for example
+ * VEVENT, VTODO and also VCALENDAR. It starts with BEGIN:COMPONENTNAME and 
+ * ends with END:COMPONENTNAME
+ *
+ * @package Sabre
+ * @subpackage VObject
+ * @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_VObject_Component extends Sabre_VObject_Element {
+
+    /**
+     * Name, for example VEVENT 
+     * 
+     * @var string 
+     */
+    public $name;
+
+    /**
+     * Children properties and components 
+     * 
+     * @var array
+     */
+    public $children = array();
+
+
+    /**
+     * Creates a new component.
+     *
+     * By default this object will iterate over its own children, but this can 
+     * be overridden with the iterator argument
+     * 
+     * @param string $name 
+     * @param Sabre_VObject_ElementList $iterator
+     */
+    public function __construct($name, Sabre_VObject_ElementList $iterator = null) {
+
+        $this->name = strtoupper($name);
+        if (!is_null($iterator)) $this->iterator = $iterator;
+
+    }
+
+    /**
+     * Turns the object back into a serialized blob. 
+     * 
+     * @return string 
+     */
+    public function serialize() {
+
+        $str = "BEGIN:" . $this->name . "\r\n";
+        foreach($this->children as $child) $str.=$child->serialize();
+        $str.= "END:" . $this->name . "\r\n";
+        
+        return $str;
+
+    }
+
+
+    /**
+     * Adds a new componenten or element
+     *
+     * You can call this method with the following syntaxes:
+     *
+     * add(Sabre_VObject_Element $element)
+     * add(string $name, $value)
+     *
+     * The first version adds an Element
+     * The second adds a property as a string. 
+     * 
+     * @param mixed $item 
+     * @param mixed $itemValue 
+     * @return void
+     */
+    public function add($item, $itemValue = null) {
+
+        if ($item instanceof Sabre_VObject_Element) {
+            if (!is_null($itemValue)) {
+                throw new InvalidArgumentException('The second argument must not be specified, when passing a VObject');
+            }
+            $item->parent = $this;
+            $this->children[] = $item;
+        } elseif(is_string($item)) {
+
+            if (!is_scalar($itemValue)) {
+                throw new InvalidArgumentException('The second argument must be scalar');
+            }
+            $item = new Sabre_VObject_Property($item,$itemValue);
+            $item->parent = $this;
+            $this->children[] = $item;
+
+        } else {
+            
+            throw new InvalidArgumentException('The first argument must either be a Sabre_VObject_Element or a string');
+
+        }
+
+    }
+
+    /**
+     * Returns an iterable list of children 
+     * 
+     * @return Sabre_VObject_ElementList 
+     */
+    public function children() {
+
+        return new Sabre_VObject_ElementList($this->children);
+
+    }
+
+    /**
+     * Returns an array with elements that match the specified name.
+     *
+     * This function is also aware of MIME-Directory groups (as they appear in 
+     * vcards). This means that if a property is grouped as "HOME.EMAIL", it 
+     * will also be returned when searching for just "EMAIL". If you want to 
+     * search for a property in a specific group, you can select on the entire 
+     * string ("HOME.EMAIL"). If you want to search on a specific property that 
+     * has not been assigned a group, specify ".EMAIL".
+     *
+     * Keys are retained from the 'children' array, which may be confusing in 
+     * certain cases. 
+     *
+     * @param string $name 
+     * @return array 
+     */
+    public function select($name) {
+
+        $group = null;
+        $name = strtoupper($name);
+        if (strpos($name,'.')!==false) {
+            list($group,$name) = explode('.', $name, 2);
+        }
+
+        $result = array();
+        foreach($this->children as $key=>$child) {
+
+            if (
+                strtoupper($child->name) === $name &&
+                (is_null($group) || ( $child instanceof Sabre_VObject_Property && strtoupper($child->group) === $group))
+            ) {
+                
+                $result[$key] = $child;
+
+            }
+        }
+
+        reset($result);
+        return $result;
+
+    }
+
+    /* Magic property accessors {{{ */
+
+    /**
+     * Using 'get' you will either get a propery or component, 
+     *
+     * If there were no child-elements found with the specified name,
+     * null is returned.
+     * 
+     * @param string $name 
+     * @return void
+     */
+    public function __get($name) {
+
+        $matches = $this->select($name);
+        if (count($matches)===0) {
+            return null;
+        } else {
+            $firstMatch = current($matches);
+            $firstMatch->setIterator(new Sabre_VObject_ElementList(array_values($matches)));
+            return $firstMatch;
+        }
+
+    }
+
+    /**
+     * This method checks if a sub-element with the specified name exists. 
+     * 
+     * @param string $name 
+     * @return bool 
+     */
+    public function __isset($name) {
+
+        $matches = $this->select($name);
+        return count($matches)>0;
+
+    }
+
+    /**
+     * Using the setter method you can add properties or subcomponents
+     *
+     * You can either pass a Sabre_VObject_Component, Sabre_VObject_Property
+     * object, or a string to automatically create a Property.
+     *
+     * If the item already exists, it will be removed. If you want to add
+     * a new item with the same name, always use the add() method.
+     * 
+     * @param string $name
+     * @param mixed $value
+     * @return void
+     */
+    public function __set($name, $value) {
+
+        $matches = $this->select($name);
+        $overWrite = count($matches)?key($matches):null;
+
+        if ($value instanceof Sabre_VObject_Component || $value instanceof Sabre_VObject_Property) {
+            $value->parent = $this;
+            if (!is_null($overWrite)) {
+                $this->children[$overWrite] = $value;
+            } else {
+                $this->children[] = $value;
+            }
+        } elseif (is_scalar($value)) {
+            $property = new Sabre_VObject_Property($name,$value);
+            $property->parent = $this;
+            if (!is_null($overWrite)) {
+                $this->children[$overWrite] = $property;
+            } else {
+                $this->children[] = $property;
+            }
+        } else {
+            throw new InvalidArgumentException('You must pass a Sabre_VObject_Component, Sabre_VObject_Property or scalar type');
+        }
+
+    }
+
+    /**
+     * Removes all properties and components within this component. 
+     * 
+     * @param string $name 
+     * @return void
+     */
+    public function __unset($name) {
+
+        $matches = $this->select($name);
+        foreach($matches as $k=>$child) {
+
+            unset($this->children[$k]);
+            $child->parent = null;
+
+        }
+
+    }
+
+    /* }}} */
+
+}
diff --git a/lib/Sabre/VObject/Element.php b/lib/Sabre/VObject/Element.php
new file mode 100644 (file)
index 0000000..cbdeebd
--- /dev/null
@@ -0,0 +1,15 @@
+<?php
+
+/**
+ * Base class for all elements
+ * 
+ * @package Sabre
+ * @subpackage VObject
+ * @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_VObject_Element extends Sabre_VObject_Node {
+
+
+}
diff --git a/lib/Sabre/VObject/Element/DateTime.php b/lib/Sabre/VObject/Element/DateTime.php
new file mode 100644 (file)
index 0000000..314fffb
--- /dev/null
@@ -0,0 +1,245 @@
+<?php
+
+/**
+ * DateTime property 
+ *
+ * This element is used for iCalendar properties such as the DTSTART property. 
+ * It basically provides a few helper functions that make it easier to deal 
+ * with these. It supports both DATE-TIME and DATE values.
+ *
+ * In order to use this correctly, you must call setDateTime and getDateTime to 
+ * retrieve and modify dates respectively.
+ *
+ * If you use the 'value' or properties directly, this object does not keep 
+ * reference and results might appear incorrectly.
+ * 
+ * @package Sabre
+ * @subpackage VObject
+ * @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_VObject_Element_DateTime extends Sabre_VObject_Property {
+
+    /**
+     * Local 'floating' time
+     */
+    const LOCAL = 1;
+
+    /**
+     * UTC-based time
+     */
+    const UTC = 2;
+
+    /**
+     * Local time plus timezone
+     */
+    const LOCALTZ = 3;
+
+    /**
+     * Only a date, time is ignored
+     */
+    const DATE = 4;
+
+    /**
+     * DateTime representation
+     *
+     * @var DateTime
+     */
+    protected $dateTime;
+
+    /**
+     * dateType 
+     * 
+     * @var int 
+     */
+    protected $dateType;
+
+    /**
+     * Updates the Date and Time. 
+     * 
+     * @param DateTime $dt 
+     * @param int $dateType 
+     * @return void
+     */
+    public function setDateTime(DateTime $dt, $dateType = self::LOCALTZ) {
+
+        switch($dateType) {
+
+            case self::LOCAL :
+                $this->setValue($dt->format('Ymd\\THis'));
+                $this->offsetUnset('VALUE');
+                $this->offsetUnset('TZID');
+                $this->offsetSet('VALUE','DATE-TIME'); 
+                break;
+            case self::UTC :
+                $dt->setTimeZone(new DateTimeZone('UTC'));
+                $this->setValue($dt->format('Ymd\\THis\\Z'));
+                $this->offsetUnset('VALUE');
+                $this->offsetUnset('TZID');
+                $this->offsetSet('VALUE','DATE-TIME');
+                break;
+            case self::LOCALTZ :
+                $this->setValue($dt->format('Ymd\\THis'));
+                $this->offsetUnset('VALUE');
+                $this->offsetUnset('TZID');
+                $this->offsetSet('VALUE','DATE-TIME');
+                $this->offsetSet('TZID', $dt->getTimeZone()->getName());
+                break; 
+            case self::DATE :
+                $this->setValue($dt->format('Ymd'));
+                $this->offsetUnset('VALUE');
+                $this->offsetUnset('TZID');
+                $this->offsetSet('VALUE','DATE');
+                break;
+            default :
+                throw new InvalidArgumentException('You must pass a valid dateType constant');
+
+        }
+        $this->dateTime = $dt;
+        $this->dateType = $dateType;
+
+    }
+
+    /**
+     * Returns the current DateTime value.
+     *
+     * If no value was set, this method returns null.
+     *
+     * @return DateTime|null 
+     */
+    public function getDateTime() {
+
+        if ($this->dateTime)
+            return $this->dateTime;
+
+        list(
+            $this->dateType,
+            $this->dateTime
+        ) = self::parseData($this->value, $this);
+        return $this->dateTime;
+
+    }
+
+    /**
+     * Returns the type of Date format.
+     *
+     * This method returns one of the format constants. If no date was set, 
+     * this method will return null.
+     *
+     * @return int|null
+     */
+    public function getDateType() {
+
+        if ($this->dateType)
+            return $this->dateType;
+
+        list(
+            $this->dateType,
+            $this->dateTime,
+        ) = self::parseData($this->value, $this);
+        return $this->dateType;
+
+    }
+
+    /**
+     * Parses the internal data structure to figure out what the current date 
+     * and time is.
+     *
+     * The returned array contains two elements:
+     *   1. A 'DateType' constant (as defined on this class), or null. 
+     *   2. A DateTime object (or null)
+     *
+     * @param string|null $propertyValue The string to parse (yymmdd or 
+     *                                   ymmddThhmmss, etc..)
+     * @param Sabre_VObject_Property|null $property The instance of the 
+     *                                              property we're parsing. 
+     * @return array 
+     */
+    static public function parseData($propertyValue, Sabre_VObject_Property $property = null) {
+
+        if (is_null($propertyValue)) {
+            return array(null, null);
+        }
+
+        $date = '(?P<year>[1-2][0-9]{3})(?P<month>[0-1][0-9])(?P<date>[0-3][0-9])';
+        $time = '(?P<hour>[0-2][0-9])(?P<minute>[0-5][0-9])(?P<second>[0-5][0-9])';
+        $regex = "/^$date(T$time(?P<isutc>Z)?)?$/";
+
+        if (!preg_match($regex, $propertyValue, $matches)) {
+            throw new InvalidArgumentException($propertyValue . ' is not a valid DateTime or Date string');
+        }
+
+        if (!isset($matches['hour'])) {
+            // Date-only
+            return array(
+                self::DATE,
+                new DateTime($matches['year'] . '-' . $matches['month'] . '-' . $matches['date'] . ' 00:00:00'),
+            );
+        }
+
+        $dateStr = 
+            $matches['year'] .'-' . 
+            $matches['month'] . '-' . 
+            $matches['date'] . ' ' .
+            $matches['hour'] . ':' .
+            $matches['minute'] . ':' .
+            $matches['second']; 
+
+        if (isset($matches['isutc'])) {
+            $dt = new DateTime($dateStr,new DateTimeZone('UTC'));
+            $dt->setTimeZone(new DateTimeZone('UTC'));
+            return array(
+                self::UTC,
+                $dt
+            );
+        }
+
+        // Finding the timezone.
+        $tzid = $property['TZID'];
+        if (!$tzid) {
+            return array(
+                self::LOCAL,
+                new DateTime($dateStr)
+            );
+        }
+
+        try {
+            $tz = new DateTimeZone($tzid->value);
+        } catch (Exception $e) {
+
+            // The id was invalid, we're going to try to find the information 
+            // through the VTIMEZONE object.
+
+            // First we find the root object
+            $root = $property;
+            while($root->parent) {
+                $root = $root->parent;
+            }
+
+            if (isset($root->VTIMEZONE)) {
+                foreach($root->VTIMEZONE as $vtimezone) {
+                    if (((string)$vtimezone->TZID) == $tzid) {
+                        if (isset($vtimezone->{'X-LIC-LOCATION'})) {
+                            $tzid = (string)$vtimezone->{'X-LIC-LOCATION'};
+                        }
+                    }
+                }
+            }
+
+            $tz = new DateTimeZone($tzid);
+            
+        }
+        $dt = new DateTime($dateStr, $tz);
+        $dt->setTimeZone($tz);
+
+        return array(
+            self::LOCALTZ,
+            $dt
+        );
+
+    }
+
+}
+
+?>
diff --git a/lib/Sabre/VObject/Element/MultiDateTime.php b/lib/Sabre/VObject/Element/MultiDateTime.php
new file mode 100644 (file)
index 0000000..0106b2f
--- /dev/null
@@ -0,0 +1,168 @@
+<?php
+
+/**
+ * Multi-DateTime property 
+ *
+ * This element is used for iCalendar properties such as the EXDATE property. 
+ * It basically provides a few helper functions that make it easier to deal 
+ * with these. It supports both DATE-TIME and DATE values.
+ *
+ * In order to use this correctly, you must call setDateTimes and getDateTimes 
+ * to retrieve and modify dates respectively.
+ *
+ * If you use the 'value' or properties directly, this object does not keep 
+ * reference and results might appear incorrectly.
+ * 
+ * @package Sabre
+ * @subpackage VObject
+ * @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_VObject_Element_MultiDateTime extends Sabre_VObject_Property {
+
+    /**
+     * DateTime representation
+     *
+     * @var DateTime[]
+     */
+    protected $dateTimes;
+
+    /**
+     * dateType
+     *
+     * This is one of the Sabre_VObject_Element_DateTime constants.
+     *
+     * @var int 
+     */
+    protected $dateType;
+
+    /**
+     * Updates the value 
+     * 
+     * @param array $dt Must be an array of DateTime objects. 
+     * @param int $dateType 
+     * @return void
+     */
+    public function setDateTimes(array $dt, $dateType = Sabre_VObject_Element_DateTime::LOCALTZ) {
+
+        foreach($dt as $i) 
+            if (!$i instanceof DateTime) 
+                throw new InvalidArgumentException('You must pass an array of DateTime objects');
+
+        $this->offsetUnset('VALUE');
+        $this->offsetUnset('TZID');
+        switch($dateType) {
+
+            case Sabre_VObject_Element_DateTime::LOCAL :
+                $val = array();
+                foreach($dt as $i) {
+                    $val[] = $i->format('Ymd\\THis');
+                }
+                $this->setValue(implode(',',$val));
+                $this->offsetSet('VALUE','DATE-TIME'); 
+                break;
+            case Sabre_VObject_Element_DateTime::UTC :
+                $val = array();
+                foreach($dt as $i) {
+                    $i->setTimeZone(new DateTimeZone('UTC'));
+                    $val[] = $i->format('Ymd\\THis\\Z');
+                }
+                $this->setValue(implode(',',$val));
+                $this->offsetSet('VALUE','DATE-TIME');
+                break;
+            case Sabre_VObject_Element_DateTime::LOCALTZ :
+                $val = array();
+                foreach($dt as $i) {
+                    $val[] = $i->format('Ymd\\THis');
+                }
+                $this->setValue(implode(',',$val));
+                $this->offsetSet('VALUE','DATE-TIME');
+                $this->offsetSet('TZID', $dt[0]->getTimeZone()->getName());
+                break; 
+            case Sabre_VObject_Element_DateTime::DATE :
+                $val = array();
+                foreach($dt as $i) {
+                    $val[] = $i->format('Ymd');
+                }
+                $this->setValue(implode(',',$val));
+                $this->offsetSet('VALUE','DATE');
+                break;
+            default :
+                throw new InvalidArgumentException('You must pass a valid dateType constant');
+
+        }
+        $this->dateTimes = $dt;
+        $this->dateType = $dateType;
+
+    }
+
+    /**
+     * Returns the current DateTime value.
+     *
+     * If no value was set, this method returns null.
+     *
+     * @return array|null 
+     */
+    public function getDateTimes() {
+
+        if ($this->dateTimes)
+            return $this->dateTimes;
+
+        $dts = array();
+    
+        if (!$this->value) {
+            $this->dateTimes = null;
+            $this->dateType = null;
+            return null;
+        }
+
+        foreach(explode(',',$this->value) as $val) {
+            list(
+                $type,
+                $dt
+            ) = Sabre_VObject_Element_DateTime::parseData($val, $this);
+            $dts[] = $dt;
+            $this->dateType = $type;
+        }
+        $this->dateTimes = $dts;
+        return $this->dateTimes;
+
+    }
+
+    /**
+     * Returns the type of Date format.
+     *
+     * This method returns one of the format constants. If no date was set, 
+     * this method will return null.
+     *
+     * @return int|null
+     */
+    public function getDateType() {
+
+        if ($this->dateType)
+            return $this->dateType;
+    
+        if (!$this->value) {
+            $this->dateTimes = null;
+            $this->dateType = null;
+            return null;
+        }
+
+        $dts = array();
+        foreach(explode(',',$this->value) as $val) {
+            list(
+                $type,
+                $dt
+            ) = Sabre_VObject_Element_DateTime::parseData($val, $this);
+            $dts[] = $dt;
+            $this->dateType = $type; 
+        }
+        $this->dateTimes = $dts;
+        return $this->dateType;
+
+    }
+
+}
+
+?>
diff --git a/lib/Sabre/VObject/ElementList.php b/lib/Sabre/VObject/ElementList.php
new file mode 100644 (file)
index 0000000..cfbfcc3
--- /dev/null
@@ -0,0 +1,172 @@
+<?php
+
+/**
+ * VObject ElementList
+ *
+ * This class represents a list of elements. Lists are the result of queries,
+ * such as doing $vcalendar->vevent where there's multiple VEVENT objects.
+ *
+ * @package Sabre
+ * @subpackage VObject
+ * @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_VObject_ElementList implements Iterator, Countable, ArrayAccess {
+
+    /**
+     * Inner elements 
+     * 
+     * @var array
+     */
+    protected $elements = array();
+
+    /**
+     * Creates the element list.
+     *
+     * @param array $elements 
+     */
+    public function __construct(array $elements) {
+
+        $this->elements = $elements;
+
+    } 
+
+    /* {{{ Iterator interface */
+
+    /**
+     * Current position  
+     * 
+     * @var int 
+     */
+    private $key = 0;
+
+    /**
+     * Returns current item in iteration 
+     * 
+     * @return Sabre_VObject_Element 
+     */
+    public function current() {
+
+        return $this->elements[$this->key];
+
+    }
+   
+    /**
+     * To the next item in the iterator 
+     * 
+     * @return void
+     */
+    public function next() {
+
+        $this->key++;
+
+    }
+
+    /**
+     * Returns the current iterator key 
+     * 
+     * @return int
+     */
+    public function key() {
+
+        return $this->key;
+
+    }
+
+    /**
+     * Returns true if the current position in the iterator is a valid one 
+     * 
+     * @return bool 
+     */
+    public function valid() {
+
+        return isset($this->elements[$this->key]);
+
+    }
+
+    /**
+     * Rewinds the iterator 
+     * 
+     * @return void 
+     */
+    public function rewind() {
+
+        $this->key = 0;
+
+    }
+
+    /* }}} */
+
+    /* {{{ Countable interface */
+
+    /**
+     * Returns the number of elements 
+     * 
+     * @return int 
+     */
+    public function count() {
+
+        return count($this->elements);
+
+    }
+
+    /* }}} */
+
+    /* {{{ ArrayAccess Interface */
+
+    
+    /**
+     * Checks if an item exists through ArrayAccess.
+     *
+     * @param int $offset 
+     * @return bool 
+     */
+    public function offsetExists($offset) {
+
+        return isset($this->elements[$offset]);
+
+    }
+
+    /**
+     * Gets an item through ArrayAccess.
+     *
+     * @param int $offset 
+     * @return mixed 
+     */
+    public function offsetGet($offset) {
+
+        return $this->elements[$offset];
+
+    }
+
+    /**
+     * Sets an item through ArrayAccess.
+     *
+     * @param int $offset 
+     * @param mixed $value 
+     * @return void
+     */
+    public function offsetSet($offset,$value) {
+
+        throw new LogicException('You can not add new objects to an ElementList');
+
+    }
+
+    /**
+     * Sets an item through ArrayAccess.
+     *
+     * This method just forwards the request to the inner iterator
+     *
+     * @param int $offset 
+     * @return void
+     */
+    public function offsetUnset($offset) {
+
+        throw new LogicException('You can not remove objects from an ElementList');
+
+    }
+
+    /* }}} */
+
+}
diff --git a/lib/Sabre/VObject/Node.php b/lib/Sabre/VObject/Node.php
new file mode 100644 (file)
index 0000000..0bd9ddd
--- /dev/null
@@ -0,0 +1,149 @@
+<?php
+
+/**
+ * Base class for all nodes 
+ * 
+ * @package Sabre
+ * @subpackage VObject
+ * @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_VObject_Node implements IteratorAggregate, ArrayAccess, Countable {
+
+    /**
+     * Turns the object back into a serialized blob. 
+     * 
+     * @return string 
+     */
+    abstract function serialize();
+
+    /**
+     * Iterator override 
+     * 
+     * @var Sabre_VObject_ElementList 
+     */
+    protected $iterator = null;
+
+    /**
+     * A link to the parent node
+     * 
+     * @var Sabre_VObject_Node 
+     */
+    protected $parent = null;
+
+    /* {{{ IteratorAggregator interface */
+
+    /**
+     * Returns the iterator for this object 
+     * 
+     * @return Sabre_VObject_ElementList 
+     */
+    public function getIterator() {
+
+        if (!is_null($this->iterator)) 
+            return $this->iterator;
+
+        return new Sabre_VObject_ElementList(array($this));
+
+    }
+
+    /**
+     * Sets the overridden iterator
+     *
+     * Note that this is not actually part of the iterator interface
+     * 
+     * @param Sabre_VObject_ElementList $iterator 
+     * @return void
+     */
+    public function setIterator(Sabre_VObject_ElementList $iterator) {
+
+        $this->iterator = $iterator;
+
+    }
+
+    /* }}} */
+
+    /* {{{ Countable interface */
+
+    /**
+     * Returns the number of elements 
+     * 
+     * @return int 
+     */
+    public function count() {
+
+        $it = $this->getIterator();
+        return $it->count();
+
+    }
+
+    /* }}} */
+
+    /* {{{ ArrayAccess Interface */
+
+    
+    /**
+     * Checks if an item exists through ArrayAccess.
+     *
+     * This method just forwards the request to the inner iterator
+     * 
+     * @param int $offset 
+     * @return bool 
+     */
+    public function offsetExists($offset) {
+
+        $iterator = $this->getIterator();
+        return $iterator->offsetExists($offset);
+
+    }
+
+    /**
+     * Gets an item through ArrayAccess.
+     *
+     * This method just forwards the request to the inner iterator
+     *
+     * @param int $offset 
+     * @return mixed 
+     */
+    public function offsetGet($offset) {
+
+        $iterator = $this->getIterator();
+        return $iterator->offsetGet($offset);
+
+    }
+
+    /**
+     * Sets an item through ArrayAccess.
+     *
+     * This method just forwards the request to the inner iterator
+     *
+     * @param int $offset 
+     * @param mixed $value 
+     * @return void
+     */
+    public function offsetSet($offset,$value) {
+
+        $iterator = $this->getIterator();
+        return $iterator->offsetSet($offset,$value);
+
+    }
+
+    /**
+     * Sets an item through ArrayAccess.
+     *
+     * This method just forwards the request to the inner iterator
+     *
+     * @param int $offset 
+     * @return void
+     */
+    public function offsetUnset($offset) {
+
+        $iterator = $this->getIterator();
+        return $iterator->offsetUnset($offset);
+
+    }
+
+    /* }}} */
+
+}
diff --git a/lib/Sabre/VObject/Parameter.php b/lib/Sabre/VObject/Parameter.php
new file mode 100644 (file)
index 0000000..8dfc0de
--- /dev/null
@@ -0,0 +1,81 @@
+<?php
+
+/**
+ * VObject Parameter
+ *
+ * This class represents a parameter. A parameter is always tied to a property.
+ * In the case of:
+ *   DTSTART;VALUE=DATE:20101108 
+ * VALUE=DATE would be the parameter name and value.
+ * 
+ * @package Sabre
+ * @subpackage VObject
+ * @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_VObject_Parameter extends Sabre_VObject_Node {
+
+    /**
+     * Parameter name 
+     * 
+     * @var string 
+     */
+    public $name;
+
+    /**
+     * Parameter value 
+     * 
+     * @var string 
+     */
+    public $value;
+
+    /**
+     * Sets up the object 
+     * 
+     * @param string $name 
+     * @param string $value 
+     */
+    public function __construct($name, $value = null) {
+
+        $this->name = strtoupper($name);
+        $this->value = $value;
+
+    } 
+
+    /**
+     * Turns the object back into a serialized blob. 
+     * 
+     * @return string 
+     */
+    public function serialize() {
+
+        $src = array(
+            '\\',
+            "\n",
+            ';',
+            ',',
+        );
+        $out = array(
+            '\\\\',
+            '\n',
+            '\;',
+            '\,',
+        );
+
+        return $this->name . '=' . str_replace($src, $out, $this->value);
+
+    }
+
+    /**
+     * Called when this object is being cast to a string 
+     * 
+     * @return string 
+     */
+    public function __toString() {
+
+        return $this->value;
+
+    }
+
+}
diff --git a/lib/Sabre/VObject/ParseException.php b/lib/Sabre/VObject/ParseException.php
new file mode 100644 (file)
index 0000000..b62e1dc
--- /dev/null
@@ -0,0 +1,12 @@
+<?php
+
+/**
+ * Exception thrown by Sabre_VObject_Reader if an invalid object was attempted to be parsed.
+ * 
+ * @package Sabre
+ * @subpackage VObject
+ * @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_VObject_ParseException extends Exception { }
diff --git a/lib/Sabre/VObject/Property.php b/lib/Sabre/VObject/Property.php
new file mode 100644 (file)
index 0000000..e6748bf
--- /dev/null
@@ -0,0 +1,289 @@
+<?php
+
+/**
+ * VObject Property
+ *
+ * A property in VObject is usually in the form PARAMNAME:paramValue.
+ * An example is : SUMMARY:Weekly meeting 
+ *
+ * Properties can also have parameters:
+ * SUMMARY;LANG=en:Weekly meeting.
+ *
+ * Parameters can be accessed using the ArrayAccess interface. 
+ *
+ * @package Sabre
+ * @subpackage VObject
+ * @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_VObject_Property extends Sabre_VObject_Element {
+
+    /**
+     * Propertyname 
+     * 
+     * @var string 
+     */
+    public $name;
+
+    /**
+     * Group name
+     * 
+     * This may be something like 'HOME' for vcards.
+     *
+     * @var string 
+     */
+    public $group;
+
+    /**
+     * Property parameters 
+     * 
+     * @var array 
+     */
+    public $parameters = array();
+
+    /**
+     * Property value 
+     * 
+     * @var string 
+     */
+    public $value;
+
+    /**
+     * Creates a new property object
+     * 
+     * By default this object will iterate over its own children, but this can 
+     * be overridden with the iterator argument
+     * 
+     * @param string $name 
+     * @param string $value
+     * @param Sabre_VObject_ElementList $iterator
+     */
+    public function __construct($name, $value = null, $iterator = null) {
+
+        $name = strtoupper($name);
+        $group = null;
+        if (strpos($name,'.')!==false) {
+            list($group, $name) = explode('.', $name);
+        }
+        $this->name = $name;
+        $this->group = $group;
+        if (!is_null($iterator)) $this->iterator = $iterator;
+        $this->setValue($value);
+
+    }
+
+    /**
+     * Updates the internal value 
+     * 
+     * @param string $value 
+     * @return void
+     */
+    public function setValue($value) {
+
+        $this->value = $value;
+
+    }
+
+    /**
+     * Turns the object back into a serialized blob. 
+     * 
+     * @return string 
+     */
+    public function serialize() {
+
+        $str = $this->name;
+        if ($this->group) $str = $this->group . '.' . $this->name;
+
+        if (count($this->parameters)) {
+            foreach($this->parameters as $param) {
+                
+                $str.=';' . $param->serialize();
+
+            }
+        }
+        $src = array(
+            '\\',
+            "\n",
+        );
+        $out = array(
+            '\\\\',
+            '\n',
+        );
+        $str.=':' . str_replace($src, $out, $this->value);
+
+        $out = '';
+        while(strlen($str)>0) {
+            if (strlen($str)>75) {
+                $out.= mb_strcut($str,0,75,'utf-8') . "\r\n";
+                $str = ' ' . mb_strcut($str,75,strlen($str),'utf-8');
+            } else {
+                $out.=$str . "\r\n";
+                $str='';
+                break;
+            }
+        }
+
+        return $out;
+
+    }
+
+    /**
+     * Adds a new componenten or element
+     *
+     * You can call this method with the following syntaxes:
+     *
+     * add(Sabre_VObject_Parameter $element)
+     * add(string $name, $value)
+     *
+     * The first version adds an Parameter 
+     * The second adds a property as a string. 
+     * 
+     * @param mixed $item 
+     * @param mixed $itemValue 
+     * @return void
+     */
+    public function add($item, $itemValue = null) {
+
+        if ($item instanceof Sabre_VObject_Parameter) {
+            if (!is_null($itemValue)) {
+                throw new InvalidArgumentException('The second argument must not be specified, when passing a VObject');
+            }
+            $item->parent = $this;
+            $this->parameters[] = $item;
+        } elseif(is_string($item)) {
+
+            if (!is_scalar($itemValue)) {
+                throw new InvalidArgumentException('The second argument must be scalar');
+            }
+            $parameter = new Sabre_VObject_Parameter($item,$itemValue);
+            $parameter->parent = $this;
+            $this->parameters[] = $parameter;
+
+        } else {
+            
+            throw new InvalidArgumentException('The first argument must either be a Sabre_VObject_Element or a string');
+
+        }
+
+    }
+
+
+    /* ArrayAccess interface {{{ */
+
+    /**
+     * Checks if an array element exists
+     * 
+     * @param mixed $name 
+     * @return bool 
+     */
+    public function offsetExists($name) {
+
+        if (is_int($name)) return parent::offsetExists($name);
+
+        $name = strtoupper($name);
+
+        foreach($this->parameters as $parameter) {
+            if ($parameter->name == $name) return true;
+        }
+        return false;
+
+    }
+
+    /**
+     * Returns a parameter, or parameter list. 
+     * 
+     * @param string $name 
+     * @return Sabre_VObject_Element 
+     */
+    public function offsetGet($name) {
+
+        if (is_int($name)) return parent::offsetGet($name);
+        $name = strtoupper($name);
+        
+        $result = array();
+        foreach($this->parameters as $parameter) {
+            if ($parameter->name == $name)
+                $result[] = $parameter;
+        }
+
+        if (count($result)===0) {
+            return null;
+        } elseif (count($result)===1) {
+            return $result[0];
+        } else {
+            $result[0]->setIterator(new Sabre_VObject_ElementList($result));
+            return $result[0];
+        }
+
+    }
+
+    /**
+     * Creates a new parameter 
+     * 
+     * @param string $name
+     * @param mixed $value
+     * @return void
+     */
+    public function offsetSet($name, $value) {
+
+        if (is_int($name)) return parent::offsetSet($name, $value);
+
+        if (is_scalar($value)) {
+            if (!is_string($name)) 
+                throw new InvalidArgumentException('A parameter name must be specified. This means you cannot use the $array[]="string" to add parameters.');
+
+            $this->offsetUnset($name);
+            $parameter = new Sabre_VObject_Parameter($name, $value);
+            $parameter->parent = $this;
+            $this->parameters[] = $parameter;
+
+        } elseif ($value instanceof Sabre_VObject_Parameter) {
+            if (!is_null($name))
+                throw new InvalidArgumentException('Don\'t specify a parameter name if you\'re passing a Sabre_VObject_Parameter. Add using $array[]=$parameterObject.');
+
+            $value->parent = $this; 
+            $this->parameters[] = $value;
+        } else {
+            throw new InvalidArgumentException('You can only add parameters to the property object');
+        }
+
+    }
+
+    /**
+     * Removes one or more parameters with the specified name 
+     * 
+     * @param string $name 
+     * @return void 
+     */
+    public function offsetUnset($name) {
+
+        if (is_int($name)) return parent::offsetUnset($name, $value);
+        $name = strtoupper($name);
+        
+        $result = array();
+        foreach($this->parameters as $key=>$parameter) {
+            if ($parameter->name == $name) {
+                $parameter->parent = null;
+                unset($this->parameters[$key]);
+            }
+
+        }
+
+    }
+
+    /* }}} */
+
+    /**
+     * Called when this object is being cast to a string 
+     * 
+     * @return string 
+     */
+    public function __toString() {
+
+        return $this->value;
+
+    }
+
+
+}
diff --git a/lib/Sabre/VObject/Reader.php b/lib/Sabre/VObject/Reader.php
new file mode 100644 (file)
index 0000000..a2c1804
--- /dev/null
@@ -0,0 +1,191 @@
+<?php
+
+/**
+ * VCALENDAR/VCARD reader
+ *
+ * This class reads the vobject file, and returns a full element tree.
+ *
+ *
+ * TODO: this class currently completely works 'statically'. This is pointless, 
+ * and defeats OOP principals. Needs refaxtoring in a future version.
+ * 
+ * @package Sabre
+ * @subpackage VObject
+ * @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_VObject_Reader {
+
+    /**
+     * This array contains a list of Property names that are automatically 
+     * mapped to specific class names.
+     *
+     * Adding to this list allows you to specify custom property classes, 
+     * adding extra functionality. 
+     * 
+     * @var array
+     */
+    static public $elementMap = array(
+        'DTSTART'   => 'Sabre_VObject_Element_DateTime',
+        'DTEND'     => 'Sabre_VObject_Element_DateTime',
+        'COMPLETED' => 'Sabre_VObject_Element_DateTime',
+        'DUE'       => 'Sabre_VObject_Element_DateTime',
+        'EXDATE'    => 'Sabre_VObject_Element_MultiDateTime',
+    );
+
+    /**
+     * Parses the file and returns the top component 
+     * 
+     * @param string $data 
+     * @return Sabre_VObject_Element 
+     */
+    static function read($data) {
+
+        // Normalizing newlines
+        $data = str_replace(array("\r","\n\n"), array("\n","\n"), $data);
+
+        $lines = explode("\n", $data);
+
+        // Unfolding lines
+        $lines2 = array();
+        foreach($lines as $line) {
+
+            // Skipping empty lines
+            if (!$line) continue;
+
+            if ($line[0]===" " || $line[0]==="\t") {
+                $lines2[count($lines2)-1].=substr($line,1);
+            } else {
+                $lines2[] = $line;
+            }
+
+        }
+
+        unset($lines);
+        
+        reset($lines2);
+
+        return self::readLine($lines2);
+       
+    }
+
+    /**
+     * Reads and parses a single line.