aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGeorg Ehrke <dev@georgswebsite.de>2012-08-09 17:32:45 +0200
committerGeorg Ehrke <dev@georgswebsite.de>2012-08-09 17:34:19 +0200
commit6e922035e1cb7b6f99f32c60dcc5eca8c3d52e7c (patch)
treee78b2cefcc3d086a9815566d821642d1a6233a6f
parent4e85848c927896b4d9084fd9019478baaee4507b (diff)
downloadnextcloud-server-6e922035e1cb7b6f99f32c60dcc5eca8c3d52e7c.tar.gz
nextcloud-server-6e922035e1cb7b6f99f32c60dcc5eca8c3d52e7c.zip
add SabreDav 1.6.4
-rwxr-xr-x3rdparty/Sabre.includes.php26
-rwxr-xr-x3rdparty/Sabre/CalDAV/Backend/Abstract.php168
-rwxr-xr-x3rdparty/Sabre/CalDAV/Backend/PDO.php396
-rwxr-xr-x3rdparty/Sabre/CalDAV/Calendar.php343
-rwxr-xr-x3rdparty/Sabre/CalDAV/CalendarObject.php273
-rwxr-xr-x3rdparty/Sabre/CalDAV/CalendarQueryParser.php296
-rwxr-xr-x3rdparty/Sabre/CalDAV/CalendarQueryValidator.php370
-rwxr-xr-x3rdparty/Sabre/CalDAV/CalendarRootNode.php75
-rwxr-xr-x3rdparty/Sabre/CalDAV/ICSExportPlugin.php139
-rwxr-xr-x3rdparty/Sabre/CalDAV/ICalendar.php18
-rwxr-xr-x3rdparty/Sabre/CalDAV/ICalendarObject.php20
-rwxr-xr-x3rdparty/Sabre/CalDAV/Plugin.php931
-rwxr-xr-x3rdparty/Sabre/CalDAV/Principal/Collection.php31
-rwxr-xr-x3rdparty/Sabre/CalDAV/Principal/ProxyRead.php178
-rwxr-xr-x3rdparty/Sabre/CalDAV/Principal/ProxyWrite.php178
-rwxr-xr-x3rdparty/Sabre/CalDAV/Principal/User.php132
-rwxr-xr-x3rdparty/Sabre/CalDAV/Property/SupportedCalendarComponentSet.php85
-rwxr-xr-x3rdparty/Sabre/CalDAV/Property/SupportedCalendarData.php38
-rwxr-xr-x3rdparty/Sabre/CalDAV/Property/SupportedCollationSet.php44
-rwxr-xr-x3rdparty/Sabre/CalDAV/Schedule/IMip.php104
-rwxr-xr-x3rdparty/Sabre/CalDAV/Schedule/IOutbox.php16
-rwxr-xr-x3rdparty/Sabre/CalDAV/Schedule/Outbox.php152
-rwxr-xr-x3rdparty/Sabre/CalDAV/Server.php68
-rwxr-xr-x3rdparty/Sabre/CalDAV/UserCalendars.php298
-rwxr-xr-x3rdparty/Sabre/CalDAV/Version.php24
-rwxr-xr-x3rdparty/Sabre/CalDAV/includes.php43
-rwxr-xr-x3rdparty/Sabre/CardDAV/AddressBook.php312
-rwxr-xr-x3rdparty/Sabre/CardDAV/AddressBookQueryParser.php219
-rwxr-xr-x3rdparty/Sabre/CardDAV/AddressBookRoot.php78
-rwxr-xr-x3rdparty/Sabre/CardDAV/Backend/Abstract.php166
-rwxr-xr-x3rdparty/Sabre/CardDAV/Backend/PDO.php330
-rwxr-xr-x3rdparty/Sabre/CardDAV/Card.php250
-rwxr-xr-x3rdparty/Sabre/CardDAV/IAddressBook.php18
-rwxr-xr-x3rdparty/Sabre/CardDAV/ICard.php18
-rwxr-xr-x3rdparty/Sabre/CardDAV/IDirectory.php21
-rwxr-xr-x3rdparty/Sabre/CardDAV/Plugin.php666
-rwxr-xr-x3rdparty/Sabre/CardDAV/Property/SupportedAddressData.php69
-rwxr-xr-x3rdparty/Sabre/CardDAV/UserAddressBooks.php257
-rwxr-xr-x3rdparty/Sabre/CardDAV/Version.php26
-rwxr-xr-x3rdparty/Sabre/CardDAV/includes.php32
-rwxr-xr-x3rdparty/Sabre/DAV/Auth/Backend/AbstractBasic.php83
-rwxr-xr-x3rdparty/Sabre/DAV/Auth/Backend/AbstractDigest.php98
-rwxr-xr-x3rdparty/Sabre/DAV/Auth/Backend/Apache.php62
-rwxr-xr-x3rdparty/Sabre/DAV/Auth/Backend/File.php75
-rwxr-xr-x3rdparty/Sabre/DAV/Auth/Backend/PDO.php65
-rwxr-xr-x3rdparty/Sabre/DAV/Auth/IBackend.php36
-rwxr-xr-x3rdparty/Sabre/DAV/Auth/Plugin.php111
-rwxr-xr-x3rdparty/Sabre/DAV/Browser/GuessContentType.php97
-rwxr-xr-x3rdparty/Sabre/DAV/Browser/MapGetToPropFind.php55
-rwxr-xr-x3rdparty/Sabre/DAV/Browser/Plugin.php489
-rwxr-xr-x3rdparty/Sabre/DAV/Browser/assets/favicon.icobin0 -> 4286 bytes
-rwxr-xr-x3rdparty/Sabre/DAV/Browser/assets/icons/addressbook.pngbin0 -> 7232 bytes
-rwxr-xr-x3rdparty/Sabre/DAV/Browser/assets/icons/calendar.pngbin0 -> 4388 bytes
-rwxr-xr-x3rdparty/Sabre/DAV/Browser/assets/icons/card.pngbin0 -> 5695 bytes
-rwxr-xr-x3rdparty/Sabre/DAV/Browser/assets/icons/collection.pngbin0 -> 3474 bytes
-rwxr-xr-x3rdparty/Sabre/DAV/Browser/assets/icons/file.pngbin0 -> 2837 bytes
-rwxr-xr-x3rdparty/Sabre/DAV/Browser/assets/icons/parent.pngbin0 -> 3474 bytes
-rwxr-xr-x3rdparty/Sabre/DAV/Browser/assets/icons/principal.pngbin0 -> 5480 bytes
-rwxr-xr-x3rdparty/Sabre/DAV/Client.php492
-rwxr-xr-x3rdparty/Sabre/DAV/Collection.php106
-rwxr-xr-x3rdparty/Sabre/DAV/Directory.php17
-rwxr-xr-x3rdparty/Sabre/DAV/Exception.php64
-rwxr-xr-x3rdparty/Sabre/DAV/Exception/BadRequest.php28
-rwxr-xr-x3rdparty/Sabre/DAV/Exception/Conflict.php28
-rwxr-xr-x3rdparty/Sabre/DAV/Exception/ConflictingLock.php35
-rwxr-xr-x3rdparty/Sabre/DAV/Exception/FileNotFound.php19
-rwxr-xr-x3rdparty/Sabre/DAV/Exception/Forbidden.php27
-rwxr-xr-x3rdparty/Sabre/DAV/Exception/InsufficientStorage.php27
-rwxr-xr-x3rdparty/Sabre/DAV/Exception/InvalidResourceType.php33
-rwxr-xr-x3rdparty/Sabre/DAV/Exception/LockTokenMatchesRequestUri.php39
-rwxr-xr-x3rdparty/Sabre/DAV/Exception/Locked.php67
-rwxr-xr-x3rdparty/Sabre/DAV/Exception/MethodNotAllowed.php45
-rwxr-xr-x3rdparty/Sabre/DAV/Exception/NotAuthenticated.php28
-rwxr-xr-x3rdparty/Sabre/DAV/Exception/NotFound.php28
-rwxr-xr-x3rdparty/Sabre/DAV/Exception/NotImplemented.php27
-rwxr-xr-x3rdparty/Sabre/DAV/Exception/PaymentRequired.php28
-rwxr-xr-x3rdparty/Sabre/DAV/Exception/PreconditionFailed.php69
-rwxr-xr-x3rdparty/Sabre/DAV/Exception/ReportNotImplemented.php30
-rwxr-xr-x3rdparty/Sabre/DAV/Exception/RequestedRangeNotSatisfiable.php29
-rwxr-xr-x3rdparty/Sabre/DAV/Exception/UnsupportedMediaType.php28
-rwxr-xr-x3rdparty/Sabre/DAV/FS/Directory.php136
-rwxr-xr-x3rdparty/Sabre/DAV/FS/File.php89
-rwxr-xr-x3rdparty/Sabre/DAV/FS/Node.php80
-rwxr-xr-x3rdparty/Sabre/DAV/FSExt/Directory.php154
-rwxr-xr-x3rdparty/Sabre/DAV/FSExt/File.php93
-rwxr-xr-x3rdparty/Sabre/DAV/FSExt/Node.php212
-rwxr-xr-x3rdparty/Sabre/DAV/File.php85
-rwxr-xr-x3rdparty/Sabre/DAV/ICollection.php74
-rwxr-xr-x3rdparty/Sabre/DAV/IExtendedCollection.php28
-rwxr-xr-x3rdparty/Sabre/DAV/IFile.php77
-rwxr-xr-x3rdparty/Sabre/DAV/INode.php46
-rwxr-xr-x3rdparty/Sabre/DAV/IProperties.php67
-rwxr-xr-x3rdparty/Sabre/DAV/IQuota.php27
-rwxr-xr-x3rdparty/Sabre/DAV/Locks/Backend/Abstract.php50
-rwxr-xr-x3rdparty/Sabre/DAV/Locks/Backend/FS.php191
-rwxr-xr-x3rdparty/Sabre/DAV/Locks/Backend/File.php181
-rwxr-xr-x3rdparty/Sabre/DAV/Locks/Backend/PDO.php165
-rwxr-xr-x3rdparty/Sabre/DAV/Locks/LockInfo.php81
-rwxr-xr-x3rdparty/Sabre/DAV/Locks/Plugin.php636
-rwxr-xr-x3rdparty/Sabre/DAV/Mount/Plugin.php80
-rwxr-xr-x3rdparty/Sabre/DAV/Node.php55
-rwxr-xr-x3rdparty/Sabre/DAV/ObjectTree.php153
-rwxr-xr-x3rdparty/Sabre/DAV/Property.php25
-rwxr-xr-x3rdparty/Sabre/DAV/Property/GetLastModified.php75
-rwxr-xr-x3rdparty/Sabre/DAV/Property/Href.php91
-rwxr-xr-x3rdparty/Sabre/DAV/Property/HrefList.php96
-rwxr-xr-x3rdparty/Sabre/DAV/Property/IHref.php25
-rwxr-xr-x3rdparty/Sabre/DAV/Property/LockDiscovery.php102
-rwxr-xr-x3rdparty/Sabre/DAV/Property/ResourceType.php125
-rwxr-xr-x3rdparty/Sabre/DAV/Property/Response.php155
-rwxr-xr-x3rdparty/Sabre/DAV/Property/ResponseList.php57
-rwxr-xr-x3rdparty/Sabre/DAV/Property/SupportedLock.php76
-rwxr-xr-x3rdparty/Sabre/DAV/Property/SupportedReportSet.php109
-rwxr-xr-x3rdparty/Sabre/DAV/Server.php2006
-rwxr-xr-x3rdparty/Sabre/DAV/ServerPlugin.php90
-rwxr-xr-x3rdparty/Sabre/DAV/SimpleCollection.php105
-rwxr-xr-x3rdparty/Sabre/DAV/SimpleDirectory.php21
-rwxr-xr-x3rdparty/Sabre/DAV/SimpleFile.php121
-rwxr-xr-x3rdparty/Sabre/DAV/StringUtil.php91
-rwxr-xr-x3rdparty/Sabre/DAV/TemporaryFileFilterPlugin.php289
-rwxr-xr-x3rdparty/Sabre/DAV/Tree.php193
-rwxr-xr-x3rdparty/Sabre/DAV/Tree/Filesystem.php123
-rwxr-xr-x3rdparty/Sabre/DAV/URLUtil.php121
-rwxr-xr-x3rdparty/Sabre/DAV/UUIDUtil.php64
-rwxr-xr-x3rdparty/Sabre/DAV/Version.php24
-rwxr-xr-x3rdparty/Sabre/DAV/XMLUtil.php186
-rwxr-xr-x3rdparty/Sabre/DAV/includes.php97
-rwxr-xr-x3rdparty/Sabre/DAVACL/AbstractPrincipalCollection.php154
-rwxr-xr-x3rdparty/Sabre/DAVACL/Exception/AceConflict.php32
-rwxr-xr-x3rdparty/Sabre/DAVACL/Exception/NeedPrivileges.php81
-rwxr-xr-x3rdparty/Sabre/DAVACL/Exception/NoAbstract.php32
-rwxr-xr-x3rdparty/Sabre/DAVACL/Exception/NotRecognizedPrincipal.php32
-rwxr-xr-x3rdparty/Sabre/DAVACL/Exception/NotSupportedPrivilege.php32
-rwxr-xr-x3rdparty/Sabre/DAVACL/IACL.php73
-rwxr-xr-x3rdparty/Sabre/DAVACL/IPrincipal.php75
-rwxr-xr-x3rdparty/Sabre/DAVACL/IPrincipalBackend.php153
-rwxr-xr-x3rdparty/Sabre/DAVACL/Plugin.php1348
-rwxr-xr-x3rdparty/Sabre/DAVACL/Principal.php279
-rwxr-xr-x3rdparty/Sabre/DAVACL/PrincipalBackend/PDO.php427
-rwxr-xr-x3rdparty/Sabre/DAVACL/PrincipalCollection.php35
-rwxr-xr-x3rdparty/Sabre/DAVACL/Property/Acl.php209
-rwxr-xr-x3rdparty/Sabre/DAVACL/Property/AclRestrictions.php32
-rwxr-xr-x3rdparty/Sabre/DAVACL/Property/CurrentUserPrivilegeSet.php75
-rwxr-xr-x3rdparty/Sabre/DAVACL/Property/Principal.php160
-rwxr-xr-x3rdparty/Sabre/DAVACL/Property/SupportedPrivilegeSet.php92
-rwxr-xr-x3rdparty/Sabre/DAVACL/Version.php24
-rwxr-xr-x3rdparty/Sabre/DAVACL/includes.php38
-rwxr-xr-x3rdparty/Sabre/HTTP/AWSAuth.php227
-rwxr-xr-x3rdparty/Sabre/HTTP/AbstractAuth.php111
-rwxr-xr-x3rdparty/Sabre/HTTP/BasicAuth.php67
-rwxr-xr-x3rdparty/Sabre/HTTP/DigestAuth.php240
-rwxr-xr-x3rdparty/Sabre/HTTP/Request.php268
-rwxr-xr-x3rdparty/Sabre/HTTP/Response.php157
-rwxr-xr-x3rdparty/Sabre/HTTP/Util.php82
-rwxr-xr-x3rdparty/Sabre/HTTP/Version.php24
-rwxr-xr-x3rdparty/Sabre/HTTP/includes.php27
-rwxr-xr-x3rdparty/Sabre/VObject/Component.php365
-rwxr-xr-x3rdparty/Sabre/VObject/Component/VAlarm.php102
-rwxr-xr-x3rdparty/Sabre/VObject/Component/VCalendar.php133
-rwxr-xr-x3rdparty/Sabre/VObject/Component/VEvent.php71
-rwxr-xr-x3rdparty/Sabre/VObject/Component/VJournal.php46
-rwxr-xr-x3rdparty/Sabre/VObject/Component/VTodo.php68
-rwxr-xr-x3rdparty/Sabre/VObject/DateTimeParser.php181
-rwxr-xr-x3rdparty/Sabre/VObject/Element.php16
-rwxr-xr-x3rdparty/Sabre/VObject/Element/DateTime.php37
-rwxr-xr-x3rdparty/Sabre/VObject/Element/MultiDateTime.php17
-rwxr-xr-x3rdparty/Sabre/VObject/ElementList.php172
-rwxr-xr-x3rdparty/Sabre/VObject/FreeBusyGenerator.php297
-rwxr-xr-x3rdparty/Sabre/VObject/Node.php149
-rwxr-xr-x3rdparty/Sabre/VObject/Parameter.php84
-rwxr-xr-x3rdparty/Sabre/VObject/ParseException.php12
-rwxr-xr-x3rdparty/Sabre/VObject/Property.php348
-rwxr-xr-x3rdparty/Sabre/VObject/Property/DateTime.php260
-rwxr-xr-x3rdparty/Sabre/VObject/Property/MultiDateTime.php166
-rwxr-xr-x3rdparty/Sabre/VObject/Reader.php183
-rwxr-xr-x3rdparty/Sabre/VObject/RecurrenceIterator.php1043
-rwxr-xr-x3rdparty/Sabre/VObject/Version.php24
-rwxr-xr-x3rdparty/Sabre/VObject/WindowsTimezoneMap.php128
-rwxr-xr-x3rdparty/Sabre/VObject/includes.php41
-rwxr-xr-x3rdparty/Sabre/autoload.php31
180 files changed, 25160 insertions, 0 deletions
diff --git a/3rdparty/Sabre.includes.php b/3rdparty/Sabre.includes.php
new file mode 100755
index 00000000000..c1334373663
--- /dev/null
+++ b/3rdparty/Sabre.includes.php
@@ -0,0 +1,26 @@
+<?php
+
+/**
+ * Library include file
+ *
+ * This file is deprecated, don't use it!
+ * Instead, use the specific includes files that are in the sub-packages.
+ *
+ * Sabre/DAV/includes.php
+ * Sabre/HTTP/includes.php
+ *
+ * etc..
+ *
+ * This file contains all includes to the rest of the SabreDAV library
+ * Make sure the lib/ directory is in PHP's include_path.
+ *
+ * @package Sabre
+ * @deprecated Don't use this file, it will be remove in a future version
+ * @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
+ */
+
+include 'Sabre/HTTP/includes.php';
+include 'Sabre/DAV/includes.php';
+
diff --git a/3rdparty/Sabre/CalDAV/Backend/Abstract.php b/3rdparty/Sabre/CalDAV/Backend/Abstract.php
new file mode 100755
index 00000000000..7aba1d69ffe
--- /dev/null
+++ b/3rdparty/Sabre/CalDAV/Backend/Abstract.php
@@ -0,0 +1,168 @@
+<?php
+
+/**
+ * Abstract Calendaring backend. Extend this class to create your own backends.
+ *
+ * @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
+ */
+abstract class Sabre_CalDAV_Backend_Abstract {
+
+ /**
+ * Returns a list of calendars for a principal.
+ *
+ * Every project is an array with the following keys:
+ * * id, a unique id that will be used by other functions to modify the
+ * calendar. This can be the same as the uri or a database key.
+ * * uri, which the basename of the uri with which the calendar is
+ * accessed.
+ * * principaluri. The owner of the calendar. Almost always the same as
+ * principalUri passed to this method.
+ *
+ * Furthermore it can contain webdav properties in clark notation. A very
+ * common one is '{DAV:}displayname'.
+ *
+ * @param string $principalUri
+ * @return array
+ */
+ abstract function getCalendarsForUser($principalUri);
+
+ /**
+ * Creates a new calendar for a principal.
+ *
+ * If the creation was a success, an id must be returned that can be used to reference
+ * this calendar in other methods, such as updateCalendar.
+ *
+ * @param string $principalUri
+ * @param string $calendarUri
+ * @param array $properties
+ * @return void
+ */
+ abstract function createCalendar($principalUri,$calendarUri,array $properties);
+
+ /**
+ * Updates properties for a calendar.
+ *
+ * The mutations array uses the propertyName in clark-notation as key,
+ * and the array value for the property value. In the case a property
+ * should be deleted, the property value will be null.
+ *
+ * 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-existent property is always successful.
+ *
+ * Lastly, it is optional to return detailed information about any
+ * failures. In this case an array should be returned with the following
+ * structure:
+ *
+ * array(
+ * 403 => array(
+ * '{DAV:}displayname' => null,
+ * ),
+ * 424 => array(
+ * '{DAV:}owner' => null,
+ * )
+ * )
+ *
+ * In this example it was forbidden to update {DAV:}displayname.
+ * (403 Forbidden), which in turn also caused {DAV:}owner to fail
+ * (424 Failed Dependency) because the request needs to be atomic.
+ *
+ * @param string $calendarId
+ * @param array $mutations
+ * @return bool|array
+ */
+ public function updateCalendar($calendarId, array $mutations) {
+
+ return false;
+
+ }
+
+ /**
+ * Delete a calendar and all it's objects
+ *
+ * @param string $calendarId
+ * @return void
+ */
+ abstract function deleteCalendar($calendarId);
+
+ /**
+ * Returns all calendar objects within a calendar.
+ *
+ * Every item contains an array with the following keys:
+ * * id - unique identifier which will be used for subsequent updates
+ * * calendardata - The iCalendar-compatible calendar data
+ * * uri - a unique key which will be used to construct the uri. This can be any arbitrary string.
+ * * lastmodified - a timestamp of the last modification time
+ * * etag - An arbitrary string, surrounded by double-quotes. (e.g.:
+ * ' "abcdef"')
+ * * calendarid - The calendarid as it was passed to this function.
+ * * size - The size of the calendar objects, in bytes.
+ *
+ * Note that the etag is optional, but it's highly encouraged to return for
+ * speed reasons.
+ *
+ * The calendardata is also optional. If it's not returned
+ * 'getCalendarObject' will be called later, which *is* expected to return
+ * calendardata.
+ *
+ * If neither etag or size are specified, the calendardata will be
+ * used/fetched to determine these numbers. If both are specified the
+ * amount of times this is needed is reduced by a great degree.
+ *
+ * @param string $calendarId
+ * @return array
+ */
+ abstract function getCalendarObjects($calendarId);
+
+ /**
+ * Returns information from a single calendar object, based on it's object
+ * uri.
+ *
+ * The returned array must have the same keys as getCalendarObjects. The
+ * 'calendardata' object is required here though, while it's not required
+ * for getCalendarObjects.
+ *
+ * @param string $calendarId
+ * @param string $objectUri
+ * @return array
+ */
+ abstract function getCalendarObject($calendarId,$objectUri);
+
+ /**
+ * Creates a new calendar object.
+ *
+ * @param string $calendarId
+ * @param string $objectUri
+ * @param string $calendarData
+ * @return void
+ */
+ abstract function createCalendarObject($calendarId,$objectUri,$calendarData);
+
+ /**
+ * Updates an existing calendarobject, based on it's uri.
+ *
+ * @param string $calendarId
+ * @param string $objectUri
+ * @param string $calendarData
+ * @return void
+ */
+ abstract function updateCalendarObject($calendarId,$objectUri,$calendarData);
+
+ /**
+ * Deletes an existing calendar object.
+ *
+ * @param string $calendarId
+ * @param string $objectUri
+ * @return void
+ */
+ abstract function deleteCalendarObject($calendarId,$objectUri);
+
+}
diff --git a/3rdparty/Sabre/CalDAV/Backend/PDO.php b/3rdparty/Sabre/CalDAV/Backend/PDO.php
new file mode 100755
index 00000000000..ddacf940c74
--- /dev/null
+++ b/3rdparty/Sabre/CalDAV/Backend/PDO.php
@@ -0,0 +1,396 @@
+<?php
+
+/**
+ * PDO CalDAV backend
+ *
+ * This backend is used to store calendar-data in a PDO database, such as
+ * sqlite or MySQL
+ *
+ * @package Sabre
+ * @subpackage CalDAV
+ * @copyright Copyright (C) 2007-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_Backend_PDO extends Sabre_CalDAV_Backend_Abstract {
+
+ /**
+ * pdo
+ *
+ * @var PDO
+ */
+ protected $pdo;
+
+ /**
+ * The table name that will be used for calendars
+ *
+ * @var string
+ */
+ protected $calendarTableName;
+
+ /**
+ * The table name that will be used for calendar objects
+ *
+ * @var string
+ */
+ protected $calendarObjectTableName;
+
+ /**
+ * List of CalDAV properties, and how they map to database fieldnames
+ *
+ * Add your own properties by simply adding on to this array
+ *
+ * @var array
+ */
+ public $propertyMap = array(
+ '{DAV:}displayname' => 'displayname',
+ '{urn:ietf:params:xml:ns:caldav}calendar-description' => 'description',
+ '{urn:ietf:params:xml:ns:caldav}calendar-timezone' => 'timezone',
+ '{http://apple.com/ns/ical/}calendar-order' => 'calendarorder',
+ '{http://apple.com/ns/ical/}calendar-color' => 'calendarcolor',
+ );
+
+ /**
+ * Creates the backend
+ *
+ * @param PDO $pdo
+ * @param string $calendarTableName
+ * @param string $calendarObjectTableName
+ */
+ public function __construct(PDO $pdo, $calendarTableName = 'calendars', $calendarObjectTableName = 'calendarobjects') {
+
+ $this->pdo = $pdo;
+ $this->calendarTableName = $calendarTableName;
+ $this->calendarObjectTableName = $calendarObjectTableName;
+
+ }
+
+ /**
+ * Returns a list of calendars for a principal.
+ *
+ * Every project is an array with the following keys:
+ * * id, a unique id that will be used by other functions to modify the
+ * calendar. This can be the same as the uri or a database key.
+ * * uri, which the basename of the uri with which the calendar is
+ * accessed.
+ * * principaluri. The owner of the calendar. Almost always the same as
+ * principalUri passed to this method.
+ *
+ * Furthermore it can contain webdav properties in clark notation. A very
+ * common one is '{DAV:}displayname'.
+ *
+ * @param string $principalUri
+ * @return array
+ */
+ public function getCalendarsForUser($principalUri) {
+
+ $fields = array_values($this->propertyMap);
+ $fields[] = 'id';
+ $fields[] = 'uri';
+ $fields[] = 'ctag';
+ $fields[] = 'components';
+ $fields[] = 'principaluri';
+
+ // Making fields a comma-delimited list
+ $fields = implode(', ', $fields);
+ $stmt = $this->pdo->prepare("SELECT " . $fields . " FROM ".$this->calendarTableName." WHERE principaluri = ? ORDER BY calendarorder ASC");
+ $stmt->execute(array($principalUri));
+
+ $calendars = array();
+ while($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+
+ $components = array();
+ if ($row['components']) {
+ $components = explode(',',$row['components']);
+ }
+
+ $calendar = array(
+ 'id' => $row['id'],
+ 'uri' => $row['uri'],
+ 'principaluri' => $row['principaluri'],
+ '{' . Sabre_CalDAV_Plugin::NS_CALENDARSERVER . '}getctag' => $row['ctag']?$row['ctag']:'0',
+ '{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}supported-calendar-component-set' => new Sabre_CalDAV_Property_SupportedCalendarComponentSet($components),
+ );
+
+
+ foreach($this->propertyMap as $xmlName=>$dbName) {
+ $calendar[$xmlName] = $row[$dbName];
+ }
+
+ $calendars[] = $calendar;
+
+ }
+
+ return $calendars;
+
+ }
+
+ /**
+ * Creates a new calendar for a principal.
+ *
+ * If the creation was a success, an id must be returned that can be used to reference
+ * this calendar in other methods, such as updateCalendar
+ *
+ * @param string $principalUri
+ * @param string $calendarUri
+ * @param array $properties
+ * @return string
+ */
+ public function createCalendar($principalUri, $calendarUri, array $properties) {
+
+ $fieldNames = array(
+ 'principaluri',
+ 'uri',
+ 'ctag',
+ );
+ $values = array(
+ ':principaluri' => $principalUri,
+ ':uri' => $calendarUri,
+ ':ctag' => 1,
+ );
+
+ // Default value
+ $sccs = '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set';
+ $fieldNames[] = 'components';
+ if (!isset($properties[$sccs])) {
+ $values[':components'] = 'VEVENT,VTODO';
+ } else {
+ if (!($properties[$sccs] instanceof Sabre_CalDAV_Property_SupportedCalendarComponentSet)) {
+ throw new Sabre_DAV_Exception('The ' . $sccs . ' property must be of type: Sabre_CalDAV_Property_SupportedCalendarComponentSet');
+ }
+ $values[':components'] = implode(',',$properties[$sccs]->getValue());
+ }
+
+ foreach($this->propertyMap as $xmlName=>$dbName) {
+ if (isset($properties[$xmlName])) {
+
+ $values[':' . $dbName] = $properties[$xmlName];
+ $fieldNames[] = $dbName;
+ }
+ }
+
+ $stmt = $this->pdo->prepare("INSERT INTO ".$this->calendarTableName." (".implode(', ', $fieldNames).") VALUES (".implode(', ',array_keys($values)).")");
+ $stmt->execute($values);
+
+ return $this->pdo->lastInsertId();
+
+ }
+
+ /**
+ * Updates properties for a calendar.
+ *
+ * The mutations array uses the propertyName in clark-notation as key,
+ * and the array value for the property value. In the case a property
+ * should be deleted, the property value will be null.
+ *
+ * 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-existent property is always successful.
+ *
+ * Lastly, it is optional to return detailed information about any
+ * failures. In this case an array should be returned with the following
+ * structure:
+ *
+ * array(
+ * 403 => array(
+ * '{DAV:}displayname' => null,
+ * ),
+ * 424 => array(
+ * '{DAV:}owner' => null,
+ * )
+ * )
+ *
+ * In this example it was forbidden to update {DAV:}displayname.
+ * (403 Forbidden), which in turn also caused {DAV:}owner to fail
+ * (424 Failed Dependency) because the request needs to be atomic.
+ *
+ * @param string $calendarId
+ * @param array $mutations
+ * @return bool|array
+ */
+ public function updateCalendar($calendarId, array $mutations) {
+
+ $newValues = array();
+ $result = array(
+ 200 => array(), // Ok
+ 403 => array(), // Forbidden
+ 424 => array(), // Failed Dependency
+ );
+
+ $hasError = false;
+
+ foreach($mutations as $propertyName=>$propertyValue) {
+
+ // We don't know about this property.
+ if (!isset($this->propertyMap[$propertyName])) {
+ $hasError = true;
+ $result[403][$propertyName] = null;
+ unset($mutations[$propertyName]);
+ continue;
+ }
+
+ $fieldName = $this->propertyMap[$propertyName];
+ $newValues[$fieldName] = $propertyValue;
+
+ }
+
+ // If there were any errors we need to fail the request
+ if ($hasError) {
+ // Properties has the remaining properties
+ foreach($mutations as $propertyName=>$propertyValue) {
+ $result[424][$propertyName] = null;
+ }
+
+ // Removing unused statuscodes for cleanliness
+ foreach($result as $status=>$properties) {
+ if (is_array($properties) && count($properties)===0) unset($result[$status]);
+ }
+
+ return $result;
+
+ }
+
+ // Success
+
+ // Now we're generating the sql query.
+ $valuesSql = array();
+ foreach($newValues as $fieldName=>$value) {
+ $valuesSql[] = $fieldName . ' = ?';
+ }
+ $valuesSql[] = 'ctag = ctag + 1';
+
+ $stmt = $this->pdo->prepare("UPDATE " . $this->calendarTableName . " SET " . implode(', ',$valuesSql) . " WHERE id = ?");
+ $newValues['id'] = $calendarId;
+ $stmt->execute(array_values($newValues));
+
+ return true;
+
+ }
+
+ /**
+ * Delete a calendar and all it's objects
+ *
+ * @param string $calendarId
+ * @return void
+ */
+ public function deleteCalendar($calendarId) {
+
+ $stmt = $this->pdo->prepare('DELETE FROM '.$this->calendarObjectTableName.' WHERE calendarid = ?');
+ $stmt->execute(array($calendarId));
+
+ $stmt = $this->pdo->prepare('DELETE FROM '.$this->calendarTableName.' WHERE id = ?');
+ $stmt->execute(array($calendarId));
+
+ }
+
+ /**
+ * Returns all calendar objects within a calendar.
+ *
+ * Every item contains an array with the following keys:
+ * * id - unique identifier which will be used for subsequent updates
+ * * calendardata - The iCalendar-compatible calendar data
+ * * uri - a unique key which will be used to construct the uri. This can be any arbitrary string.
+ * * lastmodified - a timestamp of the last modification time
+ * * etag - An arbitrary string, surrounded by double-quotes. (e.g.:
+ * ' "abcdef"')
+ * * calendarid - The calendarid as it was passed to this function.
+ * * size - The size of the calendar objects, in bytes.
+ *
+ * Note that the etag is optional, but it's highly encouraged to return for
+ * speed reasons.
+ *
+ * The calendardata is also optional. If it's not returned
+ * 'getCalendarObject' will be called later, which *is* expected to return
+ * calendardata.
+ *
+ * If neither etag or size are specified, the calendardata will be
+ * used/fetched to determine these numbers. If both are specified the
+ * amount of times this is needed is reduced by a great degree.
+ *
+ * @param string $calendarId
+ * @return array
+ */
+ public function getCalendarObjects($calendarId) {
+
+ $stmt = $this->pdo->prepare('SELECT * FROM '.$this->calendarObjectTableName.' WHERE calendarid = ?');
+ $stmt->execute(array($calendarId));
+ return $stmt->fetchAll();
+
+ }
+
+ /**
+ * Returns information from a single calendar object, based on it's object
+ * uri.
+ *
+ * The returned array must have the same keys as getCalendarObjects. The
+ * 'calendardata' object is required here though, while it's not required
+ * for getCalendarObjects.
+ *
+ * @param string $calendarId
+ * @param string $objectUri
+ * @return array
+ */
+ public function getCalendarObject($calendarId,$objectUri) {
+
+ $stmt = $this->pdo->prepare('SELECT * FROM '.$this->calendarObjectTableName.' WHERE calendarid = ? AND uri = ?');
+ $stmt->execute(array($calendarId, $objectUri));
+ return $stmt->fetch();
+
+ }
+
+ /**
+ * Creates a new calendar object.
+ *
+ * @param string $calendarId
+ * @param string $objectUri
+ * @param string $calendarData
+ * @return void
+ */
+ public function createCalendarObject($calendarId,$objectUri,$calendarData) {
+
+ $stmt = $this->pdo->prepare('INSERT INTO '.$this->calendarObjectTableName.' (calendarid, uri, calendardata, lastmodified) VALUES (?,?,?,?)');
+ $stmt->execute(array($calendarId,$objectUri,$calendarData,time()));
+ $stmt = $this->pdo->prepare('UPDATE '.$this->calendarTableName.' SET ctag = ctag + 1 WHERE id = ?');
+ $stmt->execute(array($calendarId));
+
+ }
+
+ /**
+ * Updates an existing calendarobject, based on it's uri.
+ *
+ * @param string $calendarId
+ * @param string $objectUri
+ * @param string $calendarData
+ * @return void
+ */
+ public function updateCalendarObject($calendarId,$objectUri,$calendarData) {
+
+ $stmt = $this->pdo->prepare('UPDATE '.$this->calendarObjectTableName.' SET calendardata = ?, lastmodified = ? WHERE calendarid = ? AND uri = ?');
+ $stmt->execute(array($calendarData,time(),$calendarId,$objectUri));
+ $stmt = $this->pdo->prepare('UPDATE '.$this->calendarTableName.' SET ctag = ctag + 1 WHERE id = ?');
+ $stmt->execute(array($calendarId));
+
+ }
+
+ /**
+ * Deletes an existing calendar object.
+ *
+ * @param string $calendarId
+ * @param string $objectUri
+ * @return void
+ */
+ public function deleteCalendarObject($calendarId,$objectUri) {
+
+ $stmt = $this->pdo->prepare('DELETE FROM '.$this->calendarObjectTableName.' WHERE calendarid = ? AND uri = ?');
+ $stmt->execute(array($calendarId,$objectUri));
+ $stmt = $this->pdo->prepare('UPDATE '. $this->calendarTableName .' SET ctag = ctag + 1 WHERE id = ?');
+ $stmt->execute(array($calendarId));
+
+ }
+
+
+}
diff --git a/3rdparty/Sabre/CalDAV/Calendar.php b/3rdparty/Sabre/CalDAV/Calendar.php
new file mode 100755
index 00000000000..623df2dd1b8
--- /dev/null
+++ b/3rdparty/Sabre/CalDAV/Calendar.php
@@ -0,0 +1,343 @@
+<?php
+
+/**
+ * This object represents a CalDAV calendar.
+ *
+ * A calendar can contain multiple TODO and or Events. These are represented
+ * as Sabre_CalDAV_CalendarObject objects.
+ *
+ * @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_Calendar implements Sabre_CalDAV_ICalendar, Sabre_DAV_IProperties, Sabre_DAVACL_IACL {
+
+ /**
+ * This is an array with calendar information
+ *
+ * @var array
+ */
+ protected $calendarInfo;
+
+ /**
+ * CalDAV backend
+ *
+ * @var Sabre_CalDAV_Backend_Abstract
+ */
+ protected $caldavBackend;
+
+ /**
+ * Principal backend
+ *
+ * @var Sabre_DAVACL_IPrincipalBackend
+ */
+ protected $principalBackend;
+
+ /**
+ * Constructor
+ *
+ * @param Sabre_DAVACL_IPrincipalBackend $principalBackend
+ * @param Sabre_CalDAV_Backend_Abstract $caldavBackend
+ * @param array $calendarInfo
+ */
+ public function __construct(Sabre_DAVACL_IPrincipalBackend $principalBackend, Sabre_CalDAV_Backend_Abstract $caldavBackend, $calendarInfo) {
+
+ $this->caldavBackend = $caldavBackend;
+ $this->principalBackend = $principalBackend;
+ $this->calendarInfo = $calendarInfo;
+
+
+ }
+
+ /**
+ * Returns the name of the calendar
+ *
+ * @return string
+ */
+ public function getName() {
+
+ return $this->calendarInfo['uri'];
+
+ }
+
+ /**
+ * Updates properties such as the display name and description
+ *
+ * @param array $mutations
+ * @return array
+ */
+ public function updateProperties($mutations) {
+
+ return $this->caldavBackend->updateCalendar($this->calendarInfo['id'],$mutations);
+
+ }
+
+ /**
+ * Returns the list of properties
+ *
+ * @param array $requestedProperties
+ * @return array
+ */
+ public function getProperties($requestedProperties) {
+
+ $response = array();
+
+ foreach($requestedProperties as $prop) switch($prop) {
+
+ case '{urn:ietf:params:xml:ns:caldav}supported-calendar-data' :
+ $response[$prop] = new Sabre_CalDAV_Property_SupportedCalendarData();
+ break;
+ case '{urn:ietf:params:xml:ns:caldav}supported-collation-set' :
+ $response[$prop] = new Sabre_CalDAV_Property_SupportedCollationSet();
+ break;
+ case '{DAV:}owner' :
+ $response[$prop] = new Sabre_DAVACL_Property_Principal(Sabre_DAVACL_Property_Principal::HREF,$this->calendarInfo['principaluri']);
+ break;
+ default :
+ if (isset($this->calendarInfo[$prop])) $response[$prop] = $this->calendarInfo[$prop];
+ break;
+
+ }
+ return $response;
+
+ }
+
+ /**
+ * Returns a calendar object
+ *
+ * The contained calendar objects are for example Events or Todo's.
+ *
+ * @param string $name
+ * @return Sabre_DAV_ICalendarObject
+ */
+ public function getChild($name) {
+
+ $obj = $this->caldavBackend->getCalendarObject($this->calendarInfo['id'],$name);
+ if (!$obj) throw new Sabre_DAV_Exception_NotFound('Calendar object not found');
+ return new Sabre_CalDAV_CalendarObject($this->caldavBackend,$this->calendarInfo,$obj);
+
+ }
+
+ /**
+ * Returns the full list of calendar objects
+ *
+ * @return array
+ */
+ public function getChildren() {
+
+ $objs = $this->caldavBackend->getCalendarObjects($this->calendarInfo['id']);
+ $children = array();
+ foreach($objs as $obj) {
+ $children[] = new Sabre_CalDAV_CalendarObject($this->caldavBackend,$this->calendarInfo,$obj);
+ }
+ return $children;
+
+ }
+
+ /**
+ * Checks if a child-node exists.
+ *
+ * @param string $name
+ * @return bool
+ */
+ public function childExists($name) {
+
+ $obj = $this->caldavBackend->getCalendarObject($this->calendarInfo['id'],$name);
+ if (!$obj)
+ return false;
+ else
+ return true;
+
+ }
+
+ /**
+ * Creates a new directory
+ *
+ * We actually block this, as subdirectories are not allowed in calendars.
+ *
+ * @param string $name
+ * @return void
+ */
+ public function createDirectory($name) {
+
+ throw new Sabre_DAV_Exception_MethodNotAllowed('Creating collections in calendar objects is not allowed');
+
+ }
+
+ /**
+ * Creates a new file
+ *
+ * The contents of the new file must be a valid ICalendar string.
+ *
+ * @param string $name
+ * @param resource $calendarData
+ * @return string|null
+ */
+ public function createFile($name,$calendarData = null) {
+
+ if (is_resource($calendarData)) {
+ $calendarData = stream_get_contents($calendarData);
+ }
+ return $this->caldavBackend->createCalendarObject($this->calendarInfo['id'],$name,$calendarData);
+
+ }
+
+ /**
+ * Deletes the calendar.
+ *
+ * @return void
+ */
+ public function delete() {
+
+ $this->caldavBackend->deleteCalendar($this->calendarInfo['id']);
+
+ }
+
+ /**
+ * Renames the calendar. Note that most calendars use the
+ * {DAV:}displayname to display a name to display a name.
+ *
+ * @param string $newName
+ * @return void
+ */
+ public function setName($newName) {
+
+ throw new Sabre_DAV_Exception_MethodNotAllowed('Renaming calendars is not yet supported');
+
+ }
+
+ /**
+ * Returns the last modification date as a unix timestamp.
+ *
+ * @return void
+ */
+ public function getLastModified() {
+
+ return null;
+
+ }
+
+ /**
+ * 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->calendarInfo['principaluri'];
+
+ }
+
+ /**
+ * Returns a group principal
+ *
+ * This must be a url to a principal, or null if there's no owner
+ *
+ * @return string|null
+ */
+ public function getGroup() {
+
+ return null;
+
+ }
+
+ /**
+ * Returns a list of ACE's for this node.
+ *
+ * Each ACE has the following properties:
+ * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are
+ * currently the only supported privileges
+ * * 'principal', a url to the principal who owns the node
+ * * 'protected' (optional), indicating that this ACE is not allowed to
+ * be updated.
+ *
+ * @return array
+ */
+ public function getACL() {
+
+ return array(
+ array(
+ 'privilege' => '{DAV:}read',
+ 'principal' => $this->calendarInfo['principaluri'],
+ 'protected' => true,
+ ),
+ array(
+ 'privilege' => '{DAV:}write',
+ 'principal' => $this->calendarInfo['principaluri'],
+ 'protected' => true,
+ ),
+ array(
+ 'privilege' => '{DAV:}read',
+ 'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-write',
+ 'protected' => true,
+ ),
+ array(
+ 'privilege' => '{DAV:}write',
+ 'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-write',
+ 'protected' => true,
+ ),
+ array(
+ 'privilege' => '{DAV:}read',
+ 'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-read',
+ 'protected' => true,
+ ),
+ array(
+ 'privilege' => '{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}read-free-busy',
+ 'principal' => '{DAV:}authenticated',
+ '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');
+
+ }
+
+ /**
+ * Returns the list of supported privileges for this node.
+ *
+ * The returned data structure is a list of nested privileges.
+ * See Sabre_DAVACL_Plugin::getDefaultSupportedPrivilegeSet for a simple
+ * standard structure.
+ *
+ * If null is returned from this method, the default privilege set is used,
+ * which is fine for most common usecases.
+ *
+ * @return array|null
+ */
+ public function getSupportedPrivilegeSet() {
+
+ $default = Sabre_DAVACL_Plugin::getDefaultSupportedPrivilegeSet();
+
+ // We need to inject 'read-free-busy' in the tree, aggregated under
+ // {DAV:}read.
+ foreach($default['aggregates'] as &$agg) {
+
+ if ($agg['privilege'] !== '{DAV:}read') continue;
+
+ $agg['aggregates'][] = array(
+ 'privilege' => '{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}read-free-busy',
+ );
+
+ }
+ return $default;
+
+ }
+
+}
diff --git a/3rdparty/Sabre/CalDAV/CalendarObject.php b/3rdparty/Sabre/CalDAV/CalendarObject.php
new file mode 100755
index 00000000000..72f0a578d16
--- /dev/null
+++ b/3rdparty/Sabre/CalDAV/CalendarObject.php
@@ -0,0 +1,273 @@
+<?php
+
+/**
+ * The CalendarObject represents a single VEVENT or VTODO within a Calendar.
+ *
+ * @package Sabre
+ * @subpackage CalDAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_CalDAV_CalendarObject extends Sabre_DAV_File implements Sabre_CalDAV_ICalendarObject, Sabre_DAVACL_IACL {
+
+ /**
+ * Sabre_CalDAV_Backend_Abstract
+ *
+ * @var array
+ */
+ protected $caldavBackend;
+
+ /**
+ * Array with information about this CalendarObject
+ *
+ * @var array
+ */
+ protected $objectData;
+
+ /**
+ * Array with information about the containing calendar
+ *
+ * @var array
+ */
+ protected $calendarInfo;
+
+ /**
+ * Constructor
+ *
+ * @param Sabre_CalDAV_Backend_Abstract $caldavBackend
+ * @param array $calendarInfo
+ * @param array $objectData
+ */
+ public function __construct(Sabre_CalDAV_Backend_Abstract $caldavBackend,array $calendarInfo,array $objectData) {
+
+ $this->caldavBackend = $caldavBackend;
+
+ if (!isset($objectData['calendarid'])) {
+ throw new InvalidArgumentException('The objectData argument must contain a \'calendarid\' property');
+ }
+ if (!isset($objectData['uri'])) {
+ throw new InvalidArgumentException('The objectData argument must contain an \'uri\' property');
+ }
+
+ $this->calendarInfo = $calendarInfo;
+ $this->objectData = $objectData;
+
+ }
+
+ /**
+ * Returns the uri for this object
+ *
+ * @return string
+ */
+ public function getName() {
+
+ return $this->objectData['uri'];
+
+ }
+
+ /**
+ * Returns the ICalendar-formatted object
+ *
+ * @return string
+ */
+ public function get() {
+
+ // Pre-populating the 'calendardata' is optional, if we don't have it
+ // already we fetch it from the backend.
+ if (!isset($this->objectData['calendardata'])) {
+ $this->objectData = $this->caldavBackend->getCalendarObject($this->objectData['calendarid'], $this->objectData['uri']);
+ }
+ return $this->objectData['calendardata'];
+
+ }
+
+ /**
+ * Updates the ICalendar-formatted object
+ *
+ * @param string $calendarData
+ * @return void
+ */
+ public function put($calendarData) {
+
+ if (is_resource($calendarData)) {
+ $calendarData = stream_get_contents($calendarData);
+ }
+ $etag = $this->caldavBackend->updateCalendarObject($this->calendarInfo['id'],$this->objectData['uri'],$calendarData);
+ $this->objectData['calendardata'] = $calendarData;
+ $this->objectData['etag'] = $etag;
+
+ return $etag;
+
+ }
+
+ /**
+ * Deletes the calendar object
+ *
+ * @return void
+ */
+ public function delete() {
+
+ $this->caldavBackend->deleteCalendarObject($this->calendarInfo['id'],$this->objectData['uri']);
+
+ }
+
+ /**
+ * Returns the mime content-type
+ *
+ * @return string
+ */
+ public function getContentType() {
+
+ return 'text/calendar';
+
+ }
+
+ /**
+ * Returns an ETag for this object.
+ *
+ * The ETag is an arbitrary string, but MUST be surrounded by double-quotes.
+ *
+ * @return string
+ */
+ public function getETag() {
+
+ if (isset($this->objectData['etag'])) {
+ return $this->objectData['etag'];
+ } else {
+ return '"' . md5($this->get()). '"';
+ }
+
+ }
+
+ /**
+ * Returns the last modification date as a unix timestamp
+ *
+ * @return time
+ */
+ public function getLastModified() {
+
+ return $this->objectData['lastmodified'];
+
+ }
+
+ /**
+ * Returns the size of this object in bytes
+ *
+ * @return int
+ */
+ public function getSize() {
+
+ if (array_key_exists('size',$this->objectData)) {
+ return $this->objectData['size'];
+ } else {
+ return strlen($this->get());
+ }
+
+ }
+
+ /**
+ * 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->calendarInfo['principaluri'];
+
+ }
+
+ /**
+ * Returns a group principal
+ *
+ * This must be a url to a principal, or null if there's no owner
+ *
+ * @return string|null
+ */
+ public function getGroup() {
+
+ return null;
+
+ }
+
+ /**
+ * Returns a list of ACE's for this node.
+ *
+ * Each ACE has the following properties:
+ * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are
+ * currently the only supported privileges
+ * * 'principal', a url to the principal who owns the node
+ * * 'protected' (optional), indicating that this ACE is not allowed to
+ * be updated.
+ *
+ * @return array
+ */
+ public function getACL() {
+
+ return array(
+ array(
+ 'privilege' => '{DAV:}read',
+ 'principal' => $this->calendarInfo['principaluri'],
+ 'protected' => true,
+ ),
+ array(
+ 'privilege' => '{DAV:}write',
+ 'principal' => $this->calendarInfo['principaluri'],
+ 'protected' => true,
+ ),
+ array(
+ 'privilege' => '{DAV:}read',
+ 'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-write',
+ 'protected' => true,
+ ),
+ array(
+ 'privilege' => '{DAV:}write',
+ 'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-write',
+ 'protected' => true,
+ ),
+ array(
+ 'privilege' => '{DAV:}read',
+ 'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-read',
+ 'protected' => true,
+ ),
+
+ );
+
+ }
+
+ /**
+ * 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');
+
+ }
+
+ /**
+ * Returns the list of supported privileges for this node.
+ *
+ * The returned data structure is a list of nested privileges.
+ * See Sabre_DAVACL_Plugin::getDefaultSupportedPrivilegeSet for a simple
+ * standard structure.
+ *
+ * If null is returned from this method, the default privilege set is used,
+ * which is fine for most common usecases.
+ *
+ * @return array|null
+ */
+ public function getSupportedPrivilegeSet() {
+
+ return null;
+
+ }
+
+}
+
diff --git a/3rdparty/Sabre/CalDAV/CalendarQueryParser.php b/3rdparty/Sabre/CalDAV/CalendarQueryParser.php
new file mode 100755
index 00000000000..bd0d343382f
--- /dev/null
+++ b/3rdparty/Sabre/CalDAV/CalendarQueryParser.php
@@ -0,0 +1,296 @@
+<?php
+
+/**
+ * Parses the calendar-query report request body.
+ *
+ * Whoever designed this format, and the CalDAV equivalent even more so,
+ * has no feel for design.
+ *
+ * @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_CalendarQueryParser {
+
+ /**
+ * List of requested properties the client wanted
+ *
+ * @var array
+ */
+ public $requestedProperties;
+
+ /**
+ * List of property/component filters.
+ *
+ * @var array
+ */
+ public $filters;
+
+ /**
+ * This property will contain null if CALDAV:expand was not specified,
+ * otherwise it will contain an array with 2 elements (start, end). Each
+ * contain a DateTime object.
+ *
+ * If expand is specified, recurring calendar objects are to be expanded
+ * into their individual components, and only the components that fall
+ * within the specified time-range are to be returned.
+ *
+ * For more details, see rfc4791, section 9.6.5.
+ *
+ * @var null|array
+ */
+ public $expand;
+
+ /**
+ * DOM Document
+ *
+ * @var DOMDocument
+ */
+ protected $dom;
+
+ /**
+ * DOM XPath object
+ *
+ * @var DOMXPath
+ */
+ protected $xpath;
+
+ /**
+ * Creates the parser
+ *
+ * @param DOMDocument $dom
+ */
+ public function __construct(DOMDocument $dom) {
+
+ $this->dom = $dom;
+
+ $this->xpath = new DOMXPath($dom);
+ $this->xpath->registerNameSpace('cal',Sabre_CalDAV_Plugin::NS_CALDAV);
+ $this->xpath->registerNameSpace('dav','urn:DAV');
+
+ }
+
+ /**
+ * Parses the request.
+ *
+ * @return void
+ */
+ public function parse() {
+
+ $filterNode = null;
+
+ $filter = $this->xpath->query('/cal:calendar-query/cal:filter');
+ if ($filter->length !== 1) {
+ throw new Sabre_DAV_Exception_BadRequest('Only one filter element is allowed');
+ }
+
+ $compFilters = $this->parseCompFilters($filter->item(0));
+ if (count($compFilters)!==1) {
+ throw new Sabre_DAV_Exception_BadRequest('There must be exactly 1 top-level comp-filter.');
+ }
+
+ $this->filters = $compFilters[0];
+ $this->requestedProperties = array_keys(Sabre_DAV_XMLUtil::parseProperties($this->dom->firstChild));
+
+ $expand = $this->xpath->query('/cal:calendar-query/dav:prop/cal:calendar-data/cal:expand');
+ if ($expand->length>0) {
+ $this->expand = $this->parseExpand($expand->item(0));
+ }
+
+
+ }
+
+ /**
+ * Parses all the 'comp-filter' elements from a node
+ *
+ * @param DOMElement $parentNode
+ * @return array
+ */
+ protected function parseCompFilters(DOMElement $parentNode) {
+
+ $compFilterNodes = $this->xpath->query('cal:comp-filter', $parentNode);
+ $result = array();
+
+ for($ii=0; $ii < $compFilterNodes->length; $ii++) {
+
+ $compFilterNode = $compFilterNodes->item($ii);
+
+ $compFilter = array();
+ $compFilter['name'] = $compFilterNode->getAttribute('name');
+ $compFilter['is-not-defined'] = $this->xpath->query('cal:is-not-defined', $compFilterNode)->length>0;
+ $compFilter['comp-filters'] = $this->parseCompFilters($compFilterNode);
+ $compFilter['prop-filters'] = $this->parsePropFilters($compFilterNode);
+ $compFilter['time-range'] = $this->parseTimeRange($compFilterNode);
+
+ if ($compFilter['time-range'] && !in_array($compFilter['name'],array(
+ 'VEVENT',
+ 'VTODO',
+ 'VJOURNAL',
+ 'VFREEBUSY',
+ 'VALARM',
+ ))) {
+ throw new Sabre_DAV_Exception_BadRequest('The time-range filter is not defined for the ' . $compFilter['name'] . ' component');
+ };
+
+ $result[] = $compFilter;
+
+ }
+
+ return $result;
+
+ }
+
+ /**
+ * Parses all the prop-filter elements from a node
+ *
+ * @param DOMElement $parentNode
+ * @return array
+ */
+ protected function parsePropFilters(DOMElement $parentNode) {
+
+ $propFilterNodes = $this->xpath->query('cal:prop-filter', $parentNode);
+ $result = array();
+
+ for ($ii=0; $ii < $propFilterNodes->length; $ii++) {
+
+ $propFilterNode = $propFilterNodes->item($ii);
+ $propFilter = array();
+ $propFilter['name'] = $propFilterNode->getAttribute('name');
+ $propFilter['is-not-defined'] = $this->xpath->query('cal:is-not-defined', $propFilterNode)->length>0;
+ $propFilter['param-filters'] = $this->parseParamFilters($propFilterNode);
+ $propFilter['text-match'] = $this->parseTextMatch($propFilterNode);
+ $propFilter['time-range'] = $this->parseTimeRange($propFilterNode);
+
+ $result[] = $propFilter;
+
+ }
+
+ return $result;
+
+ }
+
+ /**
+ * Parses the param-filter element
+ *
+ * @param DOMElement $parentNode
+ * @return array
+ */
+ protected function parseParamFilters(DOMElement $parentNode) {
+
+ $paramFilterNodes = $this->xpath->query('cal:param-filter', $parentNode);
+ $result = array();
+
+ for($ii=0;$ii<$paramFilterNodes->length;$ii++) {
+
+ $paramFilterNode = $paramFilterNodes->item($ii);
+ $paramFilter = array();
+ $paramFilter['name'] = $paramFilterNode->getAttribute('name');
+ $paramFilter['is-not-defined'] = $this->xpath->query('cal:is-not-defined', $paramFilterNode)->length>0;
+ $paramFilter['text-match'] = $this->parseTextMatch($paramFilterNode);
+
+ $result[] = $paramFilter;
+
+ }
+
+ return $result;
+
+ }
+
+ /**
+ * Parses the text-match element
+ *
+ * @param DOMElement $parentNode
+ * @return array|null
+ */
+ protected function parseTextMatch(DOMElement $parentNode) {
+
+ $textMatchNodes = $this->xpath->query('cal:text-match', $parentNode);
+
+ if ($textMatchNodes->length === 0)
+ return null;
+
+ $textMatchNode = $textMatchNodes->item(0);
+ $negateCondition = $textMatchNode->getAttribute('negate-condition');
+ $negateCondition = $negateCondition==='yes';
+ $collation = $textMatchNode->getAttribute('collation');
+ if (!$collation) $collation = 'i;ascii-casemap';
+
+ return array(
+ 'negate-condition' => $negateCondition,
+ 'collation' => $collation,
+ 'value' => $textMatchNode->nodeValue
+ );
+
+ }
+
+ /**
+ * Parses the time-range element
+ *
+ * @param DOMElement $parentNode
+ * @return array|null
+ */
+ protected function parseTimeRange(DOMElement $parentNode) {
+
+ $timeRangeNodes = $this->xpath->query('cal:time-range', $parentNode);
+ if ($timeRangeNodes->length === 0) {
+ return null;
+ }
+
+ $timeRangeNode = $timeRangeNodes->item(0);
+
+ if ($start = $timeRangeNode->getAttribute('start')) {
+ $start = Sabre_VObject_DateTimeParser::parseDateTime($start);
+ } else {
+ $start = null;
+ }
+ if ($end = $timeRangeNode->getAttribute('end')) {
+ $end = Sabre_VObject_DateTimeParser::parseDateTime($end);
+ } else {
+ $end = null;
+ }
+
+ if (!is_null($start) && !is_null($end) && $end <= $start) {
+ throw new Sabre_DAV_Exception_BadRequest('The end-date must be larger than the start-date in the time-range filter');
+ }
+
+ return array(
+ 'start' => $start,
+ 'end' => $end,
+ );
+
+ }
+
+ /**
+ * Parses the CALDAV:expand element
+ *
+ * @param DOMElement $parentNode
+ * @return void
+ */
+ protected function parseExpand(DOMElement $parentNode) {
+
+ $start = $parentNode->getAttribute('start');
+ if(!$start) {
+ throw new Sabre_DAV_Exception_BadRequest('The "start" attribute is required for the CALDAV:expand element');
+ }
+ $start = Sabre_VObject_DateTimeParser::parseDateTime($start);
+
+ $end = $parentNode->getAttribute('end');
+ if(!$end) {
+ throw new Sabre_DAV_Exception_BadRequest('The "end" attribute is required for the CALDAV:expand element');
+ }
+ $end = Sabre_VObject_DateTimeParser::parseDateTime($end);
+
+ if ($end <= $start) {
+ throw new Sabre_DAV_Exception_BadRequest('The end-date must be larger than the start-date in the expand element.');
+ }
+
+ return array(
+ 'start' => $start,
+ 'end' => $end,
+ );
+
+ }
+
+}
diff --git a/3rdparty/Sabre/CalDAV/CalendarQueryValidator.php b/3rdparty/Sabre/CalDAV/CalendarQueryValidator.php
new file mode 100755
index 00000000000..8f674840e87
--- /dev/null
+++ b/3rdparty/Sabre/CalDAV/CalendarQueryValidator.php
@@ -0,0 +1,370 @@
+<?php
+
+/**
+ * CalendarQuery Validator
+ *
+ * This class is responsible for checking if an iCalendar object matches a set
+ * of filters. The main function to do this is 'validate'.
+ *
+ * This is used to determine which icalendar objects should be returned for a
+ * calendar-query REPORT request.
+ *
+ * @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_CalendarQueryValidator {
+
+ /**
+ * Verify if a list of filters applies to the calendar data object
+ *
+ * The list of filters must be formatted as parsed by Sabre_CalDAV_CalendarQueryParser
+ *
+ * @param Sabre_VObject_Component $vObject
+ * @param array $filters
+ * @return bool
+ */
+ public function validate(Sabre_VObject_Component $vObject,array $filters) {
+
+ // The top level object is always a component filter.
+ // We'll parse it manually, as it's pretty simple.
+ if ($vObject->name !== $filters['name']) {
+ return false;
+ }
+
+ return
+ $this->validateCompFilters($vObject, $filters['comp-filters']) &&
+ $this->validatePropFilters($vObject, $filters['prop-filters']);
+
+
+ }
+
+ /**
+ * This method checks the validity of comp-filters.
+ *
+ * A list of comp-filters needs to be specified. Also the parent of the
+ * component we're checking should be specified, not the component to check
+ * itself.
+ *
+ * @param Sabre_VObject_Component $parent
+ * @param array $filters
+ * @return bool
+ */
+ protected function validateCompFilters(Sabre_VObject_Component $parent, array $filters) {
+
+ foreach($filters as $filter) {
+
+ $isDefined = isset($parent->$filter['name']);
+
+ if ($filter['is-not-defined']) {
+
+ if ($isDefined) {
+ return false;
+ } else {
+ continue;
+ }
+
+ }
+ if (!$isDefined) {
+ return false;
+ }
+
+ if ($filter['time-range']) {
+ foreach($parent->$filter['name'] as $subComponent) {
+ if ($this->validateTimeRange($subComponent, $filter['time-range']['start'], $filter['time-range']['end'])) {
+ continue 2;
+ }
+ }
+ return false;
+ }
+
+ if (!$filter['comp-filters'] && !$filter['prop-filters']) {
+ continue;
+ }
+
+ // If there are sub-filters, we need to find at least one component
+ // for which the subfilters hold true.
+ foreach($parent->$filter['name'] as $subComponent) {
+
+ if (
+ $this->validateCompFilters($subComponent, $filter['comp-filters']) &&
+ $this->validatePropFilters($subComponent, $filter['prop-filters'])) {
+ // We had a match, so this comp-filter succeeds
+ continue 2;
+ }
+
+ }
+
+ // If we got here it means there were sub-comp-filters or
+ // sub-prop-filters and there was no match. This means this filter
+ // needs to return false.
+ return false;
+
+ }
+
+ // If we got here it means we got through all comp-filters alive so the
+ // filters were all true.
+ return true;
+
+ }
+
+ /**
+ * This method checks the validity of prop-filters.
+ *
+ * A list of prop-filters needs to be specified. Also the parent of the
+ * property we're checking should be specified, not the property to check
+ * itself.
+ *
+ * @param Sabre_VObject_Component $parent
+ * @param array $filters
+ * @return bool
+ */
+ protected function validatePropFilters(Sabre_VObject_Component $parent, array $filters) {
+
+ foreach($filters as $filter) {
+
+ $isDefined = isset($parent->$filter['name']);
+
+ if ($filter['is-not-defined']) {
+
+ if ($isDefined) {
+ return false;
+ } else {
+ continue;
+ }
+
+ }
+ if (!$isDefined) {
+ return false;
+ }
+
+ if ($filter['time-range']) {
+ foreach($parent->$filter['name'] as $subComponent) {
+ if ($this->validateTimeRange($subComponent, $filter['time-range']['start'], $filter['time-range']['end'])) {
+ continue 2;
+ }
+ }
+ return false;
+ }
+
+ if (!$filter['param-filters'] && !$filter['text-match']) {
+ continue;
+ }
+
+ // If there are sub-filters, we need to find at least one property
+ // for which the subfilters hold true.
+ foreach($parent->$filter['name'] as $subComponent) {
+
+ if(
+ $this->validateParamFilters($subComponent, $filter['param-filters']) &&
+ (!$filter['text-match'] || $this->validateTextMatch($subComponent, $filter['text-match']))
+ ) {
+ // We had a match, so this prop-filter succeeds
+ continue 2;
+ }
+
+ }
+
+ // If we got here it means there were sub-param-filters or
+ // text-match filters and there was no match. This means the
+ // filter needs to return false.
+ return false;
+
+ }
+
+ // If we got here it means we got through all prop-filters alive so the
+ // filters were all true.
+ return true;
+
+ }
+
+ /**
+ * This method checks the validity of param-filters.
+ *
+ * A list of param-filters needs to be specified. Also the parent of the
+ * parameter we're checking should be specified, not the parameter to check
+ * itself.
+ *
+ * @param Sabre_VObject_Property $parent
+ * @param array $filters
+ * @return bool
+ */
+ protected function validateParamFilters(Sabre_VObject_Property $parent, array $filters) {
+
+ foreach($filters as $filter) {
+
+ $isDefined = isset($parent[$filter['name']]);
+
+ if ($filter['is-not-defined']) {
+
+ if ($isDefined) {
+ return false;
+ } else {
+ continue;
+ }
+
+ }
+ if (!$isDefined) {
+ return false;
+ }
+
+ if (!$filter['text-match']) {
+ continue;
+ }
+
+ // If there are sub-filters, we need to find at least one parameter
+ // for which the subfilters hold true.
+ foreach($parent[$filter['name']] as $subParam) {
+
+ if($this->validateTextMatch($subParam,$filter['text-match'])) {
+ // We had a match, so this param-filter succeeds
+ continue 2;
+ }
+
+ }
+
+ // If we got here it means there was a text-match filter and there
+ // were no matches. This means the filter needs to return false.
+ return false;
+
+ }
+
+ // If we got here it means we got through all param-filters alive so the
+ // filters were all true.
+ return true;
+
+ }
+
+ /**
+ * This method checks the validity of a text-match.
+ *
+ * A single text-match should be specified as well as the specific property
+ * or parameter we need to validate.
+ *
+ * @param Sabre_VObject_Node $parent
+ * @param array $textMatch
+ * @return bool
+ */
+ protected function validateTextMatch(Sabre_VObject_Node $parent, array $textMatch) {
+
+ $value = (string)$parent;
+
+ $isMatching = Sabre_DAV_StringUtil::textMatch($value, $textMatch['value'], $textMatch['collation']);
+
+ return ($textMatch['negate-condition'] xor $isMatching);
+
+ }
+
+ /**
+ * Validates if a component matches the given time range.
+ *
+ * This is all based on the rules specified in rfc4791, which are quite
+ * complex.
+ *
+ * @param Sabre_VObject_Node $component
+ * @param DateTime $start
+ * @param DateTime $end
+ * @return bool
+ */
+ protected function validateTimeRange(Sabre_VObject_Node $component, $start, $end) {
+
+ if (is_null($start)) {
+ $start = new DateTime('1900-01-01');
+ }
+ if (is_null($end)) {
+ $end = new DateTime('3000-01-01');
+ }
+
+ switch($component->name) {
+
+ case 'VEVENT' :
+ case 'VTODO' :
+ case 'VJOURNAL' :
+
+ return $component->isInTimeRange($start, $end);
+
+ case 'VALARM' :
+
+ // If the valarm is wrapped in a recurring event, we need to
+ // expand the recursions, and validate each.
+ //
+ // Our datamodel doesn't easily allow us to do this straight
+ // in the VALARM component code, so this is a hack, and an
+ // expensive one too.
+ if ($component->parent->name === 'VEVENT' && $component->parent->RRULE) {
+
+ // Fire up the iterator!
+ $it = new Sabre_VObject_RecurrenceIterator($component->parent->parent, (string)$component->parent->UID);
+ while($it->valid()) {
+ $expandedEvent = $it->getEventObject();
+
+ // We need to check from these expanded alarms, which
+ // one is the first to trigger. Based on this, we can
+ // determine if we can 'give up' expanding events.
+ $firstAlarm = null;
+ if ($expandedEvent->VALARM !== null) {
+ foreach($expandedEvent->VALARM as $expandedAlarm) {
+
+ $effectiveTrigger = $expandedAlarm->getEffectiveTriggerTime();
+ if ($expandedAlarm->isInTimeRange($start, $end)) {
+ return true;
+ }
+
+ if ((string)$expandedAlarm->TRIGGER['VALUE'] === 'DATE-TIME') {
+ // This is an alarm with a non-relative trigger
+ // time, likely created by a buggy client. The
+ // implication is that every alarm in this
+ // recurring event trigger at the exact same
+ // time. It doesn't make sense to traverse
+ // further.
+ } else {
+ // We store the first alarm as a means to
+ // figure out when we can stop traversing.
+ if (!$firstAlarm || $effectiveTrigger < $firstAlarm) {
+ $firstAlarm = $effectiveTrigger;
+ }
+ }
+ }
+ }
+ if (is_null($firstAlarm)) {
+ // No alarm was found.
+ //
+ // Or technically: No alarm that will change for
+ // every instance of the recurrence was found,
+ // which means we can assume there was no match.
+ return false;
+ }
+ if ($firstAlarm > $end) {
+ return false;
+ }
+ $it->next();
+ }
+ return false;
+ } else {
+ return $component->isInTimeRange($start, $end);
+ }
+
+ case 'VFREEBUSY' :
+ throw new Sabre_DAV_Exception_NotImplemented('time-range filters are currently not supported on ' . $component->name . ' components');
+
+ case 'COMPLETED' :
+ case 'CREATED' :
+ case 'DTEND' :
+ case 'DTSTAMP' :
+ case 'DTSTART' :
+ case 'DUE' :
+ case 'LAST-MODIFIED' :
+ return ($start <= $component->getDateTime() && $end >= $component->getDateTime());
+
+
+
+ default :
+ throw new Sabre_DAV_Exception_BadRequest('You cannot create a time-range filter on a ' . $component->name . ' component');
+
+ }
+
+ }
+
+}
diff --git a/3rdparty/Sabre/CalDAV/CalendarRootNode.php b/3rdparty/Sabre/CalDAV/CalendarRootNode.php
new file mode 100755
index 00000000000..3907913cc78
--- /dev/null
+++ b/3rdparty/Sabre/CalDAV/CalendarRootNode.php
@@ -0,0 +1,75 @@
+<?php
+
+/**
+ * Users collection
+ *
+ * This object is responsible for generating a collection of users.
+ *
+ * @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_CalendarRootNode extends Sabre_DAVACL_AbstractPrincipalCollection {
+
+ /**
+ * CalDAV backend
+ *
+ * @var Sabre_CalDAV_Backend_Abstract
+ */
+ protected $caldavBackend;
+
+ /**
+ * Constructor
+ *
+ * This constructor needs both an authentication and a caldav backend.
+ *
+ * By default this class will show a list of calendar collections for
+ * principals in the 'principals' collection. If your main principals are
+ * actually located in a different path, use the $principalPrefix argument
+ * to override this.
+ *
+ *
+ * @param Sabre_DAVACL_IPrincipalBackend $principalBackend
+ * @param Sabre_CalDAV_Backend_Abstract $caldavBackend
+ * @param string $principalPrefix
+ */
+ public function __construct(Sabre_DAVACL_IPrincipalBackend $principalBackend,Sabre_CalDAV_Backend_Abstract $caldavBackend, $principalPrefix = 'principals') {
+
+ parent::__construct($principalBackend, $principalPrefix);
+ $this->caldavBackend = $caldavBackend;
+
+ }
+
+ /**
+ * Returns the nodename
+ *
+ * We're overriding this, because the default will be the 'principalPrefix',
+ * and we want it to be Sabre_CalDAV_Plugin::CALENDAR_ROOT
+ *
+ * @return string
+ */
+ public function getName() {
+
+ return Sabre_CalDAV_Plugin::CALENDAR_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_CalDAV_UserCalendars($this->principalBackend, $this->caldavBackend, $principal['uri']);
+
+ }
+
+}
diff --git a/3rdparty/Sabre/CalDAV/ICSExportPlugin.php b/3rdparty/Sabre/CalDAV/ICSExportPlugin.php
new file mode 100755
index 00000000000..ec42b406b2f
--- /dev/null
+++ b/3rdparty/Sabre/CalDAV/ICSExportPlugin.php
@@ -0,0 +1,139 @@
+<?php
+
+/**
+ * ICS Exporter
+ *
+ * This plugin adds the ability to export entire calendars as .ics files.
+ * This is useful for clients that don't support CalDAV yet. They often do
+ * support ics files.
+ *
+ * @package Sabre
+ * @subpackage CalDAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_CalDAV_ICSExportPlugin extends Sabre_DAV_ServerPlugin {
+
+ /**
+ * Reference to Server class
+ *
+ * @var Sabre_DAV_Server
+ */
+ private $server;
+
+ /**
+ * Initializes the plugin and registers event handlers
+ *
+ * @param Sabre_DAV_Server $server
+ * @return void
+ */
+ public function initialize(Sabre_DAV_Server $server) {
+
+ $this->server = $server;
+ $this->server->subscribeEvent('beforeMethod',array($this,'beforeMethod'), 90);
+
+ }
+
+ /**
+ * 'beforeMethod' event handles. This event handles intercepts GET requests ending
+ * with ?export
+ *
+ * @param string $method
+ * @param string $uri
+ * @return bool
+ */
+ public function beforeMethod($method, $uri) {
+
+ if ($method!='GET') return;
+ if ($this->server->httpRequest->getQueryString()!='export') return;
+
+ // splitting uri
+ list($uri) = explode('?',$uri,2);
+
+ $node = $this->server->tree->getNodeForPath($uri);
+
+ if (!($node instanceof Sabre_CalDAV_Calendar)) return;
+
+ // Checking ACL, if available.
+ if ($aclPlugin = $this->server->getPlugin('acl')) {
+ $aclPlugin->checkPrivileges($uri, '{DAV:}read');
+ }
+
+ $this->server->httpResponse->setHeader('Content-Type','text/calendar');
+ $this->server->httpResponse->sendStatus(200);
+
+ $nodes = $this->server->getPropertiesForPath($uri, array(
+ '{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}calendar-data',
+ ),1);
+
+ $this->server->httpResponse->sendBody($this->generateICS($nodes));
+
+ // Returning false to break the event chain
+ return false;
+
+ }
+
+ /**
+ * Merges all calendar objects, and builds one big ics export
+ *
+ * @param array $nodes
+ * @return string
+ */
+ public function generateICS(array $nodes) {
+
+ $calendar = new Sabre_VObject_Component('vcalendar');
+ $calendar->version = '2.0';
+ if (Sabre_DAV_Server::$exposeVersion) {
+ $calendar->prodid = '-//SabreDAV//SabreDAV ' . Sabre_DAV_Version::VERSION . '//EN';
+ } else {
+ $calendar->prodid = '-//SabreDAV//SabreDAV//EN';
+ }
+ $calendar->calscale = 'GREGORIAN';
+
+ $collectedTimezones = array();
+
+ $timezones = array();
+ $objects = array();
+
+ foreach($nodes as $node) {
+
+ if (!isset($node[200]['{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}calendar-data'])) {
+ continue;
+ }
+ $nodeData = $node[200]['{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}calendar-data'];
+
+ $nodeComp = Sabre_VObject_Reader::read($nodeData);
+
+ foreach($nodeComp->children() as $child) {
+
+ switch($child->name) {
+ case 'VEVENT' :
+ case 'VTODO' :
+ case 'VJOURNAL' :
+ $objects[] = $child;
+ break;
+
+ // VTIMEZONE is special, because we need to filter out the duplicates
+ case 'VTIMEZONE' :
+ // Naively just checking tzid.
+ if (in_array((string)$child->TZID, $collectedTimezones)) continue;
+
+ $timezones[] = $child;
+ $collectedTimezones[] = $child->TZID;
+ break;
+
+ }
+
+ }
+
+ }
+
+ foreach($timezones as $tz) $calendar->add($tz);
+ foreach($objects as $obj) $calendar->add($obj);
+
+ return $calendar->serialize();
+
+ }
+
+}
diff --git a/3rdparty/Sabre/CalDAV/ICalendar.php b/3rdparty/Sabre/CalDAV/ICalendar.php
new file mode 100755
index 00000000000..15d51ebcf79
--- /dev/null
+++ b/3rdparty/Sabre/CalDAV/ICalendar.php
@@ -0,0 +1,18 @@
+<?php
+
+/**
+ * Calendar interface
+ *
+ * Implement this interface to allow a node to be recognized as an calendar.
+ *
+ * @package Sabre
+ * @subpackage CalDAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+interface Sabre_CalDAV_ICalendar extends Sabre_DAV_ICollection {
+
+
+
+}
diff --git a/3rdparty/Sabre/CalDAV/ICalendarObject.php b/3rdparty/Sabre/CalDAV/ICalendarObject.php
new file mode 100755
index 00000000000..280f982a310
--- /dev/null
+++ b/3rdparty/Sabre/CalDAV/ICalendarObject.php
@@ -0,0 +1,20 @@
+<?php
+
+/**
+ * CalendarObject interface
+/**
+ * Extend the ICalendarObject interface to allow your custom nodes to be picked up as
+ * CalendarObjects.
+ *
+ * Calendar objects are resources such as Events, Todo's or Journals.
+ *
+ * @package Sabre
+ * @subpackage CalDAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+interface Sabre_CalDAV_ICalendarObject extends Sabre_DAV_IFile {
+
+}
+
diff --git a/3rdparty/Sabre/CalDAV/Plugin.php b/3rdparty/Sabre/CalDAV/Plugin.php
new file mode 100755
index 00000000000..c56ab384844
--- /dev/null
+++ b/3rdparty/Sabre/CalDAV/Plugin.php
@@ -0,0 +1,931 @@
+<?php
+
+/**
+ * CalDAV plugin
+ *
+ * This plugin provides functionality added by CalDAV (RFC 4791)
+ * It implements new reports, and the MKCALENDAR method.
+ *
+ * @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_Plugin extends Sabre_DAV_ServerPlugin {
+
+ /**
+ * This is the official CalDAV namespace
+ */
+ const NS_CALDAV = 'urn:ietf:params:xml:ns:caldav';
+
+ /**
+ * This is the namespace for the proprietary calendarserver extensions
+ */
+ const NS_CALENDARSERVER = 'http://calendarserver.org/ns/';
+
+ /**
+ * The following constants are used to differentiate
+ * the various filters for the calendar-query report
+ */
+ const FILTER_COMPFILTER = 1;
+ const FILTER_TIMERANGE = 3;
+ const FILTER_PROPFILTER = 4;
+ const FILTER_PARAMFILTER = 5;
+ const FILTER_TEXTMATCH = 6;
+
+ /**
+ * The hardcoded root for calendar objects. It is unfortunate
+ * that we're stuck with it, but it will have to do for now
+ */
+ const CALENDAR_ROOT = 'calendars';
+
+ /**
+ * Reference to server object
+ *
+ * @var Sabre_DAV_Server
+ */
+ private $server;
+
+ /**
+ * The email handler for invites and other scheduling messages.
+ *
+ * @var Sabre_CalDAV_Schedule_IMip
+ */
+ protected $imipHandler;
+
+ /**
+ * Sets the iMIP handler.
+ *
+ * iMIP = The email transport of iCalendar scheduling messages. Setting
+ * this is optional, but if you want the server to allow invites to be sent
+ * out, you must set a handler.
+ *
+ * Specifically iCal will plain assume that the server supports this. If
+ * the server doesn't, iCal will display errors when inviting people to
+ * events.
+ *
+ * @param Sabre_CalDAV_Schedule_IMip $imipHandler
+ * @return void
+ */
+ public function setIMipHandler(Sabre_CalDAV_Schedule_IMip $imipHandler) {
+
+ $this->imipHandler = $imipHandler;
+
+ }
+
+ /**
+ * Use this method to tell the server this plugin defines additional
+ * HTTP methods.
+ *
+ * This method is passed a uri. It should only return HTTP methods that are
+ * available for the specified uri.
+ *
+ * @param string $uri
+ * @return array
+ */
+ public function getHTTPMethods($uri) {
+
+ // The MKCALENDAR is only available on unmapped uri's, whose
+ // parents extend IExtendedCollection
+ list($parent, $name) = Sabre_DAV_URLUtil::splitPath($uri);
+
+ $node = $this->server->tree->getNodeForPath($parent);
+
+ if ($node instanceof Sabre_DAV_IExtendedCollection) {
+ try {
+ $node->getChild($name);
+ } catch (Sabre_DAV_Exception_NotFound $e) {
+ return array('MKCALENDAR');
+ }
+ }
+ return array();
+
+ }
+
+ /**
+ * Returns a list of features for the DAV: HTTP header.
+ *
+ * @return array
+ */
+ public function getFeatures() {
+
+ return array('calendar-access', 'calendar-proxy');
+
+ }
+
+ /**
+ * Returns a plugin name.
+ *
+ * Using this name other plugins will be able to access other plugins
+ * using Sabre_DAV_Server::getPlugin
+ *
+ * @return string
+ */
+ public function getPluginName() {
+
+ return 'caldav';
+
+ }
+
+ /**
+ * Returns a list of reports this plugin supports.
+ *
+ * This will be used in the {DAV:}supported-report-set property.
+ * Note that you still need to subscribe to the 'report' event to actually
+ * implement them
+ *
+ * @param string $uri
+ * @return array
+ */
+ public function getSupportedReportSet($uri) {
+
+ $node = $this->server->tree->getNodeForPath($uri);
+
+ $reports = array();
+ if ($node instanceof Sabre_CalDAV_ICalendar || $node instanceof Sabre_CalDAV_ICalendarObject) {
+ $reports[] = '{' . self::NS_CALDAV . '}calendar-multiget';
+ $reports[] = '{' . self::NS_CALDAV . '}calendar-query';
+ }
+ if ($node instanceof Sabre_CalDAV_ICalendar) {
+ $reports[] = '{' . self::NS_CALDAV . '}free-busy-query';
+ }
+ return $reports;
+
+ }
+
+ /**
+ * Initializes the plugin
+ *
+ * @param Sabre_DAV_Server $server
+ * @return void
+ */
+ public function initialize(Sabre_DAV_Server $server) {
+
+ $this->server = $server;
+
+ $server->subscribeEvent('unknownMethod',array($this,'unknownMethod'));
+ //$server->subscribeEvent('unknownMethod',array($this,'unknownMethod2'),1000);
+ $server->subscribeEvent('report',array($this,'report'));
+ $server->subscribeEvent('beforeGetProperties',array($this,'beforeGetProperties'));
+ $server->subscribeEvent('onHTMLActionsPanel', array($this,'htmlActionsPanel'));
+ $server->subscribeEvent('onBrowserPostAction', array($this,'browserPostAction'));
+ $server->subscribeEvent('beforeWriteContent', array($this, 'beforeWriteContent'));
+ $server->subscribeEvent('beforeCreateFile', array($this, 'beforeCreateFile'));
+
+ $server->xmlNamespaces[self::NS_CALDAV] = 'cal';
+ $server->xmlNamespaces[self::NS_CALENDARSERVER] = 'cs';
+
+ $server->propertyMap['{' . self::NS_CALDAV . '}supported-calendar-component-set'] = 'Sabre_CalDAV_Property_SupportedCalendarComponentSet';
+
+ $server->resourceTypeMapping['Sabre_CalDAV_ICalendar'] = '{urn:ietf:params:xml:ns:caldav}calendar';
+ $server->resourceTypeMapping['Sabre_CalDAV_Schedule_IOutbox'] = '{urn:ietf:params:xml:ns:caldav}schedule-outbox';
+ $server->resourceTypeMapping['Sabre_CalDAV_Principal_ProxyRead'] = '{http://calendarserver.org/ns/}calendar-proxy-read';
+ $server->resourceTypeMapping['Sabre_CalDAV_Principal_ProxyWrite'] = '{http://calendarserver.org/ns/}calendar-proxy-write';
+
+ array_push($server->protectedProperties,
+
+ '{' . self::NS_CALDAV . '}supported-calendar-component-set',
+ '{' . self::NS_CALDAV . '}supported-calendar-data',
+ '{' . self::NS_CALDAV . '}max-resource-size',
+ '{' . self::NS_CALDAV . '}min-date-time',
+ '{' . self::NS_CALDAV . '}max-date-time',
+ '{' . self::NS_CALDAV . '}max-instances',
+ '{' . self::NS_CALDAV . '}max-attendees-per-instance',
+ '{' . self::NS_CALDAV . '}calendar-home-set',
+ '{' . self::NS_CALDAV . '}supported-collation-set',
+ '{' . self::NS_CALDAV . '}calendar-data',
+
+ // scheduling extension
+ '{' . self::NS_CALDAV . '}schedule-inbox-URL',
+ '{' . self::NS_CALDAV . '}schedule-outbox-URL',
+ '{' . self::NS_CALDAV . '}calendar-user-address-set',
+ '{' . self::NS_CALDAV . '}calendar-user-type',
+
+ // CalendarServer extensions
+ '{' . self::NS_CALENDARSERVER . '}getctag',
+ '{' . self::NS_CALENDARSERVER . '}calendar-proxy-read-for',
+ '{' . self::NS_CALENDARSERVER . '}calendar-proxy-write-for'
+
+ );
+ }
+
+ /**
+ * This function handles support for the MKCALENDAR method
+ *
+ * @param string $method
+ * @param string $uri
+ * @return bool
+ */
+ public function unknownMethod($method, $uri) {
+
+ switch ($method) {
+ case 'MKCALENDAR' :
+ $this->httpMkCalendar($uri);
+ // false is returned to stop the propagation of the
+ // unknownMethod event.
+ return false;
+ case 'POST' :
+ // Checking if we're talking to an outbox
+ try {
+ $node = $this->server->tree->getNodeForPath($uri);
+ } catch (Sabre_DAV_Exception_NotFound $e) {
+ return;
+ }
+ if (!$node instanceof Sabre_CalDAV_Schedule_IOutbox)
+ return;
+
+ $this->outboxRequest($node);
+ return false;
+
+ }
+
+ }
+
+ /**
+ * This functions handles REPORT requests specific to CalDAV
+ *
+ * @param string $reportName
+ * @param DOMNode $dom
+ * @return bool
+ */
+ public function report($reportName,$dom) {
+
+ switch($reportName) {
+ case '{'.self::NS_CALDAV.'}calendar-multiget' :
+ $this->calendarMultiGetReport($dom);
+ return false;
+ case '{'.self::NS_CALDAV.'}calendar-query' :
+ $this->calendarQueryReport($dom);
+ return false;
+ case '{'.self::NS_CALDAV.'}free-busy-query' :
+ $this->freeBusyQueryReport($dom);
+ return false;
+
+ }
+
+
+ }
+
+ /**
+ * This function handles the MKCALENDAR HTTP method, which creates
+ * a new calendar.
+ *
+ * @param string $uri
+ * @return void
+ */
+ public function httpMkCalendar($uri) {
+
+ // Due to unforgivable bugs in iCal, we're completely disabling MKCALENDAR support
+ // for clients matching iCal in the user agent
+ //$ua = $this->server->httpRequest->getHeader('User-Agent');
+ //if (strpos($ua,'iCal/')!==false) {
+ // throw new Sabre_DAV_Exception_Forbidden('iCal has major bugs in it\'s RFC3744 support. Therefore we are left with no other choice but disabling this feature.');
+ //}
+
+ $body = $this->server->httpRequest->getBody(true);
+ $properties = array();
+
+ if ($body) {
+
+ $dom = Sabre_DAV_XMLUtil::loadDOMDocument($body);
+
+ foreach($dom->firstChild->childNodes as $child) {
+
+ if (Sabre_DAV_XMLUtil::toClarkNotation($child)!=='{DAV:}set') continue;
+ foreach(Sabre_DAV_XMLUtil::parseProperties($child,$this->server->propertyMap) as $k=>$prop) {
+ $properties[$k] = $prop;
+ }
+
+ }
+ }
+
+ $resourceType = array('{DAV:}collection','{urn:ietf:params:xml:ns:caldav}calendar');
+
+ $this->server->createCollection($uri,$resourceType,$properties);
+
+ $this->server->httpResponse->sendStatus(201);
+ $this->server->httpResponse->setHeader('Content-Length',0);
+ }
+
+ /**
+ * beforeGetProperties
+ *
+ * This method handler is invoked before any after properties for a
+ * resource are fetched. This allows us to add in any CalDAV specific
+ * properties.
+ *
+ * @param string $path
+ * @param Sabre_DAV_INode $node
+ * @param array $requestedProperties
+ * @param array $returnedProperties
+ * @return void
+ */
+ public function beforeGetProperties($path, Sabre_DAV_INode $node, &$requestedProperties, &$returnedProperties) {
+
+ if ($node instanceof Sabre_DAVACL_IPrincipal) {
+
+ // calendar-home-set property
+ $calHome = '{' . self::NS_CALDAV . '}calendar-home-set';
+ if (in_array($calHome,$requestedProperties)) {
+ $principalId = $node->getName();
+ $calendarHomePath = self::CALENDAR_ROOT . '/' . $principalId . '/';
+ unset($requestedProperties[$calHome]);
+ $returnedProperties[200][$calHome] = new Sabre_DAV_Property_Href($calendarHomePath);
+ }
+
+ // schedule-outbox-URL property
+ $scheduleProp = '{' . self::NS_CALDAV . '}schedule-outbox-URL';
+ if (in_array($scheduleProp,$requestedProperties)) {
+ $principalId = $node->getName();
+ $outboxPath = self::CALENDAR_ROOT . '/' . $principalId . '/outbox';
+ unset($requestedProperties[$scheduleProp]);
+ $returnedProperties[200][$scheduleProp] = new Sabre_DAV_Property_Href($outboxPath);
+ }
+
+ // calendar-user-address-set property
+ $calProp = '{' . self::NS_CALDAV . '}calendar-user-address-set';
+ if (in_array($calProp,$requestedProperties)) {
+
+ $addresses = $node->getAlternateUriSet();
+ $addresses[] = $this->server->getBaseUri() . $node->getPrincipalUrl();
+ unset($requestedProperties[$calProp]);
+ $returnedProperties[200][$calProp] = new Sabre_DAV_Property_HrefList($addresses, false);
+
+ }
+
+ // These two properties are shortcuts for ical to easily find
+ // other principals this principal has access to.
+ $propRead = '{' . self::NS_CALENDARSERVER . '}calendar-proxy-read-for';
+ $propWrite = '{' . self::NS_CALENDARSERVER . '}calendar-proxy-write-for';
+ if (in_array($propRead,$requestedProperties) || in_array($propWrite,$requestedProperties)) {
+
+ $membership = $node->getGroupMembership();
+ $readList = array();
+ $writeList = array();
+
+ foreach($membership as $group) {
+
+ $groupNode = $this->server->tree->getNodeForPath($group);
+
+ // If the node is either ap proxy-read or proxy-write
+ // group, we grab the parent principal and add it to the
+ // list.
+ if ($groupNode instanceof Sabre_CalDAV_Principal_ProxyRead) {
+ list($readList[]) = Sabre_DAV_URLUtil::splitPath($group);
+ }
+ if ($groupNode instanceof Sabre_CalDAV_Principal_ProxyWrite) {
+ list($writeList[]) = Sabre_DAV_URLUtil::splitPath($group);
+ }
+
+ }
+ if (in_array($propRead,$requestedProperties)) {
+ unset($requestedProperties[$propRead]);
+ $returnedProperties[200][$propRead] = new Sabre_DAV_Property_HrefList($readList);
+ }
+ if (in_array($propWrite,$requestedProperties)) {
+ unset($requestedProperties[$propWrite]);
+ $returnedProperties[200][$propWrite] = new Sabre_DAV_Property_HrefList($writeList);
+ }
+
+ }
+
+ } // instanceof IPrincipal
+
+
+ if ($node instanceof Sabre_CalDAV_ICalendarObject) {
+ // The calendar-data property is not supposed to be a 'real'
+ // property, but in large chunks of the spec it does act as such.
+ // Therefore we simply expose it as a property.
+ $calDataProp = '{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}calendar-data';
+ if (in_array($calDataProp, $requestedProperties)) {
+ unset($requestedProperties[$calDataProp]);
+ $val = $node->get();
+ if (is_resource($val))
+ $val = stream_get_contents($val);
+
+ // Taking out \r to not screw up the xml output
+ $returnedProperties[200][$calDataProp] = str_replace("\r","", $val);
+
+ }
+ }
+
+ }
+
+ /**
+ * This function handles the calendar-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 calendarMultiGetReport($dom) {
+
+ $properties = array_keys(Sabre_DAV_XMLUtil::parseProperties($dom->firstChild));
+ $hrefElems = $dom->getElementsByTagNameNS('urn:DAV','href');
+
+ $xpath = new DOMXPath($dom);
+ $xpath->registerNameSpace('cal',Sabre_CalDAV_Plugin::NS_CALDAV);
+ $xpath->registerNameSpace('dav','urn:DAV');
+
+ $expand = $xpath->query('/cal:calendar-multiget/dav:prop/cal:calendar-data/cal:expand');
+ if ($expand->length>0) {
+ $expandElem = $expand->item(0);
+ $start = $expandElem->getAttribute('start');
+ $end = $expandElem->getAttribute('end');
+ if(!$start || !$end) {
+ throw new Sabre_DAV_Exception_BadRequest('The "start" and "end" attributes are required for the CALDAV:expand element');
+ }
+ $start = Sabre_VObject_DateTimeParser::parseDateTime($start);
+ $end = Sabre_VObject_DateTimeParser::parseDateTime($end);
+
+ if ($end <= $start) {
+ throw new Sabre_DAV_Exception_BadRequest('The end-date must be larger than the start-date in the expand element.');
+ }
+
+ $expand = true;
+
+ } else {
+
+ $expand = false;
+
+ }
+
+ foreach($hrefElems as $elem) {
+ $uri = $this->server->calculateUri($elem->nodeValue);
+ list($objProps) = $this->server->getPropertiesForPath($uri,$properties);
+
+ if ($expand && isset($objProps[200]['{' . self::NS_CALDAV . '}calendar-data'])) {
+ $vObject = Sabre_VObject_Reader::read($objProps[200]['{' . self::NS_CALDAV . '}calendar-data']);
+ $vObject->expand($start, $end);
+ $objProps[200]['{' . self::NS_CALDAV . '}calendar-data'] = $vObject->serialize();
+ }
+
+ $propertyList[]=$objProps;
+
+ }
+
+ $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 calendar-query REPORT
+ *
+ * This report is used by clients to request calendar objects based on
+ * complex conditions.
+ *
+ * @param DOMNode $dom
+ * @return void
+ */
+ public function calendarQueryReport($dom) {
+
+ $parser = new Sabre_CalDAV_CalendarQueryParser($dom);
+ $parser->parse();
+
+ $requestedCalendarData = true;
+ $requestedProperties = $parser->requestedProperties;
+
+ if (!in_array('{urn:ietf:params:xml:ns:caldav}calendar-data', $requestedProperties)) {
+
+ // We always retrieve calendar-data, as we need it for filtering.
+ $requestedProperties[] = '{urn:ietf:params:xml:ns:caldav}calendar-data';
+
+ // If calendar-data wasn't explicitly requested, we need to remove
+ // it after processing.
+ $requestedCalendarData = false;
+ }
+
+ // These are the list of nodes that potentially match the requirement
+ $candidateNodes = $this->server->getPropertiesForPath(
+ $this->server->getRequestUri(),
+ $requestedProperties,
+ $this->server->getHTTPDepth(0)
+ );
+
+ $verifiedNodes = array();
+
+ $validator = new Sabre_CalDAV_CalendarQueryValidator();
+
+ foreach($candidateNodes as $node) {
+
+ // If the node didn't have a calendar-data property, it must not be a calendar object
+ if (!isset($node[200]['{urn:ietf:params:xml:ns:caldav}calendar-data']))
+ continue;
+
+ $vObject = Sabre_VObject_Reader::read($node[200]['{urn:ietf:params:xml:ns:caldav}calendar-data']);
+ if ($validator->validate($vObject,$parser->filters)) {
+
+ if (!$requestedCalendarData) {
+ unset($node[200]['{urn:ietf:params:xml:ns:caldav}calendar-data']);
+ }
+ if ($parser->expand) {
+ $vObject->expand($parser->expand['start'], $parser->expand['end']);
+ $node[200]['{' . self::NS_CALDAV . '}calendar-data'] = $vObject->serialize();
+ }
+ $verifiedNodes[] = $node;
+ }
+
+ }
+
+ $this->server->httpResponse->sendStatus(207);
+ $this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8');
+ $this->server->httpResponse->sendBody($this->server->generateMultiStatus($verifiedNodes));
+
+ }
+
+ /**
+ * This method is responsible for parsing the request and generating the
+ * response for the CALDAV:free-busy-query REPORT.
+ *
+ * @param DOMNode $dom
+ * @return void
+ */
+ protected function freeBusyQueryReport(DOMNode $dom) {
+
+ $start = null;
+ $end = null;
+
+ foreach($dom->firstChild->childNodes as $childNode) {
+
+ $clark = Sabre_DAV_XMLUtil::toClarkNotation($childNode);
+ if ($clark == '{' . self::NS_CALDAV . '}time-range') {
+ $start = $childNode->getAttribute('start');
+ $end = $childNode->getAttribute('end');
+ break;
+ }
+
+ }
+ if ($start) {
+ $start = Sabre_VObject_DateTimeParser::parseDateTime($start);
+ }
+ if ($end) {
+ $end = Sabre_VObject_DateTimeParser::parseDateTime($end);
+ }
+
+ if (!$start && !$end) {
+ throw new Sabre_DAV_Exception_BadRequest('The freebusy report must have a time-range filter');
+ }
+ $acl = $this->server->getPlugin('acl');
+
+ if (!$acl) {
+ throw new Sabre_DAV_Exception('The ACL plugin must be loaded for free-busy queries to work');
+ }
+ $uri = $this->server->getRequestUri();
+ $acl->checkPrivileges($uri,'{' . self::NS_CALDAV . '}read-free-busy');
+
+ $calendar = $this->server->tree->getNodeForPath($uri);
+ if (!$calendar instanceof Sabre_CalDAV_ICalendar) {
+ throw new Sabre_DAV_Exception_NotImplemented('The free-busy-query REPORT is only implemented on calendars');
+ }
+
+ $objects = array_map(function($child) {
+ $obj = $child->get();
+ if (is_resource($obj)) {
+ $obj = stream_get_contents($obj);
+ }
+ return $obj;
+ }, $calendar->getChildren());
+
+ $generator = new Sabre_VObject_FreeBusyGenerator();
+ $generator->setObjects($objects);
+ $generator->setTimeRange($start, $end);
+ $result = $generator->getResult();
+ $result = $result->serialize();
+
+ $this->server->httpResponse->sendStatus(200);
+ $this->server->httpResponse->setHeader('Content-Type', 'text/calendar');
+ $this->server->httpResponse->setHeader('Content-Length', strlen($result));
+ $this->server->httpResponse->sendBody($result);
+
+ }
+
+ /**
+ * This method is triggered before a file gets updated with new content.
+ *
+ * This plugin uses this method to ensure that CalDAV objects receive
+ * valid calendar data.
+ *
+ * @param string $path
+ * @param Sabre_DAV_IFile $node
+ * @param resource $data
+ * @return void
+ */
+ public function beforeWriteContent($path, Sabre_DAV_IFile $node, &$data) {
+
+ if (!$node instanceof Sabre_CalDAV_ICalendarObject)
+ return;
+
+ $this->validateICalendar($data);
+
+ }
+
+ /**
+ * This method is triggered before a new file is created.
+ *
+ * This plugin uses this method to ensure that newly created calendar
+ * objects contain valid calendar data.
+ *
+ * @param string $path
+ * @param resource $data
+ * @param Sabre_DAV_ICollection $parentNode
+ * @return void
+ */
+ public function beforeCreateFile($path, &$data, Sabre_DAV_ICollection $parentNode) {
+
+ if (!$parentNode instanceof Sabre_CalDAV_Calendar)
+ return;
+
+ $this->validateICalendar($data);
+
+ }
+
+ /**
+ * Checks if the submitted iCalendar data is in fact, valid.
+ *
+ * An exception is thrown if it's not.
+ *
+ * @param resource|string $data
+ * @return void
+ */
+ protected function validateICalendar(&$data) {
+
+ // If it's a stream, we convert it to a string first.
+ if (is_resource($data)) {
+ $data = stream_get_contents($data);
+ }
+
+ // Converting the data to unicode, if needed.
+ $data = Sabre_DAV_StringUtil::ensureUTF8($data);
+
+ try {
+
+ $vobj = Sabre_VObject_Reader::read($data);
+
+ } catch (Sabre_VObject_ParseException $e) {
+
+ throw new Sabre_DAV_Exception_UnsupportedMediaType('This resource only supports valid iCalendar 2.0 data. Parse error: ' . $e->getMessage());
+
+ }
+
+ if ($vobj->name !== 'VCALENDAR') {
+ throw new Sabre_DAV_Exception_UnsupportedMediaType('This collection can only support iCalendar objects.');
+ }
+
+ $foundType = null;
+ $foundUID = null;
+ foreach($vobj->getComponents() as $component) {
+ switch($component->name) {
+ case 'VTIMEZONE' :
+ continue 2;
+ case 'VEVENT' :
+ case 'VTODO' :
+ case 'VJOURNAL' :
+ if (is_null($foundType)) {
+ $foundType = $component->name;
+ if (!isset($component->UID)) {
+ throw new Sabre_DAV_Exception_BadRequest('Every ' . $component->name . ' component must have an UID');
+ }
+ $foundUID = (string)$component->UID;
+ } else {
+ if ($foundType !== $component->name) {
+ throw new Sabre_DAV_Exception_BadRequest('A calendar object must only contain 1 component. We found a ' . $component->name . ' as well as a ' . $foundType);
+ }
+ if ($foundUID !== (string)$component->UID) {
+ throw new Sabre_DAV_Exception_BadRequest('Every ' . $component->name . ' in this object must have identical UIDs');
+ }
+ }
+ break;
+ default :
+ throw new Sabre_DAV_Exception_BadRequest('You are not allowed to create components of type: ' . $component->name . ' here');
+
+ }
+ }
+ if (!$foundType)
+ throw new Sabre_DAV_Exception_BadRequest('iCalendar object must contain at least 1 of VEVENT, VTODO or VJOURNAL');
+
+ }
+
+ /**
+ * This method handles POST requests to the schedule-outbox
+ *
+ * @param Sabre_CalDAV_Schedule_IOutbox $outboxNode
+ * @return void
+ */
+ public function outboxRequest(Sabre_CalDAV_Schedule_IOutbox $outboxNode) {
+
+ $originator = $this->server->httpRequest->getHeader('Originator');
+ $recipients = $this->server->httpRequest->getHeader('Recipient');
+
+ if (!$originator) {
+ throw new Sabre_DAV_Exception_BadRequest('The Originator: header must be specified when making POST requests');
+ }
+ if (!$recipients) {
+ throw new Sabre_DAV_Exception_BadRequest('The Recipient: header must be specified when making POST requests');
+ }
+
+ if (!preg_match('/^mailto:(.*)@(.*)$/i', $originator)) {
+ throw new Sabre_DAV_Exception_BadRequest('Originator must start with mailto: and must be valid email address');
+ }
+ $originator = substr($originator,7);
+
+ $recipients = explode(',',$recipients);
+ foreach($recipients as $k=>$recipient) {
+
+ $recipient = trim($recipient);
+ if (!preg_match('/^mailto:(.*)@(.*)$/i', $recipient)) {
+ throw new Sabre_DAV_Exception_BadRequest('Recipients must start with mailto: and must be valid email address');
+ }
+ $recipient = substr($recipient, 7);
+ $recipients[$k] = $recipient;
+ }
+
+ // We need to make sure that 'originator' matches one of the email
+ // addresses of the selected principal.
+ $principal = $outboxNode->getOwner();
+ $props = $this->server->getProperties($principal,array(
+ '{' . self::NS_CALDAV . '}calendar-user-address-set',
+ ));
+
+ $addresses = array();
+ if (isset($props['{' . self::NS_CALDAV . '}calendar-user-address-set'])) {
+ $addresses = $props['{' . self::NS_CALDAV . '}calendar-user-address-set']->getHrefs();
+ }
+
+ if (!in_array('mailto:' . $originator, $addresses)) {
+ throw new Sabre_DAV_Exception_Forbidden('The addresses specified in the Originator header did not match any addresses in the owners calendar-user-address-set header');
+ }
+
+ try {
+ $vObject = Sabre_VObject_Reader::read($this->server->httpRequest->getBody(true));
+ } catch (Sabre_VObject_ParseException $e) {
+ throw new Sabre_DAV_Exception_BadRequest('The request body must be a valid iCalendar object. Parse error: ' . $e->getMessage());
+ }
+
+ // Checking for the object type
+ $componentType = null;
+ foreach($vObject->getComponents() as $component) {
+ if ($component->name !== 'VTIMEZONE') {
+ $componentType = $component->name;
+ break;
+ }
+ }
+ if (is_null($componentType)) {
+ throw new Sabre_DAV_Exception_BadRequest('We expected at least one VTODO, VJOURNAL, VFREEBUSY or VEVENT component');
+ }
+
+ // Validating the METHOD
+ $method = strtoupper((string)$vObject->METHOD);
+ if (!$method) {
+ throw new Sabre_DAV_Exception_BadRequest('A METHOD property must be specified in iTIP messages');
+ }
+
+ if (in_array($method, array('REQUEST','REPLY','ADD','CANCEL')) && $componentType==='VEVENT') {
+ $result = $this->iMIPMessage($originator, $recipients, $vObject);
+ $this->server->httpResponse->sendStatus(200);
+ $this->server->httpResponse->setHeader('Content-Type','application/xml');
+ $this->server->httpResponse->sendBody($this->generateScheduleResponse($result));
+ } else {
+ throw new Sabre_DAV_Exception_NotImplemented('This iTIP method is currently not implemented');
+ }
+
+ }
+
+ /**
+ * Sends an iMIP message by email.
+ *
+ * This method must return an array with status codes per recipient.
+ * This should look something like:
+ *
+ * array(
+ * 'user1@example.org' => '2.0;Success'
+ * )
+ *
+ * Formatting for this status code can be found at:
+ * https://tools.ietf.org/html/rfc5545#section-3.8.8.3
+ *
+ * A list of valid status codes can be found at:
+ * https://tools.ietf.org/html/rfc5546#section-3.6
+ *
+ * @param string $originator
+ * @param array $recipients
+ * @param Sabre_VObject_Component $vObject
+ * @return array
+ */
+ protected function iMIPMessage($originator, array $recipients, Sabre_VObject_Component $vObject) {
+
+ if (!$this->imipHandler) {
+ $resultStatus = '5.2;This server does not support this operation';
+ } else {
+ $this->imipHandler->sendMessage($originator, $recipients, $vObject);
+ $resultStatus = '2.0;Success';
+ }
+
+ $result = array();
+ foreach($recipients as $recipient) {
+ $result[$recipient] = $resultStatus;
+ }
+
+ return $result;
+
+
+ }
+
+ /**
+ * Generates a schedule-response XML body
+ *
+ * The recipients array is a key->value list, containing email addresses
+ * and iTip status codes. See the iMIPMessage method for a description of
+ * the value.
+ *
+ * @param array $recipients
+ * @return string
+ */
+ public function generateScheduleResponse(array $recipients) {
+
+ $dom = new DOMDocument('1.0','utf-8');
+ $dom->formatOutput = true;
+ $xscheduleResponse = $dom->createElement('cal:schedule-response');
+ $dom->appendChild($xscheduleResponse);
+
+ foreach($this->server->xmlNamespaces as $namespace=>$prefix) {
+
+ $xscheduleResponse->setAttribute('xmlns:' . $prefix, $namespace);
+
+ }
+
+ foreach($recipients as $recipient=>$status) {
+ $xresponse = $dom->createElement('cal:response');
+
+ $xrecipient = $dom->createElement('cal:recipient');
+ $xrecipient->appendChild($dom->createTextNode($recipient));
+ $xresponse->appendChild($xrecipient);
+
+ $xrequestStatus = $dom->createElement('cal:request-status');
+ $xrequestStatus->appendChild($dom->createTextNode($status));
+ $xresponse->appendChild($xrequestStatus);
+
+ $xscheduleResponse->appendChild($xresponse);
+
+ }
+
+ return $dom->saveXML();
+
+ }
+
+ /**
+ * This method is used to generate HTML output for the
+ * Sabre_DAV_Browser_Plugin. This allows us to generate an interface users
+ * can use to create new calendars.
+ *
+ * @param Sabre_DAV_INode $node
+ * @param string $output
+ * @return bool
+ */
+ public function htmlActionsPanel(Sabre_DAV_INode $node, &$output) {
+
+ if (!$node instanceof Sabre_CalDAV_UserCalendars)
+ return;
+
+ $output.= '<tr><td colspan="2"><form method="post" action="">
+ <h3>Create new calendar</h3>
+ <input type="hidden" name="sabreAction" value="mkcalendar" />
+ <label>Name (uri):</label> <input type="text" name="name" /><br />
+ <label>Display name:</label> <input type="text" name="{DAV:}displayname" /><br />
+ <input type="submit" value="create" />
+ </form>
+ </td></tr>';
+
+ return false;
+
+ }
+
+ /**
+ * This method allows us to intercept the 'mkcalendar' sabreAction. This
+ * action enables the user to create new calendars from the browser plugin.
+ *
+ * @param string $uri
+ * @param string $action
+ * @param array $postVars
+ * @return bool
+ */
+ public function browserPostAction($uri, $action, array $postVars) {
+
+ if ($action!=='mkcalendar')
+ return;
+
+ $resourceType = array('{DAV:}collection','{urn:ietf:params:xml:ns:caldav}calendar');
+ $properties = array();
+ if (isset($postVars['{DAV:}displayname'])) {
+ $properties['{DAV:}displayname'] = $postVars['{DAV:}displayname'];
+ }
+ $this->server->createCollection($uri . '/' . $postVars['name'],$resourceType,$properties);
+ return false;
+
+ }
+
+}
diff --git a/3rdparty/Sabre/CalDAV/Principal/Collection.php b/3rdparty/Sabre/CalDAV/Principal/Collection.php
new file mode 100755
index 00000000000..abbefa5567a
--- /dev/null
+++ b/3rdparty/Sabre/CalDAV/Principal/Collection.php
@@ -0,0 +1,31 @@
+<?php
+
+/**
+ * Principal collection
+ *
+ * This is an alternative collection to the standard ACL principal collection.
+ * This collection adds support for the calendar-proxy-read and
+ * calendar-proxy-write sub-principals, as defined by the caldav-proxy
+ * specification.
+ *
+ * @package Sabre
+ * @subpackage CalDAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_CalDAV_Principal_Collection extends Sabre_DAVACL_AbstractPrincipalCollection {
+
+ /**
+ * Returns a child object based on principal information
+ *
+ * @param array $principalInfo
+ * @return Sabre_CalDAV_Principal_User
+ */
+ public function getChildForPrincipal(array $principalInfo) {
+
+ return new Sabre_CalDAV_Principal_User($this->principalBackend, $principalInfo);
+
+ }
+
+}
diff --git a/3rdparty/Sabre/CalDAV/Principal/ProxyRead.php b/3rdparty/Sabre/CalDAV/Principal/ProxyRead.php
new file mode 100755
index 00000000000..4b3f035634a
--- /dev/null
+++ b/3rdparty/Sabre/CalDAV/Principal/ProxyRead.php
@@ -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 alternative 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/3rdparty/Sabre/CalDAV/Principal/ProxyWrite.php b/3rdparty/Sabre/CalDAV/Principal/ProxyWrite.php
new file mode 100755
index 00000000000..dd0c2e86edd
--- /dev/null
+++ b/3rdparty/Sabre/CalDAV/Principal/ProxyWrite.php
@@ -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 alternative 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/3rdparty/Sabre/CalDAV/Principal/User.php b/3rdparty/Sabre/CalDAV/Principal/User.php
new file mode 100755
index 00000000000..8453b877a73
--- /dev/null
+++ b/3rdparty/Sabre/CalDAV/Principal/User.php
@@ -0,0 +1,132 @@
+<?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) {
+
+ $principal = $this->principalBackend->getPrincipalByPath($this->getPrincipalURL() . '/' . $name);
+ if (!$principal) {
+ throw new Sabre_DAV_Exception_NotFound('Node with name ' . $name . ' was not found');
+ }
+ 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_NotFound('Node with name ' . $name . ' was not found');
+
+ }
+
+ /**
+ * Returns an array with all the child nodes
+ *
+ * @return Sabre_DAV_INode[]
+ */
+ public function getChildren() {
+
+ $r = array();
+ if ($this->principalBackend->getPrincipalByPath($this->getPrincipalURL() . '/calendar-proxy-read')) {
+ $r[] = new Sabre_CalDAV_Principal_ProxyRead($this->principalBackend, $this->principalProperties);
+ }
+ if ($this->principalBackend->getPrincipalByPath($this->getPrincipalURL() . '/calendar-proxy-write')) {
+ $r[] = new Sabre_CalDAV_Principal_ProxyWrite($this->principalBackend, $this->principalProperties);
+ }
+
+ return $r;
+
+ }
+
+ /**
+ * Returns whether or not the child node exists
+ *
+ * @param string $name
+ * @return bool
+ */
+ public function childExists($name) {
+
+ try {
+ $this->getChild($name);
+ return true;
+ } catch (Sabre_DAV_Exception_NotFound $e) {
+ return false;
+ }
+
+ }
+
+ /**
+ * 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() {
+
+ $acl = parent::getACL();
+ $acl[] = array(
+ 'privilege' => '{DAV:}read',
+ 'principal' => $this->principalProperties['uri'] . '/calendar-proxy-read',
+ 'protected' => true,
+ );
+ $acl[] = array(
+ 'privilege' => '{DAV:}read',
+ 'principal' => $this->principalProperties['uri'] . '/calendar-proxy-write',
+ 'protected' => true,
+ );
+ return $acl;
+
+ }
+
+}
diff --git a/3rdparty/Sabre/CalDAV/Property/SupportedCalendarComponentSet.php b/3rdparty/Sabre/CalDAV/Property/SupportedCalendarComponentSet.php
new file mode 100755
index 00000000000..2ea078d7dac
--- /dev/null
+++ b/3rdparty/Sabre/CalDAV/Property/SupportedCalendarComponentSet.php
@@ -0,0 +1,85 @@
+<?php
+
+/**
+ * Supported component set property
+ *
+ * This property is a representation of the supported-calendar_component-set
+ * property in the CalDAV namespace. It simply requires an array of components,
+ * such as VEVENT, VTODO
+ *
+ * @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_Property_SupportedCalendarComponentSet extends Sabre_DAV_Property {
+
+ /**
+ * List of supported components, such as "VEVENT, VTODO"
+ *
+ * @var array
+ */
+ private $components;
+
+ /**
+ * Creates the property
+ *
+ * @param array $components
+ */
+ public function __construct(array $components) {
+
+ $this->components = $components;
+
+ }
+
+ /**
+ * Returns the list of supported components
+ *
+ * @return array
+ */
+ public function getValue() {
+
+ return $this->components;
+
+ }
+
+ /**
+ * 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;
+ foreach($this->components as $component) {
+
+ $xcomp = $doc->createElement('cal:comp');
+ $xcomp->setAttribute('name',$component);
+ $node->appendChild($xcomp);
+
+ }
+
+ }
+
+ /**
+ * Unserializes the DOMElement back into a Property class.
+ *
+ * @param DOMElement $node
+ * @return Sabre_CalDAV_Property_SupportedCalendarComponentSet
+ */
+ static function unserialize(DOMElement $node) {
+
+ $components = array();
+ foreach($node->childNodes as $childNode) {
+ if (Sabre_DAV_XMLUtil::toClarkNotation($childNode)==='{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}comp') {
+ $components[] = $childNode->getAttribute('name');
+ }
+ }
+ return new self($components);
+
+ }
+
+}
diff --git a/3rdparty/Sabre/CalDAV/Property/SupportedCalendarData.php b/3rdparty/Sabre/CalDAV/Property/SupportedCalendarData.php
new file mode 100755
index 00000000000..1d848dd5cf6
--- /dev/null
+++ b/3rdparty/Sabre/CalDAV/Property/SupportedCalendarData.php
@@ -0,0 +1,38 @@
+<?php
+
+/**
+ * Supported-calendar-data property
+ *
+ * This property is a representation of the supported-calendar-data property
+ * in the CalDAV namespace. SabreDAV only has support for text/calendar;2.0
+ * so the value is currently hardcoded.
+ *
+ * @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_Property_SupportedCalendarData extends Sabre_DAV_Property {
+
+ /**
+ * 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_CalDAV_Plugin::NS_CALDAV])?$server->xmlNamespaces[Sabre_CalDAV_Plugin::NS_CALDAV]:'cal';
+
+ $caldata = $doc->createElement($prefix . ':calendar-data');
+ $caldata->setAttribute('content-type','text/calendar');
+ $caldata->setAttribute('version','2.0');
+
+ $node->appendChild($caldata);
+ }
+
+}
diff --git a/3rdparty/Sabre/CalDAV/Property/SupportedCollationSet.php b/3rdparty/Sabre/CalDAV/Property/SupportedCollationSet.php
new file mode 100755
index 00000000000..24e84d4c17d
--- /dev/null
+++ b/3rdparty/Sabre/CalDAV/Property/SupportedCollationSet.php
@@ -0,0 +1,44 @@
+<?php
+
+/**
+ * supported-collation-set property
+ *
+ * This property is a representation of the supported-collation-set property
+ * in the CalDAV namespace.
+ *
+ * @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_Property_SupportedCollationSet extends Sabre_DAV_Property {
+
+ /**
+ * Serializes the property in a DOM document
+ *
+ * @param Sabre_DAV_Server $server
+ * @param DOMElement $node
+ * @return void
+ */
+ public function serialize(Sabre_DAV_Server $server,DOMElement $node) {
+
+ $doc = $node->ownerDocument;
+
+ $prefix = $node->lookupPrefix('urn:ietf:params:xml:ns:caldav');
+ if (!$prefix) $prefix = 'cal';
+
+ $node->appendChild(
+ $doc->createElement($prefix . ':supported-collation','i;ascii-casemap')
+ );
+ $node->appendChild(
+ $doc->createElement($prefix . ':supported-collation','i;octet')
+ );
+ $node->appendChild(
+ $doc->createElement($prefix . ':supported-collation','i;unicode-casemap')
+ );
+
+
+ }
+
+}
diff --git a/3rdparty/Sabre/CalDAV/Schedule/IMip.php b/3rdparty/Sabre/CalDAV/Schedule/IMip.php
new file mode 100755
index 00000000000..37e75fcc4a7
--- /dev/null
+++ b/3rdparty/Sabre/CalDAV/Schedule/IMip.php
@@ -0,0 +1,104 @@
+<?php
+
+/**
+ * iMIP handler.
+ *
+ * This class is responsible for sending out iMIP messages. iMIP is the
+ * email-based transport for iTIP. iTIP deals with scheduling operations for
+ * iCalendar objects.
+ *
+ * If you want to customize the email that gets sent out, you can do so by
+ * extending this class and overriding the sendMessage method.
+ *
+ * @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_Schedule_IMip {
+
+ /**
+ * Email address used in From: header.
+ *
+ * @var string
+ */
+ protected $senderEmail;
+
+ /**
+ * Creates the email handler.
+ *
+ * @param string $senderEmail. The 'senderEmail' is the email that shows up
+ * in the 'From:' address. This should
+ * generally be some kind of no-reply email
+ * address you own.
+ */
+ public function __construct($senderEmail) {
+
+ $this->senderEmail = $senderEmail;
+
+ }
+
+ /**
+ * Sends one or more iTip messages through email.
+ *
+ * @param string $originator
+ * @param array $recipients
+ * @param Sabre_VObject_Component $vObject
+ * @return void
+ */
+ public function sendMessage($originator, array $recipients, Sabre_VObject_Component $vObject) {
+
+ foreach($recipients as $recipient) {
+
+ $to = $recipient;
+ $replyTo = $originator;
+ $subject = 'SabreDAV iTIP message';
+
+ switch(strtoupper($vObject->METHOD)) {
+ case 'REPLY' :
+ $subject = 'Response for: ' . $vObject->VEVENT->SUMMARY;
+ break;
+ case 'REQUEST' :
+ $subject = 'Invitation for: ' .$vObject->VEVENT->SUMMARY;
+ break;
+ case 'CANCEL' :
+ $subject = 'Cancelled event: ' . $vObject->VEVENT->SUMMARY;
+ break;
+ }
+
+ $headers = array();
+ $headers[] = 'Reply-To: ' . $replyTo;
+ $headers[] = 'From: ' . $this->senderEmail;
+ $headers[] = 'Content-Type: text/calendar; method=' . (string)$vObject->method . '; charset=utf-8';
+ if (Sabre_DAV_Server::$exposeVersion) {
+ $headers[] = 'X-Sabre-Version: ' . Sabre_DAV_Version::VERSION . '-' . Sabre_DAV_Version::STABILITY;
+ }
+
+ $vcalBody = $vObject->serialize();
+
+ $this->mail($to, $subject, $vcalBody, $headers);
+
+ }
+
+ }
+
+ /**
+ * This function is reponsible for sending the actual email.
+ *
+ * @param string $to Recipient email address
+ * @param string $subject Subject of the email
+ * @param string $body iCalendar body
+ * @param array $headers List of headers
+ * @return void
+ */
+ protected function mail($to, $subject, $body, array $headers) {
+
+ mail($to, $subject, $body, implode("\r\n", $headers));
+
+ }
+
+
+}
+
+?>
diff --git a/3rdparty/Sabre/CalDAV/Schedule/IOutbox.php b/3rdparty/Sabre/CalDAV/Schedule/IOutbox.php
new file mode 100755
index 00000000000..46d77514bc0
--- /dev/null
+++ b/3rdparty/Sabre/CalDAV/Schedule/IOutbox.php
@@ -0,0 +1,16 @@
+<?php
+
+/**
+ * Implement this interface to have a node be recognized as a CalDAV scheduling
+ * outbox.
+ *
+ * @package Sabre
+ * @subpackage CalDAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+interface Sabre_CalDAV_Schedule_IOutbox extends Sabre_DAV_ICollection, Sabre_DAVACL_IACL {
+
+
+}
diff --git a/3rdparty/Sabre/CalDAV/Schedule/Outbox.php b/3rdparty/Sabre/CalDAV/Schedule/Outbox.php
new file mode 100755
index 00000000000..014c37230d1
--- /dev/null
+++ b/3rdparty/Sabre/CalDAV/Schedule/Outbox.php
@@ -0,0 +1,152 @@
+<?php
+
+/**
+ * The CalDAV scheduling outbox
+ *
+ * The outbox is mainly used as an endpoint in the tree for a client to do
+ * free-busy requests. This functionality is completely handled by the
+ * Scheduling plugin, so this object is actually mostly static.
+ *
+ * @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_Schedule_Outbox extends Sabre_DAV_Directory implements Sabre_CalDAV_Schedule_IOutbox {
+
+ /**
+ * The principal Uri
+ *
+ * @var string
+ */
+ protected $principalUri;
+
+ /**
+ * Constructor
+ *
+ * @param string $principalUri
+ */
+ public function __construct($principalUri) {
+
+ $this->principalUri = $principalUri;
+
+ }
+
+ /**
+ * Returns the name of the node.
+ *
+ * This is used to generate the url.
+ *
+ * @return string
+ */
+ public function getName() {
+
+ return 'outbox';
+
+ }
+
+ /**
+ * Returns an array with all the child nodes
+ *
+ * @return Sabre_DAV_INode[]
+ */
+ public function getChildren() {
+
+ return array();
+
+ }
+
+ /**
+ * 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' => '{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}schedule-query-freebusy',
+ 'principal' => $this->getOwner(),
+ 'protected' => true,
+ ),
+ array(
+ 'privilege' => '{DAV:}read',
+ 'principal' => $this->getOwner(),
+ '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('You\'re not allowed to update the ACL');
+
+ }
+
+ /**
+ * Returns the list of supported privileges for this node.
+ *
+ * The returned data structure is a list of nested privileges.
+ * See Sabre_DAVACL_Plugin::getDefaultSupportedPrivilegeSet for a simple
+ * standard structure.
+ *
+ * If null is returned from this method, the default privilege set is used,
+ * which is fine for most common usecases.
+ *
+ * @return array|null
+ */
+ public function getSupportedPrivilegeSet() {
+
+ $default = Sabre_DAVACL_Plugin::getDefaultSupportedPrivilegeSet();
+ $default['aggregates'][] = array(
+ 'privilege' => '{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}schedule-query-freebusy',
+ );
+
+ return $default;
+
+ }
+
+}
diff --git a/3rdparty/Sabre/CalDAV/Server.php b/3rdparty/Sabre/CalDAV/Server.php
new file mode 100755
index 00000000000..325e3d80a7f
--- /dev/null
+++ b/3rdparty/Sabre/CalDAV/Server.php
@@ -0,0 +1,68 @@
+<?php
+
+/**
+ * CalDAV server
+ *
+ * Deprecated! Warning: This class is now officially deprecated
+ *
+ * This script is a convenience script. It quickly sets up a WebDAV server
+ * with caldav and ACL support, and it creates the root 'principals' and
+ * 'calendars' collections.
+ *
+ * Note that if you plan to do anything moderately complex, you are advised to
+ * not subclass this server, but use Sabre_DAV_Server directly instead. This
+ * class is nothing more than an 'easy setup'.
+ *
+ * @package Sabre
+ * @subpackage CalDAV
+ * @deprecated Don't use this class anymore, it will be removed in version 1.7.
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_CalDAV_Server extends Sabre_DAV_Server {
+
+ /**
+ * The authentication realm
+ *
+ * Note that if this changes, the hashes in the auth backend must also
+ * be recalculated.
+ *
+ * @var string
+ */
+ public $authRealm = 'SabreDAV';
+
+ /**
+ * Sets up the object. A PDO object must be passed to setup all the backends.
+ *
+ * @param PDO $pdo
+ */
+ public function __construct(PDO $pdo) {
+
+ /* Backends */
+ $authBackend = new Sabre_DAV_Auth_Backend_PDO($pdo);
+ $calendarBackend = new Sabre_CalDAV_Backend_PDO($pdo);
+ $principalBackend = new Sabre_DAVACL_PrincipalBackend_PDO($pdo);
+
+ /* Directory structure */
+ $tree = array(
+ new Sabre_CalDAV_Principal_Collection($principalBackend),
+ new Sabre_CalDAV_CalendarRootNode($principalBackend, $calendarBackend),
+ );
+
+ /* Initializing server */
+ parent::__construct($tree);
+
+ /* Server Plugins */
+ $authPlugin = new Sabre_DAV_Auth_Plugin($authBackend,$this->authRealm);
+ $this->addPlugin($authPlugin);
+
+ $aclPlugin = new Sabre_DAVACL_Plugin();
+ $this->addPlugin($aclPlugin);
+
+ $caldavPlugin = new Sabre_CalDAV_Plugin();
+ $this->addPlugin($caldavPlugin);
+
+ }
+
+}
diff --git a/3rdparty/Sabre/CalDAV/UserCalendars.php b/3rdparty/Sabre/CalDAV/UserCalendars.php
new file mode 100755
index 00000000000..b8d3f0573fa
--- /dev/null
+++ b/3rdparty/Sabre/CalDAV/UserCalendars.php
@@ -0,0 +1,298 @@
+<?php
+
+/**
+ * The UserCalenders class contains all calendars associated to one 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_UserCalendars implements Sabre_DAV_IExtendedCollection, Sabre_DAVACL_IACL {
+
+ /**
+ * Principal backend
+ *
+ * @var Sabre_DAVACL_IPrincipalBackend
+ */
+ protected $principalBackend;
+
+ /**
+ * CalDAV backend
+ *
+ * @var Sabre_CalDAV_Backend_Abstract
+ */
+ protected $caldavBackend;
+
+ /**
+ * Principal information
+ *
+ * @var array
+ */
+ protected $principalInfo;
+
+ /**
+ * Constructor
+ *
+ * @param Sabre_DAVACL_IPrincipalBackend $principalBackend
+ * @param Sabre_CalDAV_Backend_Abstract $caldavBackend
+ * @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);
+
+ }
+
+ /**
+ * Returns the name of this object
+ *
+ * @return string
+ */
+ public function getName() {
+
+ list(,$name) = Sabre_DAV_URLUtil::splitPath($this->principalInfo['uri']);
+ return $name;
+
+ }
+
+ /**
+ * Updates the name of this object
+ *
+ * @param string $name
+ * @return void
+ */
+ public function setName($name) {
+
+ throw new Sabre_DAV_Exception_Forbidden();
+
+ }
+
+ /**
+ * Deletes this object
+ *
+ * @return void
+ */
+ public function delete() {
+
+ throw new Sabre_DAV_Exception_Forbidden();
+
+ }
+
+ /**
+ * 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_CalDAV_Calendar
+ */
+ public function getChild($name) {
+
+ foreach($this->getChildren() as $child) {
+ if ($name==$child->getName())
+ return $child;
+
+ }
+ throw new Sabre_DAV_Exception_NotFound('Calendar with name \'' . $name . '\' could not be found');
+
+ }
+
+ /**
+ * Checks if a calendar exists.
+ *
+ * @param string $name
+ * @todo needs optimizing
+ * @return bool
+ */
+ public function childExists($name) {
+
+ foreach($this->getChildren() as $child) {
+ if ($name==$child->getName())
+ return true;
+
+ }
+ return false;
+
+ }
+
+ /**
+ * Returns a list of calendars
+ *
+ * @return array
+ */
+ public function getChildren() {
+
+ $calendars = $this->caldavBackend->getCalendarsForUser($this->principalInfo['uri']);
+ $objs = array();
+ foreach($calendars as $calendar) {
+ $objs[] = new Sabre_CalDAV_Calendar($this->principalBackend, $this->caldavBackend, $calendar);
+ }
+ $objs[] = new Sabre_CalDAV_Schedule_Outbox($this->principalInfo['uri']);
+ return $objs;
+
+ }
+
+ /**
+ * Creates a new calendar
+ *
+ * @param string $name
+ * @param array $resourceType
+ * @param array $properties
+ * @return void
+ */
+ public function createExtendedCollection($name, array $resourceType, array $properties) {
+
+ if (!in_array('{urn:ietf:params:xml:ns:caldav}calendar',$resourceType) || count($resourceType)!==2) {
+ throw new Sabre_DAV_Exception_InvalidResourceType('Unknown resourceType for this collection');
+ }
+ $this->caldavBackend->createCalendar($this->principalInfo['uri'], $name, $properties);
+
+ }
+
+ /**
+ * Returns the owner principal
+ *
+ * This must be a url to a principal, or null if there's no owner
+ *
+ * @return string|null
+ */
+ public function getOwner() {
+
+ return $this->principalInfo['uri'];
+
+ }
+
+ /**
+ * Returns a group principal
+ *
+ * This must be a url to a principal, or null if there's no owner
+ *
+ * @return string|null
+ */
+ public function getGroup() {
+
+ return null;
+
+ }
+
+ /**
+ * Returns a list of ACE's for this node.
+ *
+ * Each ACE has the following properties:
+ * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are
+ * currently the only supported privileges
+ * * 'principal', a url to the principal who owns the node
+ * * 'protected' (optional), indicating that this ACE is not allowed to
+ * be updated.
+ *
+ * @return array
+ */
+ public function getACL() {
+
+ return array(
+ array(
+ 'privilege' => '{DAV:}read',
+ 'principal' => $this->principalInfo['uri'],
+ 'protected' => true,
+ ),
+ array(
+ 'privilege' => '{DAV:}write',
+ 'principal' => $this->principalInfo['uri'],
+ 'protected' => true,
+ ),
+ array(
+ 'privilege' => '{DAV:}read',
+ 'principal' => $this->principalInfo['uri'] . '/calendar-proxy-write',
+ 'protected' => true,
+ ),
+ array(
+ 'privilege' => '{DAV:}write',
+ 'principal' => $this->principalInfo['uri'] . '/calendar-proxy-write',
+ 'protected' => true,
+ ),
+ array(
+ 'privilege' => '{DAV:}read',
+ 'principal' => $this->principalInfo['uri'] . '/calendar-proxy-read',
+ 'protected' => true,
+ ),
+
+ );
+
+ }
+
+ /**
+ * Updates the ACL
+ *
+ * This method will receive a list of new ACE's.
+ *
+ * @param array $acl
+ * @return void
+ */
+ public function setACL(array $acl) {
+
+ throw new Sabre_DAV_Exception_MethodNotAllowed('Changing ACL is not yet supported');
+
+ }
+
+ /**
+ * Returns the list of supported privileges for this node.
+ *
+ * The returned data structure is a list of nested privileges.
+ * See Sabre_DAVACL_Plugin::getDefaultSupportedPrivilegeSet for a simple
+ * standard structure.
+ *
+ * If null is returned from this method, the default privilege set is used,
+ * which is fine for most common usecases.
+ *
+ * @return array|null
+ */
+ public function getSupportedPrivilegeSet() {
+
+ return null;
+
+ }
+
+}
diff --git a/3rdparty/Sabre/CalDAV/Version.php b/3rdparty/Sabre/CalDAV/Version.php
new file mode 100755
index 00000000000..ace9901c089
--- /dev/null
+++ b/3rdparty/Sabre/CalDAV/Version.php
@@ -0,0 +1,24 @@
+<?php
+
+/**
+ * This class contains the Sabre_CalDAV version constants.
+ *
+ * @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_Version {
+
+ /**
+ * Full version number
+ */
+ const VERSION = '1.6.4';
+
+ /**
+ * Stability : alpha, beta, stable
+ */
+ const STABILITY = 'stable';
+
+}
diff --git a/3rdparty/Sabre/CalDAV/includes.php b/3rdparty/Sabre/CalDAV/includes.php
new file mode 100755
index 00000000000..1ecb870a0e1
--- /dev/null
+++ b/3rdparty/Sabre/CalDAV/includes.php
@@ -0,0 +1,43 @@
+<?php
+
+/**
+ * Sabre_CalDAV includes file
+ *
+ * Including this file will automatically include all files from the
+ * Sabre_CalDAV package.
+ *
+ * This often allows faster loadtimes, as autoload-speed is often quite slow.
+ *
+ * @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
+ */
+
+// Begin includes
+include __DIR__ . '/Backend/Abstract.php';
+include __DIR__ . '/Backend/PDO.php';
+include __DIR__ . '/CalendarQueryParser.php';
+include __DIR__ . '/CalendarQueryValidator.php';
+include __DIR__ . '/CalendarRootNode.php';
+include __DIR__ . '/ICalendar.php';
+include __DIR__ . '/ICalendarObject.php';
+include __DIR__ . '/ICSExportPlugin.php';
+include __DIR__ . '/Plugin.php';
+include __DIR__ . '/Principal/Collection.php';
+include __DIR__ . '/Principal/ProxyRead.php';
+include __DIR__ . '/Principal/ProxyWrite.php';
+include __DIR__ . '/Principal/User.php';
+include __DIR__ . '/Property/SupportedCalendarComponentSet.php';
+include __DIR__ . '/Property/SupportedCalendarData.php';
+include __DIR__ . '/Property/SupportedCollationSet.php';
+include __DIR__ . '/Schedule/IMip.php';
+include __DIR__ . '/Schedule/IOutbox.php';
+include __DIR__ . '/Schedule/Outbox.php';
+include __DIR__ . '/Server.php';
+include __DIR__ . '/UserCalendars.php';
+include __DIR__ . '/Version.php';
+include __DIR__ . '/Calendar.php';
+include __DIR__ . '/CalendarObject.php';
+// End includes
diff --git a/3rdparty/Sabre/CardDAV/AddressBook.php b/3rdparty/Sabre/CardDAV/AddressBook.php
new file mode 100755
index 00000000000..12297175a85
--- /dev/null
+++ b/3rdparty/Sabre/CardDAV/AddressBook.php
@@ -0,0 +1,312 @@
+<?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
+ */
+ 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_NotFound('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.
+ *
+ * This method may return an ETag.
+ *
+ * @param string $name
+ * @param resource $vcardData
+ * @return void|null
+ */
+ public function createFile($name,$vcardData = null) {
+
+ if (is_resource($vcardData)) {
+ $vcardData = stream_get_contents($vcardData);
+ }
+ // Converting to UTF-8, if needed
+ $vcardData = Sabre_DAV_StringUtil::ensureUTF8($vcardData);
+
+ return $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-existent property is always successful.
+ *
+ * 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 array
+ */
+ 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');
+
+ }
+
+ /**
+ * Returns the list of supported privileges for this node.
+ *
+ * The returned data structure is a list of nested privileges.
+ * See Sabre_DAVACL_Plugin::getDefaultSupportedPrivilegeSet for a simple
+ * standard structure.
+ *
+ * If null is returned from this method, the default privilege set is used,
+ * which is fine for most common usecases.
+ *
+ * @return array|null
+ */
+ public function getSupportedPrivilegeSet() {
+
+ return null;
+
+ }
+
+}
diff --git a/3rdparty/Sabre/CardDAV/AddressBookQueryParser.php b/3rdparty/Sabre/CardDAV/AddressBookQueryParser.php
new file mode 100755
index 00000000000..46bb8ff18dd
--- /dev/null
+++ b/3rdparty/Sabre/CardDAV/AddressBookQueryParser.php
@@ -0,0 +1,219 @@
+<?php
+
+/**
+ * Parses the addressbook-query report request body.
+ *
+ * Whoever designed this format, and the CalDAV equivalent 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 DOMDocument $dom
+ */
+ 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.
+ *
+ * @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');
+
+ // According to the CardDAV spec there needs to be exactly 1 filter
+ // element. However, KDE 4.8.2 contains a bug that will encode 0 filter
+ // elements, so this is a workaround for that.
+ //
+ // See: https://bugs.kde.org/show_bug.cgi?id=300047
+ if ($filter->length === 0) {
+ $test = null;
+ $filter = null;
+ } elseif ($filter->length === 1) {
+ $filter = $filter->item(0);
+ $test = $this->xpath->evaluate('string(@test)', $filter);
+ } else {
+ throw new Sabre_DAV_Exception_BadRequest('Only one filter element is allowed');
+ }
+
+ 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 array
+ */
+ 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/3rdparty/Sabre/CardDAV/AddressBookRoot.php b/3rdparty/Sabre/CardDAV/AddressBookRoot.php
new file mode 100755
index 00000000000..9d37b15f08e
--- /dev/null
+++ b/3rdparty/Sabre/CardDAV/AddressBookRoot.php
@@ -0,0 +1,78 @@
+<?php
+
+/**
+ * AddressBook rootnode
+ *
+ * This object lists a collection of users, which can contain addressbooks.
+ *
+ * @package Sabre
+ * @subpackage CardDAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_CardDAV_AddressBookRoot extends Sabre_DAVACL_AbstractPrincipalCollection {
+
+ /**
+ * Principal Backend
+ *
+ * @var Sabre_DAVACL_IPrincipalBackend
+ */
+ protected $principalBackend;
+
+ /**
+ * CardDAV backend
+ *
+ * @var Sabre_CardDAV_Backend_Abstract
+ */
+ protected $carddavBackend;
+
+ /**
+ * Constructor
+ *
+ * This constructor needs both a principal and a carddav backend.
+ *
+ * By default this class will show a list of addressbook collections for
+ * principals in the 'principals' collection. If your main principals are
+ * actually located in a different path, use the $principalPrefix argument
+ * to override this.
+ *
+ * @param Sabre_DAVACL_IPrincipalBackend $principalBackend
+ * @param Sabre_CardDAV_Backend_Abstract $carddavBackend
+ * @param string $principalPrefix
+ */
+ public function __construct(Sabre_DAVACL_IPrincipalBackend $principalBackend,Sabre_CardDAV_Backend_Abstract $carddavBackend, $principalPrefix = 'principals') {
+
+ $this->carddavBackend = $carddavBackend;
+ parent::__construct($principalBackend, $principalPrefix);
+
+ }
+
+ /**
+ * Returns the name of the node
+ *
+ * @return string
+ */
+ public function getName() {
+
+ return Sabre_CardDAV_Plugin::ADDRESSBOOK_ROOT;
+
+ }
+
+ /**
+ * This method returns a node for a principal.
+ *
+ * The passed array contains principal information, and is guaranteed to
+ * at least contain a uri item. Other properties may or may not be
+ * supplied by the authentication backend.
+ *
+ * @param array $principal
+ * @return Sabre_DAV_INode
+ */
+ public function getChildForPrincipal(array $principal) {
+
+ return new Sabre_CardDAV_UserAddressBooks($this->carddavBackend, $principal['uri']);
+
+ }
+
+}
diff --git a/3rdparty/Sabre/CardDAV/Backend/Abstract.php b/3rdparty/Sabre/CardDAV/Backend/Abstract.php
new file mode 100755
index 00000000000..e4806b7161f
--- /dev/null
+++ b/3rdparty/Sabre/CardDAV/Backend/Abstract.php
@@ -0,0 +1,166 @@
+<?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
+ *
+ * It's recommended to also return the following properties:
+ * * etag - A unique etag. This must change every time the card changes.
+ * * size - The size of the card in bytes.
+ *
+ * If these last two properties are provided, less time will be spent
+ * calculating them. If they are specified, you can also ommit carddata.
+ * This may speed up certain requests, especially with large cards.
+ *
+ * @param mixed $addressbookId
+ * @return array
+ */
+ public abstract function getCards($addressbookId);
+
+ /**
+ * Returns a specfic card.
+ *
+ * The same set of properties must be returned as with getCards. The only
+ * exception is that 'carddata' is absolutely required.
+ *
+ * @param mixed $addressBookId
+ * @param string $cardUri
+ * @return array
+ */
+ public abstract function getCard($addressBookId, $cardUri);
+
+ /**
+ * Creates a new card.
+ *
+ * The addressbook id will be passed as the first argument. This is the
+ * same id as it is returned from the getAddressbooksForUser method.
+ *
+ * The cardUri is a base uri, and doesn't include the full path. The
+ * cardData argument is the vcard body, and is passed as a string.
+ *
+ * It is possible to return an ETag from this method. This ETag is for the
+ * newly created resource, and must be enclosed with double quotes (that
+ * is, the string itself must contain the double quotes).
+ *
+ * You should only return the ETag if you store the carddata as-is. If a
+ * subsequent GET request on the same card does not have the same body,
+ * byte-by-byte and you did return an ETag here, clients tend to get
+ * confused.
+ *
+ * If you don't return an ETag, you can just return null.
+ *
+ * @param mixed $addressBookId
+ * @param string $cardUri
+ * @param string $cardData
+ * @return string|null
+ */
+ abstract public function createCard($addressBookId, $cardUri, $cardData);
+
+ /**
+ * Updates a card.
+ *
+ * The addressbook id will be passed as the first argument. This is the
+ * same id as it is returned from the getAddressbooksForUser method.
+ *
+ * The cardUri is a base uri, and doesn't include the full path. The
+ * cardData argument is the vcard body, and is passed as a string.
+ *
+ * It is possible to return an ETag from this method. This ETag should
+ * match that of the updated resource, and must be enclosed with double
+ * quotes (that is: the string itself must contain the actual quotes).
+ *
+ * You should only return the ETag if you store the carddata as-is. If a
+ * subsequent GET request on the same card does not have the same body,
+ * byte-by-byte and you did return an ETag here, clients tend to get
+ * confused.
+ *
+ * If you don't return an ETag, you can just return null.
+ *
+ * @param mixed $addressBookId
+ * @param string $cardUri
+ * @param string $cardData
+ * @return string|null
+ */
+ 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/3rdparty/Sabre/CardDAV/Backend/PDO.php b/3rdparty/Sabre/CardDAV/Backend/PDO.php
new file mode 100755
index 00000000000..413a77f3bcc
--- /dev/null
+++ b/3rdparty/Sabre/CardDAV/Backend/PDO.php
@@ -0,0 +1,330 @@
+<?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
+ * @param string $addressBooksTableName
+ * @param string $cardsTableName
+ */
+ 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 = ?');
+ $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.
+ *
+ * This method should return the following properties for each card:
+ * * carddata - raw vcard data
+ * * uri - Some unique url
+ * * lastmodified - A unix timestamp
+ *
+ * It's recommended to also return the following properties:
+ * * etag - A unique etag. This must change every time the card changes.
+ * * size - The size of the card in bytes.
+ *
+ * If these last two properties are provided, less time will be spent
+ * calculating them. If they are specified, you can also ommit carddata.
+ * This may speed up certain requests, especially with large cards.
+ *
+ * @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.
+ *
+ * The same set of properties must be returned as with getCards. The only
+ * exception is that 'carddata' is absolutely required.
+ *
+ * @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.
+ *
+ * The addressbook id will be passed as the first argument. This is the
+ * same id as it is returned from the getAddressbooksForUser method.
+ *
+ * The cardUri is a base uri, and doesn't include the full path. The
+ * cardData argument is the vcard body, and is passed as a string.
+ *
+ * It is possible to return an ETag from this method. This ETag is for the
+ * newly created resource, and must be enclosed with double quotes (that
+ * is, the string itself must contain the double quotes).
+ *
+ * You should only return the ETag if you store the carddata as-is. If a
+ * subsequent GET request on the same card does not have the same body,
+ * byte-by-byte and you did return an ETag here, clients tend to get
+ * confused.
+ *
+ * If you don't return an ETag, you can just return null.
+ *
+ * @param mixed $addressBookId
+ * @param string $cardUri
+ * @param string $cardData
+ * @return string|null
+ */
+ 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 '"' . md5($cardData) . '"';
+
+ }
+
+ /**
+ * Updates a card.
+ *
+ * The addressbook id will be passed as the first argument. This is the
+ * same id as it is returned from the getAddressbooksForUser method.
+ *
+ * The cardUri is a base uri, and doesn't include the full path. The
+ * cardData argument is the vcard body, and is passed as a string.
+ *
+ * It is possible to return an ETag from this method. This ETag should
+ * match that of the updated resource, and must be enclosed with double
+ * quotes (that is: the string itself must contain the actual quotes).
+ *
+ * You should only return the ETag if you store the carddata as-is. If a
+ * subsequent GET request on the same card does not have the same body,
+ * byte-by-byte and you did return an ETag here, clients tend to get
+ * confused.
+ *
+ * If you don't return an ETag, you can just return null.
+ *
+ * @param mixed $addressBookId
+ * @param string $cardUri
+ * @param string $cardData
+ * @return string|null
+ */
+ public function updateCard($addressBookId, $cardUri, $cardData) {
+
+ $stmt = $this->pdo->prepare('UPDATE ' . $this->cardsTableName . ' SET carddata = ?, lastmodified = ? WHERE uri = ? AND addressbookid =?');
+ $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 '"' . md5($cardData) . '"';
+
+ }
+
+ /**
+ * 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/3rdparty/Sabre/CardDAV/Card.php b/3rdparty/Sabre/CardDAV/Card.php
new file mode 100755
index 00000000000..d7c66333837
--- /dev/null
+++ b/3rdparty/Sabre/CardDAV/Card.php
@@ -0,0 +1,250 @@
+<?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() {
+
+ // Pre-populating 'carddata' is optional. If we don't yet have it
+ // already, we fetch it from the backend.
+ if (!isset($this->cardData['carddata'])) {
+ $this->cardData = $this->carddavBackend->getCard($this->addressBookInfo['id'], $this->cardData['uri']);
+ }
+ return $this->cardData['carddata'];
+
+ }
+
+ /**
+ * 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);
+
+ $etag = $this->carddavBackend->updateCard($this->addressBookInfo['id'],$this->cardData['uri'],$cardData);
+ $this->cardData['carddata'] = $cardData;
+ $this->cardData['etag'] = $etag;
+
+ return $etag;
+
+ }
+
+ /**
+ * 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() {
+
+ if (isset($this->cardData['etag'])) {
+ return $this->cardData['etag'];
+ } else {
+ return '"' . md5($this->get()) . '"';
+ }
+
+ }
+
+ /**
+ * 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() {
+
+ if (array_key_exists('size', $this->cardData)) {
+ return $this->cardData['size'];
+ } else {
+ return strlen($this->get());
+ }
+
+ }
+
+ /**
+ * 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');
+
+ }
+
+ /**
+ * Returns the list of supported privileges for this node.
+ *
+ * The returned data structure is a list of nested privileges.
+ * See Sabre_DAVACL_Plugin::getDefaultSupportedPrivilegeSet for a simple
+ * standard structure.
+ *
+ * If null is returned from this method, the default privilege set is used,
+ * which is fine for most common usecases.
+ *
+ * @return array|null
+ */
+ public function getSupportedPrivilegeSet() {
+
+ return null;
+
+ }
+
+}
+
diff --git a/3rdparty/Sabre/CardDAV/IAddressBook.php b/3rdparty/Sabre/CardDAV/IAddressBook.php
new file mode 100755
index 00000000000..2bc275bcf74
--- /dev/null
+++ b/3rdparty/Sabre/CardDAV/IAddressBook.php
@@ -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/3rdparty/Sabre/CardDAV/ICard.php b/3rdparty/Sabre/CardDAV/ICard.php
new file mode 100755
index 00000000000..a17299316c1
--- /dev/null
+++ b/3rdparty/Sabre/CardDAV/ICard.php
@@ -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/3rdparty/Sabre/CardDAV/IDirectory.php b/3rdparty/Sabre/CardDAV/IDirectory.php
new file mode 100755
index 00000000000..22d4afeb24a
--- /dev/null
+++ b/3rdparty/Sabre/CardDAV/IDirectory.php
@@ -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/3rdparty/Sabre/CardDAV/Plugin.php b/3rdparty/Sabre/CardDAV/Plugin.php
new file mode 100755
index 00000000000..ca20e468497
--- /dev/null
+++ b/3rdparty/Sabre/CardDAV/Plugin.php
@@ -0,0 +1,666 @@
+<?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('updateProperties', array($this, 'updateProperties'));
+ $server->subscribeEvent('report', array($this,'report'));
+ $server->subscribeEvent('onHTMLActionsPanel', array($this,'htmlActionsPanel'));
+ $server->subscribeEvent('onBrowserPostAction', array($this,'browserPostAction'));
+ $server->subscribeEvent('beforeWriteContent', array($this, 'beforeWriteContent'));
+ $server->subscribeEvent('beforeCreateFile', array($this, 'beforeCreateFile'));
+
+ /* 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';
+ $server->protectedProperties[] = '{' . self::NS_CARDDAV . '}addressbook-home-set';
+ $server->protectedProperties[] = '{' . self::NS_CARDDAV . '}supported-collation-set';
+
+ $server->propertyMap['{http://calendarserver.org/ns/}me-card'] = 'Sabre_DAV_Property_Href';
+
+ $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);
+
+ }
+ }
+
+ if ($node instanceof Sabre_CardDAV_UserAddressBooks) {
+
+ $meCardProp = '{http://calendarserver.org/ns/}me-card';
+ if (in_array($meCardProp, $requestedProperties)) {
+
+ $props = $this->server->getProperties($node->getOwner(), array('{http://sabredav.org/ns}vcard-url'));
+ if (isset($props['{http://sabredav.org/ns}vcard-url'])) {
+
+ $returnedProperties[200][$meCardProp] = new Sabre_DAV_Property_Href(
+ $props['{http://sabredav.org/ns}vcard-url']
+ );
+ $pos = array_search($meCardProp, $requestedProperties);
+ unset($requestedProperties[$pos]);
+
+ }
+
+ }
+
+ }
+
+ }
+
+ /**
+ * This event is triggered when a PROPPATCH method is executed
+ *
+ * @param array $mutations
+ * @param array $result
+ * @param Sabre_DAV_INode $node
+ * @return void
+ */
+ public function updateProperties(&$mutations, &$result, $node) {
+
+ if (!$node instanceof Sabre_CardDAV_UserAddressBooks) {
+ return true;
+ }
+
+ $meCard = '{http://calendarserver.org/ns/}me-card';
+
+ // The only property we care about
+ if (!isset($mutations[$meCard]))
+ return true;
+
+ $value = $mutations[$meCard];
+ unset($mutations[$meCard]);
+
+ if ($value instanceof Sabre_DAV_Property_IHref) {
+ $value = $value->getHref();
+ $value = $this->server->calculateUri($value);
+ } elseif (!is_null($value)) {
+ $result[400][$meCard] = null;
+ return false;
+ }
+
+ $innerResult = $this->server->updateProperties(
+ $node->getOwner(),
+ array(
+ '{http://sabredav.org/ns}vcard-url' => $value,
+ )
+ );
+
+ $closureResult = false;
+ foreach($innerResult as $status => $props) {
+ if (is_array($props) && array_key_exists('{http://sabredav.org/ns}vcard-url', $props)) {
+ $result[$status][$meCard] = null;
+ $closureResult = ($status>=200 && $status<300);
+ }
+
+ }
+
+ return $result;
+
+ }
+
+ /**
+ * 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 method is triggered before a file gets updated with new content.
+ *
+ * This plugin uses this method to ensure that Card nodes receive valid
+ * vcard data.
+ *
+ * @param string $path
+ * @param Sabre_DAV_IFile $node
+ * @param resource $data
+ * @return void
+ */
+ public function beforeWriteContent($path, Sabre_DAV_IFile $node, &$data) {
+
+ if (!$node instanceof Sabre_CardDAV_ICard)
+ return;
+
+ $this->validateVCard($data);
+
+ }
+
+ /**
+ * This method is triggered before a new file is created.
+ *
+ * This plugin uses this method to ensure that Card nodes receive valid
+ * vcard data.
+ *
+ * @param string $path
+ * @param resource $data
+ * @param Sabre_DAV_ICollection $parentNode
+ * @return void
+ */
+ public function beforeCreateFile($path, &$data, Sabre_DAV_ICollection $parentNode) {
+
+ if (!$parentNode instanceof Sabre_CardDAV_IAddressBook)
+ return;
+
+ $this->validateVCard($data);
+
+ }
+
+ /**
+ * Checks if the submitted iCalendar data is in fact, valid.
+ *
+ * An exception is thrown if it's not.
+ *
+ * @param resource|string $data
+ * @return void
+ */
+ protected function validateVCard(&$data) {
+
+ // If it's a stream, we convert it to a string first.
+ if (is_resource($data)) {
+ $data = stream_get_contents($data);
+ }
+
+ // Converting the data to unicode, if needed.
+ $data = Sabre_DAV_StringUtil::ensureUTF8($data);
+
+ try {
+
+ $vobj = Sabre_VObject_Reader::read($data);
+
+ } catch (Sabre_VObject_ParseException $e) {
+
+ throw new Sabre_DAV_Exception_UnsupportedMediaType('This resource only supports valid vcard data. Parse error: ' . $e->getMessage());
+
+ }
+
+ if ($vobj->name !== 'VCARD') {
+ throw new Sabre_DAV_Exception_UnsupportedMediaType('This collection can only support vcard objects.');
+ }
+
+ }
+
+
+ /**
+ * 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);
+
+ if (!$filters) return 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 whether
+ // 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 array $vProperties
+ * @param array $filters
+ * @param string $test
+ * @return bool
+ */
+ protected function validateParamFilters(array $vProperties, array $filters, $test) {
+
+ 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 {
+
+ $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 whether
+ // 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';
+
+ }
+
+ /**
+ * This method is used to generate HTML output for the
+ * Sabre_DAV_Browser_Plugin. This allows us to generate an interface users
+ * can use to create new calendars.
+ *
+ * @param Sabre_DAV_INode $node
+ * @param string $output
+ * @return bool
+ */
+ public function htmlActionsPanel(Sabre_DAV_INode $node, &$output) {
+
+ if (!$node instanceof Sabre_CardDAV_UserAddressBooks)
+ return;
+
+ $output.= '<tr><td colspan="2"><form method="post" action="">
+ <h3>Create new address book</h3>
+ <input type="hidden" name="sabreAction" value="mkaddressbook" />
+ <label>Name (uri):</label> <input type="text" name="name" /><br />
+ <label>Display name:</label> <input type="text" name="{DAV:}displayname" /><br />
+ <input type="submit" value="create" />
+ </form>
+ </td></tr>';
+
+ return false;
+
+ }
+
+ /**
+ * This method allows us to intercept the 'mkcalendar' sabreAction. This
+ * action enables the user to create new calendars from the browser plugin.
+ *
+ * @param string $uri
+ * @param string $action
+ * @param array $postVars
+ * @return bool
+ */
+ public function browserPostAction($uri, $action, array $postVars) {
+
+ if ($action!=='mkaddressbook')
+ return;
+
+ $resourceType = array('{DAV:}collection','{urn:ietf:params:xml:ns:carddav}addressbook');
+ $properties = array();
+ if (isset($postVars['{DAV:}displayname'])) {
+ $properties['{DAV:}displayname'] = $postVars['{DAV:}displayname'];
+ }
+ $this->server->createCollection($uri . '/' . $postVars['name'],$resourceType,$properties);
+ return false;
+
+ }
+
+}
diff --git a/3rdparty/Sabre/CardDAV/Property/SupportedAddressData.php b/3rdparty/Sabre/CardDAV/Property/SupportedAddressData.php
new file mode 100755
index 00000000000..36d9306e7aa
--- /dev/null
+++ b/3rdparty/Sabre/CardDAV/Property/SupportedAddressData.php
@@ -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|null $supportedData
+ */
+ 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/3rdparty/Sabre/CardDAV/UserAddressBooks.php b/3rdparty/Sabre/CardDAV/UserAddressBooks.php
new file mode 100755
index 00000000000..3f11fb11238
--- /dev/null
+++ b/3rdparty/Sabre/CardDAV/UserAddressBooks.php
@@ -0,0 +1,257 @@
+<?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_NotFound('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');
+
+ }
+
+ /**
+ * Returns the list of supported privileges for this node.
+ *
+ * The returned data structure is a list of nested privileges.
+ * See Sabre_DAVACL_Plugin::getDefaultSupportedPrivilegeSet for a simple
+ * standard structure.
+ *
+ * If null is returned from this method, the default privilege set is used,
+ * which is fine for most common usecases.
+ *
+ * @return array|null
+ */
+ public function getSupportedPrivilegeSet() {
+
+ return null;
+
+ }
+
+}
diff --git a/3rdparty/Sabre/CardDAV/Version.php b/3rdparty/Sabre/CardDAV/Version.php
new file mode 100755
index 00000000000..d0623f0d3e8
--- /dev/null
+++ b/3rdparty/Sabre/CardDAV/Version.php
@@ -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.6.3';
+
+ /**
+ * Stability : alpha, beta, stable
+ */
+ const STABILITY = 'stable';
+
+}
diff --git a/3rdparty/Sabre/CardDAV/includes.php b/3rdparty/Sabre/CardDAV/includes.php
new file mode 100755
index 00000000000..c3b8c04b077
--- /dev/null
+++ b/3rdparty/Sabre/CardDAV/includes.php
@@ -0,0 +1,32 @@
+<?php
+
+/**
+ * Sabre_CardDAV includes file
+ *
+ * Including this file will automatically include all files from the
+ * Sabre_CardDAV package.
+ *
+ * This often allows faster loadtimes, as autoload-speed is often quite slow.
+ *
+ * @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
+ */
+
+// Begin includes
+include __DIR__ . '/AddressBookQueryParser.php';
+include __DIR__ . '/AddressBookRoot.php';
+include __DIR__ . '/Backend/Abstract.php';
+include __DIR__ . '/Backend/PDO.php';
+include __DIR__ . '/IAddressBook.php';
+include __DIR__ . '/ICard.php';
+include __DIR__ . '/IDirectory.php';
+include __DIR__ . '/Plugin.php';
+include __DIR__ . '/Property/SupportedAddressData.php';
+include __DIR__ . '/UserAddressBooks.php';
+include __DIR__ . '/Version.php';
+include __DIR__ . '/AddressBook.php';
+include __DIR__ . '/Card.php';
+// End includes
diff --git a/3rdparty/Sabre/DAV/Auth/Backend/AbstractBasic.php b/3rdparty/Sabre/DAV/Auth/Backend/AbstractBasic.php
new file mode 100755
index 00000000000..1e89b84f9a1
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Auth/Backend/AbstractBasic.php
@@ -0,0 +1,83 @@
+<?php
+/**
+ * HTTP Basic authentication backend class
+ *
+ * This class can be used by authentication objects wishing to use HTTP Basic
+ * Most of the digest logic is handled, implementors just need to worry about
+ * the validateUserPass method.
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author James David Low (http://jameslow.com/)
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+abstract class Sabre_DAV_Auth_Backend_AbstractBasic implements Sabre_DAV_Auth_IBackend {
+
+ /**
+ * This variable holds the currently logged in username.
+ *
+ * @var string|null
+ */
+ protected $currentUser;
+
+ /**
+ * Validates a username and password
+ *
+ * This method should return true or false depending on if login
+ * succeeded.
+ *
+ * @param string $username
+ * @param string $password
+ * @return bool
+ */
+ abstract protected function validateUserPass($username, $password);
+
+ /**
+ * Returns information about the currently logged in username.
+ *
+ * If nobody is currently logged in, this method should return null.
+ *
+ * @return string|null
+ */
+ public function getCurrentUser() {
+ return $this->currentUser;
+ }
+
+
+ /**
+ * Authenticates the user based on the current request.
+ *
+ * If authentication is successful, true must be returned.
+ * If authentication fails, an exception must be thrown.
+ *
+ * @param Sabre_DAV_Server $server
+ * @param string $realm
+ * @throws Sabre_DAV_Exception_NotAuthenticated
+ * @return bool
+ */
+ public function authenticate(Sabre_DAV_Server $server, $realm) {
+
+ $auth = new Sabre_HTTP_BasicAuth();
+ $auth->setHTTPRequest($server->httpRequest);
+ $auth->setHTTPResponse($server->httpResponse);
+ $auth->setRealm($realm);
+ $userpass = $auth->getUserPass();
+ if (!$userpass) {
+ $auth->requireLogin();
+ throw new Sabre_DAV_Exception_NotAuthenticated('No basic authentication headers were found');
+ }
+
+ // Authenticates the user
+ if (!$this->validateUserPass($userpass[0],$userpass[1])) {
+ $auth->requireLogin();
+ throw new Sabre_DAV_Exception_NotAuthenticated('Username or password does not match');
+ }
+ $this->currentUser = $userpass[0];
+ return true;
+ }
+
+
+}
+
diff --git a/3rdparty/Sabre/DAV/Auth/Backend/AbstractDigest.php b/3rdparty/Sabre/DAV/Auth/Backend/AbstractDigest.php
new file mode 100755
index 00000000000..9833928b976
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Auth/Backend/AbstractDigest.php
@@ -0,0 +1,98 @@
+<?php
+
+/**
+ * HTTP Digest authentication backend class
+ *
+ * This class can be used by authentication objects wishing to use HTTP Digest
+ * Most of the digest logic is handled, implementors just need to worry about
+ * the getDigestHash method
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+abstract class Sabre_DAV_Auth_Backend_AbstractDigest implements Sabre_DAV_Auth_IBackend {
+
+ /**
+ * This variable holds the currently logged in username.
+ *
+ * @var array|null
+ */
+ protected $currentUser;
+
+ /**
+ * Returns a users digest hash based on the username and realm.
+ *
+ * If the user was not known, null must be returned.
+ *
+ * @param string $realm
+ * @param string $username
+ * @return string|null
+ */
+ abstract public function getDigestHash($realm, $username);
+
+ /**
+ * Authenticates the user based on the current request.
+ *
+ * If authentication is successful, true must be returned.
+ * If authentication fails, an exception must be thrown.
+ *
+ * @param Sabre_DAV_Server $server
+ * @param string $realm
+ * @throws Sabre_DAV_Exception_NotAuthenticated
+ * @return bool
+ */
+ public function authenticate(Sabre_DAV_Server $server, $realm) {
+
+ $digest = new Sabre_HTTP_DigestAuth();
+
+ // Hooking up request and response objects
+ $digest->setHTTPRequest($server->httpRequest);
+ $digest->setHTTPResponse($server->httpResponse);
+
+ $digest->setRealm($realm);
+ $digest->init();
+
+ $username = $digest->getUsername();
+
+ // No username was given
+ if (!$username) {
+ $digest->requireLogin();
+ throw new Sabre_DAV_Exception_NotAuthenticated('No digest authentication headers were found');
+ }
+
+ $hash = $this->getDigestHash($realm, $username);
+ // If this was false, the user account didn't exist
+ if ($hash===false || is_null($hash)) {
+ $digest->requireLogin();
+ throw new Sabre_DAV_Exception_NotAuthenticated('The supplied username was not on file');
+ }
+ if (!is_string($hash)) {
+ throw new Sabre_DAV_Exception('The returned value from getDigestHash must be a string or null');
+ }
+
+ // If this was false, the password or part of the hash was incorrect.
+ if (!$digest->validateA1($hash)) {
+ $digest->requireLogin();
+ throw new Sabre_DAV_Exception_NotAuthenticated('Incorrect username');
+ }
+
+ $this->currentUser = $username;
+ return true;
+
+ }
+
+ /**
+ * Returns the currently logged in username.
+ *
+ * @return string|null
+ */
+ public function getCurrentUser() {
+
+ return $this->currentUser;
+
+ }
+
+}
diff --git a/3rdparty/Sabre/DAV/Auth/Backend/Apache.php b/3rdparty/Sabre/DAV/Auth/Backend/Apache.php
new file mode 100755
index 00000000000..d4294ea4d86
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Auth/Backend/Apache.php
@@ -0,0 +1,62 @@
+<?php
+
+/**
+ * Apache authenticator
+ *
+ * This authentication backend assumes that authentication has been
+ * configured in apache, rather than within SabreDAV.
+ *
+ * Make sure apache is properly configured for this to work.
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_Auth_Backend_Apache implements Sabre_DAV_Auth_IBackend {
+
+ /**
+ * Current apache user
+ *
+ * @var string
+ */
+ protected $remoteUser;
+
+ /**
+ * Authenticates the user based on the current request.
+ *
+ * If authentication is successful, true must be returned.
+ * If authentication fails, an exception must be thrown.
+ *
+ * @param Sabre_DAV_Server $server
+ * @param string $realm
+ * @return bool
+ */
+ public function authenticate(Sabre_DAV_Server $server, $realm) {
+
+ $remoteUser = $server->httpRequest->getRawServerValue('REMOTE_USER');
+ if (is_null($remoteUser)) {
+ throw new Sabre_DAV_Exception('We did not receive the $_SERVER[REMOTE_USER] property. This means that apache might have been misconfigured');
+ }
+
+ $this->remoteUser = $remoteUser;
+ return true;
+
+ }
+
+ /**
+ * Returns information about the currently logged in user.
+ *
+ * If nobody is currently logged in, this method should return null.
+ *
+ * @return array|null
+ */
+ public function getCurrentUser() {
+
+ return $this->remoteUser;
+
+ }
+
+}
+
diff --git a/3rdparty/Sabre/DAV/Auth/Backend/File.php b/3rdparty/Sabre/DAV/Auth/Backend/File.php
new file mode 100755
index 00000000000..de308d64a67
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Auth/Backend/File.php
@@ -0,0 +1,75 @@
+<?php
+
+/**
+ * This is an authentication backend that uses a file to manage passwords.
+ *
+ * The backend file must conform to Apache's htdigest format
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_Auth_Backend_File extends Sabre_DAV_Auth_Backend_AbstractDigest {
+
+ /**
+ * List of users
+ *
+ * @var array
+ */
+ protected $users = array();
+
+ /**
+ * Creates the backend object.
+ *
+ * If the filename argument is passed in, it will parse out the specified file fist.
+ *
+ * @param string|null $filename
+ */
+ public function __construct($filename=null) {
+
+ if (!is_null($filename))
+ $this->loadFile($filename);
+
+ }
+
+ /**
+ * Loads an htdigest-formatted file. This method can be called multiple times if
+ * more than 1 file is used.
+ *
+ * @param string $filename
+ * @return void
+ */
+ public function loadFile($filename) {
+
+ foreach(file($filename,FILE_IGNORE_NEW_LINES) as $line) {
+
+ if (substr_count($line, ":") !== 2)
+ throw new Sabre_DAV_Exception('Malformed htdigest file. Every line should contain 2 colons');
+
+ list($username,$realm,$A1) = explode(':',$line);
+
+ if (!preg_match('/^[a-zA-Z0-9]{32}$/', $A1))
+ throw new Sabre_DAV_Exception('Malformed htdigest file. Invalid md5 hash');
+
+ $this->users[$realm . ':' . $username] = $A1;
+
+ }
+
+ }
+
+ /**
+ * Returns a users' information
+ *
+ * @param string $realm
+ * @param string $username
+ * @return string
+ */
+ public function getDigestHash($realm, $username) {
+
+ return isset($this->users[$realm . ':' . $username])?$this->users[$realm . ':' . $username]:false;
+
+ }
+
+}
diff --git a/3rdparty/Sabre/DAV/Auth/Backend/PDO.php b/3rdparty/Sabre/DAV/Auth/Backend/PDO.php
new file mode 100755
index 00000000000..eac18a23fbb
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Auth/Backend/PDO.php
@@ -0,0 +1,65 @@
+<?php
+
+/**
+ * This is an authentication backend that uses a file to manage passwords.
+ *
+ * The backend file must conform to Apache's htdigest format
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_Auth_Backend_PDO extends Sabre_DAV_Auth_Backend_AbstractDigest {
+
+ /**
+ * Reference to PDO connection
+ *
+ * @var PDO
+ */
+ protected $pdo;
+
+ /**
+ * PDO table name we'll be using
+ *
+ * @var string
+ */
+ protected $tableName;
+
+
+ /**
+ * Creates the backend object.
+ *
+ * If the filename argument is passed in, it will parse out the specified file fist.
+ *
+ * @param PDO $pdo
+ * @param string $tableName The PDO table name to use
+ */
+ public function __construct(PDO $pdo, $tableName = 'users') {
+
+ $this->pdo = $pdo;
+ $this->tableName = $tableName;
+
+ }
+
+ /**
+ * Returns the digest hash for a user.
+ *
+ * @param string $realm
+ * @param string $username
+ * @return string|null
+ */
+ public function getDigestHash($realm,$username) {
+
+ $stmt = $this->pdo->prepare('SELECT username, digesta1 FROM '.$this->tableName.' WHERE username = ?');
+ $stmt->execute(array($username));
+ $result = $stmt->fetchAll();
+
+ if (!count($result)) return;
+
+ return $result[0]['digesta1'];
+
+ }
+
+}
diff --git a/3rdparty/Sabre/DAV/Auth/IBackend.php b/3rdparty/Sabre/DAV/Auth/IBackend.php
new file mode 100755
index 00000000000..5be5d1bc93d
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Auth/IBackend.php
@@ -0,0 +1,36 @@
+<?php
+
+/**
+ * This is the base class for any authentication object.
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+interface Sabre_DAV_Auth_IBackend {
+
+ /**
+ * Authenticates the user based on the current request.
+ *
+ * If authentication is successful, true must be returned.
+ * If authentication fails, an exception must be thrown.
+ *
+ * @param Sabre_DAV_Server $server
+ * @param string $realm
+ * @return bool
+ */
+ function authenticate(Sabre_DAV_Server $server,$realm);
+
+ /**
+ * Returns information about the currently logged in username.
+ *
+ * If nobody is currently logged in, this method should return null.
+ *
+ * @return string|null
+ */
+ function getCurrentUser();
+
+}
+
diff --git a/3rdparty/Sabre/DAV/Auth/Plugin.php b/3rdparty/Sabre/DAV/Auth/Plugin.php
new file mode 100755
index 00000000000..55a4e391674
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Auth/Plugin.php
@@ -0,0 +1,111 @@
+<?php
+
+/**
+ * This plugin provides Authentication for a WebDAV server.
+ *
+ * It relies on a Backend object, which provides user information.
+ *
+ * Additionally, it provides support for:
+ * * {DAV:}current-user-principal property from RFC5397
+ * * {DAV:}principal-collection-set property from RFC3744
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_Auth_Plugin extends Sabre_DAV_ServerPlugin {
+
+ /**
+ * Reference to main server object
+ *
+ * @var Sabre_DAV_Server
+ */
+ private $server;
+
+ /**
+ * Authentication backend
+ *
+ * @var Sabre_DAV_Auth_IBackend
+ */
+ private $authBackend;
+
+ /**
+ * The authentication realm.
+ *
+ * @var string
+ */
+ private $realm;
+
+ /**
+ * __construct
+ *
+ * @param Sabre_DAV_Auth_IBackend $authBackend
+ * @param string $realm
+ */
+ public function __construct(Sabre_DAV_Auth_IBackend $authBackend, $realm) {
+
+ $this->authBackend = $authBackend;
+ $this->realm = $realm;
+
+ }
+
+ /**
+ * Initializes the plugin. This function is automatically called by the server
+ *
+ * @param Sabre_DAV_Server $server
+ * @return void
+ */
+ public function initialize(Sabre_DAV_Server $server) {
+
+ $this->server = $server;
+ $this->server->subscribeEvent('beforeMethod',array($this,'beforeMethod'),10);
+
+ }
+
+ /**
+ * 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 'auth';
+
+ }
+
+ /**
+ * Returns the current users' principal uri.
+ *
+ * If nobody is logged in, this will return null.
+ *
+ * @return string|null
+ */
+ public function getCurrentUser() {
+
+ $userInfo = $this->authBackend->getCurrentUser();
+ if (!$userInfo) return null;
+
+ return $userInfo;
+
+ }
+
+ /**
+ * This method is called before any HTTP method and forces users to be authenticated
+ *
+ * @param string $method
+ * @param string $uri
+ * @throws Sabre_DAV_Exception_NotAuthenticated
+ * @return bool
+ */
+ public function beforeMethod($method, $uri) {
+
+ $this->authBackend->authenticate($this->server,$this->realm);
+
+ }
+
+}
diff --git a/3rdparty/Sabre/DAV/Browser/GuessContentType.php b/3rdparty/Sabre/DAV/Browser/GuessContentType.php
new file mode 100755
index 00000000000..b6c00d461cb
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Browser/GuessContentType.php
@@ -0,0 +1,97 @@
+<?php
+
+/**
+ * GuessContentType plugin
+ *
+ * A lot of the built-in File objects just return application/octet-stream
+ * as a content-type by default. This is a problem for some clients, because
+ * they expect a correct contenttype.
+ *
+ * There's really no accurate, fast and portable way to determine the contenttype
+ * so this extension does what the rest of the world does, and guesses it based
+ * on the file extension.
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_Browser_GuessContentType extends Sabre_DAV_ServerPlugin {
+
+ /**
+ * List of recognized file extensions
+ *
+ * Feel free to add more
+ *
+ * @var array
+ */
+ public $extensionMap = array(
+
+ // images
+ 'jpg' => 'image/jpeg',
+ 'gif' => 'image/gif',
+ 'png' => 'image/png',
+
+ // groupware
+ 'ics' => 'text/calendar',
+ 'vcf' => 'text/x-vcard',
+
+ // text
+ 'txt' => 'text/plain',
+
+ );
+
+ /**
+ * Initializes the plugin
+ *
+ * @param Sabre_DAV_Server $server
+ * @return void
+ */
+ public function initialize(Sabre_DAV_Server $server) {
+
+ // Using a relatively low priority (200) to allow other extensions
+ // to set the content-type first.
+ $server->subscribeEvent('afterGetProperties',array($this,'afterGetProperties'),200);
+
+ }
+
+ /**
+ * Handler for teh afterGetProperties event
+ *
+ * @param string $path
+ * @param array $properties
+ * @return void
+ */
+ public function afterGetProperties($path, &$properties) {
+
+ if (array_key_exists('{DAV:}getcontenttype', $properties[404])) {
+
+ list(, $fileName) = Sabre_DAV_URLUtil::splitPath($path);
+ $contentType = $this->getContentType($fileName);
+
+ if ($contentType) {
+ $properties[200]['{DAV:}getcontenttype'] = $contentType;
+ unset($properties[404]['{DAV:}getcontenttype']);
+ }
+
+ }
+
+ }
+
+ /**
+ * Simple method to return the contenttype
+ *
+ * @param string $fileName
+ * @return string
+ */
+ protected function getContentType($fileName) {
+
+ // Just grabbing the extension
+ $extension = strtolower(substr($fileName,strrpos($fileName,'.')+1));
+ if (isset($this->extensionMap[$extension]))
+ return $this->extensionMap[$extension];
+
+ }
+
+}
diff --git a/3rdparty/Sabre/DAV/Browser/MapGetToPropFind.php b/3rdparty/Sabre/DAV/Browser/MapGetToPropFind.php
new file mode 100755
index 00000000000..15884887641
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Browser/MapGetToPropFind.php
@@ -0,0 +1,55 @@
+<?php
+
+/**
+ * This is a simple plugin that will map any GET request for non-files to
+ * PROPFIND allprops-requests.
+ *
+ * This should allow easy debugging of PROPFIND
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_Browser_MapGetToPropFind extends Sabre_DAV_ServerPlugin {
+
+ /**
+ * reference to server class
+ *
+ * @var Sabre_DAV_Server
+ */
+ protected $server;
+
+ /**
+ * Initializes the plugin and subscribes to events
+ *
+ * @param Sabre_DAV_Server $server
+ * @return void
+ */
+ public function initialize(Sabre_DAV_Server $server) {
+
+ $this->server = $server;
+ $this->server->subscribeEvent('beforeMethod',array($this,'httpGetInterceptor'));
+ }
+
+ /**
+ * This method intercepts GET requests to non-files, and changes it into an HTTP PROPFIND request
+ *
+ * @param string $method
+ * @param string $uri
+ * @return bool
+ */
+ public function httpGetInterceptor($method, $uri) {
+
+ if ($method!='GET') return true;
+
+ $node = $this->server->tree->getNodeForPath($uri);
+ if ($node instanceof Sabre_DAV_IFile) return;
+
+ $this->server->invokeMethod('PROPFIND',$uri);
+ return false;
+
+ }
+
+}
diff --git a/3rdparty/Sabre/DAV/Browser/Plugin.php b/3rdparty/Sabre/DAV/Browser/Plugin.php
new file mode 100755
index 00000000000..09bbdd2ae02
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Browser/Plugin.php
@@ -0,0 +1,489 @@
+<?php
+
+/**
+ * Browser Plugin
+ *
+ * This plugin provides a html representation, so that a WebDAV server may be accessed
+ * using a browser.
+ *
+ * The class intercepts GET requests to collection resources and generates a simple
+ * html index.
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_Browser_Plugin extends Sabre_DAV_ServerPlugin {
+
+ /**
+ * List of default icons for nodes.
+ *
+ * This is an array with class / interface names as keys, and asset names
+ * as values.
+ *
+ * The evaluation order is reversed. The last item in the list gets
+ * precendence.
+ *
+ * @var array
+ */
+ public $iconMap = array(
+ 'Sabre_DAV_IFile' => 'icons/file',
+ 'Sabre_DAV_ICollection' => 'icons/collection',
+ 'Sabre_DAVACL_IPrincipal' => 'icons/principal',
+ 'Sabre_CalDAV_ICalendar' => 'icons/calendar',
+ 'Sabre_CardDAV_IAddressBook' => 'icons/addressbook',
+ 'Sabre_CardDAV_ICard' => 'icons/card',
+ );
+
+ /**
+ * The file extension used for all icons
+ *
+ * @var string
+ */
+ public $iconExtension = '.png';
+
+ /**
+ * reference to server class
+ *
+ * @var Sabre_DAV_Server
+ */
+ protected $server;
+
+ /**
+ * enablePost turns on the 'actions' panel, which allows people to create
+ * folders and upload files straight from a browser.
+ *
+ * @var bool
+ */
+ protected $enablePost = true;
+
+ /**
+ * By default the browser plugin will generate a favicon and other images.
+ * To turn this off, set this property to false.
+ *
+ * @var bool
+ */
+ protected $enableAssets = true;
+
+ /**
+ * Creates the object.
+ *
+ * By default it will allow file creation and uploads.
+ * Specify the first argument as false to disable this
+ *
+ * @param bool $enablePost
+ * @param bool $enableAssets
+ */
+ public function __construct($enablePost=true, $enableAssets = true) {
+
+ $this->enablePost = $enablePost;
+ $this->enableAssets = $enableAssets;
+
+ }
+
+ /**
+ * Initializes the plugin and subscribes to events
+ *
+ * @param Sabre_DAV_Server $server
+ * @return void
+ */
+ public function initialize(Sabre_DAV_Server $server) {
+
+ $this->server = $server;
+ $this->server->subscribeEvent('beforeMethod',array($this,'httpGetInterceptor'));
+ $this->server->subscribeEvent('onHTMLActionsPanel', array($this, 'htmlActionsPanel'),200);
+ if ($this->enablePost) $this->server->subscribeEvent('unknownMethod',array($this,'httpPOSTHandler'));
+ }
+
+ /**
+ * This method intercepts GET requests to collections and returns the html
+ *
+ * @param string $method
+ * @param string $uri
+ * @return bool
+ */
+ public function httpGetInterceptor($method, $uri) {
+
+ if ($method !== 'GET') return true;
+
+ // We're not using straight-up $_GET, because we want everything to be
+ // unit testable.
+ $getVars = array();
+ parse_str($this->server->httpRequest->getQueryString(), $getVars);
+
+ if (isset($getVars['sabreAction']) && $getVars['sabreAction'] === 'asset' && isset($getVars['assetName'])) {
+ $this->serveAsset($getVars['assetName']);
+ return false;
+ }
+
+ try {
+ $node = $this->server->tree->getNodeForPath($uri);
+ } catch (Sabre_DAV_Exception_NotFound $e) {
+ // We're simply stopping when the file isn't found to not interfere
+ // with other plugins.
+ return;
+ }
+ if ($node instanceof Sabre_DAV_IFile)
+ return;
+
+ $this->server->httpResponse->sendStatus(200);
+ $this->server->httpResponse->setHeader('Content-Type','text/html; charset=utf-8');
+
+ $this->server->httpResponse->sendBody(
+ $this->generateDirectoryIndex($uri)
+ );
+
+ return false;
+
+ }
+
+ /**
+ * Handles POST requests for tree operations.
+ *
+ * @param string $method
+ * @param string $uri
+ * @return bool
+ */
+ public function httpPOSTHandler($method, $uri) {
+
+ if ($method!='POST') return;
+ $contentType = $this->server->httpRequest->getHeader('Content-Type');
+ list($contentType) = explode(';', $contentType);
+ if ($contentType !== 'application/x-www-form-urlencoded' &&
+ $contentType !== 'multipart/form-data') {
+ return;
+ }
+ $postVars = $this->server->httpRequest->getPostVars();
+
+ if (!isset($postVars['sabreAction']))
+ return;
+
+ if ($this->server->broadcastEvent('onBrowserPostAction', array($uri, $postVars['sabreAction'], $postVars))) {
+
+ switch($postVars['sabreAction']) {
+
+ case 'mkcol' :
+ if (isset($postVars['name']) && trim($postVars['name'])) {
+ // Using basename() because we won't allow slashes
+ list(, $folderName) = Sabre_DAV_URLUtil::splitPath(trim($postVars['name']));
+ $this->server->createDirectory($uri . '/' . $folderName);
+ }
+ break;
+ case 'put' :
+ if ($_FILES) $file = current($_FILES);
+ else break;
+
+ list(, $newName) = Sabre_DAV_URLUtil::splitPath(trim($file['name']));
+ if (isset($postVars['name']) && trim($postVars['name']))
+ $newName = trim($postVars['name']);
+
+ // Making sure we only have a 'basename' component
+ list(, $newName) = Sabre_DAV_URLUtil::splitPath($newName);
+
+ if (is_uploaded_file($file['tmp_name'])) {
+ $this->server->createFile($uri . '/' . $newName, fopen($file['tmp_name'],'r'));
+ }
+ break;
+
+ }
+
+ }
+ $this->server->httpResponse->setHeader('Location',$this->server->httpRequest->getUri());
+ $this->server->httpResponse->sendStatus(302);
+ return false;
+
+ }
+
+ /**
+ * Escapes a string for html.
+ *
+ * @param string $value
+ * @return string
+ */
+ public function escapeHTML($value) {
+
+ return htmlspecialchars($value,ENT_QUOTES,'UTF-8');
+
+ }
+
+ /**
+ * Generates the html directory index for a given url
+ *
+ * @param string $path
+ * @return string
+ */
+ public function generateDirectoryIndex($path) {
+
+ $version = '';
+ if (Sabre_DAV_Server::$exposeVersion) {
+ $version = Sabre_DAV_Version::VERSION ."-". Sabre_DAV_Version::STABILITY;
+ }
+
+ $html = "<html>
+<head>
+ <title>Index for " . $this->escapeHTML($path) . "/ - SabreDAV " . $version . "</title>
+ <style type=\"text/css\">
+ body { Font-family: arial}
+ h1 { font-size: 150% }
+ </style>
+ ";
+
+ if ($this->enableAssets) {
+ $html.='<link rel="shortcut icon" href="'.$this->getAssetUrl('favicon.ico').'" type="image/vnd.microsoft.icon" />';
+ }
+
+ $html .= "</head>
+<body>
+ <h1>Index for " . $this->escapeHTML($path) . "/</h1>
+ <table>
+ <tr><th width=\"24\"></th><th>Name</th><th>Type</th><th>Size</th><th>Last modified</th></tr>
+ <tr><td colspan=\"5\"><hr /></td></tr>";
+
+ $files = $this->server->getPropertiesForPath($path,array(
+ '{DAV:}displayname',
+ '{DAV:}resourcetype',
+ '{DAV:}getcontenttype',
+ '{DAV:}getcontentlength',
+ '{DAV:}getlastmodified',
+ ),1);
+
+ $parent = $this->server->tree->getNodeForPath($path);
+
+
+ if ($path) {
+
+ list($parentUri) = Sabre_DAV_URLUtil::splitPath($path);
+ $fullPath = Sabre_DAV_URLUtil::encodePath($this->server->getBaseUri() . $parentUri);
+
+ $icon = $this->enableAssets?'<a href="' . $fullPath . '"><img src="' . $this->getAssetUrl('icons/parent' . $this->iconExtension) . '" width="24" alt="Parent" /></a>':'';
+ $html.= "<tr>
+ <td>$icon</td>
+ <td><a href=\"{$fullPath}\">..</a></td>
+ <td>[parent]</td>
+ <td></td>
+ <td></td>
+ </tr>";
+
+ }
+
+ foreach($files as $file) {
+
+ // This is the current directory, we can skip it
+ if (rtrim($file['href'],'/')==$path) continue;
+
+ list(, $name) = Sabre_DAV_URLUtil::splitPath($file['href']);
+
+ $type = null;
+
+
+ if (isset($file[200]['{DAV:}resourcetype'])) {
+ $type = $file[200]['{DAV:}resourcetype']->getValue();
+
+ // resourcetype can have multiple values
+ if (!is_array($type)) $type = array($type);
+
+ foreach($type as $k=>$v) {
+
+ // Some name mapping is preferred
+ switch($v) {
+ case '{DAV:}collection' :
+ $type[$k] = 'Collection';
+ break;
+ case '{DAV:}principal' :
+ $type[$k] = 'Principal';
+ break;
+ case '{urn:ietf:params:xml:ns:carddav}addressbook' :
+ $type[$k] = 'Addressbook';
+ break;
+ case '{urn:ietf:params:xml:ns:caldav}calendar' :
+ $type[$k] = 'Calendar';
+ break;
+ case '{urn:ietf:params:xml:ns:caldav}schedule-inbox' :
+ $type[$k] = 'Schedule Inbox';
+ break;
+ case '{urn:ietf:params:xml:ns:caldav}schedule-outbox' :
+ $type[$k] = 'Schedule Outbox';
+ break;
+ case '{http://calendarserver.org/ns/}calendar-proxy-read' :
+ $type[$k] = 'Proxy-Read';
+ break;
+ case '{http://calendarserver.org/ns/}calendar-proxy-write' :
+ $type[$k] = 'Proxy-Write';
+ break;
+ }
+
+ }
+ $type = implode(', ', $type);
+ }
+
+ // If no resourcetype was found, we attempt to use
+ // the contenttype property
+ if (!$type && isset($file[200]['{DAV:}getcontenttype'])) {
+ $type = $file[200]['{DAV:}getcontenttype'];
+ }
+ if (!$type) $type = 'Unknown';
+
+ $size = isset($file[200]['{DAV:}getcontentlength'])?(int)$file[200]['{DAV:}getcontentlength']:'';
+ $lastmodified = isset($file[200]['{DAV:}getlastmodified'])?$file[200]['{DAV:}getlastmodified']->getTime()->format(DateTime::ATOM):'';
+
+ $fullPath = Sabre_DAV_URLUtil::encodePath('/' . trim($this->server->getBaseUri() . ($path?$path . '/':'') . $name,'/'));
+
+ $displayName = isset($file[200]['{DAV:}displayname'])?$file[200]['{DAV:}displayname']:$name;
+
+ $displayName = $this->escapeHTML($displayName);
+ $type = $this->escapeHTML($type);
+
+ $icon = '';
+
+ if ($this->enableAssets) {
+ $node = $parent->getChild($name);
+ foreach(array_reverse($this->iconMap) as $class=>$iconName) {
+
+ if ($node instanceof $class) {
+ $icon = '<a href="' . $fullPath . '"><img src="' . $this->getAssetUrl($iconName . $this->iconExtension) . '" alt="" width="24" /></a>';
+ break;
+ }
+
+
+ }
+
+ }
+
+ $html.= "<tr>
+ <td>$icon</td>
+ <td><a href=\"{$fullPath}\">{$displayName}</a></td>
+ <td>{$type}</td>
+ <td>{$size}</td>
+ <td>{$lastmodified}</td>
+ </tr>";
+
+ }
+
+ $html.= "<tr><td colspan=\"5\"><hr /></td></tr>";
+
+ $output = '';
+
+ if ($this->enablePost) {
+ $this->server->broadcastEvent('onHTMLActionsPanel',array($parent, &$output));
+ }
+
+ $html.=$output;
+
+ $html.= "</table>
+ <address>Generated by SabreDAV " . $version . " (c)2007-2012 <a href=\"http://code.google.com/p/sabredav/\">http://code.google.com/p/sabredav/</a></address>
+ </body>
+ </html>";
+
+ return $html;
+
+ }
+
+ /**
+ * This method is used to generate the 'actions panel' output for
+ * collections.
+ *
+ * This specifically generates the interfaces for creating new files, and
+ * creating new directories.
+ *
+ * @param Sabre_DAV_INode $node
+ * @param mixed $output
+ * @return void
+ */
+ public function htmlActionsPanel(Sabre_DAV_INode $node, &$output) {
+
+ if (!$node instanceof Sabre_DAV_ICollection)
+ return;
+
+ // We also know fairly certain that if an object is a non-extended
+ // SimpleCollection, we won't need to show the panel either.
+ if (get_class($node)==='Sabre_DAV_SimpleCollection')
+ return;
+
+ $output.= '<tr><td colspan="2"><form method="post" action="">
+ <h3>Create new folder</h3>
+ <input type="hidden" name="sabreAction" value="mkcol" />
+ Name: <input type="text" name="name" /><br />
+ <input type="submit" value="create" />
+ </form>
+ <form method="post" action="" enctype="multipart/form-data">
+ <h3>Upload file</h3>
+ <input type="hidden" name="sabreAction" value="put" />
+ Name (optional): <input type="text" name="name" /><br />
+ File: <input type="file" name="file" /><br />
+ <input type="submit" value="upload" />
+ </form>
+ </td></tr>';
+
+ }
+
+ /**
+ * This method takes a path/name of an asset and turns it into url
+ * suiteable for http access.
+ *
+ * @param string $assetName
+ * @return string
+ */
+ protected function getAssetUrl($assetName) {
+
+ return $this->server->getBaseUri() . '?sabreAction=asset&assetName=' . urlencode($assetName);
+
+ }
+
+ /**
+ * This method returns a local pathname to an asset.
+ *
+ * @param string $assetName
+ * @return string
+ */
+ protected function getLocalAssetPath($assetName) {
+
+ // Making sure people aren't trying to escape from the base path.
+ $assetSplit = explode('/', $assetName);
+ if (in_array('..',$assetSplit)) {
+ throw new Sabre_DAV_Exception('Incorrect asset path');
+ }
+ $path = __DIR__ . '/assets/' . $assetName;
+ return $path;
+
+ }
+
+ /**
+ * This method reads an asset from disk and generates a full http response.
+ *
+ * @param string $assetName
+ * @return void
+ */
+ protected function serveAsset($assetName) {
+
+ $assetPath = $this->getLocalAssetPath($assetName);
+ if (!file_exists($assetPath)) {
+ throw new Sabre_DAV_Exception_NotFound('Could not find an asset with this name');
+ }
+ // Rudimentary mime type detection
+ switch(strtolower(substr($assetPath,strpos($assetPath,'.')+1))) {
+
+ case 'ico' :
+ $mime = 'image/vnd.microsoft.icon';
+ break;
+
+ case 'png' :
+ $mime = 'image/png';
+ break;
+
+ default:
+ $mime = 'application/octet-stream';
+ break;
+
+ }
+
+ $this->server->httpResponse->setHeader('Content-Type', $mime);
+ $this->server->httpResponse->setHeader('Content-Length', filesize($assetPath));
+ $this->server->httpResponse->setHeader('Cache-Control', 'public, max-age=1209600');
+ $this->server->httpResponse->sendStatus(200);
+ $this->server->httpResponse->sendBody(fopen($assetPath,'r'));
+
+ }
+
+}
diff --git a/3rdparty/Sabre/DAV/Browser/assets/favicon.ico b/3rdparty/Sabre/DAV/Browser/assets/favicon.ico
new file mode 100755
index 00000000000..2b2c10a22cc
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Browser/assets/favicon.ico
Binary files differ
diff --git a/3rdparty/Sabre/DAV/Browser/assets/icons/addressbook.png b/3rdparty/Sabre/DAV/Browser/assets/icons/addressbook.png
new file mode 100755
index 00000000000..c9acc84172d
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Browser/assets/icons/addressbook.png
Binary files differ
diff --git a/3rdparty/Sabre/DAV/Browser/assets/icons/calendar.png b/3rdparty/Sabre/DAV/Browser/assets/icons/calendar.png
new file mode 100755
index 00000000000..3ecd6a800a0
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Browser/assets/icons/calendar.png
Binary files differ
diff --git a/3rdparty/Sabre/DAV/Browser/assets/icons/card.png b/3rdparty/Sabre/DAV/Browser/assets/icons/card.png
new file mode 100755
index 00000000000..2ce954866d8
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Browser/assets/icons/card.png
Binary files differ
diff --git a/3rdparty/Sabre/DAV/Browser/assets/icons/collection.png b/3rdparty/Sabre/DAV/Browser/assets/icons/collection.png
new file mode 100755
index 00000000000..156fa64fd50
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Browser/assets/icons/collection.png
Binary files differ
diff --git a/3rdparty/Sabre/DAV/Browser/assets/icons/file.png b/3rdparty/Sabre/DAV/Browser/assets/icons/file.png
new file mode 100755
index 00000000000..3b98551cec3
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Browser/assets/icons/file.png
Binary files differ
diff --git a/3rdparty/Sabre/DAV/Browser/assets/icons/parent.png b/3rdparty/Sabre/DAV/Browser/assets/icons/parent.png
new file mode 100755
index 00000000000..156fa64fd50
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Browser/assets/icons/parent.png
Binary files differ
diff --git a/3rdparty/Sabre/DAV/Browser/assets/icons/principal.png b/3rdparty/Sabre/DAV/Browser/assets/icons/principal.png
new file mode 100755
index 00000000000..f8988f828e6
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Browser/assets/icons/principal.png
Binary files differ
diff --git a/3rdparty/Sabre/DAV/Client.php b/3rdparty/Sabre/DAV/Client.php
new file mode 100755
index 00000000000..9a428765e90
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Client.php
@@ -0,0 +1,492 @@
+<?php
+
+/**
+ * SabreDAV DAV client
+ *
+ * This client wraps around Curl to provide a convenient API to a WebDAV
+ * server.
+ *
+ * NOTE: This class is experimental, it's api will likely change in the future.
+ *
+ * @package Sabre
+ * @subpackage DAVClient
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_Client {
+
+ public $propertyMap = array();
+
+ protected $baseUri;
+ protected $userName;
+ protected $password;
+ protected $proxy;
+
+ /**
+ * Basic authentication
+ */
+ const AUTH_BASIC = 1;
+
+ /**
+ * Digest authentication
+ */
+ const AUTH_DIGEST = 2;
+
+ /**
+ * The authentication type we're using.
+ *
+ * This is a bitmask of AUTH_BASIC and AUTH_DIGEST.
+ *
+ * If DIGEST is used, the client makes 1 extra request per request, to get
+ * the authentication tokens.
+ *
+ * @var int
+ */
+ protected $authType;
+
+ /**
+ * Constructor
+ *
+ * Settings are provided through the 'settings' argument. The following
+ * settings are supported:
+ *
+ * * baseUri
+ * * userName (optional)
+ * * password (optional)
+ * * proxy (optional)
+ *
+ * @param array $settings
+ */
+ public function __construct(array $settings) {
+
+ if (!isset($settings['baseUri'])) {
+ throw new InvalidArgumentException('A baseUri must be provided');
+ }
+
+ $validSettings = array(
+ 'baseUri',
+ 'userName',
+ 'password',
+ 'proxy',
+ );
+
+ foreach($validSettings as $validSetting) {
+ if (isset($settings[$validSetting])) {
+ $this->$validSetting = $settings[$validSetting];
+ }
+ }
+
+ if (isset($settings['authType'])) {
+ $this->authType = $settings['authType'];
+ } else {
+ $this->authType = self::AUTH_BASIC | self::AUTH_DIGEST;
+ }
+
+ $this->propertyMap['{DAV:}resourcetype'] = 'Sabre_DAV_Property_ResourceType';
+
+ }
+
+ /**
+ * Does a PROPFIND request
+ *
+ * The list of requested properties must be specified as an array, in clark
+ * notation.
+ *
+ * The returned array will contain a list of filenames as keys, and
+ * properties as values.
+ *
+ * The properties array will contain the list of properties. Only properties
+ * that are actually returned from the server (without error) will be
+ * returned, anything else is discarded.
+ *
+ * Depth should be either 0 or 1. A depth of 1 will cause a request to be
+ * made to the server to also return all child resources.
+ *
+ * @param string $url
+ * @param array $properties
+ * @param int $depth
+ * @return array
+ */
+ public function propFind($url, array $properties, $depth = 0) {
+
+ $body = '<?xml version="1.0"?>' . "\n";
+ $body.= '<d:propfind xmlns:d="DAV:">' . "\n";
+ $body.= ' <d:prop>' . "\n";
+
+ foreach($properties as $property) {
+
+ list(
+ $namespace,
+ $elementName
+ ) = Sabre_DAV_XMLUtil::parseClarkNotation($property);
+
+ if ($namespace === 'DAV:') {
+ $body.=' <d:' . $elementName . ' />' . "\n";
+ } else {
+ $body.=" <x:" . $elementName . " xmlns:x=\"" . $namespace . "\"/>\n";
+ }
+
+ }
+
+ $body.= ' </d:prop>' . "\n";
+ $body.= '</d:propfind>';
+
+ $response = $this->request('PROPFIND', $url, $body, array(
+ 'Depth' => $depth,
+ 'Content-Type' => 'application/xml'
+ ));
+
+ $result = $this->parseMultiStatus($response['body']);
+
+ // If depth was 0, we only return the top item
+ if ($depth===0) {
+ reset($result);
+ $result = current($result);
+ return $result[200];
+ }
+
+ $newResult = array();
+ foreach($result as $href => $statusList) {
+
+ $newResult[$href] = $statusList[200];
+
+ }
+
+ return $newResult;
+
+ }
+
+ /**
+ * Updates a list of properties on the server
+ *
+ * The list of properties must have clark-notation properties for the keys,
+ * and the actual (string) value for the value. If the value is null, an
+ * attempt is made to delete the property.
+ *
+ * @todo Must be building the request using the DOM, and does not yet
+ * support complex properties.
+ * @param string $url
+ * @param array $properties
+ * @return void
+ */
+ public function propPatch($url, array $properties) {
+
+ $body = '<?xml version="1.0"?>' . "\n";
+ $body.= '<d:propertyupdate xmlns:d="DAV:">' . "\n";
+
+ foreach($properties as $propName => $propValue) {
+
+ list(
+ $namespace,
+ $elementName
+ ) = Sabre_DAV_XMLUtil::parseClarkNotation($propName);
+
+ if ($propValue === null) {
+
+ $body.="<d:remove><d:prop>\n";
+
+ if ($namespace === 'DAV:') {
+ $body.=' <d:' . $elementName . ' />' . "\n";
+ } else {
+ $body.=" <x:" . $elementName . " xmlns:x=\"" . $namespace . "\"/>\n";
+ }
+
+ $body.="</d:prop></d:remove>\n";
+
+ } else {
+
+ $body.="<d:set><d:prop>\n";
+ if ($namespace === 'DAV:') {
+ $body.=' <d:' . $elementName . '>';
+ } else {
+ $body.=" <x:" . $elementName . " xmlns:x=\"" . $namespace . "\">";
+ }
+ // Shitty.. i know
+ $body.=htmlspecialchars($propValue, ENT_NOQUOTES, 'UTF-8');
+ if ($namespace === 'DAV:') {
+ $body.='</d:' . $elementName . '>' . "\n";
+ } else {
+ $body.="</x:" . $elementName . ">\n";
+ }
+ $body.="</d:prop></d:set>\n";
+
+ }
+
+ }
+
+ $body.= '</d:propertyupdate>';
+
+ $this->request('PROPPATCH', $url, $body, array(
+ 'Content-Type' => 'application/xml'
+ ));
+
+ }
+
+ /**
+ * Performs an HTTP options request
+ *
+ * This method returns all the features from the 'DAV:' header as an array.
+ * If there was no DAV header, or no contents this method will return an
+ * empty array.
+ *
+ * @return array
+ */
+ public function options() {
+
+ $result = $this->request('OPTIONS');
+ if (!isset($result['headers']['dav'])) {
+ return array();
+ }
+
+ $features = explode(',', $result['headers']['dav']);
+ foreach($features as &$v) {
+ $v = trim($v);
+ }
+ return $features;
+
+ }
+
+ /**
+ * Performs an actual HTTP request, and returns the result.
+ *
+ * If the specified url is relative, it will be expanded based on the base
+ * url.
+ *
+ * The returned array contains 3 keys:
+ * * body - the response body
+ * * httpCode - a HTTP code (200, 404, etc)
+ * * headers - a list of response http headers. The header names have
+ * been lowercased.
+ *
+ * @param string $method
+ * @param string $url
+ * @param string $body
+ * @param array $headers
+ * @return array
+ */
+ public function request($method, $url = '', $body = null, $headers = array()) {
+
+ $url = $this->getAbsoluteUrl($url);
+
+ $curlSettings = array(
+ CURLOPT_RETURNTRANSFER => true,
+ // Return headers as part of the response
+ CURLOPT_HEADER => true,
+ CURLOPT_POSTFIELDS => $body,
+ // Automatically follow redirects
+ CURLOPT_FOLLOWLOCATION => true,
+ CURLOPT_MAXREDIRS => 5,
+ );
+
+ switch ($method) {
+ case 'HEAD' :
+
+ // do not read body with HEAD requests (this is neccessary because cURL does not ignore the body with HEAD
+ // requests when the Content-Length header is given - which in turn is perfectly valid according to HTTP
+ // specs...) cURL does unfortunately return an error in this case ("transfer closed transfer closed with
+ // ... bytes remaining to read") this can be circumvented by explicitly telling cURL to ignore the
+ // response body
+ $curlSettings[CURLOPT_NOBODY] = true;
+ $curlSettings[CURLOPT_CUSTOMREQUEST] = 'HEAD';
+ break;
+
+ default:
+ $curlSettings[CURLOPT_CUSTOMREQUEST] = $method;
+ break;
+
+ }
+
+ // Adding HTTP headers
+ $nHeaders = array();
+ foreach($headers as $key=>$value) {
+
+ $nHeaders[] = $key . ': ' . $value;
+
+ }
+ $curlSettings[CURLOPT_HTTPHEADER] = $nHeaders;
+
+ if ($this->proxy) {
+ $curlSettings[CURLOPT_PROXY] = $this->proxy;
+ }
+
+ if ($this->userName && $this->authType) {
+ $curlType = 0;
+ if ($this->authType & self::AUTH_BASIC) {
+ $curlType |= CURLAUTH_BASIC;
+ }
+ if ($this->authType & self::AUTH_DIGEST) {
+ $curlType |= CURLAUTH_DIGEST;
+ }
+ $curlSettings[CURLOPT_HTTPAUTH] = $curlType;
+ $curlSettings[CURLOPT_USERPWD] = $this->userName . ':' . $this->password;
+ }
+
+ list(
+ $response,
+ $curlInfo,
+ $curlErrNo,
+ $curlError
+ ) = $this->curlRequest($url, $curlSettings);
+
+ $headerBlob = substr($response, 0, $curlInfo['header_size']);
+ $response = substr($response, $curlInfo['header_size']);
+
+ // In the case of 100 Continue, or redirects we'll have multiple lists
+ // of headers for each separate HTTP response. We can easily split this
+ // because they are separated by \r\n\r\n
+ $headerBlob = explode("\r\n\r\n", trim($headerBlob, "\r\n"));
+
+ // We only care about the last set of headers
+ $headerBlob = $headerBlob[count($headerBlob)-1];
+
+ // Splitting headers
+ $headerBlob = explode("\r\n", $headerBlob);
+
+ $headers = array();
+ foreach($headerBlob as $header) {
+ $parts = explode(':', $header, 2);
+ if (count($parts)==2) {
+ $headers[strtolower(trim($parts[0]))] = trim($parts[1]);
+ }
+ }
+
+ $response = array(
+ 'body' => $response,
+ 'statusCode' => $curlInfo['http_code'],
+ 'headers' => $headers
+ );
+
+ if ($curlErrNo) {
+ throw new Sabre_DAV_Exception('[CURL] Error while making request: ' . $curlError . ' (error code: ' . $curlErrNo . ')');
+ }
+
+ if ($response['statusCode']>=400) {
+ switch ($response['statusCode']) {
+ case 404:
+ throw new Sabre_DAV_Exception_NotFound('Resource ' . $url . ' not found.');
+ break;
+
+ default:
+ throw new Sabre_DAV_Exception('HTTP error response. (errorcode ' . $response['statusCode'] . ')');
+ }
+ }
+
+ return $response;
+
+ }
+
+ /**
+ * Wrapper for all curl functions.
+ *
+ * The only reason this was split out in a separate method, is so it
+ * becomes easier to unittest.
+ *
+ * @param string $url
+ * @param array $settings
+ * @return array
+ */
+ protected function curlRequest($url, $settings) {
+
+ $curl = curl_init($url);
+ curl_setopt_array($curl, $settings);
+
+ return array(
+ curl_exec($curl),
+ curl_getinfo($curl),
+ curl_errno($curl),
+ curl_error($curl)
+ );
+
+ }
+
+ /**
+ * Returns the full url based on the given url (which may be relative). All
+ * urls are expanded based on the base url as given by the server.
+ *
+ * @param string $url
+ * @return string
+ */
+ protected function getAbsoluteUrl($url) {
+
+ // If the url starts with http:// or https://, the url is already absolute.
+ if (preg_match('/^http(s?):\/\//', $url)) {
+ return $url;
+ }
+
+ // If the url starts with a slash, we must calculate the url based off
+ // the root of the base url.
+ if (strpos($url,'/') === 0) {
+ $parts = parse_url($this->baseUri);
+ return $parts['scheme'] . '://' . $parts['host'] . (isset($parts['port'])?':' . $parts['port']:'') . $url;
+ }
+
+ // Otherwise...
+ return $this->baseUri . $url;
+
+ }
+
+ /**
+ * Parses a WebDAV multistatus response body
+ *
+ * This method returns an array with the following structure
+ *
+ * array(
+ * 'url/to/resource' => array(
+ * '200' => array(
+ * '{DAV:}property1' => 'value1',
+ * '{DAV:}property2' => 'value2',
+ * ),
+ * '404' => array(
+ * '{DAV:}property1' => null,
+ * '{DAV:}property2' => null,
+ * ),
+ * )
+ * 'url/to/resource2' => array(
+ * .. etc ..
+ * )
+ * )
+ *
+ *
+ * @param string $body xml body
+ * @return array
+ */
+ public function parseMultiStatus($body) {
+
+ $body = Sabre_DAV_XMLUtil::convertDAVNamespace($body);
+
+ $responseXML = simplexml_load_string($body, null, LIBXML_NOBLANKS | LIBXML_NOCDATA);
+ if ($responseXML===false) {
+ throw new InvalidArgumentException('The passed data is not valid XML');
+ }
+
+ $responseXML->registerXPathNamespace('d', 'urn:DAV');
+
+ $propResult = array();
+
+ foreach($responseXML->xpath('d:response') as $response) {
+ $response->registerXPathNamespace('d', 'urn:DAV');
+ $href = $response->xpath('d:href');
+ $href = (string)$href[0];
+
+ $properties = array();
+
+ foreach($response->xpath('d:propstat') as $propStat) {
+
+ $propStat->registerXPathNamespace('d', 'urn:DAV');
+ $status = $propStat->xpath('d:status');
+ list($httpVersion, $statusCode, $message) = explode(' ', (string)$status[0],3);
+
+ $properties[$statusCode] = Sabre_DAV_XMLUtil::parseProperties(dom_import_simplexml($propStat), $this->propertyMap);
+
+ }
+
+ $propResult[$href] = $properties;
+
+ }
+
+ return $propResult;
+
+ }
+
+}
diff --git a/3rdparty/Sabre/DAV/Collection.php b/3rdparty/Sabre/DAV/Collection.php
new file mode 100755
index 00000000000..776c22531b2
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Collection.php
@@ -0,0 +1,106 @@
+<?php
+
+/**
+ * Collection class
+ *
+ * This is a helper class, that should aid in getting collections classes setup.
+ * Most of its methods are implemented, and throw permission denied exceptions
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+abstract class Sabre_DAV_Collection extends Sabre_DAV_Node implements Sabre_DAV_ICollection {
+
+ /**
+ * Returns a child object, by its name.
+ *
+ * This method makes use of the getChildren method to grab all the child nodes, and compares the name.
+ * Generally its wise to override this, as this can usually be optimized
+ *
+ * @param string $name
+ * @throws Sabre_DAV_Exception_NotFound
+ * @return Sabre_DAV_INode
+ */
+ public function getChild($name) {
+
+ foreach($this->getChildren() as $child) {
+
+ if ($child->getName()==$name) return $child;
+
+ }
+ throw new Sabre_DAV_Exception_NotFound('File not found: ' . $name);
+
+ }
+
+ /**
+ * Checks is a child-node exists.
+ *
+ * It is generally a good idea to try and override this. Usually it can be optimized.
+ *
+ * @param string $name
+ * @return bool
+ */
+ public function childExists($name) {
+
+ try {
+
+ $this->getChild($name);
+ return true;
+
+ } catch(Sabre_DAV_Exception_NotFound $e) {
+
+ return false;
+
+ }
+
+ }
+
+ /**
+ * Creates a new file in the directory
+ *
+ * Data will either be supplied as a stream resource, or in certain cases
+ * as a string. Keep in mind that you may have to support either.
+ *
+ * After succesful creation of the file, you may choose to return the ETag
+ * of the new file here.
+ *
+ * The returned ETag must be surrounded by double-quotes (The quotes should
+ * be part of the actual string).
+ *
+ * If you cannot accurately determine the ETag, you should not return it.
+ * If you don't store the file exactly as-is (you're transforming it
+ * somehow) you should also not return an ETag.
+ *
+ * This means that if a subsequent GET to this new file does not exactly
+ * return the same contents of what was submitted here, you are strongly
+ * recommended to omit the ETag.
+ *
+ * @param string $name Name of the file
+ * @param resource|string $data Initial payload
+ * @return null|string
+ */
+ 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');
+
+ }
+
+
+}
+
diff --git a/3rdparty/Sabre/DAV/Directory.php b/3rdparty/Sabre/DAV/Directory.php
new file mode 100755
index 00000000000..6db8febc02e
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Directory.php
@@ -0,0 +1,17 @@
+<?php
+
+/**
+ * Directory class
+ *
+ * This class is now deprecated in favor of the Sabre_DAV_Collection class.
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @deprecated Use Sabre_DAV_Collection instead
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+abstract class Sabre_DAV_Directory extends Sabre_DAV_Collection {
+}
+
diff --git a/3rdparty/Sabre/DAV/Exception.php b/3rdparty/Sabre/DAV/Exception.php
new file mode 100755
index 00000000000..a2cd6cf5820
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Exception.php
@@ -0,0 +1,64 @@
+<?php
+
+/**
+ * SabreDAV base exception
+ *
+ * This is SabreDAV's base exception file, use this to implement your own exception.
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+
+/**
+ * Main Exception class.
+ *
+ * This class defines a getHTTPCode method, which should return the appropriate HTTP code for the Exception occurred.
+ * The default for this is 500.
+ *
+ * This class also allows you to generate custom xml data for your exceptions. This will be displayed
+ * in the 'error' element in the failing response.
+ */
+class Sabre_DAV_Exception extends Exception {
+
+ /**
+ * Returns the HTTP statuscode for this exception
+ *
+ * @return int
+ */
+ public function getHTTPCode() {
+
+ return 500;
+
+ }
+
+ /**
+ * This method allows the exception to include additional information into the WebDAV error response
+ *
+ * @param Sabre_DAV_Server $server
+ * @param DOMElement $errorNode
+ * @return void
+ */
+ public function serialize(Sabre_DAV_Server $server,DOMElement $errorNode) {
+
+
+ }
+
+ /**
+ * This method allows the exception to return any extra HTTP response headers.
+ *
+ * The headers must be returned as an array.
+ *
+ * @param Sabre_DAV_Server $server
+ * @return array
+ */
+ public function getHTTPHeaders(Sabre_DAV_Server $server) {
+
+ return array();
+
+ }
+
+}
+
diff --git a/3rdparty/Sabre/DAV/Exception/BadRequest.php b/3rdparty/Sabre/DAV/Exception/BadRequest.php
new file mode 100755
index 00000000000..b198648a754
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Exception/BadRequest.php
@@ -0,0 +1,28 @@
+<?php
+
+/**
+ * BadRequest
+ *
+ * The BadRequest is thrown when the user submitted an invalid HTTP request
+ * BadRequest
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_Exception_BadRequest extends Sabre_DAV_Exception {
+
+ /**
+ * Returns the HTTP statuscode for this exception
+ *
+ * @return int
+ */
+ public function getHTTPCode() {
+
+ return 400;
+
+ }
+
+}
diff --git a/3rdparty/Sabre/DAV/Exception/Conflict.php b/3rdparty/Sabre/DAV/Exception/Conflict.php
new file mode 100755
index 00000000000..6b0bd1fad73
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Exception/Conflict.php
@@ -0,0 +1,28 @@
+<?php
+
+/**
+ * Conflict
+ *
+ * A 409 Conflict is thrown when a user tried to make a directory over an existing
+ * file or in a parent directory that doesn't exist.
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_Exception_Conflict extends Sabre_DAV_Exception {
+
+ /**
+ * Returns the HTTP statuscode for this exception
+ *
+ * @return int
+ */
+ public function getHTTPCode() {
+
+ return 409;
+
+ }
+
+}
diff --git a/3rdparty/Sabre/DAV/Exception/ConflictingLock.php b/3rdparty/Sabre/DAV/Exception/ConflictingLock.php
new file mode 100755
index 00000000000..6121868e69e
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Exception/ConflictingLock.php
@@ -0,0 +1,35 @@
+<?php
+
+/**
+ * ConflictingLock
+ *
+ * Similar to Exception_Locked, this exception thrown when a LOCK request
+ * was made, on a resource which was already locked
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_Exception_ConflictingLock extends Sabre_DAV_Exception_Locked {
+
+ /**
+ * This method allows the exception to include additional information into the WebDAV error response
+ *
+ * @param Sabre_DAV_Server $server
+ * @param DOMElement $errorNode
+ * @return void
+ */
+ public function serialize(Sabre_DAV_Server $server,DOMElement $errorNode) {
+
+ if ($this->lock) {
+ $error = $errorNode->ownerDocument->createElementNS('DAV:','d:no-conflicting-lock');
+ $errorNode->appendChild($error);
+ if (!is_object($this->lock)) var_dump($this->lock);
+ $error->appendChild($errorNode->ownerDocument->createElementNS('DAV:','d:href',$this->lock->uri));
+ }
+
+ }
+
+}
diff --git a/3rdparty/Sabre/DAV/Exception/FileNotFound.php b/3rdparty/Sabre/DAV/Exception/FileNotFound.php
new file mode 100755
index 00000000000..d76e400c93b
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Exception/FileNotFound.php
@@ -0,0 +1,19 @@
+<?php
+
+/**
+ * FileNotFound
+ *
+ * Deprecated: Warning, this class is deprecated and will be removed in a
+ * future version of SabreDAV. Please use Sabre_DAV_Exception_NotFound instead.
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @deprecated Use Sabre_DAV_Exception_NotFound instead
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_Exception_FileNotFound extends Sabre_DAV_Exception_NotFound {
+
+}
+
diff --git a/3rdparty/Sabre/DAV/Exception/Forbidden.php b/3rdparty/Sabre/DAV/Exception/Forbidden.php
new file mode 100755
index 00000000000..20b1056e31b
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Exception/Forbidden.php
@@ -0,0 +1,27 @@
+<?php
+
+/**
+ * Forbidden
+ *
+ * This exception is thrown whenever a user tries to do an operation he's not allowed to
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_Exception_Forbidden extends Sabre_DAV_Exception {
+
+ /**
+ * Returns the HTTP statuscode for this exception
+ *
+ * @return int
+ */
+ public function getHTTPCode() {
+
+ return 403;
+
+ }
+
+}
diff --git a/3rdparty/Sabre/DAV/Exception/InsufficientStorage.php b/3rdparty/Sabre/DAV/Exception/InsufficientStorage.php
new file mode 100755
index 00000000000..1a15089b0a3
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Exception/InsufficientStorage.php
@@ -0,0 +1,27 @@
+<?php
+
+/**
+ * InsufficientStorage
+ *
+ * This Exception can be thrown, when for example a harddisk is full or a quota is exceeded
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_Exception_InsufficientStorage extends Sabre_DAV_Exception {
+
+ /**
+ * Returns the HTTP statuscode for this exception
+ *
+ * @return int
+ */
+ public function getHTTPCode() {
+
+ return 507;
+
+ }
+
+}
diff --git a/3rdparty/Sabre/DAV/Exception/InvalidResourceType.php b/3rdparty/Sabre/DAV/Exception/InvalidResourceType.php
new file mode 100755
index 00000000000..2230f1d0811
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Exception/InvalidResourceType.php
@@ -0,0 +1,33 @@
+<?php
+
+/**
+ * InvalidResourceType
+ *
+ * This exception is thrown when the user tried to create a new collection, with
+ * a special resourcetype value that was not recognized by the server.
+ *
+ * See RFC5689 section 3.3
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_Exception_InvalidResourceType extends Sabre_DAV_Exception_Forbidden {
+
+ /**
+ * This method allows the exception to include additional information into the WebDAV error response
+ *
+ * @param Sabre_DAV_Server $server
+ * @param DOMElement $errorNode
+ * @return void
+ */
+ public function serialize(Sabre_DAV_Server $server,DOMElement $errorNode) {
+
+ $error = $errorNode->ownerDocument->createElementNS('DAV:','d:valid-resourcetype');
+ $errorNode->appendChild($error);
+
+ }
+
+}
diff --git a/3rdparty/Sabre/DAV/Exception/LockTokenMatchesRequestUri.php b/3rdparty/Sabre/DAV/Exception/LockTokenMatchesRequestUri.php
new file mode 100755
index 00000000000..80ab7aff65a
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Exception/LockTokenMatchesRequestUri.php
@@ -0,0 +1,39 @@
+<?php
+
+/**
+ * LockTokenMatchesRequestUri
+ *
+ * This exception is thrown by UNLOCK if a supplied lock-token is invalid
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_Exception_LockTokenMatchesRequestUri extends Sabre_DAV_Exception_Conflict {
+
+ /**
+ * Creates the exception
+ */
+ public function __construct() {
+
+ $this->message = 'The locktoken supplied does not match any locks on this entity';
+
+ }
+
+ /**
+ * This method allows the exception to include additional information into the WebDAV error response
+ *
+ * @param Sabre_DAV_Server $server
+ * @param DOMElement $errorNode
+ * @return void
+ */
+ public function serialize(Sabre_DAV_Server $server,DOMElement $errorNode) {
+
+ $error = $errorNode->ownerDocument->createElementNS('DAV:','d:lock-token-matches-request-uri');
+ $errorNode->appendChild($error);
+
+ }
+
+}
diff --git a/3rdparty/Sabre/DAV/Exception/Locked.php b/3rdparty/Sabre/DAV/Exception/Locked.php
new file mode 100755
index 00000000000..976365ac1f8
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Exception/Locked.php
@@ -0,0 +1,67 @@
+<?php
+
+/**
+ * Locked
+ *
+ * The 423 is thrown when a client tried to access a resource that was locked, without supplying a valid lock token
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_Exception_Locked extends Sabre_DAV_Exception {
+
+ /**
+ * Lock information
+ *
+ * @var Sabre_DAV_Locks_LockInfo
+ */
+ protected $lock;
+
+ /**
+ * Creates the exception
+ *
+ * A LockInfo object should be passed if the user should be informed
+ * which lock actually has the file locked.
+ *
+ * @param Sabre_DAV_Locks_LockInfo $lock
+ */
+ public function __construct(Sabre_DAV_Locks_LockInfo $lock = null) {
+
+ $this->lock = $lock;
+
+ }
+
+ /**
+ * Returns the HTTP statuscode for this exception
+ *
+ * @return int
+ */
+ public function getHTTPCode() {
+
+ return 423;
+
+ }
+
+ /**
+ * This method allows the exception to include additional information into the WebDAV error response
+ *
+ * @param Sabre_DAV_Server $server
+ * @param DOMElement $errorNode
+ * @return void
+ */
+ public function serialize(Sabre_DAV_Server $server,DOMElement $errorNode) {
+
+ if ($this->lock) {
+ $error = $errorNode->ownerDocument->createElementNS('DAV:','d:lock-token-submitted');
+ $errorNode->appendChild($error);
+ if (!is_object($this->lock)) var_dump($this->lock);
+ $error->appendChild($errorNode->ownerDocument->createElementNS('DAV:','d:href',$this->lock->uri));
+ }
+
+ }
+
+}
+
diff --git a/3rdparty/Sabre/DAV/Exception/MethodNotAllowed.php b/3rdparty/Sabre/DAV/Exception/MethodNotAllowed.php
new file mode 100755
index 00000000000..31875751505
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Exception/MethodNotAllowed.php
@@ -0,0 +1,45 @@
+<?php
+
+/**
+ * MethodNotAllowed
+ *
+ * The 405 is thrown when a client tried to create a directory on an already existing directory
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_Exception_MethodNotAllowed extends Sabre_DAV_Exception {
+
+ /**
+ * Returns the HTTP statuscode for this exception
+ *
+ * @return int
+ */
+ public function getHTTPCode() {
+
+ return 405;
+
+ }
+
+ /**
+ * This method allows the exception to return any extra HTTP response headers.
+ *
+ * The headers must be returned as an array.
+ *
+ * @param Sabre_DAV_Server $server
+ * @return array
+ */
+ public function getHTTPHeaders(Sabre_DAV_Server $server) {
+
+ $methods = $server->getAllowedMethods($server->getRequestUri());
+
+ return array(
+ 'Allow' => strtoupper(implode(', ',$methods)),
+ );
+
+ }
+
+}
diff --git a/3rdparty/Sabre/DAV/Exception/NotAuthenticated.php b/3rdparty/Sabre/DAV/Exception/NotAuthenticated.php
new file mode 100755
index 00000000000..87ca624429f
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Exception/NotAuthenticated.php
@@ -0,0 +1,28 @@
+<?php
+
+/**
+ * NotAuthenticated
+ *
+ * This exception is thrown when the client did not provide valid
+ * authentication credentials.
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_Exception_NotAuthenticated extends Sabre_DAV_Exception {
+
+ /**
+ * Returns the HTTP statuscode for this exception
+ *
+ * @return int
+ */
+ public function getHTTPCode() {
+
+ return 401;
+
+ }
+
+}
diff --git a/3rdparty/Sabre/DAV/Exception/NotFound.php b/3rdparty/Sabre/DAV/Exception/NotFound.php
new file mode 100755
index 00000000000..2b9da560d23
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Exception/NotFound.php
@@ -0,0 +1,28 @@
+<?php
+
+/**
+ * NotFound
+ *
+ * This Exception is thrown when a Node couldn't be found. It returns HTTP error code 404
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_Exception_NotFound extends Sabre_DAV_Exception {
+
+ /**
+ * Returns the HTTP statuscode for this exception
+ *
+ * @return int
+ */
+ public function getHTTPCode() {
+
+ return 404;
+
+ }
+
+}
+
diff --git a/3rdparty/Sabre/DAV/Exception/NotImplemented.php b/3rdparty/Sabre/DAV/Exception/NotImplemented.php
new file mode 100755
index 00000000000..d017a19f559
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Exception/NotImplemented.php
@@ -0,0 +1,27 @@
+<?php
+
+/**
+ * NotImplemented
+ *
+ * This exception is thrown when the client tried to call an unsupported HTTP method or other feature
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_Exception_NotImplemented extends Sabre_DAV_Exception {
+
+ /**
+ * Returns the HTTP statuscode for this exception
+ *
+ * @return int
+ */
+ public function getHTTPCode() {
+
+ return 501;
+
+ }
+
+}
diff --git a/3rdparty/Sabre/DAV/Exception/PaymentRequired.php b/3rdparty/Sabre/DAV/Exception/PaymentRequired.php
new file mode 100755
index 00000000000..4982f45a4b5
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Exception/PaymentRequired.php
@@ -0,0 +1,28 @@
+<?php
+
+/**
+ * Payment Required
+ *
+ * The PaymentRequired exception may be thrown in a case where a user must pay
+ * to access a certain resource or operation.
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_Exception_PaymentRequired extends Sabre_DAV_Exception {
+
+ /**
+ * Returns the HTTP statuscode for this exception
+ *
+ * @return int
+ */
+ public function getHTTPCode() {
+
+ return 402;
+
+ }
+
+}
diff --git a/3rdparty/Sabre/DAV/Exception/PreconditionFailed.php b/3rdparty/Sabre/DAV/Exception/PreconditionFailed.php
new file mode 100755
index 00000000000..213e9c52317
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Exception/PreconditionFailed.php
@@ -0,0 +1,69 @@
+<?php
+
+/**
+ * PreconditionFailed
+ *
+ * This exception is normally thrown when a client submitted a conditional request,
+ * like for example an If, If-None-Match or If-Match header, which caused the HTTP
+ * request to not execute (the condition of the header failed)
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_Exception_PreconditionFailed extends Sabre_DAV_Exception {
+
+ /**
+ * When this exception is thrown, the header-name might be set.
+ *
+ * This allows the exception-catching code to determine which HTTP header
+ * caused the exception.
+ *
+ * @var string
+ */
+ public $header = null;
+
+ /**
+ * Create the exception
+ *
+ * @param string $message
+ * @param string $header
+ */
+ public function __construct($message, $header=null) {
+
+ parent::__construct($message);
+ $this->header = $header;
+
+ }
+
+ /**
+ * Returns the HTTP statuscode for this exception
+ *
+ * @return int
+ */
+ public function getHTTPCode() {
+
+ return 412;
+
+ }
+
+ /**
+ * This method allows the exception to include additional information into the WebDAV error response
+ *
+ * @param Sabre_DAV_Server $server
+ * @param DOMElement $errorNode
+ * @return void
+ */
+ public function serialize(Sabre_DAV_Server $server,DOMElement $errorNode) {
+
+ if ($this->header) {
+ $prop = $errorNode->ownerDocument->createElement('s:header');
+ $prop->nodeValue = $this->header;
+ $errorNode->appendChild($prop);
+ }
+
+ }
+
+}
diff --git a/3rdparty/Sabre/DAV/Exception/ReportNotImplemented.php b/3rdparty/Sabre/DAV/Exception/ReportNotImplemented.php
new file mode 100755
index 00000000000..e86800f3038
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Exception/ReportNotImplemented.php
@@ -0,0 +1,30 @@
+<?php
+
+/**
+ * ReportNotImplemented
+ *
+ * This exception is thrown when the client requested an unknown report through the REPORT method
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_Exception_ReportNotImplemented extends Sabre_DAV_Exception_NotImplemented {
+
+ /**
+ * This method allows the exception to include additional information into the WebDAV error response
+ *
+ * @param Sabre_DAV_Server $server
+ * @param DOMElement $errorNode
+ * @return void
+ */
+ public function serialize(Sabre_DAV_Server $server,DOMElement $errorNode) {
+
+ $error = $errorNode->ownerDocument->createElementNS('DAV:','d:supported-report');
+ $errorNode->appendChild($error);
+
+ }
+
+}
diff --git a/3rdparty/Sabre/DAV/Exception/RequestedRangeNotSatisfiable.php b/3rdparty/Sabre/DAV/Exception/RequestedRangeNotSatisfiable.php
new file mode 100755
index 00000000000..29ee3654a7e
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Exception/RequestedRangeNotSatisfiable.php
@@ -0,0 +1,29 @@
+<?php
+
+/**
+ * RequestedRangeNotSatisfiable
+ *
+ * This exception is normally thrown when the user
+ * request a range that is out of the entity bounds.
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_Exception_RequestedRangeNotSatisfiable extends Sabre_DAV_Exception {
+
+ /**
+ * returns the http statuscode for this exception
+ *
+ * @return int
+ */
+ public function getHTTPCode() {
+
+ return 416;
+
+ }
+
+}
+
diff --git a/3rdparty/Sabre/DAV/Exception/UnsupportedMediaType.php b/3rdparty/Sabre/DAV/Exception/UnsupportedMediaType.php
new file mode 100755
index 00000000000..9a4693b21a8
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Exception/UnsupportedMediaType.php
@@ -0,0 +1,28 @@
+<?php
+
+/**
+ * UnSupportedMediaType
+ *
+ * The 415 Unsupported Media Type status code is generally sent back when the client
+ * tried to call an HTTP method, with a body the server didn't understand
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_Exception_UnsupportedMediaType extends Sabre_DAV_Exception {
+
+ /**
+ * returns the http statuscode for this exception
+ *
+ * @return int
+ */
+ public function getHTTPCode() {
+
+ return 415;
+
+ }
+
+}
diff --git a/3rdparty/Sabre/DAV/FS/Directory.php b/3rdparty/Sabre/DAV/FS/Directory.php
new file mode 100755
index 00000000000..3af2d755583
--- /dev/null
+++ b/3rdparty/Sabre/DAV/FS/Directory.php
@@ -0,0 +1,136 @@
+<?php
+
+/**
+ * Directory class
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_FS_Directory extends Sabre_DAV_FS_Node implements Sabre_DAV_ICollection, Sabre_DAV_IQuota {
+
+ /**
+ * Creates a new file in the directory
+ *
+ * Data will either be supplied as a stream resource, or in certain cases
+ * as a string. Keep in mind that you may have to support either.
+ *
+ * After succesful creation of the file, you may choose to return the ETag
+ * of the new file here.
+ *
+ * The returned ETag must be surrounded by double-quotes (The quotes should
+ * be part of the actual string).
+ *
+ * If you cannot accurately determine the ETag, you should not return it.
+ * If you don't store the file exactly as-is (you're transforming it
+ * somehow) you should also not return an ETag.
+ *
+ * This means that if a subsequent GET to this new file does not exactly
+ * return the same contents of what was submitted here, you are strongly
+ * recommended to omit the ETag.
+ *
+ * @param string $name Name of the file
+ * @param resource|string $data Initial payload
+ * @return null|string
+ */
+ public function createFile($name, $data = null) {
+
+ $newPath = $this->path . '/' . $name;
+ file_put_contents($newPath,$data);
+
+ }
+
+ /**
+ * Creates a new subdirectory
+ *
+ * @param string $name
+ * @return void
+ */
+ public function createDirectory($name) {
+
+ $newPath = $this->path . '/' . $name;
+ mkdir($newPath);
+
+ }
+
+ /**
+ * Returns a specific child node, referenced by its name
+ *
+ * @param string $name
+ * @throws Sabre_DAV_Exception_NotFound
+ * @return Sabre_DAV_INode
+ */
+ public function getChild($name) {
+
+ $path = $this->path . '/' . $name;
+
+ if (!file_exists($path)) throw new Sabre_DAV_Exception_NotFound('File with name ' . $path . ' could not be located');
+
+ if (is_dir($path)) {
+
+ return new Sabre_DAV_FS_Directory($path);
+
+ } else {
+
+ return new Sabre_DAV_FS_File($path);
+
+ }
+
+ }
+
+ /**
+ * Returns an array with all the child nodes
+ *
+ * @return Sabre_DAV_INode[]
+ */
+ public function getChildren() {
+
+ $nodes = array();
+ foreach(scandir($this->path) as $node) if($node!='.' && $node!='..') $nodes[] = $this->getChild($node);
+ return $nodes;
+
+ }
+
+ /**
+ * Checks if a child exists.
+ *
+ * @param string $name
+ * @return bool
+ */
+ public function childExists($name) {
+
+ $path = $this->path . '/' . $name;
+ return file_exists($path);
+
+ }
+
+ /**
+ * Deletes all files in this directory, and then itself
+ *
+ * @return void
+ */
+ public function delete() {
+
+ foreach($this->getChildren() as $child) $child->delete();
+ rmdir($this->path);
+
+ }
+
+ /**
+ * Returns available diskspace information
+ *
+ * @return array
+ */
+ public function getQuotaInfo() {
+
+ return array(
+ disk_total_space($this->path)-disk_free_space($this->path),
+ disk_free_space($this->path)
+ );
+
+ }
+
+}
+
diff --git a/3rdparty/Sabre/DAV/FS/File.php b/3rdparty/Sabre/DAV/FS/File.php
new file mode 100755
index 00000000000..6a8039fe303
--- /dev/null
+++ b/3rdparty/Sabre/DAV/FS/File.php
@@ -0,0 +1,89 @@
+<?php
+
+/**
+ * File class
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_FS_File extends Sabre_DAV_FS_Node implements Sabre_DAV_IFile {
+
+ /**
+ * Updates the data
+ *
+ * @param resource $data
+ * @return void
+ */
+ public function put($data) {
+
+ file_put_contents($this->path,$data);
+
+ }
+
+ /**
+ * Returns the data
+ *
+ * @return string
+ */
+ public function get() {
+
+ return fopen($this->path,'r');
+
+ }
+
+ /**
+ * Delete the current file
+ *
+ * @return void
+ */
+ public function delete() {
+
+ unlink($this->path);
+
+ }
+
+ /**
+ * Returns the size of the node, in bytes
+ *
+ * @return int
+ */
+ public function getSize() {
+
+ return filesize($this->path);
+
+ }
+
+ /**
+ * Returns the ETag for a file
+ *
+ * An ETag is a unique identifier representing the current version of the file. If the file changes, the ETag MUST change.
+ * The ETag is an arbitrary string, but MUST be surrounded by double-quotes.
+ *
+ * Return null if the ETag can not effectively be determined
+ *
+ * @return mixed
+ */
+ public function getETag() {
+
+ return null;
+
+ }
+
+ /**
+ * Returns the mime-type for a file
+ *
+ * If null is returned, we'll assume application/octet-stream
+ *
+ * @return mixed
+ */
+ public function getContentType() {
+
+ return null;
+
+ }
+
+}
+
diff --git a/3rdparty/Sabre/DAV/FS/Node.php b/3rdparty/Sabre/DAV/FS/Node.php
new file mode 100755
index 00000000000..1283e9d0fdc
--- /dev/null
+++ b/3rdparty/Sabre/DAV/FS/Node.php
@@ -0,0 +1,80 @@
+<?php
+
+/**
+ * Base node-class
+ *
+ * The node class implements the method used by both the File and the Directory classes
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+abstract class Sabre_DAV_FS_Node implements Sabre_DAV_INode {
+
+ /**
+ * The path to the current node
+ *
+ * @var string
+ */
+ protected $path;
+
+ /**
+ * Sets up the node, expects a full path name
+ *
+ * @param string $path
+ */
+ public function __construct($path) {
+
+ $this->path = $path;
+
+ }
+
+
+
+ /**
+ * Returns the name of the node
+ *
+ * @return string
+ */
+ public function getName() {
+
+ list(, $name) = Sabre_DAV_URLUtil::splitPath($this->path);
+ return $name;
+
+ }
+
+ /**
+ * Renames the node
+ *
+ * @param string $name The new name
+ * @return void
+ */
+ public function setName($name) {
+
+ list($parentPath, ) = Sabre_DAV_URLUtil::splitPath($this->path);
+ list(, $newName) = Sabre_DAV_URLUtil::splitPath($name);
+
+ $newPath = $parentPath . '/' . $newName;
+ rename($this->path,$newPath);
+
+ $this->path = $newPath;
+
+ }
+
+
+
+ /**
+ * Returns the last modification time, as a unix timestamp
+ *
+ * @return int
+ */
+ public function getLastModified() {
+
+ return filemtime($this->path);
+
+ }
+
+}
+
diff --git a/3rdparty/Sabre/DAV/FSExt/Directory.php b/3rdparty/Sabre/DAV/FSExt/Directory.php
new file mode 100755
index 00000000000..540057183b3
--- /dev/null
+++ b/3rdparty/Sabre/DAV/FSExt/Directory.php
@@ -0,0 +1,154 @@
+<?php
+
+/**
+ * Directory class
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_FSExt_Directory extends Sabre_DAV_FSExt_Node implements Sabre_DAV_ICollection, Sabre_DAV_IQuota {
+
+ /**
+ * Creates a new file in the directory
+ *
+ * Data will either be supplied as a stream resource, or in certain cases
+ * as a string. Keep in mind that you may have to support either.
+ *
+ * After succesful creation of the file, you may choose to return the ETag
+ * of the new file here.
+ *
+ * The returned ETag must be surrounded by double-quotes (The quotes should
+ * be part of the actual string).
+ *
+ * If you cannot accurately determine the ETag, you should not return it.
+ * If you don't store the file exactly as-is (you're transforming it
+ * somehow) you should also not return an ETag.
+ *
+ * This means that if a subsequent GET to this new file does not exactly
+ * return the same contents of what was submitted here, you are strongly
+ * recommended to omit the ETag.
+ *
+ * @param string $name Name of the file
+ * @param resource|string $data Initial payload
+ * @return null|string
+ */
+ public function createFile($name, $data = null) {
+
+ // We're not allowing dots
+ if ($name=='.' || $name=='..') throw new Sabre_DAV_Exception_Forbidden('Permission denied to . and ..');
+ $newPath = $this->path . '/' . $name;
+ file_put_contents($newPath,$data);
+
+ return '"' . md5_file($newPath) . '"';
+
+ }
+
+ /**
+ * Creates a new subdirectory
+ *
+ * @param string $name
+ * @return void
+ */
+ public function createDirectory($name) {
+
+ // We're not allowing dots
+ if ($name=='.' || $name=='..') throw new Sabre_DAV_Exception_Forbidden('Permission denied to . and ..');
+ $newPath = $this->path . '/' . $name;
+ mkdir($newPath);
+
+ }
+
+ /**
+ * Returns a specific child node, referenced by its name
+ *
+ * @param string $name
+ * @throws Sabre_DAV_Exception_NotFound
+ * @return Sabre_DAV_INode
+ */
+ public function getChild($name) {
+
+ $path = $this->path . '/' . $name;
+
+ if (!file_exists($path)) throw new Sabre_DAV_Exception_NotFound('File could not be located');
+ if ($name=='.' || $name=='..') throw new Sabre_DAV_Exception_Forbidden('Permission denied to . and ..');
+
+ if (is_dir($path)) {
+
+ return new Sabre_DAV_FSExt_Directory($path);
+
+ } else {
+
+ return new Sabre_DAV_FSExt_File($path);
+
+ }
+
+ }
+
+ /**
+ * Checks if a child exists.
+ *
+ * @param string $name
+ * @return bool
+ */
+ public function childExists($name) {
+
+ if ($name=='.' || $name=='..')
+ throw new Sabre_DAV_Exception_Forbidden('Permission denied to . and ..');
+
+ $path = $this->path . '/' . $name;
+ return file_exists($path);
+
+ }
+
+ /**
+ * Returns an array with all the child nodes
+ *
+ * @return Sabre_DAV_INode[]
+ */
+ public function getChildren() {
+
+ $nodes = array();
+ foreach(scandir($this->path) as $node) if($node!='.' && $node!='..' && $node!='.sabredav') $nodes[] = $this->getChild($node);
+ return $nodes;
+
+ }
+
+ /**
+ * Deletes all files in this directory, and then itself
+ *
+ * @return bool
+ */
+ public function delete() {
+
+ // Deleting all children
+ foreach($this->getChildren() as $child) $child->delete();
+
+ // Removing resource info, if its still around
+ if (file_exists($this->path . '/.sabredav')) unlink($this->path . '/.sabredav');
+
+ // Removing the directory itself
+ rmdir($this->path);
+
+ return parent::delete();
+
+ }
+
+ /**
+ * Returns available diskspace information
+ *
+ * @return array
+ */
+ public function getQuotaInfo() {
+
+ return array(
+ disk_total_space($this->path)-disk_free_space($this->path),
+ disk_free_space($this->path)
+ );
+
+ }
+
+}
+
diff --git a/3rdparty/Sabre/DAV/FSExt/File.php b/3rdparty/Sabre/DAV/FSExt/File.php
new file mode 100755
index 00000000000..b93ce5aee21
--- /dev/null
+++ b/3rdparty/Sabre/DAV/FSExt/File.php
@@ -0,0 +1,93 @@
+<?php
+
+/**
+ * File class
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_FSExt_File extends Sabre_DAV_FSExt_Node implements Sabre_DAV_IFile {
+
+ /**
+ * Updates the data
+ *
+ * data is a readable stream resource.
+ *
+ * @param resource $data
+ * @return void
+ */
+ public function put($data) {
+
+ file_put_contents($this->path,$data);
+ return '"' . md5_file($this->path) . '"';
+
+ }
+
+ /**
+ * Returns the data
+ *
+ * @return string
+ */
+ public function get() {
+
+ return fopen($this->path,'r');
+
+ }
+
+ /**
+ * Delete the current file
+ *
+ * @return bool
+ */
+ public function delete() {
+
+ unlink($this->path);
+ return parent::delete();
+
+ }
+
+ /**
+ * Returns the ETag for a file
+ *
+ * An ETag is a unique identifier representing the current version of the file. If the file changes, the ETag MUST change.
+ * The ETag is an arbitrary string, but MUST be surrounded by double-quotes.
+ *
+ * Return null if the ETag can not effectively be determined
+ *
+ * @return string|null
+ */
+ public function getETag() {
+
+ return '"' . md5_file($this->path). '"';
+
+ }
+
+ /**
+ * Returns the mime-type for a file
+ *
+ * If null is returned, we'll assume application/octet-stream
+ *
+ * @return string|null
+ */
+ public function getContentType() {
+
+ return null;
+
+ }
+
+ /**
+ * Returns the size of the file, in bytes
+ *
+ * @return int
+ */
+ public function getSize() {
+
+ return filesize($this->path);
+
+ }
+
+}
+
diff --git a/3rdparty/Sabre/DAV/FSExt/Node.php b/3rdparty/Sabre/DAV/FSExt/Node.php
new file mode 100755
index 00000000000..68ca06beb7e
--- /dev/null
+++ b/3rdparty/Sabre/DAV/FSExt/Node.php
@@ -0,0 +1,212 @@
+<?php
+
+/**
+ * Base node-class
+ *
+ * The node class implements the method used by both the File and the Directory classes
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+abstract class Sabre_DAV_FSExt_Node extends Sabre_DAV_FS_Node implements Sabre_DAV_IProperties {
+
+ /**
+ * Updates properties on this node,
+ *
+ * @param array $properties
+ * @see Sabre_DAV_IProperties::updateProperties
+ * @return bool|array
+ */
+ public function updateProperties($properties) {
+
+ $resourceData = $this->getResourceData();
+
+ foreach($properties as $propertyName=>$propertyValue) {
+
+ // If it was null, we need to delete the property
+ if (is_null($propertyValue)) {
+ if (isset($resourceData['properties'][$propertyName])) {
+ unset($resourceData['properties'][$propertyName]);
+ }
+ } else {
+ $resourceData['properties'][$propertyName] = $propertyValue;
+ }
+
+ }
+
+ $this->putResourceData($resourceData);
+ return true;
+ }
+
+ /**
+ * Returns a list of properties for this nodes.;
+ *
+ * The properties list is a list of propertynames the client requested, encoded as xmlnamespace#tagName, for example: http://www.example.org/namespace#author
+ * If the array is empty, all properties should be returned
+ *
+ * @param array $properties
+ * @return array
+ */
+ function getProperties($properties) {
+
+ $resourceData = $this->getResourceData();
+
+ // if the array was empty, we need to return everything
+ if (!$properties) return $resourceData['properties'];
+
+ $props = array();
+ foreach($properties as $property) {
+ if (isset($resourceData['properties'][$property])) $props[$property] = $resourceData['properties'][$property];
+ }
+
+ return $props;
+
+ }
+
+ /**
+ * Returns the path to the resource file
+ *
+ * @return string
+ */
+ protected function getResourceInfoPath() {
+
+ list($parentDir) = Sabre_DAV_URLUtil::splitPath($this->path);
+ return $parentDir . '/.sabredav';
+
+ }
+
+ /**
+ * Returns all the stored resource information
+ *
+ * @return array
+ */
+ protected function getResourceData() {
+
+ $path = $this->getResourceInfoPath();
+ if (!file_exists($path)) return array('properties' => array());
+
+ // opening up the file, and creating a shared lock
+ $handle = fopen($path,'r');
+ flock($handle,LOCK_SH);
+ $data = '';
+
+ // Reading data until the eof
+ while(!feof($handle)) {
+ $data.=fread($handle,8192);
+ }
+
+ // We're all good
+ fclose($handle);
+
+ // Unserializing and checking if the resource file contains data for this file
+ $data = unserialize($data);
+ if (!isset($data[$this->getName()])) {
+ return array('properties' => array());
+ }
+
+ $data = $data[$this->getName()];
+ if (!isset($data['properties'])) $data['properties'] = array();
+ return $data;
+
+ }
+
+ /**
+ * Updates the resource information
+ *
+ * @param array $newData
+ * @return void
+ */
+ protected function putResourceData(array $newData) {
+
+ $path = $this->getResourceInfoPath();
+
+ // opening up the file, and creating a shared lock
+ $handle = fopen($path,'a+');
+ flock($handle,LOCK_EX);
+ $data = '';
+
+ rewind($handle);
+
+ // Reading data until the eof
+ while(!feof($handle)) {
+ $data.=fread($handle,8192);
+ }
+
+ // Unserializing and checking if the resource file contains data for this file
+ $data = unserialize($data);
+ $data[$this->getName()] = $newData;
+ ftruncate($handle,0);
+ rewind($handle);
+
+ fwrite($handle,serialize($data));
+ fclose($handle);
+
+ }
+
+ /**
+ * Renames the node
+ *
+ * @param string $name The new name
+ * @return void
+ */
+ public function setName($name) {
+
+ list($parentPath, ) = Sabre_DAV_URLUtil::splitPath($this->path);
+ list(, $newName) = Sabre_DAV_URLUtil::splitPath($name);
+ $newPath = $parentPath . '/' . $newName;
+
+ // We're deleting the existing resourcedata, and recreating it
+ // for the new path.
+ $resourceData = $this->getResourceData();
+ $this->deleteResourceData();
+
+ rename($this->path,$newPath);
+ $this->path = $newPath;
+ $this->putResourceData($resourceData);
+
+
+ }
+
+ /**
+ * @return bool
+ */
+ public function deleteResourceData() {
+
+ // When we're deleting this node, we also need to delete any resource information
+ $path = $this->getResourceInfoPath();
+ if (!file_exists($path)) return true;
+
+ // opening up the file, and creating a shared lock
+ $handle = fopen($path,'a+');
+ flock($handle,LOCK_EX);
+ $data = '';
+
+ rewind($handle);
+
+ // Reading data until the eof
+ while(!feof($handle)) {
+ $data.=fread($handle,8192);
+ }
+
+ // Unserializing and checking if the resource file contains data for this file
+ $data = unserialize($data);
+ if (isset($data[$this->getName()])) unset($data[$this->getName()]);
+ ftruncate($handle,0);
+ rewind($handle);
+ fwrite($handle,serialize($data));
+ fclose($handle);
+
+ return true;
+ }
+
+ public function delete() {
+
+ return $this->deleteResourceData();
+
+ }
+
+}
+
diff --git a/3rdparty/Sabre/DAV/File.php b/3rdparty/Sabre/DAV/File.php
new file mode 100755
index 00000000000..3126bd8d364
--- /dev/null
+++ b/3rdparty/Sabre/DAV/File.php
@@ -0,0 +1,85 @@
+<?php
+
+/**
+ * File class
+ *
+ * This is a helper class, that should aid in getting file classes setup.
+ * Most of its methods are implemented, and throw permission denied exceptions
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+abstract class Sabre_DAV_File extends Sabre_DAV_Node implements Sabre_DAV_IFile {
+
+ /**
+ * Updates the data
+ *
+ * data is a readable stream resource.
+ *
+ * @param resource $data
+ * @return void
+ */
+ public function put($data) {
+
+ throw new Sabre_DAV_Exception_Forbidden('Permission denied to change data');
+
+ }
+
+ /**
+ * Returns the data
+ *
+ * This method may either return a string or a readable stream resource
+ *
+ * @return mixed
+ */
+ public function get() {
+
+ throw new Sabre_DAV_Exception_Forbidden('Permission denied to read this file');
+
+ }
+
+ /**
+ * Returns the size of the file, in bytes.
+ *
+ * @return int
+ */
+ public function getSize() {
+
+ return 0;
+
+ }
+
+ /**
+ * Returns the ETag for a file
+ *
+ * An ETag is a unique identifier representing the current version of the file. If the file changes, the ETag MUST change.
+ * The ETag is an arbitrary string, but MUST be surrounded by double-quotes.
+ *
+ * Return null if the ETag can not effectively be determined
+ *
+ * @return string|null
+ */
+ public function getETag() {
+
+ return null;
+
+ }
+
+ /**
+ * Returns the mime-type for a file
+ *
+ * If null is returned, we'll assume application/octet-stream
+ *
+ * @return string|null
+ */
+ public function getContentType() {
+
+ return null;
+
+ }
+
+}
+
diff --git a/3rdparty/Sabre/DAV/ICollection.php b/3rdparty/Sabre/DAV/ICollection.php
new file mode 100755
index 00000000000..4626038a66e
--- /dev/null
+++ b/3rdparty/Sabre/DAV/ICollection.php
@@ -0,0 +1,74 @@
+<?php
+
+/**
+ * The ICollection Interface
+ *
+ * This interface should be implemented by each class that represents a collection
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+interface Sabre_DAV_ICollection extends Sabre_DAV_INode {
+
+ /**
+ * Creates a new file in the directory
+ *
+ * Data will either be supplied as a stream resource, or in certain cases
+ * as a string. Keep in mind that you may have to support either.
+ *
+ * After succesful creation of the file, you may choose to return the ETag
+ * of the new file here.
+ *
+ * The returned ETag must be surrounded by double-quotes (The quotes should
+ * be part of the actual string).
+ *
+ * If you cannot accurately determine the ETag, you should not return it.
+ * If you don't store the file exactly as-is (you're transforming it
+ * somehow) you should also not return an ETag.
+ *
+ * This means that if a subsequent GET to this new file does not exactly
+ * return the same contents of what was submitted here, you are strongly
+ * recommended to omit the ETag.
+ *
+ * @param string $name Name of the file
+ * @param resource|string $data Initial payload
+ * @return null|string
+ */
+ function createFile($name, $data = null);
+
+ /**
+ * Creates a new subdirectory
+ *
+ * @param string $name
+ * @return void
+ */
+ function createDirectory($name);
+
+ /**
+ * Returns a specific child node, referenced by its name
+ *
+ * @param string $name
+ * @return Sabre_DAV_INode
+ */
+ function getChild($name);
+
+ /**
+ * Returns an array with all the child nodes
+ *
+ * @return Sabre_DAV_INode[]
+ */
+ function getChildren();
+
+ /**
+ * Checks if a child-node with the specified name exists
+ *
+ * @param string $name
+ * @return bool
+ */
+ function childExists($name);
+
+}
+
diff --git a/3rdparty/Sabre/DAV/IExtendedCollection.php b/3rdparty/Sabre/DAV/IExtendedCollection.php
new file mode 100755
index 00000000000..6ec345f9a62
--- /dev/null
+++ b/3rdparty/Sabre/DAV/IExtendedCollection.php
@@ -0,0 +1,28 @@
+<?php
+
+/**
+ * The IExtendedCollection interface.
+ *
+ * This interface can be used to create special-type of collection-resources
+ * as defined by RFC 5689.
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+interface Sabre_DAV_IExtendedCollection extends Sabre_DAV_ICollection {
+
+ /**
+ * Creates a new collection
+ *
+ * @param string $name
+ * @param array $resourceType
+ * @param array $properties
+ * @return void
+ */
+ function createExtendedCollection($name, array $resourceType, array $properties);
+
+}
+
diff --git a/3rdparty/Sabre/DAV/IFile.php b/3rdparty/Sabre/DAV/IFile.php
new file mode 100755
index 00000000000..478f822ae71
--- /dev/null
+++ b/3rdparty/Sabre/DAV/IFile.php
@@ -0,0 +1,77 @@
+<?php
+
+/**
+ * This interface represents a file in the directory tree
+ *
+ * A file is a bit of a broad definition. In general it implies that on
+ * this specific node a PUT or GET method may be performed, to either update,
+ * or retrieve the contents of the file.
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+interface Sabre_DAV_IFile extends Sabre_DAV_INode {
+
+ /**
+ * Updates the data
+ *
+ * The data argument is a readable stream resource.
+ *
+ * After a succesful put operation, you may choose to return an ETag. The
+ * etag must always be surrounded by double-quotes. These quotes must
+ * appear in the actual string you're returning.
+ *
+ * Clients may use the ETag from a PUT request to later on make sure that
+ * when they update the file, the contents haven't changed in the mean
+ * time.
+ *
+ * If you don't plan to store the file byte-by-byte, and you return a
+ * different object on a subsequent GET you are strongly recommended to not
+ * return an ETag, and just return null.
+ *
+ * @param resource $data
+ * @return string|null
+ */
+ function put($data);
+
+ /**
+ * Returns the data
+ *
+ * This method may either return a string or a readable stream resource
+ *
+ * @return mixed
+ */
+ function get();
+
+ /**
+ * Returns the mime-type for a file
+ *
+ * If null is returned, we'll assume application/octet-stream
+ *
+ * @return void
+ */
+ function getContentType();
+
+ /**
+ * Returns the ETag for a file
+ *
+ * An ETag is a unique identifier representing the current version of the file. If the file changes, the ETag MUST change.
+ *
+ * Return null if the ETag can not effectively be determined
+ *
+ * @return void
+ */
+ function getETag();
+
+ /**
+ * Returns the size of the node, in bytes
+ *
+ * @return int
+ */
+ function getSize();
+
+}
+
diff --git a/3rdparty/Sabre/DAV/INode.php b/3rdparty/Sabre/DAV/INode.php
new file mode 100755
index 00000000000..c57d3923105
--- /dev/null
+++ b/3rdparty/Sabre/DAV/INode.php
@@ -0,0 +1,46 @@
+<?php
+
+/**
+ * The INode interface is the base interface, and the parent class of both ICollection and IFile
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+interface Sabre_DAV_INode {
+
+ /**
+ * Deleted the current node
+ *
+ * @return void
+ */
+ function delete();
+
+ /**
+ * Returns the name of the node.
+ *
+ * This is used to generate the url.
+ *
+ * @return string
+ */
+ function getName();
+
+ /**
+ * Renames the node
+ *
+ * @param string $name The new name
+ * @return void
+ */
+ function setName($name);
+
+ /**
+ * Returns the last modification time, as a unix timestamp
+ *
+ * @return int
+ */
+ function getLastModified();
+
+}
+
diff --git a/3rdparty/Sabre/DAV/IProperties.php b/3rdparty/Sabre/DAV/IProperties.php
new file mode 100755
index 00000000000..38eaab16dad
--- /dev/null
+++ b/3rdparty/Sabre/DAV/IProperties.php
@@ -0,0 +1,67 @@
+<?php
+
+/**
+ * IProperties interface
+ *
+ * Implement this interface to support custom WebDAV properties requested and sent from clients.
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+interface Sabre_DAV_IProperties extends Sabre_DAV_INode {
+
+ /**
+ * 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-existent property is always successful.
+ *
+ * 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
+ */
+ function updateProperties($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
+ */
+ function getProperties($properties);
+
+}
+
diff --git a/3rdparty/Sabre/DAV/IQuota.php b/3rdparty/Sabre/DAV/IQuota.php
new file mode 100755
index 00000000000..3fe4c4eced4
--- /dev/null
+++ b/3rdparty/Sabre/DAV/IQuota.php
@@ -0,0 +1,27 @@
+<?php
+
+/**
+ * IQuota interface
+ *
+ * Implement this interface to add the ability to return quota information. The ObjectTree
+ * will check for quota information on any given node. If the information is not available it will
+ * attempt to fetch the information from the root node.
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+interface Sabre_DAV_IQuota extends Sabre_DAV_ICollection {
+
+ /**
+ * Returns the quota information
+ *
+ * This method MUST return an array with 2 values, the first being the total used space,
+ * the second the available space (in bytes)
+ */
+ function getQuotaInfo();
+
+}
+
diff --git a/3rdparty/Sabre/DAV/Locks/Backend/Abstract.php b/3rdparty/Sabre/DAV/Locks/Backend/Abstract.php
new file mode 100755
index 00000000000..127e643a2b9
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Locks/Backend/Abstract.php
@@ -0,0 +1,50 @@
+<?php
+
+/**
+ * The Lock manager allows you to handle all file-locks centrally.
+ *
+ * This is an alternative approach to doing this on a per-node basis
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+abstract class Sabre_DAV_Locks_Backend_Abstract {
+
+ /**
+ * Returns a list of Sabre_DAV_Locks_LockInfo objects
+ *
+ * This method should return all the locks for a particular uri, including
+ * locks that might be set on a parent uri.
+ *
+ * If returnChildLocks is set to true, this method should also look for
+ * any locks in the subtree of the uri for locks.
+ *
+ * @param string $uri
+ * @param bool $returnChildLocks
+ * @return array
+ */
+ abstract function getLocks($uri, $returnChildLocks);
+
+ /**
+ * Locks a uri
+ *
+ * @param string $uri
+ * @param Sabre_DAV_Locks_LockInfo $lockInfo
+ * @return bool
+ */
+ abstract function lock($uri,Sabre_DAV_Locks_LockInfo $lockInfo);
+
+ /**
+ * Removes a lock from a uri
+ *
+ * @param string $uri
+ * @param Sabre_DAV_Locks_LockInfo $lockInfo
+ * @return bool
+ */
+ abstract function unlock($uri,Sabre_DAV_Locks_LockInfo $lockInfo);
+
+}
+
diff --git a/3rdparty/Sabre/DAV/Locks/Backend/FS.php b/3rdparty/Sabre/DAV/Locks/Backend/FS.php
new file mode 100755
index 00000000000..02cab87fc82
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Locks/Backend/FS.php
@@ -0,0 +1,191 @@
+<?php
+
+/**
+ * This Lock Backend stores all its data in the filesystem in separate file per
+ * node.
+ *
+ * This Lock Manager is now deprecated. It has a bug that allows parent
+ * collections to be deletes when children deeper in the tree are locked.
+ *
+ * This also means that using this backend means you will not pass the Neon
+ * Litmus test.
+ *
+ * You are recommended to use either the PDO or the File backend instead.
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @deprecated
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_Locks_Backend_FS extends Sabre_DAV_Locks_Backend_Abstract {
+
+ /**
+ * The default data directory
+ *
+ * @var string
+ */
+ private $dataDir;
+
+ public function __construct($dataDir) {
+
+ $this->dataDir = $dataDir;
+
+ }
+
+ protected function getFileNameForUri($uri) {
+
+ return $this->dataDir . '/sabredav_' . md5($uri) . '.locks';
+
+ }
+
+
+ /**
+ * Returns a list of Sabre_DAV_Locks_LockInfo objects
+ *
+ * This method should return all the locks for a particular uri, including
+ * locks that might be set on a parent uri.
+ *
+ * If returnChildLocks is set to true, this method should also look for
+ * any locks in the subtree of the uri for locks.
+ *
+ * @param string $uri
+ * @param bool $returnChildLocks
+ * @return array
+ */
+ public function getLocks($uri, $returnChildLocks) {
+
+ $lockList = array();
+ $currentPath = '';
+
+ foreach(explode('/',$uri) as $uriPart) {
+
+ // weird algorithm that can probably be improved, but we're traversing the path top down
+ if ($currentPath) $currentPath.='/';
+ $currentPath.=$uriPart;
+
+ $uriLocks = $this->getData($currentPath);
+
+ foreach($uriLocks as $uriLock) {
+
+ // Unless we're on the leaf of the uri-tree we should ignore locks with depth 0
+ if($uri==$currentPath || $uriLock->depth!=0) {
+ $uriLock->uri = $currentPath;
+ $lockList[] = $uriLock;
+ }
+
+ }
+
+ }
+
+ // Checking if we can remove any of these locks
+ foreach($lockList as $k=>$lock) {
+ if (time() > $lock->timeout + $lock->created) unset($lockList[$k]);
+ }
+ return $lockList;
+
+ }
+
+ /**
+ * Locks a uri
+ *
+ * @param string $uri
+ * @param Sabre_DAV_Locks_LockInfo $lockInfo
+ * @return bool
+ */
+ public function lock($uri,Sabre_DAV_Locks_LockInfo $lockInfo) {
+
+ // We're making the lock timeout 30 minutes
+ $lockInfo->timeout = 1800;
+ $lockInfo->created = time();
+
+ $locks = $this->getLocks($uri,false);
+ foreach($locks as $k=>$lock) {
+ if ($lock->token == $lockInfo->token) unset($locks[$k]);
+ }
+ $locks[] = $lockInfo;
+ $this->putData($uri,$locks);
+ return true;
+
+ }
+
+ /**
+ * Removes a lock from a uri
+ *
+ * @param string $uri
+ * @param Sabre_DAV_Locks_LockInfo $lockInfo
+ * @return bool
+ */
+ public function unlock($uri,Sabre_DAV_Locks_LockInfo $lockInfo) {
+
+ $locks = $this->getLocks($uri,false);
+ foreach($locks as $k=>$lock) {
+
+ if ($lock->token == $lockInfo->token) {
+
+ unset($locks[$k]);
+ $this->putData($uri,$locks);
+ return true;
+
+ }
+ }
+ return false;
+
+ }
+
+ /**
+ * Returns the stored data for a uri
+ *
+ * @param string $uri
+ * @return array
+ */
+ protected function getData($uri) {
+
+ $path = $this->getFilenameForUri($uri);
+ if (!file_exists($path)) return array();
+
+ // opening up the file, and creating a shared lock
+ $handle = fopen($path,'r');
+ flock($handle,LOCK_SH);
+ $data = '';
+
+ // Reading data until the eof
+ while(!feof($handle)) {
+ $data.=fread($handle,8192);
+ }
+
+ // We're all good
+ fclose($handle);
+
+ // Unserializing and checking if the resource file contains data for this file
+ $data = unserialize($data);
+ if (!$data) return array();
+ return $data;
+
+ }
+
+ /**
+ * Updates the lock information
+ *
+ * @param string $uri
+ * @param array $newData
+ * @return void
+ */
+ protected function putData($uri,array $newData) {
+
+ $path = $this->getFileNameForUri($uri);
+
+ // opening up the file, and creating a shared lock
+ $handle = fopen($path,'a+');
+ flock($handle,LOCK_EX);
+ ftruncate($handle,0);
+ rewind($handle);
+
+ fwrite($handle,serialize($newData));
+ fclose($handle);
+
+ }
+
+}
+
diff --git a/3rdparty/Sabre/DAV/Locks/Backend/File.php b/3rdparty/Sabre/DAV/Locks/Backend/File.php
new file mode 100755
index 00000000000..c33f963514b
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Locks/Backend/File.php
@@ -0,0 +1,181 @@
+<?php
+
+/**
+ * The Lock manager allows you to handle all file-locks centrally.
+ *
+ * This Lock Manager stores all its data in a single file.
+ *
+ * Note that this is not nearly as robust as a database, you are encouraged
+ * to use the PDO backend instead.
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_Locks_Backend_File extends Sabre_DAV_Locks_Backend_Abstract {
+
+ /**
+ * The storage file
+ *
+ * @var string
+ */
+ private $locksFile;
+
+ /**
+ * Constructor
+ *
+ * @param string $locksFile path to file
+ */
+ public function __construct($locksFile) {
+
+ $this->locksFile = $locksFile;
+
+ }
+
+ /**
+ * Returns a list of Sabre_DAV_Locks_LockInfo objects
+ *
+ * This method should return all the locks for a particular uri, including
+ * locks that might be set on a parent uri.
+ *
+ * If returnChildLocks is set to true, this method should also look for
+ * any locks in the subtree of the uri for locks.
+ *
+ * @param string $uri
+ * @param bool $returnChildLocks
+ * @return array
+ */
+ public function getLocks($uri, $returnChildLocks) {
+
+ $newLocks = array();
+
+ $locks = $this->getData();
+
+ foreach($locks as $lock) {
+
+ if ($lock->uri === $uri ||
+ //deep locks on parents
+ ($lock->depth!=0 && strpos($uri, $lock->uri . '/')===0) ||
+
+ // locks on children
+ ($returnChildLocks && (strpos($lock->uri, $uri . '/')===0)) ) {
+
+ $newLocks[] = $lock;
+
+ }
+
+ }
+
+ // Checking if we can remove any of these locks
+ foreach($newLocks as $k=>$lock) {
+ if (time() > $lock->timeout + $lock->created) unset($newLocks[$k]);
+ }
+ return $newLocks;
+
+ }
+
+ /**
+ * Locks a uri
+ *
+ * @param string $uri
+ * @param Sabre_DAV_Locks_LockInfo $lockInfo
+ * @return bool
+ */
+ public function lock($uri, Sabre_DAV_Locks_LockInfo $lockInfo) {
+
+ // We're making the lock timeout 30 minutes
+ $lockInfo->timeout = 1800;
+ $lockInfo->created = time();
+ $lockInfo->uri = $uri;
+
+ $locks = $this->getData();
+
+ foreach($locks as $k=>$lock) {
+ if (
+ ($lock->token == $lockInfo->token) ||
+ (time() > $lock->timeout + $lock->created)
+ ) {
+ unset($locks[$k]);
+ }
+ }
+ $locks[] = $lockInfo;
+ $this->putData($locks);
+ return true;
+
+ }
+
+ /**
+ * Removes a lock from a uri
+ *
+ * @param string $uri
+ * @param Sabre_DAV_Locks_LockInfo $lockInfo
+ * @return bool
+ */
+ public function unlock($uri, Sabre_DAV_Locks_LockInfo $lockInfo) {
+
+ $locks = $this->getData();
+ foreach($locks as $k=>$lock) {
+
+ if ($lock->token == $lockInfo->token) {
+
+ unset($locks[$k]);
+ $this->putData($locks);
+ return true;
+
+ }
+ }
+ return false;
+
+ }
+
+ /**
+ * Loads the lockdata from the filesystem.
+ *
+ * @return array
+ */
+ protected function getData() {
+
+ if (!file_exists($this->locksFile)) return array();
+
+ // opening up the file, and creating a shared lock
+ $handle = fopen($this->locksFile,'r');
+ flock($handle,LOCK_SH);
+
+ // Reading data until the eof
+ $data = stream_get_contents($handle);
+
+ // We're all good
+ fclose($handle);
+
+ // Unserializing and checking if the resource file contains data for this file
+ $data = unserialize($data);
+ if (!$data) return array();
+ return $data;
+
+ }
+
+ /**
+ * Saves the lockdata
+ *
+ * @param array $newData
+ * @return void
+ */
+ protected function putData(array $newData) {
+
+ // opening up the file, and creating an exclusive lock
+ $handle = fopen($this->locksFile,'a+');
+ flock($handle,LOCK_EX);
+
+ // We can only truncate and rewind once the lock is acquired.
+ ftruncate($handle,0);
+ rewind($handle);
+
+ fwrite($handle,serialize($newData));
+ fclose($handle);
+
+ }
+
+}
+
diff --git a/3rdparty/Sabre/DAV/Locks/Backend/PDO.php b/3rdparty/Sabre/DAV/Locks/Backend/PDO.php
new file mode 100755
index 00000000000..acce80638ec
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Locks/Backend/PDO.php
@@ -0,0 +1,165 @@
+<?php
+
+/**
+ * The Lock manager allows you to handle all file-locks centrally.
+ *
+ * This Lock Manager stores all its data in a database. You must pass a PDO
+ * connection object in the constructor.
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_Locks_Backend_PDO extends Sabre_DAV_Locks_Backend_Abstract {
+
+ /**
+ * The PDO connection object
+ *
+ * @var pdo
+ */
+ private $pdo;
+
+ /**
+ * The PDO tablename this backend uses.
+ *
+ * @var string
+ */
+ protected $tableName;
+
+ /**
+ * Constructor
+ *
+ * @param PDO $pdo
+ * @param string $tableName
+ */
+ public function __construct(PDO $pdo, $tableName = 'locks') {
+
+ $this->pdo = $pdo;
+ $this->tableName = $tableName;
+
+ }
+
+ /**
+ * Returns a list of Sabre_DAV_Locks_LockInfo objects
+ *
+ * This method should return all the locks for a particular uri, including
+ * locks that might be set on a parent uri.
+ *
+ * If returnChildLocks is set to true, this method should also look for
+ * any locks in the subtree of the uri for locks.
+ *
+ * @param string $uri
+ * @param bool $returnChildLocks
+ * @return array
+ */
+ public function getLocks($uri, $returnChildLocks) {
+
+ // NOTE: the following 10 lines or so could be easily replaced by
+ // pure sql. MySQL's non-standard string concatenation prevents us
+ // from doing this though.
+ $query = 'SELECT owner, token, timeout, created, scope, depth, uri FROM '.$this->tableName.' WHERE ((created + timeout) > CAST(? AS UNSIGNED INTEGER)) AND ((uri = ?)';
+ $params = array(time(),$uri);
+
+ // We need to check locks for every part in the uri.
+ $uriParts = explode('/',$uri);
+
+ // We already covered the last part of the uri
+ array_pop($uriParts);
+
+ $currentPath='';
+
+ foreach($uriParts as $part) {
+
+ if ($currentPath) $currentPath.='/';
+ $currentPath.=$part;
+
+ $query.=' OR (depth!=0 AND uri = ?)';
+ $params[] = $currentPath;
+
+ }
+
+ if ($returnChildLocks) {
+
+ $query.=' OR (uri LIKE ?)';
+ $params[] = $uri . '/%';
+
+ }
+ $query.=')';
+
+ $stmt = $this->pdo->prepare($query);
+ $stmt->execute($params);
+ $result = $stmt->fetchAll();
+
+ $lockList = array();
+ foreach($result as $row) {
+
+ $lockInfo = new Sabre_DAV_Locks_LockInfo();
+ $lockInfo->owner = $row['owner'];
+ $lockInfo->token = $row['token'];
+ $lockInfo->timeout = $row['timeout'];
+ $lockInfo->created = $row['created'];
+ $lockInfo->scope = $row['scope'];
+ $lockInfo->depth = $row['depth'];
+ $lockInfo->uri = $row['uri'];
+ $lockList[] = $lockInfo;
+
+ }
+
+ return $lockList;
+
+ }
+
+ /**
+ * Locks a uri
+ *
+ * @param string $uri
+ * @param Sabre_DAV_Locks_LockInfo $lockInfo
+ * @return bool
+ */
+ public function lock($uri,Sabre_DAV_Locks_LockInfo $lockInfo) {
+
+ // We're making the lock timeout 30 minutes
+ $lockInfo->timeout = 30*60;
+ $lockInfo->created = time();
+ $lockInfo->uri = $uri;
+
+ $locks = $this->getLocks($uri,false);
+ $exists = false;
+ foreach($locks as $lock) {
+ if ($lock->token == $lockInfo->token) $exists = true;
+ }
+
+ if ($exists) {
+ $stmt = $this->pdo->prepare('UPDATE '.$this->tableName.' SET owner = ?, timeout = ?, scope = ?, depth = ?, uri = ?, created = ? WHERE token = ?');
+ $stmt->execute(array($lockInfo->owner,$lockInfo->timeout,$lockInfo->scope,$lockInfo->depth,$uri,$lockInfo->created,$lockInfo->token));
+ } else {
+ $stmt = $this->pdo->prepare('INSERT INTO '.$this->tableName.' (owner,timeout,scope,depth,uri,created,token) VALUES (?,?,?,?,?,?,?)');
+ $stmt->execute(array($lockInfo->owner,$lockInfo->timeout,$lockInfo->scope,$lockInfo->depth,$uri,$lockInfo->created,$lockInfo->token));
+ }
+
+ return true;
+
+ }
+
+
+
+ /**
+ * Removes a lock from a uri
+ *
+ * @param string $uri
+ * @param Sabre_DAV_Locks_LockInfo $lockInfo
+ * @return bool
+ */
+ public function unlock($uri,Sabre_DAV_Locks_LockInfo $lockInfo) {
+
+ $stmt = $this->pdo->prepare('DELETE FROM '.$this->tableName.' WHERE uri = ? AND token = ?');
+ $stmt->execute(array($uri,$lockInfo->token));
+
+ return $stmt->rowCount()===1;
+
+ }
+
+}
+
diff --git a/3rdparty/Sabre/DAV/Locks/LockInfo.php b/3rdparty/Sabre/DAV/Locks/LockInfo.php
new file mode 100755
index 00000000000..9df014a4281
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Locks/LockInfo.php
@@ -0,0 +1,81 @@
+<?php
+
+/**
+ * LockInfo class
+ *
+ * An object of the LockInfo class holds all the information relevant to a
+ * single lock.
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_Locks_LockInfo {
+
+ /**
+ * A shared lock
+ */
+ const SHARED = 1;
+
+ /**
+ * An exclusive lock
+ */
+ const EXCLUSIVE = 2;
+
+ /**
+ * A never expiring timeout
+ */
+ const TIMEOUT_INFINITE = -1;
+
+ /**
+ * The owner of the lock
+ *
+ * @var string
+ */
+ public $owner;
+
+ /**
+ * The locktoken
+ *
+ * @var string
+ */
+ public $token;
+
+ /**
+ * How long till the lock is expiring
+ *
+ * @var int
+ */
+ public $timeout;
+
+ /**
+ * UNIX Timestamp of when this lock was created
+ *
+ * @var int
+ */
+ public $created;
+
+ /**
+ * Exclusive or shared lock
+ *
+ * @var int
+ */
+ public $scope = self::EXCLUSIVE;
+
+ /**
+ * Depth of lock, can be 0 or Sabre_DAV_Server::DEPTH_INFINITY
+ */
+ public $depth = 0;
+
+ /**
+ * The uri this lock locks
+ *
+ * TODO: This value is not always set
+ * @var mixed
+ */
+ public $uri;
+
+}
+
diff --git a/3rdparty/Sabre/DAV/Locks/Plugin.php b/3rdparty/Sabre/DAV/Locks/Plugin.php
new file mode 100755
index 00000000000..035b3a63863
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Locks/Plugin.php
@@ -0,0 +1,636 @@
+<?php
+
+/**
+ * Locking plugin
+ *
+ * This plugin provides locking support to a WebDAV server.
+ * The easiest way to get started, is by hooking it up as such:
+ *
+ * $lockBackend = new Sabre_DAV_Locks_Backend_File('./mylockdb');
+ * $lockPlugin = new Sabre_DAV_Locks_Plugin($lockBackend);
+ * $server->addPlugin($lockPlugin);
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_Locks_Plugin extends Sabre_DAV_ServerPlugin {
+
+ /**
+ * locksBackend
+ *
+ * @var Sabre_DAV_Locks_Backend_Abstract
+ */
+ private $locksBackend;
+
+ /**
+ * server
+ *
+ * @var Sabre_DAV_Server
+ */
+ private $server;
+
+ /**
+ * __construct
+ *
+ * @param Sabre_DAV_Locks_Backend_Abstract $locksBackend
+ */
+ public function __construct(Sabre_DAV_Locks_Backend_Abstract $locksBackend = null) {
+
+ $this->locksBackend = $locksBackend;
+
+ }
+
+ /**
+ * Initializes the plugin
+ *
+ * This method is automatically called by the Server class after addPlugin.
+ *
+ * @param Sabre_DAV_Server $server
+ * @return void
+ */
+ public function initialize(Sabre_DAV_Server $server) {
+
+ $this->server = $server;
+ $server->subscribeEvent('unknownMethod',array($this,'unknownMethod'));
+ $server->subscribeEvent('beforeMethod',array($this,'beforeMethod'),50);
+ $server->subscribeEvent('afterGetProperties',array($this,'afterGetProperties'));
+
+ }
+
+ /**
+ * 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 'locks';
+
+ }
+
+ /**
+ * This method is called by the Server if the user used an HTTP method
+ * the server didn't recognize.
+ *
+ * This plugin intercepts the LOCK and UNLOCK methods.
+ *
+ * @param string $method
+ * @param string $uri
+ * @return bool
+ */
+ public function unknownMethod($method, $uri) {
+
+ switch($method) {
+
+ case 'LOCK' : $this->httpLock($uri); return false;
+ case 'UNLOCK' : $this->httpUnlock($uri); return false;
+
+ }
+
+ }
+
+ /**
+ * This method is called after most properties have been found
+ * it allows us to add in any Lock-related properties
+ *
+ * @param string $path
+ * @param array $newProperties
+ * @return bool
+ */
+ public function afterGetProperties($path, &$newProperties) {
+
+ foreach($newProperties[404] as $propName=>$discard) {
+
+ switch($propName) {
+
+ case '{DAV:}supportedlock' :
+ $val = false;
+ if ($this->locksBackend) $val = true;
+ $newProperties[200][$propName] = new Sabre_DAV_Property_SupportedLock($val);
+ unset($newProperties[404][$propName]);
+ break;
+
+ case '{DAV:}lockdiscovery' :
+ $newProperties[200][$propName] = new Sabre_DAV_Property_LockDiscovery($this->getLocks($path));
+ unset($newProperties[404][$propName]);
+ break;
+
+ }
+
+
+ }
+ return true;
+
+ }
+
+
+ /**
+ * This method is called before the logic for any HTTP method is
+ * handled.
+ *
+ * This plugin uses that feature to intercept access to locked resources.
+ *
+ * @param string $method
+ * @param string $uri
+ * @return bool
+ */
+ public function beforeMethod($method, $uri) {
+
+ switch($method) {
+
+ case 'DELETE' :
+ $lastLock = null;
+ if (!$this->validateLock($uri,$lastLock, true))
+ throw new Sabre_DAV_Exception_Locked($lastLock);
+ break;
+ case 'MKCOL' :
+ case 'PROPPATCH' :
+ case 'PUT' :
+ $lastLock = null;
+ if (!$this->validateLock($uri,$lastLock))
+ throw new Sabre_DAV_Exception_Locked($lastLock);
+ break;
+ case 'MOVE' :
+ $lastLock = null;
+ if (!$this->validateLock(array(
+ $uri,
+ $this->server->calculateUri($this->server->httpRequest->getHeader('Destination')),
+ ),$lastLock, true))
+ throw new Sabre_DAV_Exception_Locked($lastLock);
+ break;
+ case 'COPY' :
+ $lastLock = null;
+ if (!$this->validateLock(
+ $this->server->calculateUri($this->server->httpRequest->getHeader('Destination')),
+ $lastLock, true))
+ throw new Sabre_DAV_Exception_Locked($lastLock);
+ break;
+ }
+
+ return true;
+
+ }
+
+ /**
+ * Use this method to tell the server this plugin defines additional
+ * HTTP methods.
+ *
+ * This method is passed a uri. It should only return HTTP methods that are
+ * available for the specified uri.
+ *
+ * @param string $uri
+ * @return array
+ */
+ public function getHTTPMethods($uri) {
+
+ if ($this->locksBackend)
+ return array('LOCK','UNLOCK');
+
+ return array();
+
+ }
+
+ /**
+ * Returns a list of features for the HTTP OPTIONS Dav: header.
+ *
+ * In this case this is only the number 2. The 2 in the Dav: header
+ * indicates the server supports locks.
+ *
+ * @return array
+ */
+ public function getFeatures() {
+
+ return array(2);
+
+ }
+
+ /**
+ * Returns all lock information on a particular uri
+ *
+ * This function should return an array with Sabre_DAV_Locks_LockInfo objects. If there are no locks on a file, return an empty array.
+ *
+ * Additionally there is also the possibility of locks on parent nodes, so we'll need to traverse every part of the tree
+ * If the $returnChildLocks argument is set to true, we'll also traverse all the children of the object
+ * for any possible locks and return those as well.
+ *
+ * @param string $uri
+ * @param bool $returnChildLocks
+ * @return array
+ */
+ public function getLocks($uri, $returnChildLocks = false) {
+
+ $lockList = array();
+
+ if ($this->locksBackend)
+ $lockList = array_merge($lockList,$this->locksBackend->getLocks($uri, $returnChildLocks));
+
+ return $lockList;
+
+ }
+
+ /**
+ * Locks an uri
+ *
+ * The WebDAV lock request can be operated to either create a new lock on a file, or to refresh an existing lock
+ * If a new lock is created, a full XML body should be supplied, containing information about the lock such as the type
+ * of lock (shared or exclusive) and the owner of the lock
+ *
+ * If a lock is to be refreshed, no body should be supplied and there should be a valid If header containing the lock
+ *
+ * Additionally, a lock can be requested for a non-existent file. In these case we're obligated to create an empty file as per RFC4918:S7.3
+ *
+ * @param string $uri
+ * @return void
+ */
+ protected function httpLock($uri) {
+
+ $lastLock = null;
+ if (!$this->validateLock($uri,$lastLock)) {
+
+ // If the existing lock was an exclusive lock, we need to fail
+ if (!$lastLock || $lastLock->scope == Sabre_DAV_Locks_LockInfo::EXCLUSIVE) {
+ //var_dump($lastLock);
+ throw new Sabre_DAV_Exception_ConflictingLock($lastLock);
+ }
+
+ }
+
+ if ($body = $this->server->httpRequest->getBody(true)) {
+ // This is a new lock request
+ $lockInfo = $this->parseLockRequest($body);
+ $lockInfo->depth = $this->server->getHTTPDepth();
+ $lockInfo->uri = $uri;
+ if($lastLock && $lockInfo->scope != Sabre_DAV_Locks_LockInfo::SHARED) throw new Sabre_DAV_Exception_ConflictingLock($lastLock);
+
+ } elseif ($lastLock) {
+
+ // This must have been a lock refresh
+ $lockInfo = $lastLock;
+
+ // The resource could have been locked through another uri.
+ if ($uri!=$lockInfo->uri) $uri = $lockInfo->uri;
+
+ } else {
+
+ // There was neither a lock refresh nor a new lock request
+ throw new Sabre_DAV_Exception_BadRequest('An xml body is required for lock requests');
+
+ }
+
+ if ($timeout = $this->getTimeoutHeader()) $lockInfo->timeout = $timeout;
+
+ $newFile = false;
+
+ // If we got this far.. we should go check if this node actually exists. If this is not the case, we need to create it first
+ try {
+ $this->server->tree->getNodeForPath($uri);
+
+ // We need to call the beforeWriteContent event for RFC3744
+ // Edit: looks like this is not used, and causing problems now.
+ //
+ // See Issue 222
+ // $this->server->broadcastEvent('beforeWriteContent',array($uri));
+
+ } catch (Sabre_DAV_Exception_NotFound $e) {
+
+ // It didn't, lets create it
+ $this->server->createFile($uri,fopen('php://memory','r'));
+ $newFile = true;
+
+ }
+
+ $this->lockNode($uri,$lockInfo);
+
+ $this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8');
+ $this->server->httpResponse->setHeader('Lock-Token','<opaquelocktoken:' . $lockInfo->token . '>');
+ $this->server->httpResponse->sendStatus($newFile?201:200);
+ $this->server->httpResponse->sendBody($this->generateLockResponse($lockInfo));
+
+ }
+
+ /**
+ * Unlocks a uri
+ *
+ * This WebDAV method allows you to remove a lock from a node. The client should provide a valid locktoken through the Lock-token http header
+ * The server should return 204 (No content) on success
+ *
+ * @param string $uri
+ * @return void
+ */
+ protected function httpUnlock($uri) {
+
+ $lockToken = $this->server->httpRequest->getHeader('Lock-Token');
+
+ // If the locktoken header is not supplied, we need to throw a bad request exception
+ if (!$lockToken) throw new Sabre_DAV_Exception_BadRequest('No lock token was supplied');
+
+ $locks = $this->getLocks($uri);
+
+ // Windows sometimes forgets to include < and > in the Lock-Token
+ // header
+ if ($lockToken[0]!=='<') $lockToken = '<' . $lockToken . '>';
+
+ foreach($locks as $lock) {
+
+ if ('<opaquelocktoken:' . $lock->token . '>' == $lockToken) {
+
+ $this->unlockNode($uri,$lock);
+ $this->server->httpResponse->setHeader('Content-Length','0');
+ $this->server->httpResponse->sendStatus(204);
+ return;
+
+ }
+
+ }
+
+ // If we got here, it means the locktoken was invalid
+ throw new Sabre_DAV_Exception_LockTokenMatchesRequestUri();
+
+ }
+
+ /**
+ * Locks a uri
+ *
+ * All the locking information is supplied in the lockInfo object. The object has a suggested timeout, but this can be safely ignored
+ * It is important that if the existing timeout is ignored, the property is overwritten, as this needs to be sent back to the client
+ *
+ * @param string $uri
+ * @param Sabre_DAV_Locks_LockInfo $lockInfo
+ * @return bool
+ */
+ public function lockNode($uri,Sabre_DAV_Locks_LockInfo $lockInfo) {
+
+ if (!$this->server->broadcastEvent('beforeLock',array($uri,$lockInfo))) return;
+
+ if ($this->locksBackend) return $this->locksBackend->lock($uri,$lockInfo);
+ throw new Sabre_DAV_Exception_MethodNotAllowed('Locking support is not enabled for this resource. No Locking backend was found so if you didn\'t expect this error, please check your configuration.');
+
+ }
+
+ /**
+ * Unlocks a uri
+ *
+ * This method removes a lock from a uri. It is assumed all the supplied information is correct and verified
+ *
+ * @param string $uri
+ * @param Sabre_DAV_Locks_LockInfo $lockInfo
+ * @return bool
+ */
+ public function unlockNode($uri,Sabre_DAV_Locks_LockInfo $lockInfo) {
+
+ if (!$this->server->broadcastEvent('beforeUnlock',array($uri,$lockInfo))) return;
+ if ($this->locksBackend) return $this->locksBackend->unlock($uri,$lockInfo);
+
+ }
+
+
+ /**
+ * Returns the contents of the HTTP Timeout header.
+ *
+ * The method formats the header into an integer.
+ *
+ * @return int
+ */
+ public function getTimeoutHeader() {
+
+ $header = $this->server->httpRequest->getHeader('Timeout');
+
+ if ($header) {
+
+ if (stripos($header,'second-')===0) $header = (int)(substr($header,7));
+ else if (strtolower($header)=='infinite') $header=Sabre_DAV_Locks_LockInfo::TIMEOUT_INFINITE;
+ else throw new Sabre_DAV_Exception_BadRequest('Invalid HTTP timeout header');
+
+ } else {
+
+ $header = 0;
+
+ }
+
+ return $header;
+
+ }
+
+ /**
+ * Generates the response for successful LOCK requests
+ *
+ * @param Sabre_DAV_Locks_LockInfo $lockInfo
+ * @return string
+ */
+ protected function generateLockResponse(Sabre_DAV_Locks_LockInfo $lockInfo) {
+
+ $dom = new DOMDocument('1.0','utf-8');
+ $dom->formatOutput = true;
+
+ $prop = $dom->createElementNS('DAV:','d:prop');
+ $dom->appendChild($prop);
+
+ $lockDiscovery = $dom->createElementNS('DAV:','d:lockdiscovery');
+ $prop->appendChild($lockDiscovery);
+
+ $lockObj = new Sabre_DAV_Property_LockDiscovery(array($lockInfo),true);
+ $lockObj->serialize($this->server,$lockDiscovery);
+
+ return $dom->saveXML();
+
+ }
+
+ /**
+ * validateLock should be called when a write operation is about to happen
+ * It will check if the requested url is locked, and see if the correct lock tokens are passed
+ *
+ * @param mixed $urls List of relevant urls. Can be an array, a string or nothing at all for the current request uri
+ * @param mixed $lastLock This variable will be populated with the last checked lock object (Sabre_DAV_Locks_LockInfo)
+ * @param bool $checkChildLocks If set to true, this function will also look for any locks set on child resources of the supplied urls. This is needed for for example deletion of entire trees.
+ * @return bool
+ */
+ protected function validateLock($urls = null,&$lastLock = null, $checkChildLocks = false) {
+
+ if (is_null($urls)) {
+ $urls = array($this->server->getRequestUri());
+ } elseif (is_string($urls)) {
+ $urls = array($urls);
+ } elseif (!is_array($urls)) {
+ throw new Sabre_DAV_Exception('The urls parameter should either be null, a string or an array');
+ }
+
+ $conditions = $this->getIfConditions();
+
+ // We're going to loop through the urls and make sure all lock conditions are satisfied
+ foreach($urls as $url) {
+
+ $locks = $this->getLocks($url, $checkChildLocks);
+
+ // If there were no conditions, but there were locks, we fail
+ if (!$conditions && $locks) {
+ reset($locks);
+ $lastLock = current($locks);
+ return false;
+ }
+
+ // If there were no locks or conditions, we go to the next url
+ if (!$locks && !$conditions) continue;
+
+ foreach($conditions as $condition) {
+
+ if (!$condition['uri']) {
+ $conditionUri = $this->server->getRequestUri();
+ } else {
+ $conditionUri = $this->server->calculateUri($condition['uri']);
+ }
+
+ // If the condition has a url, and it isn't part of the affected url at all, check the next condition
+ if ($conditionUri && strpos($url,$conditionUri)!==0) continue;
+
+ // The tokens array contians arrays with 2 elements. 0=true/false for normal/not condition, 1=locktoken
+ // At least 1 condition has to be satisfied
+ foreach($condition['tokens'] as $conditionToken) {
+
+ $etagValid = true;
+ $lockValid = true;
+
+ // key 2 can contain an etag
+ if ($conditionToken[2]) {
+
+ $uri = $conditionUri?$conditionUri:$this->server->getRequestUri();
+ $node = $this->server->tree->getNodeForPath($uri);
+ $etagValid = $node->getETag()==$conditionToken[2];
+
+ }
+
+ // key 1 can contain a lock token
+ if ($conditionToken[1]) {
+
+ $lockValid = false;
+ // Match all the locks
+ foreach($locks as $lockIndex=>$lock) {
+
+ $lockToken = 'opaquelocktoken:' . $lock->token;
+
+ // Checking NOT
+ if (!$conditionToken[0] && $lockToken != $conditionToken[1]) {
+
+ // Condition valid, onto the next
+ $lockValid = true;
+ break;
+ }
+ if ($conditionToken[0] && $lockToken == $conditionToken[1]) {
+
+ $lastLock = $lock;
+ // Condition valid and lock matched
+ unset($locks[$lockIndex]);
+ $lockValid = true;
+ break;
+
+ }
+
+ }
+
+ }
+
+ // If, after checking both etags and locks they are stil valid,
+ // we can continue with the next condition.
+ if ($etagValid && $lockValid) continue 2;
+ }
+ // No conditions matched, so we fail
+ throw new Sabre_DAV_Exception_PreconditionFailed('The tokens provided in the if header did not match','If');
+ }
+
+ // Conditions were met, we'll also need to check if all the locks are gone
+ if (count($locks)) {
+
+ reset($locks);
+
+ // There's still locks, we fail
+ $lastLock = current($locks);
+ return false;
+
+ }
+
+
+ }
+
+ // We got here, this means every condition was satisfied
+ return true;
+
+ }
+
+ /**
+ * This method is created to extract information from the WebDAV HTTP 'If:' header
+ *
+ * The If header can be quite complex, and has a bunch of features. We're using a regex to extract all relevant information
+ * The function will return an array, containing structs with the following keys
+ *
+ * * uri - the uri the condition applies to. If this is returned as an
+ * empty string, this implies it's referring to the request url.
+ * * tokens - The lock token. another 2 dimensional array containing 2 elements (0 = true/false.. If this is a negative condition its set to false, 1 = the actual token)
+ * * etag - an etag, if supplied
+ *
+ * @return array
+ */
+ public function getIfConditions() {
+
+ $header = $this->server->httpRequest->getHeader('If');
+ if (!$header) return array();
+
+ $matches = array();
+
+ $regex = '/(?:\<(?P<uri>.*?)\>\s)?\((?P<not>Not\s)?(?:\<(?P<token>[^\>]*)\>)?(?:\s?)(?:\[(?P<etag>[^\]]*)\])?\)/im';
+ preg_match_all($regex,$header,$matches,PREG_SET_ORDER);
+
+ $conditions = array();
+
+ foreach($matches as $match) {
+
+ $condition = array(
+ 'uri' => $match['uri'],
+ 'tokens' => array(
+ array($match['not']?0:1,$match['token'],isset($match['etag'])?$match['etag']:'')
+ ),
+ );
+
+ if (!$condition['uri'] && count($conditions)) $conditions[count($conditions)-1]['tokens'][] = array(
+ $match['not']?0:1,
+ $match['token'],
+ isset($match['etag'])?$match['etag']:''
+ );
+ else {
+ $conditions[] = $condition;
+ }
+
+ }
+
+ return $conditions;
+
+ }
+
+ /**
+ * Parses a webdav lock xml body, and returns a new Sabre_DAV_Locks_LockInfo object
+ *
+ * @param string $body
+ * @return Sabre_DAV_Locks_LockInfo
+ */
+ protected function parseLockRequest($body) {
+
+ $xml = simplexml_load_string($body,null,LIBXML_NOWARNING);
+ $xml->registerXPathNamespace('d','DAV:');
+ $lockInfo = new Sabre_DAV_Locks_LockInfo();
+
+ $children = $xml->children("DAV:");
+ $lockInfo->owner = (string)$children->owner;
+
+ $lockInfo->token = Sabre_DAV_UUIDUtil::getUUID();
+ $lockInfo->scope = count($xml->xpath('d:lockscope/d:exclusive'))>0?Sabre_DAV_Locks_LockInfo::EXCLUSIVE:Sabre_DAV_Locks_LockInfo::SHARED;
+
+ return $lockInfo;
+
+ }
+
+
+}
diff --git a/3rdparty/Sabre/DAV/Mount/Plugin.php b/3rdparty/Sabre/DAV/Mount/Plugin.php
new file mode 100755
index 00000000000..b37a90ae993
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Mount/Plugin.php
@@ -0,0 +1,80 @@
+<?php
+
+/**
+ * This plugin provides support for RFC4709: Mounting WebDAV servers
+ *
+ * Simply append ?mount to any collection to generate the davmount response.
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ */
+class Sabre_DAV_Mount_Plugin extends Sabre_DAV_ServerPlugin {
+
+ /**
+ * Reference to Server class
+ *
+ * @var Sabre_DAV_Server
+ */
+ private $server;
+
+ /**
+ * Initializes the plugin and registers event handles
+ *
+ * @param Sabre_DAV_Server $server
+ * @return void
+ */
+ public function initialize(Sabre_DAV_Server $server) {
+
+ $this->server = $server;
+ $this->server->subscribeEvent('beforeMethod',array($this,'beforeMethod'), 90);
+
+ }
+
+ /**
+ * 'beforeMethod' event handles. This event handles intercepts GET requests ending
+ * with ?mount
+ *
+ * @param string $method
+ * @param string $uri
+ * @return bool
+ */
+ public function beforeMethod($method, $uri) {
+
+ if ($method!='GET') return;
+ if ($this->server->httpRequest->getQueryString()!='mount') return;
+
+ $currentUri = $this->server->httpRequest->getAbsoluteUri();
+
+ // Stripping off everything after the ?
+ list($currentUri) = explode('?',$currentUri);
+
+ $this->davMount($currentUri);
+
+ // Returning false to break the event chain
+ return false;
+
+ }
+
+ /**
+ * Generates the davmount response
+ *
+ * @param string $uri absolute uri
+ * @return void
+ */
+ public function davMount($uri) {
+
+ $this->server->httpResponse->sendStatus(200);
+ $this->server->httpResponse->setHeader('Content-Type','application/davmount+xml');
+ ob_start();
+ echo '<?xml version="1.0"?>', "\n";
+ echo "<dm:mount xmlns:dm=\"http://purl.org/NET/webdav/mount\">\n";
+ echo " <dm:url>", htmlspecialchars($uri, ENT_NOQUOTES, 'UTF-8'), "</dm:url>\n";
+ echo "</dm:mount>";
+ $this->server->httpResponse->sendBody(ob_get_clean());
+
+ }
+
+
+}
diff --git a/3rdparty/Sabre/DAV/Node.php b/3rdparty/Sabre/DAV/Node.php
new file mode 100755
index 00000000000..070b7176afd
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Node.php
@@ -0,0 +1,55 @@
+<?php
+
+/**
+ * Node class
+ *
+ * This is a helper class, that should aid in getting nodes setup.
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+abstract class Sabre_DAV_Node implements Sabre_DAV_INode {
+
+ /**
+ * Returns the last modification time
+ *
+ * In this case, it will simply return the current time
+ *
+ * @return int
+ */
+ public function getLastModified() {
+
+ return time();
+
+ }
+
+ /**
+ * Deleted 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');
+
+ }
+
+}
+
diff --git a/3rdparty/Sabre/DAV/ObjectTree.php b/3rdparty/Sabre/DAV/ObjectTree.php
new file mode 100755
index 00000000000..bce51463900
--- /dev/null
+++ b/3rdparty/Sabre/DAV/ObjectTree.php
@@ -0,0 +1,153 @@
+<?php
+
+/**
+ * ObjectTree class
+ *
+ * This implementation of the Tree class makes use of the INode, IFile and ICollection API's
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_ObjectTree extends Sabre_DAV_Tree {
+
+ /**
+ * The root node
+ *
+ * @var Sabre_DAV_ICollection
+ */
+ protected $rootNode;
+
+ /**
+ * This is the node cache. Accessed nodes are stored here
+ *
+ * @var array
+ */
+ protected $cache = array();
+
+ /**
+ * Creates the object
+ *
+ * This method expects the rootObject to be passed as a parameter
+ *
+ * @param Sabre_DAV_ICollection $rootNode
+ */
+ public function __construct(Sabre_DAV_ICollection $rootNode) {
+
+ $this->rootNode = $rootNode;
+
+ }
+
+ /**
+ * Returns the INode object for the requested path
+ *
+ * @param string $path
+ * @return Sabre_DAV_INode
+ */
+ public function getNodeForPath($path) {
+
+ $path = trim($path,'/');
+ if (isset($this->cache[$path])) return $this->cache[$path];
+
+ //if (!$path || $path=='.') return $this->rootNode;
+ $currentNode = $this->rootNode;
+
+ // We're splitting up the path variable into folder/subfolder components and traverse to the correct node..
+ foreach(explode('/',$path) as $pathPart) {
+
+ // If this part of the path is just a dot, it actually means we can skip it
+ if ($pathPart=='.' || $pathPart=='') continue;
+
+ if (!($currentNode instanceof Sabre_DAV_ICollection))
+ throw new Sabre_DAV_Exception_NotFound('Could not find node at path: ' . $path);
+
+ $currentNode = $currentNode->getChild($pathPart);
+
+ }
+
+ $this->cache[$path] = $currentNode;
+ return $currentNode;
+
+ }
+
+ /**
+ * This function allows you to check if a node exists.
+ *
+ * @param string $path
+ * @return bool
+ */
+ public function nodeExists($path) {
+
+ try {
+
+ // The root always exists
+ if ($path==='') return true;
+
+ list($parent, $base) = Sabre_DAV_URLUtil::splitPath($path);
+
+ $parentNode = $this->getNodeForPath($parent);
+ if (!$parentNode instanceof Sabre_DAV_ICollection) return false;
+ return $parentNode->childExists($base);
+
+ } catch (Sabre_DAV_Exception_NotFound $e) {
+
+ return false;
+
+ }
+
+ }
+
+ /**
+ * Returns a list of childnodes for a given path.
+ *
+ * @param string $path
+ * @return array
+ */
+ public function getChildren($path) {
+
+ $node = $this->getNodeForPath($path);
+ $children = $node->getChildren();
+ foreach($children as $child) {
+
+ $this->cache[trim($path,'/') . '/' . $child->getName()] = $child;
+
+ }
+ return $children;
+
+ }
+
+ /**
+ * This method is called with every tree update
+ *
+ * Examples of tree updates are:
+ * * node deletions
+ * * node creations
+ * * copy
+ * * move
+ * * renaming nodes
+ *
+ * If Tree classes implement a form of caching, this will allow
+ * them to make sure caches will be expired.
+ *
+ * If a path is passed, it is assumed that the entire subtree is dirty
+ *
+ * @param string $path
+ * @return void
+ */
+ public function markDirty($path) {
+
+ // We don't care enough about sub-paths
+ // flushing the entire cache
+ $path = trim($path,'/');
+ foreach($this->cache as $nodePath=>$node) {
+ if ($nodePath == $path || strpos($nodePath,$path.'/')===0)
+ unset($this->cache[$nodePath]);
+
+ }
+
+ }
+
+}
+
diff --git a/3rdparty/Sabre/DAV/Property.php b/3rdparty/Sabre/DAV/Property.php
new file mode 100755
index 00000000000..1cfada3236c
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Property.php
@@ -0,0 +1,25 @@
+<?php
+
+/**
+ * Abstract property class
+ *
+ * Extend this class to create custom complex properties
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+abstract class Sabre_DAV_Property {
+
+ abstract function serialize(Sabre_DAV_Server $server, DOMElement $prop);
+
+ static function unserialize(DOMElement $prop) {
+
+ throw new Sabre_DAV_Exception('Unserialize has not been implemented for this class');
+
+ }
+
+}
+
diff --git a/3rdparty/Sabre/DAV/Property/GetLastModified.php b/3rdparty/Sabre/DAV/Property/GetLastModified.php
new file mode 100755
index 00000000000..bd63f573140
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Property/GetLastModified.php
@@ -0,0 +1,75 @@
+<?php
+
+/**
+ * This property represents the {DAV:}getlastmodified property.
+ *
+ * Although this is normally a simple property, windows requires us to add
+ * some new attributes.
+ *
+ * This class uses unix timestamps internally, and converts them to RFC 1123 times for
+ * serialization
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_Property_GetLastModified extends Sabre_DAV_Property {
+
+ /**
+ * time
+ *
+ * @var int
+ */
+ public $time;
+
+ /**
+ * __construct
+ *
+ * @param int|DateTime $time
+ */
+ public function __construct($time) {
+
+ if ($time instanceof DateTime) {
+ $this->time = $time;
+ } elseif (is_int($time) || ctype_digit($time)) {
+ $this->time = new DateTime('@' . $time);
+ } else {
+ $this->time = new DateTime($time);
+ }
+
+ // Setting timezone to UTC
+ $this->time->setTimezone(new DateTimeZone('UTC'));
+
+ }
+
+ /**
+ * serialize
+ *
+ * @param Sabre_DAV_Server $server
+ * @param DOMElement $prop
+ * @return void
+ */
+ public function serialize(Sabre_DAV_Server $server, DOMElement $prop) {
+
+ $doc = $prop->ownerDocument;
+ $prop->setAttribute('xmlns:b','urn:uuid:c2f41010-65b3-11d1-a29f-00aa00c14882/');
+ $prop->setAttribute('b:dt','dateTime.rfc1123');
+ $prop->nodeValue = Sabre_HTTP_Util::toHTTPDate($this->time);
+
+ }
+
+ /**
+ * getTime
+ *
+ * @return DateTime
+ */
+ public function getTime() {
+
+ return $this->time;
+
+ }
+
+}
+
diff --git a/3rdparty/Sabre/DAV/Property/Href.php b/3rdparty/Sabre/DAV/Property/Href.php
new file mode 100755
index 00000000000..dac564f24d7
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Property/Href.php
@@ -0,0 +1,91 @@
+<?php
+
+/**
+ * Href property
+ *
+ * The href property represents a url within a {DAV:}href element.
+ * This is used by many WebDAV extensions, but not really within the WebDAV core spec
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_Property_Href extends Sabre_DAV_Property implements Sabre_DAV_Property_IHref {
+
+ /**
+ * href
+ *
+ * @var string
+ */
+ private $href;
+
+ /**
+ * Automatically prefix the url with the server base directory
+ *
+ * @var bool
+ */
+ private $autoPrefix = true;
+
+ /**
+ * __construct
+ *
+ * @param string $href
+ * @param bool $autoPrefix
+ */
+ public function __construct($href, $autoPrefix = true) {
+
+ $this->href = $href;
+ $this->autoPrefix = $autoPrefix;
+
+ }
+
+ /**
+ * Returns the uri
+ *
+ * @return string
+ */
+ public function getHref() {
+
+ return $this->href;
+
+ }
+
+ /**
+ * Serializes this property.
+ *
+ * It will additionally prepend the href property with the server's base uri.
+ *
+ * @param Sabre_DAV_Server $server
+ * @param DOMElement $dom
+ * @return void
+ */
+ public function serialize(Sabre_DAV_Server $server, DOMElement $dom) {
+
+ $prefix = $server->xmlNamespaces['DAV:'];
+
+ $elem = $dom->ownerDocument->createElement($prefix . ':href');
+ $elem->nodeValue = ($this->autoPrefix?$server->getBaseUri():'') . $this->href;
+ $dom->appendChild($elem);
+
+ }
+
+ /**
+ * Unserializes this property from a DOM Element
+ *
+ * This method returns an instance of this class.
+ * It will only decode {DAV:}href values. For non-compatible elements null will be returned.
+ *
+ * @param DOMElement $dom
+ * @return Sabre_DAV_Property_Href
+ */
+ static function unserialize(DOMElement $dom) {
+
+ if (Sabre_DAV_XMLUtil::toClarkNotation($dom->firstChild)==='{DAV:}href') {
+ return new self($dom->firstChild->textContent,false);
+ }
+
+ }
+
+}
diff --git a/3rdparty/Sabre/DAV/Property/HrefList.php b/3rdparty/Sabre/DAV/Property/HrefList.php
new file mode 100755
index 00000000000..7a52272e885
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Property/HrefList.php
@@ -0,0 +1,96 @@
+<?php
+
+/**
+ * HrefList property
+ *
+ * This property contains multiple {DAV:}href elements, each containing a url.
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_Property_HrefList extends Sabre_DAV_Property {
+
+ /**
+ * hrefs
+ *
+ * @var array
+ */
+ private $hrefs;
+
+ /**
+ * Automatically prefix the url with the server base directory
+ *
+ * @var bool
+ */
+ private $autoPrefix = true;
+
+ /**
+ * __construct
+ *
+ * @param array $hrefs
+ * @param bool $autoPrefix
+ */
+ public function __construct(array $hrefs, $autoPrefix = true) {
+
+ $this->hrefs = $hrefs;
+ $this->autoPrefix = $autoPrefix;
+
+ }
+
+ /**
+ * Returns the uris
+ *
+ * @return array
+ */
+ public function getHrefs() {
+
+ return $this->hrefs;
+
+ }
+
+ /**
+ * Serializes this property.
+ *
+ * It will additionally prepend the href property with the server's base uri.
+ *
+ * @param Sabre_DAV_Server $server
+ * @param DOMElement $dom
+ * @return void
+ */
+ public function serialize(Sabre_DAV_Server $server,DOMElement $dom) {
+
+ $prefix = $server->xmlNamespaces['DAV:'];
+
+ foreach($this->hrefs as $href) {
+ $elem = $dom->ownerDocument->createElement($prefix . ':href');
+ $elem->nodeValue = ($this->autoPrefix?$server->getBaseUri():'') . $href;
+ $dom->appendChild($elem);
+ }
+
+ }
+
+ /**
+ * Unserializes this property from a DOM Element
+ *
+ * This method returns an instance of this class.
+ * It will only decode {DAV:}href values.
+ *
+ * @param DOMElement $dom
+ * @return Sabre_DAV_Property_Href
+ */
+ static function unserialize(DOMElement $dom) {
+
+ $hrefs = array();
+ foreach($dom->childNodes as $child) {
+ if (Sabre_DAV_XMLUtil::toClarkNotation($child)==='{DAV:}href') {
+ $hrefs[] = $child->textContent;
+ }
+ }
+ return new self($hrefs, false);
+
+ }
+
+}
diff --git a/3rdparty/Sabre/DAV/Property/IHref.php b/3rdparty/Sabre/DAV/Property/IHref.php
new file mode 100755
index 00000000000..5c0409064cb
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Property/IHref.php
@@ -0,0 +1,25 @@
+<?php
+
+/**
+ * IHref interface
+ *
+ * Any property implementing this interface can expose a related url.
+ * This is used by certain subsystems to aquire more information about for example
+ * the owner of a file
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+interface Sabre_DAV_Property_IHref {
+
+ /**
+ * getHref
+ *
+ * @return string
+ */
+ function getHref();
+
+}
diff --git a/3rdparty/Sabre/DAV/Property/LockDiscovery.php b/3rdparty/Sabre/DAV/Property/LockDiscovery.php
new file mode 100755
index 00000000000..2ded5649a44
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Property/LockDiscovery.php
@@ -0,0 +1,102 @@
+<?php
+
+/**
+ * Represents {DAV:}lockdiscovery property
+ *
+ * This property contains all the open locks on a given resource
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_Property_LockDiscovery extends Sabre_DAV_Property {
+
+ /**
+ * locks
+ *
+ * @var array
+ */
+ public $locks;
+
+ /**
+ * Should we show the locktoken as well?
+ *
+ * @var bool
+ */
+ public $revealLockToken;
+
+ /**
+ * Hides the {DAV:}lockroot element from the response.
+ *
+ * It was reported that showing the lockroot in the response can break
+ * Office 2000 compatibility.
+ */
+ static public $hideLockRoot = false;
+
+ /**
+ * __construct
+ *
+ * @param array $locks
+ * @param bool $revealLockToken
+ */
+ public function __construct($locks, $revealLockToken = false) {
+
+ $this->locks = $locks;
+ $this->revealLockToken = $revealLockToken;
+
+ }
+
+ /**
+ * serialize
+ *
+ * @param Sabre_DAV_Server $server
+ * @param DOMElement $prop
+ * @return void
+ */
+ public function serialize(Sabre_DAV_Server $server, DOMElement $prop) {
+
+ $doc = $prop->ownerDocument;
+
+ foreach($this->locks as $lock) {
+
+ $activeLock = $doc->createElementNS('DAV:','d:activelock');
+ $prop->appendChild($activeLock);
+
+ $lockScope = $doc->createElementNS('DAV:','d:lockscope');
+ $activeLock->appendChild($lockScope);
+
+ $lockScope->appendChild($doc->createElementNS('DAV:','d:' . ($lock->scope==Sabre_DAV_Locks_LockInfo::EXCLUSIVE?'exclusive':'shared')));
+
+ $lockType = $doc->createElementNS('DAV:','d:locktype');
+ $activeLock->appendChild($lockType);
+
+ $lockType->appendChild($doc->createElementNS('DAV:','d:write'));
+
+ /* {DAV:}lockroot */
+ if (!self::$hideLockRoot) {
+ $lockRoot = $doc->createElementNS('DAV:','d:lockroot');
+ $activeLock->appendChild($lockRoot);
+ $href = $doc->createElementNS('DAV:','d:href');
+ $href->appendChild($doc->createTextNode($server->getBaseUri() . $lock->uri));
+ $lockRoot->appendChild($href);
+ }
+
+ $activeLock->appendChild($doc->createElementNS('DAV:','d:depth',($lock->depth == Sabre_DAV_Server::DEPTH_INFINITY?'infinity':$lock->depth)));
+ $activeLock->appendChild($doc->createElementNS('DAV:','d:timeout','Second-' . $lock->timeout));
+
+ if ($this->revealLockToken) {
+ $lockToken = $doc->createElementNS('DAV:','d:locktoken');
+ $activeLock->appendChild($lockToken);
+ $lockToken->appendChild($doc->createElementNS('DAV:','d:href','opaquelocktoken:' . $lock->token));
+ }
+
+ $activeLock->appendChild($doc->createElementNS('DAV:','d:owner',$lock->owner));
+
+ }
+
+ }
+
+}
+
diff --git a/3rdparty/Sabre/DAV/Property/ResourceType.php b/3rdparty/Sabre/DAV/Property/ResourceType.php
new file mode 100755
index 00000000000..f6269611e54
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Property/ResourceType.php
@@ -0,0 +1,125 @@
+<?php
+
+/**
+ * This class represents the {DAV:}resourcetype property
+ *
+ * Normally for files this is empty, and for collection {DAV:}collection.
+ * However, other specs define different values for this.
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_Property_ResourceType extends Sabre_DAV_Property {
+
+ /**
+ * resourceType
+ *
+ * @var array
+ */
+ public $resourceType = array();
+
+ /**
+ * __construct
+ *
+ * @param mixed $resourceType
+ */
+ public function __construct($resourceType = array()) {
+
+ if ($resourceType === Sabre_DAV_Server::NODE_FILE)
+ $this->resourceType = array();
+ elseif ($resourceType === Sabre_DAV_Server::NODE_DIRECTORY)
+ $this->resourceType = array('{DAV:}collection');
+ elseif (is_array($resourceType))
+ $this->resourceType = $resourceType;
+ else
+ $this->resourceType = array($resourceType);
+
+ }
+
+ /**
+ * serialize
+ *
+ * @param Sabre_DAV_Server $server
+ * @param DOMElement $prop
+ * @return void
+ */
+ public function serialize(Sabre_DAV_Server $server, DOMElement $prop) {
+
+ $propName = null;
+ $rt = $this->resourceType;
+
+ foreach($rt as $resourceType) {
+ if (preg_match('/^{([^}]*)}(.*)$/',$resourceType,$propName)) {
+
+ if (isset($server->xmlNamespaces[$propName[1]])) {
+ $prop->appendChild($prop->ownerDocument->createElement($server->xmlNamespaces[$propName[1]] . ':' . $propName[2]));
+ } else {
+ $prop->appendChild($prop->ownerDocument->createElementNS($propName[1],'custom:' . $propName[2]));
+ }
+
+ }
+ }
+
+ }
+
+ /**
+ * Returns the values in clark-notation
+ *
+ * For example array('{DAV:}collection')
+ *
+ * @return array
+ */
+ public function getValue() {
+
+ return $this->resourceType;
+
+ }
+
+ /**
+ * Checks if the principal contains a certain value
+ *
+ * @param string $type
+ * @return bool
+ */
+ public function is($type) {
+
+ return in_array($type, $this->resourceType);
+
+ }
+
+ /**
+ * Adds a resourcetype value to this property
+ *
+ * @param string $type
+ * @return void
+ */
+ public function add($type) {
+
+ $this->resourceType[] = $type;
+ $this->resourceType = array_unique($this->resourceType);
+
+ }
+
+ /**
+ * Unserializes a DOM element into a ResourceType property.
+ *
+ * @param DOMElement $dom
+ * @return Sabre_DAV_Property_ResourceType
+ */
+ static public function unserialize(DOMElement $dom) {
+
+ $value = array();
+ foreach($dom->childNodes as $child) {
+
+ $value[] = Sabre_DAV_XMLUtil::toClarkNotation($child);
+
+ }
+
+ return new self($value);
+
+ }
+
+}
diff --git a/3rdparty/Sabre/DAV/Property/Response.php b/3rdparty/Sabre/DAV/Property/Response.php
new file mode 100755
index 00000000000..88afbcfb26d
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Property/Response.php
@@ -0,0 +1,155 @@
+<?php
+
+/**
+ * Response property
+ *
+ * This class represents the {DAV:}response XML element.
+ * This is used by the Server class to encode individual items within a multistatus
+ * response.
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_Property_Response extends Sabre_DAV_Property implements Sabre_DAV_Property_IHref {
+
+ /**
+ * Url for the response
+ *
+ * @var string
+ */
+ private $href;
+
+ /**
+ * Propertylist, ordered by HTTP status code
+ *
+ * @var array
+ */
+ private $responseProperties;
+
+ /**
+ * The responseProperties argument is a list of properties
+ * within an array with keys representing HTTP status codes
+ *
+ * @param string $href
+ * @param array $responseProperties
+ */
+ public function __construct($href, array $responseProperties) {
+
+ $this->href = $href;
+ $this->responseProperties = $responseProperties;
+
+ }
+
+ /**
+ * Returns the url
+ *
+ * @return string
+ */
+ public function getHref() {
+
+ return $this->href;
+
+ }
+
+ /**
+ * Returns the property list
+ *
+ * @return array
+ */
+ public function getResponseProperties() {
+
+ return $this->responseProperties;
+
+ }
+
+ /**
+ * serialize
+ *
+ * @param Sabre_DAV_Server $server
+ * @param DOMElement $dom
+ * @return void
+ */
+ public function serialize(Sabre_DAV_Server $server, DOMElement $dom) {
+
+ $document = $dom->ownerDocument;
+ $properties = $this->responseProperties;
+
+ $xresponse = $document->createElement('d:response');
+ $dom->appendChild($xresponse);
+
+ $uri = Sabre_DAV_URLUtil::encodePath($this->href);
+
+ // Adding the baseurl to the beginning of the url
+ $uri = $server->getBaseUri() . $uri;
+
+ $xresponse->appendChild($document->createElement('d:href',$uri));
+
+ // The properties variable is an array containing properties, grouped by
+ // HTTP status
+ foreach($properties as $httpStatus=>$propertyGroup) {
+
+ // The 'href' is also in this array, and it's special cased.
+ // We will ignore it
+ if ($httpStatus=='href') continue;
+
+ // If there are no properties in this group, we can also just carry on
+ if (!count($propertyGroup)) continue;
+
+ $xpropstat = $document->createElement('d:propstat');
+ $xresponse->appendChild($xpropstat);
+
+ $xprop = $document->createElement('d:prop');
+ $xpropstat->appendChild($xprop);
+
+ $nsList = $server->xmlNamespaces;
+
+ foreach($propertyGroup as $propertyName=>$propertyValue) {
+
+ $propName = null;
+ preg_match('/^{([^}]*)}(.*)$/',$propertyName,$propName);
+
+ // special case for empty namespaces
+ if ($propName[1]=='') {
+
+ $currentProperty = $document->createElement($propName[2]);
+ $xprop->appendChild($currentProperty);
+ $currentProperty->setAttribute('xmlns','');
+
+ } else {
+
+ if (!isset($nsList[$propName[1]])) {
+ $nsList[$propName[1]] = 'x' . count($nsList);
+ }
+
+ // If the namespace was defined in the top-level xml namespaces, it means
+ // there was already a namespace declaration, and we don't have to worry about it.
+ if (isset($server->xmlNamespaces[$propName[1]])) {
+ $currentProperty = $document->createElement($nsList[$propName[1]] . ':' . $propName[2]);
+ } else {
+ $currentProperty = $document->createElementNS($propName[1],$nsList[$propName[1]].':' . $propName[2]);
+ }
+ $xprop->appendChild($currentProperty);
+
+ }
+
+ if (is_scalar($propertyValue)) {
+ $text = $document->createTextNode($propertyValue);
+ $currentProperty->appendChild($text);
+ } elseif ($propertyValue instanceof Sabre_DAV_Property) {
+ $propertyValue->serialize($server,$currentProperty);
+ } elseif (!is_null($propertyValue)) {
+ throw new Sabre_DAV_Exception('Unknown property value type: ' . gettype($propertyValue) . ' for property: ' . $propertyName);
+ }
+
+ }
+
+ $xpropstat->appendChild($document->createElement('d:status',$server->httpResponse->getStatusMessage($httpStatus)));
+
+ }
+
+ }
+
+}
diff --git a/3rdparty/Sabre/DAV/Property/ResponseList.php b/3rdparty/Sabre/DAV/Property/ResponseList.php
new file mode 100755
index 00000000000..cae923afbf9
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Property/ResponseList.php
@@ -0,0 +1,57 @@
+<?php
+
+/**
+ * ResponseList property
+ *
+ * This class represents multiple {DAV:}response XML elements.
+ * This is used by the Server class to encode items within a multistatus
+ * response.
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_Property_ResponseList extends Sabre_DAV_Property {
+
+ /**
+ * Response objects.
+ *
+ * @var array
+ */
+ private $responses;
+
+ /**
+ * The only valid argument is a list of Sabre_DAV_Property_Response
+ * objects.
+ *
+ * @param array $responses;
+ */
+ public function __construct($responses) {
+
+ foreach($responses as $response) {
+ if (!($response instanceof Sabre_DAV_Property_Response)) {
+ throw new InvalidArgumentException('You must pass an array of Sabre_DAV_Property_Response objects');
+ }
+ }
+ $this->responses = $responses;
+
+ }
+
+ /**
+ * serialize
+ *
+ * @param Sabre_DAV_Server $server
+ * @param DOMElement $dom
+ * @return void
+ */
+ public function serialize(Sabre_DAV_Server $server,DOMElement $dom) {
+
+ foreach($this->responses as $response) {
+ $response->serialize($server, $dom);
+ }
+
+ }
+
+}
diff --git a/3rdparty/Sabre/DAV/Property/SupportedLock.php b/3rdparty/Sabre/DAV/Property/SupportedLock.php
new file mode 100755
index 00000000000..4e3aaf23a1a
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Property/SupportedLock.php
@@ -0,0 +1,76 @@
+<?php
+
+/**
+ * This class represents the {DAV:}supportedlock property
+ *
+ * This property contains information about what kind of locks
+ * this server supports.
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_Property_SupportedLock extends Sabre_DAV_Property {
+
+ /**
+ * supportsLocks
+ *
+ * @var mixed
+ */
+ public $supportsLocks = false;
+
+ /**
+ * __construct
+ *
+ * @param mixed $supportsLocks
+ */
+ public function __construct($supportsLocks) {
+
+ $this->supportsLocks = $supportsLocks;
+
+ }
+
+ /**
+ * serialize
+ *
+ * @param Sabre_DAV_Server $server
+ * @param DOMElement $prop
+ * @return void
+ */
+ public function serialize(Sabre_DAV_Server $server,DOMElement $prop) {
+
+ $doc = $prop->ownerDocument;
+
+ if (!$this->supportsLocks) return null;
+
+ $lockEntry1 = $doc->createElementNS('DAV:','d:lockentry');
+ $lockEntry2 = $doc->createElementNS('DAV:','d:lockentry');
+
+ $prop->appendChild($lockEntry1);
+ $prop->appendChild($lockEntry2);
+
+ $lockScope1 = $doc->createElementNS('DAV:','d:lockscope');
+ $lockScope2 = $doc->createElementNS('DAV:','d:lockscope');
+ $lockType1 = $doc->createElementNS('DAV:','d:locktype');
+ $lockType2 = $doc->createElementNS('DAV:','d:locktype');
+
+ $lockEntry1->appendChild($lockScope1);
+ $lockEntry1->appendChild($lockType1);
+ $lockEntry2->appendChild($lockScope2);
+ $lockEntry2->appendChild($lockType2);
+
+ $lockScope1->appendChild($doc->createElementNS('DAV:','d:exclusive'));
+ $lockScope2->appendChild($doc->createElementNS('DAV:','d:shared'));
+
+ $lockType1->appendChild($doc->createElementNS('DAV:','d:write'));
+ $lockType2->appendChild($doc->createElementNS('DAV:','d:write'));
+
+ //$frag->appendXML('<d:lockentry><d:lockscope><d:exclusive /></d:lockscope><d:locktype><d:write /></d:locktype></d:lockentry>');
+ //$frag->appendXML('<d:lockentry><d:lockscope><d:shared /></d:lockscope><d:locktype><d:write /></d:locktype></d:lockentry>');
+
+ }
+
+}
+
diff --git a/3rdparty/Sabre/DAV/Property/SupportedReportSet.php b/3rdparty/Sabre/DAV/Property/SupportedReportSet.php
new file mode 100755
index 00000000000..e62699f3b5a
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Property/SupportedReportSet.php
@@ -0,0 +1,109 @@
+<?php
+
+/**
+ * supported-report-set property.
+ *
+ * This property is defined in RFC3253, but since it's
+ * so common in other webdav-related specs, it is part of the core server.
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_Property_SupportedReportSet extends Sabre_DAV_Property {
+
+ /**
+ * List of reports
+ *
+ * @var array
+ */
+ protected $reports = array();
+
+ /**
+ * Creates the property
+ *
+ * Any reports passed in the constructor
+ * should be valid report-types in clark-notation.
+ *
+ * Either a string or an array of strings must be passed.
+ *
+ * @param mixed $reports
+ */
+ public function __construct($reports = null) {
+
+ if (!is_null($reports))
+ $this->addReport($reports);
+
+ }
+
+ /**
+ * Adds a report to this property
+ *
+ * The report must be a string in clark-notation.
+ * Multiple reports can be specified as an array.
+ *
+ * @param mixed $report
+ * @return void
+ */
+ public function addReport($report) {
+
+ if (!is_array($report)) $report = array($report);
+
+ foreach($report as $r) {
+
+ if (!preg_match('/^{([^}]*)}(.*)$/',$r))
+ throw new Sabre_DAV_Exception('Reportname must be in clark-notation');
+
+ $this->reports[] = $r;
+
+ }
+
+ }
+
+ /**
+ * Returns the list of supported reports
+ *
+ * @return array
+ */
+ public function getValue() {
+
+ return $this->reports;
+
+ }
+
+ /**
+ * Serializes the node
+ *
+ * @param Sabre_DAV_Server $server
+ * @param DOMElement $prop
+ * @return void
+ */
+ public function serialize(Sabre_DAV_Server $server, DOMElement $prop) {
+
+ foreach($this->reports as $reportName) {
+
+ $supportedReport = $prop->ownerDocument->createElement('d:supported-report');
+ $prop->appendChild($supportedReport);
+
+ $report = $prop->ownerDocument->createElement('d:report');
+ $supportedReport->appendChild($report);
+
+ preg_match('/^{([^}]*)}(.*)$/',$reportName,$matches);
+
+ list(, $namespace, $element) = $matches;
+
+ $prefix = isset($server->xmlNamespaces[$namespace])?$server->xmlNamespaces[$namespace]:null;
+
+ if ($prefix) {
+ $report->appendChild($prop->ownerDocument->createElement($prefix . ':' . $element));
+ } else {
+ $report->appendChild($prop->ownerDocument->createElementNS($namespace, 'x:' . $element));
+ }
+
+ }
+
+ }
+
+}
diff --git a/3rdparty/Sabre/DAV/Server.php b/3rdparty/Sabre/DAV/Server.php
new file mode 100755
index 00000000000..0dfac8b0c71
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Server.php
@@ -0,0 +1,2006 @@
+<?php
+
+/**
+ * Main DAV server class
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_Server {
+
+ /**
+ * Inifinity is used for some request supporting the HTTP Depth header and indicates that the operation should traverse the entire tree
+ */
+ const DEPTH_INFINITY = -1;
+
+ /**
+ * Nodes that are files, should have this as the type property
+ */
+ const NODE_FILE = 1;
+
+ /**
+ * Nodes that are directories, should use this value as the type property
+ */
+ const NODE_DIRECTORY = 2;
+
+ /**
+ * XML namespace for all SabreDAV related elements
+ */
+ const NS_SABREDAV = 'http://sabredav.org/ns';
+
+ /**
+ * The tree object
+ *
+ * @var Sabre_DAV_Tree
+ */
+ public $tree;
+
+ /**
+ * The base uri
+ *
+ * @var string
+ */
+ protected $baseUri = null;
+
+ /**
+ * httpResponse
+ *
+ * @var Sabre_HTTP_Response
+ */
+ public $httpResponse;
+
+ /**
+ * httpRequest
+ *
+ * @var Sabre_HTTP_Request
+ */
+ public $httpRequest;
+
+ /**
+ * The list of plugins
+ *
+ * @var array
+ */
+ protected $plugins = array();
+
+ /**
+ * This array contains a list of callbacks we should call when certain events are triggered
+ *
+ * @var array
+ */
+ protected $eventSubscriptions = array();
+
+ /**
+ * This is a default list of namespaces.
+ *
+ * If you are defining your own custom namespace, add it here to reduce
+ * bandwidth and improve legibility of xml bodies.
+ *
+ * @var array
+ */
+ public $xmlNamespaces = array(
+ 'DAV:' => 'd',
+ 'http://sabredav.org/ns' => 's',
+ );
+
+ /**
+ * The propertymap can be used to map properties from
+ * requests to property classes.
+ *
+ * @var array
+ */
+ public $propertyMap = array(
+ '{DAV:}resourcetype' => 'Sabre_DAV_Property_ResourceType',
+ );
+
+ public $protectedProperties = array(
+ // RFC4918
+ '{DAV:}getcontentlength',
+ '{DAV:}getetag',
+ '{DAV:}getlastmodified',
+ '{DAV:}lockdiscovery',
+ '{DAV:}resourcetype',
+ '{DAV:}supportedlock',
+
+ // RFC4331
+ '{DAV:}quota-available-bytes',
+ '{DAV:}quota-used-bytes',
+
+ // RFC3744
+ '{DAV:}supported-privilege-set',
+ '{DAV:}current-user-privilege-set',
+ '{DAV:}acl',
+ '{DAV:}acl-restrictions',
+ '{DAV:}inherited-acl-set',
+
+ );
+
+ /**
+ * This is a flag that allow or not showing file, line and code
+ * of the exception in the returned XML
+ *
+ * @var bool
+ */
+ public $debugExceptions = false;
+
+ /**
+ * This property allows you to automatically add the 'resourcetype' value
+ * based on a node's classname or interface.
+ *
+ * The preset ensures that {DAV:}collection is automaticlly added for nodes
+ * implementing Sabre_DAV_ICollection.
+ *
+ * @var array
+ */
+ public $resourceTypeMapping = array(
+ 'Sabre_DAV_ICollection' => '{DAV:}collection',
+ );
+
+ /**
+ * If this setting is turned off, SabreDAV's version number will be hidden
+ * from various places.
+ *
+ * Some people feel this is a good security measure.
+ *
+ * @var bool
+ */
+ static public $exposeVersion = true;
+
+ /**
+ * Sets up the server
+ *
+ * If a Sabre_DAV_Tree object is passed as an argument, it will
+ * use it as the directory tree. If a Sabre_DAV_INode is passed, it
+ * will create a Sabre_DAV_ObjectTree and use the node as the root.
+ *
+ * If nothing is passed, a Sabre_DAV_SimpleCollection is created in
+ * a Sabre_DAV_ObjectTree.
+ *
+ * If an array is passed, we automatically create a root node, and use
+ * the nodes in the array as top-level children.
+ *
+ * @param Sabre_DAV_Tree|Sabre_DAV_INode|null $treeOrNode The tree object
+ */
+ public function __construct($treeOrNode = null) {
+
+ if ($treeOrNode instanceof Sabre_DAV_Tree) {
+ $this->tree = $treeOrNode;
+ } elseif ($treeOrNode instanceof Sabre_DAV_INode) {
+ $this->tree = new Sabre_DAV_ObjectTree($treeOrNode);
+ } elseif (is_array($treeOrNode)) {
+
+ // If it's an array, a list of nodes was passed, and we need to
+ // create the root node.
+ foreach($treeOrNode as $node) {
+ if (!($node instanceof Sabre_DAV_INode)) {
+ throw new Sabre_DAV_Exception('Invalid argument passed to constructor. If you\'re passing an array, all the values must implement Sabre_DAV_INode');
+ }
+ }
+
+ $root = new Sabre_DAV_SimpleCollection('root', $treeOrNode);
+ $this->tree = new Sabre_DAV_ObjectTree($root);
+
+ } elseif (is_null($treeOrNode)) {
+ $root = new Sabre_DAV_SimpleCollection('root');
+ $this->tree = new Sabre_DAV_ObjectTree($root);
+ } else {
+ throw new Sabre_DAV_Exception('Invalid argument passed to constructor. Argument must either be an instance of Sabre_DAV_Tree, Sabre_DAV_INode, an array or null');
+ }
+ $this->httpResponse = new Sabre_HTTP_Response();
+ $this->httpRequest = new Sabre_HTTP_Request();
+
+ }
+
+ /**
+ * Starts the DAV Server
+ *
+ * @return void
+ */
+ public function exec() {
+
+ try {
+
+ $this->invokeMethod($this->httpRequest->getMethod(), $this->getRequestUri());
+
+ } catch (Exception $e) {
+
+ $DOM = new DOMDocument('1.0','utf-8');
+ $DOM->formatOutput = true;
+
+ $error = $DOM->createElementNS('DAV:','d:error');
+ $error->setAttribute('xmlns:s',self::NS_SABREDAV);
+ $DOM->appendChild($error);
+
+ $error->appendChild($DOM->createElement('s:exception',get_class($e)));
+ $error->appendChild($DOM->createElement('s:message',$e->getMessage()));
+ if ($this->debugExceptions) {
+ $error->appendChild($DOM->createElement('s:file',$e->getFile()));
+ $error->appendChild($DOM->createElement('s:line',$e->getLine()));
+ $error->appendChild($DOM->createElement('s:code',$e->getCode()));
+ $error->appendChild($DOM->createElement('s:stacktrace',$e->getTraceAsString()));
+
+ }
+ if (self::$exposeVersion) {
+ $error->appendChild($DOM->createElement('s:sabredav-version',Sabre_DAV_Version::VERSION));
+ }
+
+ if($e instanceof Sabre_DAV_Exception) {
+
+ $httpCode = $e->getHTTPCode();
+ $e->serialize($this,$error);
+ $headers = $e->getHTTPHeaders($this);
+
+ } else {
+
+ $httpCode = 500;
+ $headers = array();
+
+ }
+ $headers['Content-Type'] = 'application/xml; charset=utf-8';
+
+ $this->httpResponse->sendStatus($httpCode);
+ $this->httpResponse->setHeaders($headers);
+ $this->httpResponse->sendBody($DOM->saveXML());
+
+ }
+
+ }
+
+ /**
+ * Sets the base server uri
+ *
+ * @param string $uri
+ * @return void
+ */
+ public function setBaseUri($uri) {
+
+ // If the baseUri does not end with a slash, we must add it
+ if ($uri[strlen($uri)-1]!=='/')
+ $uri.='/';
+
+ $this->baseUri = $uri;
+
+ }
+
+ /**
+ * Returns the base responding uri
+ *
+ * @return string
+ */
+ public function getBaseUri() {
+
+ if (is_null($this->baseUri)) $this->baseUri = $this->guessBaseUri();
+ return $this->baseUri;
+
+ }
+
+ /**
+ * This method attempts to detect the base uri.
+ * Only the PATH_INFO variable is considered.
+ *
+ * If this variable is not set, the root (/) is assumed.
+ *
+ * @return string
+ */
+ public function guessBaseUri() {
+
+ $pathInfo = $this->httpRequest->getRawServerValue('PATH_INFO');
+ $uri = $this->httpRequest->getRawServerValue('REQUEST_URI');
+
+ // If PATH_INFO is found, we can assume it's accurate.
+ if (!empty($pathInfo)) {
+
+ // We need to make sure we ignore the QUERY_STRING part
+ if ($pos = strpos($uri,'?'))
+ $uri = substr($uri,0,$pos);
+
+ // PATH_INFO is only set for urls, such as: /example.php/path
+ // in that case PATH_INFO contains '/path'.
+ // Note that REQUEST_URI is percent encoded, while PATH_INFO is
+ // not, Therefore they are only comparable if we first decode
+ // REQUEST_INFO as well.
+ $decodedUri = Sabre_DAV_URLUtil::decodePath($uri);
+
+ // A simple sanity check:
+ if(substr($decodedUri,strlen($decodedUri)-strlen($pathInfo))===$pathInfo) {
+ $baseUri = substr($decodedUri,0,strlen($decodedUri)-strlen($pathInfo));
+ return rtrim($baseUri,'/') . '/';
+ }
+
+ throw new Sabre_DAV_Exception('The REQUEST_URI ('. $uri . ') did not end with the contents of PATH_INFO (' . $pathInfo . '). This server might be misconfigured.');
+
+ }
+
+ // The last fallback is that we're just going to assume the server root.
+ return '/';
+
+ }
+
+ /**
+ * Adds a plugin to the server
+ *
+ * For more information, console the documentation of Sabre_DAV_ServerPlugin
+ *
+ * @param Sabre_DAV_ServerPlugin $plugin
+ * @return void
+ */
+ public function addPlugin(Sabre_DAV_ServerPlugin $plugin) {
+
+ $this->plugins[$plugin->getPluginName()] = $plugin;
+ $plugin->initialize($this);
+
+ }
+
+ /**
+ * Returns an initialized plugin by it's name.
+ *
+ * This function returns null if the plugin was not found.
+ *
+ * @param string $name
+ * @return Sabre_DAV_ServerPlugin
+ */
+ public function getPlugin($name) {
+
+ if (isset($this->plugins[$name]))
+ return $this->plugins[$name];
+
+ // This is a fallback and deprecated.
+ foreach($this->plugins as $plugin) {
+ if (get_class($plugin)===$name) return $plugin;
+ }
+
+ return null;
+
+ }
+
+ /**
+ * Returns all plugins
+ *
+ * @return array
+ */
+ public function getPlugins() {
+
+ return $this->plugins;
+
+ }
+
+
+ /**
+ * Subscribe to an event.
+ *
+ * When the event is triggered, we'll call all the specified callbacks.
+ * It is possible to control the order of the callbacks through the
+ * priority argument.
+ *
+ * This is for example used to make sure that the authentication plugin
+ * is triggered before anything else. If it's not needed to change this
+ * number, it is recommended to ommit.
+ *
+ * @param string $event
+ * @param callback $callback
+ * @param int $priority
+ * @return void
+ */
+ public function subscribeEvent($event, $callback, $priority = 100) {
+
+ if (!isset($this->eventSubscriptions[$event])) {
+ $this->eventSubscriptions[$event] = array();
+ }
+ while(isset($this->eventSubscriptions[$event][$priority])) $priority++;
+ $this->eventSubscriptions[$event][$priority] = $callback;
+ ksort($this->eventSubscriptions[$event]);
+
+ }
+
+ /**
+ * Broadcasts an event
+ *
+ * This method will call all subscribers. If one of the subscribers returns false, the process stops.
+ *
+ * The arguments parameter will be sent to all subscribers
+ *
+ * @param string $eventName
+ * @param array $arguments
+ * @return bool
+ */
+ public function broadcastEvent($eventName,$arguments = array()) {
+
+ if (isset($this->eventSubscriptions[$eventName])) {
+
+ foreach($this->eventSubscriptions[$eventName] as $subscriber) {
+
+ $result = call_user_func_array($subscriber,$arguments);
+ if ($result===false) return false;
+
+ }
+
+ }
+
+ return true;
+
+ }
+
+ /**
+ * Handles a http request, and execute a method based on its name
+ *
+ * @param string $method
+ * @param string $uri
+ * @return void
+ */
+ public function invokeMethod($method, $uri) {
+
+ $method = strtoupper($method);
+
+ if (!$this->broadcastEvent('beforeMethod',array($method, $uri))) return;
+
+ // Make sure this is a HTTP method we support
+ $internalMethods = array(
+ 'OPTIONS',
+ 'GET',
+ 'HEAD',
+ 'DELETE',
+ 'PROPFIND',
+ 'MKCOL',
+ 'PUT',
+ 'PROPPATCH',
+ 'COPY',
+ 'MOVE',
+ 'REPORT'
+ );
+
+ if (in_array($method,$internalMethods)) {
+
+ call_user_func(array($this,'http' . $method), $uri);
+
+ } else {
+
+ if ($this->broadcastEvent('unknownMethod',array($method, $uri))) {
+ // Unsupported method
+ throw new Sabre_DAV_Exception_NotImplemented('There was no handler found for this "' . $method . '" method');
+ }
+
+ }
+
+ }
+
+ // {{{ HTTP Method implementations
+
+ /**
+ * HTTP OPTIONS
+ *
+ * @param string $uri
+ * @return void
+ */
+ protected function httpOptions($uri) {
+
+ $methods = $this->getAllowedMethods($uri);
+
+ $this->httpResponse->setHeader('Allow',strtoupper(implode(', ',$methods)));
+ $features = array('1','3', 'extended-mkcol');
+
+ foreach($this->plugins as $plugin) $features = array_merge($features,$plugin->getFeatures());
+
+ $this->httpResponse->setHeader('DAV',implode(', ',$features));
+ $this->httpResponse->setHeader('MS-Author-Via','DAV');
+ $this->httpResponse->setHeader('Accept-Ranges','bytes');
+ if (self::$exposeVersion) {
+ $this->httpResponse->setHeader('X-Sabre-Version',Sabre_DAV_Version::VERSION);
+ }
+ $this->httpResponse->setHeader('Content-Length',0);
+ $this->httpResponse->sendStatus(200);
+
+ }
+
+ /**
+ * HTTP GET
+ *
+ * This method simply fetches the contents of a uri, like normal
+ *
+ * @param string $uri
+ * @return bool
+ */
+ protected function httpGet($uri) {
+
+ $node = $this->tree->getNodeForPath($uri,0);
+
+ if (!$this->checkPreconditions(true)) return false;
+
+ if (!($node instanceof Sabre_DAV_IFile)) throw new Sabre_DAV_Exception_NotImplemented('GET is only implemented on File objects');
+ $body = $node->get();
+
+ // Converting string into stream, if needed.
+ if (is_string($body)) {
+ $stream = fopen('php://temp','r+');
+ fwrite($stream,$body);
+ rewind($stream);
+ $body = $stream;
+ }
+
+ /*
+ * TODO: getetag, getlastmodified, getsize should also be used using
+ * this method
+ */
+ $httpHeaders = $this->getHTTPHeaders($uri);
+
+ /* ContentType needs to get a default, because many webservers will otherwise
+ * default to text/html, and we don't want this for security reasons.
+ */
+ if (!isset($httpHeaders['Content-Type'])) {
+ $httpHeaders['Content-Type'] = 'application/octet-stream';
+ }
+
+
+ if (isset($httpHeaders['Content-Length'])) {
+
+ $nodeSize = $httpHeaders['Content-Length'];
+
+ // Need to unset Content-Length, because we'll handle that during figuring out the range
+ unset($httpHeaders['Content-Length']);
+
+ } else {
+ $nodeSize = null;
+ }
+
+ $this->httpResponse->setHeaders($httpHeaders);
+
+ $range = $this->getHTTPRange();
+ $ifRange = $this->httpRequest->getHeader('If-Range');
+ $ignoreRangeHeader = false;
+
+ // If ifRange is set, and range is specified, we first need to check
+ // the precondition.
+ if ($nodeSize && $range && $ifRange) {
+
+ // if IfRange is parsable as a date we'll treat it as a DateTime
+ // otherwise, we must treat it as an etag.
+ try {
+ $ifRangeDate = new DateTime($ifRange);
+
+ // It's a date. We must check if the entity is modified since
+ // the specified date.
+ if (!isset($httpHeaders['Last-Modified'])) $ignoreRangeHeader = true;
+ else {
+ $modified = new DateTime($httpHeaders['Last-Modified']);
+ if($modified > $ifRangeDate) $ignoreRangeHeader = true;
+ }
+
+ } catch (Exception $e) {
+
+ // It's an entity. We can do a simple comparison.
+ if (!isset($httpHeaders['ETag'])) $ignoreRangeHeader = true;
+ elseif ($httpHeaders['ETag']!==$ifRange) $ignoreRangeHeader = true;
+ }
+ }
+
+ // We're only going to support HTTP ranges if the backend provided a filesize
+ if (!$ignoreRangeHeader && $nodeSize && $range) {
+
+ // Determining the exact byte offsets
+ if (!is_null($range[0])) {
+
+ $start = $range[0];
+ $end = $range[1]?$range[1]:$nodeSize-1;
+ if($start >= $nodeSize)
+ throw new Sabre_DAV_Exception_RequestedRangeNotSatisfiable('The start offset (' . $range[0] . ') exceeded the size of the entity (' . $nodeSize . ')');
+
+ if($end < $start) throw new Sabre_DAV_Exception_RequestedRangeNotSatisfiable('The end offset (' . $range[1] . ') is lower than the start offset (' . $range[0] . ')');
+ if($end >= $nodeSize) $end = $nodeSize-1;
+
+ } else {
+
+ $start = $nodeSize-$range[1];
+ $end = $nodeSize-1;
+
+ if ($start<0) $start = 0;
+
+ }
+
+ // New read/write stream
+ $newStream = fopen('php://temp','r+');
+
+ stream_copy_to_stream($body, $newStream, $end-$start+1, $start);
+ rewind($newStream);
+
+ $this->httpResponse->setHeader('Content-Length', $end-$start+1);
+ $this->httpResponse->setHeader('Content-Range','bytes ' . $start . '-' . $end . '/' . $nodeSize);
+ $this->httpResponse->sendStatus(206);
+ $this->httpResponse->sendBody($newStream);
+
+
+ } else {
+
+ if ($nodeSize) $this->httpResponse->setHeader('Content-Length',$nodeSize);
+ $this->httpResponse->sendStatus(200);
+ $this->httpResponse->sendBody($body);
+
+ }
+
+ }
+
+ /**
+ * HTTP HEAD
+ *
+ * This method is normally used to take a peak at a url, and only get the HTTP response headers, without the body
+ * This is used by clients to determine if a remote file was changed, so they can use a local cached version, instead of downloading it again
+ *
+ * @param string $uri
+ * @return void
+ */
+ protected function httpHead($uri) {
+
+ $node = $this->tree->getNodeForPath($uri);
+ /* This information is only collection for File objects.
+ * Ideally we want to throw 405 Method Not Allowed for every
+ * non-file, but MS Office does not like this
+ */
+ if ($node instanceof Sabre_DAV_IFile) {
+ $headers = $this->getHTTPHeaders($this->getRequestUri());
+ if (!isset($headers['Content-Type'])) {
+ $headers['Content-Type'] = 'application/octet-stream';
+ }
+ $this->httpResponse->setHeaders($headers);
+ }
+ $this->httpResponse->sendStatus(200);
+
+ }
+
+ /**
+ * HTTP Delete
+ *
+ * The HTTP delete method, deletes a given uri
+ *
+ * @param string $uri
+ * @return void
+ */
+ protected function httpDelete($uri) {
+
+ if (!$this->broadcastEvent('beforeUnbind',array($uri))) return;
+ $this->tree->delete($uri);
+ $this->broadcastEvent('afterUnbind',array($uri));
+
+ $this->httpResponse->sendStatus(204);
+ $this->httpResponse->setHeader('Content-Length','0');
+
+ }
+
+
+ /**
+ * WebDAV PROPFIND
+ *
+ * This WebDAV method requests information about an uri resource, or a list of resources
+ * If a client wants to receive the properties for a single resource it will add an HTTP Depth: header with a 0 value
+ * If the value is 1, it means that it also expects a list of sub-resources (e.g.: files in a directory)
+ *
+ * The request body contains an XML data structure that has a list of properties the client understands
+ * The response body is also an xml document, containing information about every uri resource and the requested properties
+ *
+ * It has to return a HTTP 207 Multi-status status code
+ *
+ * @param string $uri
+ * @return void
+ */
+ protected function httpPropfind($uri) {
+
+ // $xml = new Sabre_DAV_XMLReader(file_get_contents('php://input'));
+ $requestedProperties = $this->parsePropfindRequest($this->httpRequest->getBody(true));
+
+ $depth = $this->getHTTPDepth(1);
+ // The only two options for the depth of a propfind is 0 or 1
+ if ($depth!=0) $depth = 1;
+
+ $newProperties = $this->getPropertiesForPath($uri,$requestedProperties,$depth);
+
+ // This is a multi-status response
+ $this->httpResponse->sendStatus(207);
+ $this->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8');
+
+ // Normally this header is only needed for OPTIONS responses, however..
+ // iCal seems to also depend on these being set for PROPFIND. Since
+ // this is not harmful, we'll add it.
+ $features = array('1','3', 'extended-mkcol');
+ foreach($this->plugins as $plugin) $features = array_merge($features,$plugin->getFeatures());
+ $this->httpResponse->setHeader('DAV',implode(', ',$features));
+
+ $data = $this->generateMultiStatus($newProperties);
+ $this->httpResponse->sendBody($data);
+
+ }
+
+ /**
+ * WebDAV PROPPATCH
+ *
+ * This method is called to update properties on a Node. The request is an XML body with all the mutations.
+ * In this XML body it is specified which properties should be set/updated and/or deleted
+ *
+ * @param string $uri
+ * @return void
+ */
+ protected function httpPropPatch($uri) {
+
+ $newProperties = $this->parsePropPatchRequest($this->httpRequest->getBody(true));
+
+ $result = $this->updateProperties($uri, $newProperties);
+
+ $this->httpResponse->sendStatus(207);
+ $this->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8');
+
+ $this->httpResponse->sendBody(
+ $this->generateMultiStatus(array($result))
+ );
+
+ }
+
+ /**
+ * HTTP PUT method
+ *
+ * This HTTP method updates a file, or creates a new one.
+ *
+ * If a new resource was created, a 201 Created status code should be returned. If an existing resource is updated, it's a 204 No Content
+ *
+ * @param string $uri
+ * @return bool
+ */
+ protected function httpPut($uri) {
+
+ $body = $this->httpRequest->getBody();
+
+ // Intercepting Content-Range
+ if ($this->httpRequest->getHeader('Content-Range')) {
+ /**
+ Content-Range is dangerous for PUT requests: PUT per definition
+ stores a full resource. draft-ietf-httpbis-p2-semantics-15 says
+ in section 7.6:
+ An origin server SHOULD reject any PUT request that contains a
+ Content-Range header field, since it might be misinterpreted as
+ partial content (or might be partial content that is being mistakenly
+ PUT as a full representation). Partial content updates are possible
+ by targeting a separately identified resource with state that
+ overlaps a portion of the larger resource, or by using a different
+ method that has been specifically defined for partial updates (for
+ example, the PATCH method defined in [RFC5789]).
+ This clarifies RFC2616 section 9.6:
+ The recipient of the entity MUST NOT ignore any Content-*
+ (e.g. Content-Range) headers that it does not understand or implement
+ and MUST return a 501 (Not Implemented) response in such cases.
+ OTOH is a PUT request with a Content-Range currently the only way to
+ continue an aborted upload request and is supported by curl, mod_dav,
+ Tomcat and others. Since some clients do use this feature which results
+ in unexpected behaviour (cf PEAR::HTTP_WebDAV_Client 1.0.1), we reject
+ all PUT requests with a Content-Range for now.
+ */
+
+ throw new Sabre_DAV_Exception_NotImplemented('PUT with Content-Range is not allowed.');
+ }
+
+ // Intercepting the Finder problem
+ if (($expected = $this->httpRequest->getHeader('X-Expected-Entity-Length')) && $expected > 0) {
+
+ /**
+ Many webservers will not cooperate well with Finder PUT requests,
+ because it uses 'Chunked' transfer encoding for the request body.
+
+ The symptom of this problem is that Finder sends files to the
+ server, but they arrive as 0-length files in PHP.
+
+ If we don't do anything, the user might think they are uploading
+ files successfully, but they end up empty on the server. Instead,
+ we throw back an error if we detect this.
+
+ The reason Finder uses Chunked, is because it thinks the files
+ might change as it's being uploaded, and therefore the
+ Content-Length can vary.
+
+ Instead it sends the X-Expected-Entity-Length header with the size
+ of the file at the very start of the request. If this header is set,
+ but we don't get a request body we will fail the request to
+ protect the end-user.
+ */
+
+ // Only reading first byte
+ $firstByte = fread($body,1);
+ if (strlen($firstByte)!==1) {
+ throw new Sabre_DAV_Exception_Forbidden('This server is not compatible with OS/X finder. Consider using a different WebDAV client or webserver.');
+ }
+
+ // The body needs to stay intact, so we copy everything to a
+ // temporary stream.
+
+ $newBody = fopen('php://temp','r+');
+ fwrite($newBody,$firstByte);
+ stream_copy_to_stream($body, $newBody);
+ rewind($newBody);
+
+ $body = $newBody;
+
+ }
+
+ if ($this->tree->nodeExists($uri)) {
+
+ $node = $this->tree->getNodeForPath($uri);
+
+ // Checking If-None-Match and related headers.
+ if (!$this->checkPreconditions()) return;
+
+ // If the node is a collection, we'll deny it
+ if (!($node instanceof Sabre_DAV_IFile)) throw new Sabre_DAV_Exception_Conflict('PUT is not allowed on non-files.');
+ if (!$this->broadcastEvent('beforeWriteContent',array($uri, $node, &$body))) return false;
+
+ $etag = $node->put($body);
+
+ $this->broadcastEvent('afterWriteContent',array($uri, $node));
+
+ $this->httpResponse->setHeader('Content-Length','0');
+ if ($etag) $this->httpResponse->setHeader('ETag',$etag);
+ $this->httpResponse->sendStatus(204);
+
+ } else {
+
+ $etag = null;
+ // If we got here, the resource didn't exist yet.
+ if (!$this->createFile($this->getRequestUri(),$body,$etag)) {
+ // For one reason or another the file was not created.
+ return;
+ }
+
+ $this->httpResponse->setHeader('Content-Length','0');
+ if ($etag) $this->httpResponse->setHeader('ETag', $etag);
+ $this->httpResponse->sendStatus(201);
+
+ }
+
+ }
+
+
+ /**
+ * WebDAV MKCOL
+ *
+ * The MKCOL method is used to create a new collection (directory) on the server
+ *
+ * @param string $uri
+ * @return void
+ */
+ protected function httpMkcol($uri) {
+
+ $requestBody = $this->httpRequest->getBody(true);
+
+ if ($requestBody) {
+
+ $contentType = $this->httpRequest->getHeader('Content-Type');
+ if (strpos($contentType,'application/xml')!==0 && strpos($contentType,'text/xml')!==0) {
+
+ // We must throw 415 for unsupported mkcol bodies
+ throw new Sabre_DAV_Exception_UnsupportedMediaType('The request body for the MKCOL request must have an xml Content-Type');
+
+ }
+
+ $dom = Sabre_DAV_XMLUtil::loadDOMDocument($requestBody);
+ if (Sabre_DAV_XMLUtil::toClarkNotation($dom->firstChild)!=='{DAV:}mkcol') {
+
+ // We must throw 415 for unsupported mkcol bodies
+ throw new Sabre_DAV_Exception_UnsupportedMediaType('The request body for the MKCOL request must be a {DAV:}mkcol request construct.');
+
+ }
+
+ $properties = array();
+ foreach($dom->firstChild->childNodes as $childNode) {
+
+ if (Sabre_DAV_XMLUtil::toClarkNotation($childNode)!=='{DAV:}set') continue;
+ $properties = array_merge($properties, Sabre_DAV_XMLUtil::parseProperties($childNode, $this->propertyMap));
+
+ }
+ if (!isset($properties['{DAV:}resourcetype']))
+ throw new Sabre_DAV_Exception_BadRequest('The mkcol request must include a {DAV:}resourcetype property');
+
+ $resourceType = $properties['{DAV:}resourcetype']->getValue();
+ unset($properties['{DAV:}resourcetype']);
+
+ } else {
+
+ $properties = array();
+ $resourceType = array('{DAV:}collection');
+
+ }
+
+ $result = $this->createCollection($uri, $resourceType, $properties);
+
+ if (is_array($result)) {
+ $this->httpResponse->sendStatus(207);
+ $this->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8');
+
+ $this->httpResponse->sendBody(
+ $this->generateMultiStatus(array($result))
+ );
+
+ } else {
+ $this->httpResponse->setHeader('Content-Length','0');
+ $this->httpResponse->sendStatus(201);
+ }
+
+ }
+
+ /**
+ * WebDAV HTTP MOVE method
+ *
+ * This method moves one uri to a different uri. A lot of the actual request processing is done in getCopyMoveInfo
+ *
+ * @param string $uri
+ * @return void
+ */
+ protected function httpMove($uri) {
+
+ $moveInfo = $this->getCopyAndMoveInfo();
+
+ // If the destination is part of the source tree, we must fail
+ if ($moveInfo['destination']==$uri)
+ throw new Sabre_DAV_Exception_Forbidden('Source and destination uri are identical.');
+
+ if ($moveInfo['destinationExists']) {
+
+ if (!$this->broadcastEvent('beforeUnbind',array($moveInfo['destination']))) return false;
+ $this->tree->delete($moveInfo['destination']);
+ $this->broadcastEvent('afterUnbind',array($moveInfo['destination']));
+
+ }
+
+ if (!$this->broadcastEvent('beforeUnbind',array($uri))) return false;
+ if (!$this->broadcastEvent('beforeBind',array($moveInfo['destination']))) return false;
+ $this->tree->move($uri,$moveInfo['destination']);
+ $this->broadcastEvent('afterUnbind',array($uri));
+ $this->broadcastEvent('afterBind',array($moveInfo['destination']));
+
+ // If a resource was overwritten we should send a 204, otherwise a 201
+ $this->httpResponse->setHeader('Content-Length','0');
+ $this->httpResponse->sendStatus($moveInfo['destinationExists']?204:201);
+
+ }
+
+ /**
+ * WebDAV HTTP COPY method
+ *
+ * This method copies one uri to a different uri, and works much like the MOVE request
+ * A lot of the actual request processing is done in getCopyMoveInfo
+ *
+ * @param string $uri
+ * @return bool
+ */
+ protected function httpCopy($uri) {
+
+ $copyInfo = $this->getCopyAndMoveInfo();
+ // If the destination is part of the source tree, we must fail
+ if ($copyInfo['destination']==$uri)
+ throw new Sabre_DAV_Exception_Forbidden('Source and destination uri are identical.');
+
+ if ($copyInfo['destinationExists']) {
+ if (!$this->broadcastEvent('beforeUnbind',array($copyInfo['destination']))) return false;
+ $this->tree->delete($copyInfo['destination']);
+
+ }
+ if (!$this->broadcastEvent('beforeBind',array($copyInfo['destination']))) return false;
+ $this->tree->copy($uri,$copyInfo['destination']);
+ $this->broadcastEvent('afterBind',array($copyInfo['destination']));
+
+ // If a resource was overwritten we should send a 204, otherwise a 201
+ $this->httpResponse->setHeader('Content-Length','0');
+ $this->httpResponse->sendStatus($copyInfo['destinationExists']?204:201);
+
+ }
+
+
+
+ /**
+ * HTTP REPORT method implementation
+ *
+ * Although the REPORT method is not part of the standard WebDAV spec (it's from rfc3253)
+ * It's used in a lot of extensions, so it made sense to implement it into the core.
+ *
+ * @param string $uri
+ * @return void
+ */
+ protected function httpReport($uri) {
+
+ $body = $this->httpRequest->getBody(true);
+ $dom = Sabre_DAV_XMLUtil::loadDOMDocument($body);
+
+ $reportName = Sabre_DAV_XMLUtil::toClarkNotation($dom->firstChild);
+
+ if ($this->broadcastEvent('report',array($reportName,$dom, $uri))) {
+
+ // If broadcastEvent returned true, it means the report was not supported
+ throw new Sabre_DAV_Exception_ReportNotImplemented();
+
+ }
+
+ }
+
+ // }}}
+ // {{{ HTTP/WebDAV protocol helpers
+
+ /**
+ * Returns an array with all the supported HTTP methods for a specific uri.
+ *
+ * @param string $uri
+ * @return array
+ */
+ public function getAllowedMethods($uri) {
+
+ $methods = array(
+ 'OPTIONS',
+ 'GET',
+ 'HEAD',
+ 'DELETE',
+ 'PROPFIND',
+ 'PUT',
+ 'PROPPATCH',
+ 'COPY',
+ 'MOVE',
+ 'REPORT'
+ );
+
+ // The MKCOL is only allowed on an unmapped uri
+ try {
+ $this->tree->getNodeForPath($uri);
+ } catch (Sabre_DAV_Exception_NotFound $e) {
+ $methods[] = 'MKCOL';
+ }
+
+ // We're also checking if any of the plugins register any new methods
+ foreach($this->plugins as $plugin) $methods = array_merge($methods, $plugin->getHTTPMethods($uri));
+ array_unique($methods);
+
+ return $methods;
+
+ }
+
+ /**
+ * Gets the uri for the request, keeping the base uri into consideration
+ *
+ * @return string
+ */
+ public function getRequestUri() {
+
+ return $this->calculateUri($this->httpRequest->getUri());
+
+ }
+
+ /**
+ * Calculates the uri for a request, making sure that the base uri is stripped out
+ *
+ * @param string $uri
+ * @throws Sabre_DAV_Exception_Forbidden A permission denied exception is thrown whenever there was an attempt to supply a uri outside of the base uri
+ * @return string
+ */
+ public function calculateUri($uri) {
+
+ if ($uri[0]!='/' && strpos($uri,'://')) {
+
+ $uri = parse_url($uri,PHP_URL_PATH);
+
+ }
+
+ $uri = str_replace('//','/',$uri);
+
+ if (strpos($uri,$this->getBaseUri())===0) {
+
+ return trim(Sabre_DAV_URLUtil::decodePath(substr($uri,strlen($this->getBaseUri()))),'/');
+
+ // A special case, if the baseUri was accessed without a trailing
+ // slash, we'll accept it as well.
+ } elseif ($uri.'/' === $this->getBaseUri()) {
+
+ return '';
+
+ } else {
+
+ throw new Sabre_DAV_Exception_Forbidden('Requested uri (' . $uri . ') is out of base uri (' . $this->getBaseUri() . ')');
+
+ }
+
+ }
+
+ /**
+ * Returns the HTTP depth header
+ *
+ * This method returns the contents of the HTTP depth request header. If the depth header was 'infinity' it will return the Sabre_DAV_Server::DEPTH_INFINITY object
+ * It is possible to supply a default depth value, which is used when the depth header has invalid content, or is completely non-existent
+ *
+ * @param mixed $default
+ * @return int
+ */
+ public function getHTTPDepth($default = self::DEPTH_INFINITY) {
+
+ // If its not set, we'll grab the default
+ $depth = $this->httpRequest->getHeader('Depth');
+
+ if (is_null($depth)) return $default;
+
+ if ($depth == 'infinity') return self::DEPTH_INFINITY;
+
+
+ // If its an unknown value. we'll grab the default
+ if (!ctype_digit($depth)) return $default;
+
+ return (int)$depth;
+
+ }
+
+ /**
+ * Returns the HTTP range header
+ *
+ * This method returns null if there is no well-formed HTTP range request
+ * header or array($start, $end).
+ *
+ * The first number is the offset of the first byte in the range.
+ * The second number is the offset of the last byte in the range.
+ *
+ * If the second offset is null, it should be treated as the offset of the last byte of the entity
+ * If the first offset is null, the second offset should be used to retrieve the last x bytes of the entity
+ *
+ * @return array|null
+ */
+ public function getHTTPRange() {
+
+ $range = $this->httpRequest->getHeader('range');
+ if (is_null($range)) return null;
+
+ // Matching "Range: bytes=1234-5678: both numbers are optional
+
+ if (!preg_match('/^bytes=([0-9]*)-([0-9]*)$/i',$range,$matches)) return null;
+
+ if ($matches[1]==='' && $matches[2]==='') return null;
+
+ return array(
+ $matches[1]!==''?$matches[1]:null,
+ $matches[2]!==''?$matches[2]:null,
+ );
+
+ }
+
+
+ /**
+ * Returns information about Copy and Move requests
+ *
+ * This function is created to help getting information about the source and the destination for the
+ * WebDAV MOVE and COPY HTTP request. It also validates a lot of information and throws proper exceptions
+ *
+ * The returned value is an array with the following keys:
+ * * destination - Destination path
+ * * destinationExists - Whether or not the destination is an existing url (and should therefore be overwritten)
+ *
+ * @return array
+ */
+ public function getCopyAndMoveInfo() {
+
+ // Collecting the relevant HTTP headers
+ if (!$this->httpRequest->getHeader('Destination')) throw new Sabre_DAV_Exception_BadRequest('The destination header was not supplied');
+ $destination = $this->calculateUri($this->httpRequest->getHeader('Destination'));
+ $overwrite = $this->httpRequest->getHeader('Overwrite');
+ if (!$overwrite) $overwrite = 'T';
+ if (strtoupper($overwrite)=='T') $overwrite = true;
+ elseif (strtoupper($overwrite)=='F') $overwrite = false;
+ // We need to throw a bad request exception, if the header was invalid
+ else throw new Sabre_DAV_Exception_BadRequest('The HTTP Overwrite header should be either T or F');
+
+ list($destinationDir) = Sabre_DAV_URLUtil::splitPath($destination);
+
+ try {
+ $destinationParent = $this->tree->getNodeForPath($destinationDir);
+ if (!($destinationParent instanceof Sabre_DAV_ICollection)) throw new Sabre_DAV_Exception_UnsupportedMediaType('The destination node is not a collection');
+ } catch (Sabre_DAV_Exception_NotFound $e) {
+
+ // If the destination parent node is not found, we throw a 409
+ throw new Sabre_DAV_Exception_Conflict('The destination node is not found');
+ }
+
+ try {
+
+ $destinationNode = $this->tree->getNodeForPath($destination);
+
+ // If this succeeded, it means the destination already exists
+ // we'll need to throw precondition failed in case overwrite is false
+ if (!$overwrite) throw new Sabre_DAV_Exception_PreconditionFailed('The destination node already exists, and the overwrite header is set to false','Overwrite');
+
+ } catch (Sabre_DAV_Exception_NotFound $e) {
+
+ // Destination didn't exist, we're all good
+ $destinationNode = false;
+
+
+
+ }
+
+ // These are the three relevant properties we need to return
+ return array(
+ 'destination' => $destination,
+ 'destinationExists' => $destinationNode==true,
+ 'destinationNode' => $destinationNode,
+ );
+
+ }
+
+ /**
+ * Returns a list of properties for a path
+ *
+ * This is a simplified version getPropertiesForPath.
+ * if you aren't interested in status codes, but you just
+ * want to have a flat list of properties. Use this method.
+ *
+ * @param string $path
+ * @param array $propertyNames
+ */
+ public function getProperties($path, $propertyNames) {
+
+ $result = $this->getPropertiesForPath($path,$propertyNames,0);
+ return $result[0][200];
+
+ }
+
+ /**
+ * A kid-friendly way to fetch properties for a node's children.
+ *
+ * The returned array will be indexed by the path of the of child node.
+ * Only properties that are actually found will be returned.
+ *
+ * The parent node will not be returned.
+ *
+ * @param string $path
+ * @param array $propertyNames
+ * @return array
+ */
+ public function getPropertiesForChildren($path, $propertyNames) {
+
+ $result = array();
+ foreach($this->getPropertiesForPath($path,$propertyNames,1) as $k=>$row) {
+
+ // Skipping the parent path
+ if ($k === 0) continue;
+
+ $result[$row['href']] = $row[200];
+
+ }
+ return $result;
+
+ }
+
+ /**
+ * Returns a list of HTTP headers for a particular resource
+ *
+ * The generated http headers are based on properties provided by the
+ * resource. The method basically provides a simple mapping between
+ * DAV property and HTTP header.
+ *
+ * The headers are intended to be used for HEAD and GET requests.
+ *
+ * @param string $path
+ * @return array
+ */
+ public function getHTTPHeaders($path) {
+
+ $propertyMap = array(
+ '{DAV:}getcontenttype' => 'Content-Type',
+ '{DAV:}getcontentlength' => 'Content-Length',
+ '{DAV:}getlastmodified' => 'Last-Modified',
+ '{DAV:}getetag' => 'ETag',
+ );
+
+ $properties = $this->getProperties($path,array_keys($propertyMap));
+
+ $headers = array();
+ foreach($propertyMap as $property=>$header) {
+ if (!isset($properties[$property])) continue;
+
+ if (is_scalar($properties[$property])) {
+ $headers[$header] = $properties[$property];
+
+ // GetLastModified gets special cased
+ } elseif ($properties[$property] instanceof Sabre_DAV_Property_GetLastModified) {
+ $headers[$header] = Sabre_HTTP_Util::toHTTPDate($properties[$property]->getTime());
+ }
+
+ }
+
+ return $headers;
+
+ }
+
+ /**
+ * Returns a list of properties for a given path
+ *
+ * The path that should be supplied should have the baseUrl stripped out
+ * The list of properties should be supplied in Clark notation. If the list is empty
+ * 'allprops' is assumed.
+ *
+ * If a depth of 1 is requested child elements will also be returned.
+ *
+ * @param string $path
+ * @param array $propertyNames
+ * @param int $depth
+ * @return array
+ */
+ public function getPropertiesForPath($path, $propertyNames = array(), $depth = 0) {
+
+ if ($depth!=0) $depth = 1;
+
+ $returnPropertyList = array();
+
+ $parentNode = $this->tree->getNodeForPath($path);
+ $nodes = array(
+ $path => $parentNode
+ );
+ if ($depth==1 && $parentNode instanceof Sabre_DAV_ICollection) {
+ foreach($this->tree->getChildren($path) as $childNode)
+ $nodes[$path . '/' . $childNode->getName()] = $childNode;
+ }
+
+ // If the propertyNames array is empty, it means all properties are requested.
+ // We shouldn't actually return everything we know though, and only return a
+ // sensible list.
+ $allProperties = count($propertyNames)==0;
+
+ foreach($nodes as $myPath=>$node) {
+
+ $currentPropertyNames = $propertyNames;
+
+ $newProperties = array(
+ '200' => array(),
+ '404' => array(),
+ );
+
+ if ($allProperties) {
+ // Default list of propertyNames, when all properties were requested.
+ $currentPropertyNames = array(
+ '{DAV:}getlastmodified',
+ '{DAV:}getcontentlength',
+ '{DAV:}resourcetype',
+ '{DAV:}quota-used-bytes',
+ '{DAV:}quota-available-bytes',
+ '{DAV:}getetag',
+ '{DAV:}getcontenttype',
+ );
+ }
+
+ // If the resourceType was not part of the list, we manually add it
+ // and mark it for removal. We need to know the resourcetype in order
+ // to make certain decisions about the entry.
+ // WebDAV dictates we should add a / and the end of href's for collections
+ $removeRT = false;
+ if (!in_array('{DAV:}resourcetype',$currentPropertyNames)) {
+ $currentPropertyNames[] = '{DAV:}resourcetype';
+ $removeRT = true;
+ }
+
+ $result = $this->broadcastEvent('beforeGetProperties',array($myPath, $node, &$currentPropertyNames, &$newProperties));
+ // If this method explicitly returned false, we must ignore this
+ // node as it is inaccessible.
+ if ($result===false) continue;
+
+ if (count($currentPropertyNames) > 0) {
+
+ if ($node instanceof Sabre_DAV_IProperties)
+ $newProperties['200'] = $newProperties[200] + $node->getProperties($currentPropertyNames);
+
+ }
+
+
+ foreach($currentPropertyNames as $prop) {
+
+ if (isset($newProperties[200][$prop])) continue;
+
+ switch($prop) {
+ case '{DAV:}getlastmodified' : if ($node->getLastModified()) $newProperties[200][$prop] = new Sabre_DAV_Property_GetLastModified($node->getLastModified()); break;
+ case '{DAV:}getcontentlength' :
+ if ($node instanceof Sabre_DAV_IFile) {
+ $size = $node->getSize();
+ if (!is_null($size)) {
+ $newProperties[200][$prop] = (int)$node->getSize();
+ }
+ }
+ break;
+ case '{DAV:}quota-used-bytes' :
+ if ($node instanceof Sabre_DAV_IQuota) {
+ $quotaInfo = $node->getQuotaInfo();
+ $newProperties[200][$prop] = $quotaInfo[0];
+ }
+ break;
+ case '{DAV:}quota-available-bytes' :
+ if ($node instanceof Sabre_DAV_IQuota) {
+ $quotaInfo = $node->getQuotaInfo();
+ $newProperties[200][$prop] = $quotaInfo[1];
+ }
+ break;
+ case '{DAV:}getetag' : if ($node instanceof Sabre_DAV_IFile && $etag = $node->getETag()) $newProperties[200][$prop] = $etag; break;
+ case '{DAV:}getcontenttype' : if ($node instanceof Sabre_DAV_IFile && $ct = $node->getContentType()) $newProperties[200][$prop] = $ct; break;
+ case '{DAV:}supported-report-set' :
+ $reports = array();
+ foreach($this->plugins as $plugin) {
+ $reports = array_merge($reports, $plugin->getSupportedReportSet($myPath));
+ }
+ $newProperties[200][$prop] = new Sabre_DAV_Property_SupportedReportSet($reports);
+ break;
+ case '{DAV:}resourcetype' :
+ $newProperties[200]['{DAV:}resourcetype'] = new Sabre_DAV_Property_ResourceType();
+ foreach($this->resourceTypeMapping as $className => $resourceType) {
+ if ($node instanceof $className) $newProperties[200]['{DAV:}resourcetype']->add($resourceType);
+ }
+ break;
+
+ }
+
+ // If we were unable to find the property, we will list it as 404.
+ if (!$allProperties && !isset($newProperties[200][$prop])) $newProperties[404][$prop] = null;
+
+ }
+
+ $this->broadcastEvent('afterGetProperties',array(trim($myPath,'/'),&$newProperties));
+
+ $newProperties['href'] = trim($myPath,'/');
+
+ // Its is a WebDAV recommendation to add a trailing slash to collectionnames.
+ // Apple's iCal also requires a trailing slash for principals (rfc 3744).
+ // Therefore we add a trailing / for any non-file. This might need adjustments
+ // if we find there are other edge cases.
+ if ($myPath!='' && isset($newProperties[200]['{DAV:}resourcetype']) && count($newProperties[200]['{DAV:}resourcetype']->getValue())>0) $newProperties['href'] .='/';
+
+ // If the resourcetype property was manually added to the requested property list,
+ // we will remove it again.
+ if ($removeRT) unset($newProperties[200]['{DAV:}resourcetype']);
+
+ $returnPropertyList[] = $newProperties;
+
+ }
+
+ return $returnPropertyList;
+
+ }
+
+ /**
+ * This method is invoked by sub-systems creating a new file.
+ *
+ * Currently this is done by HTTP PUT and HTTP LOCK (in the Locks_Plugin).
+ * It was important to get this done through a centralized function,
+ * allowing plugins to intercept this using the beforeCreateFile event.
+ *
+ * This method will return true if the file was actually created
+ *
+ * @param string $uri
+ * @param resource $data
+ * @param string $etag
+ * @return bool
+ */
+ public function createFile($uri,$data, &$etag = null) {
+
+ list($dir,$name) = Sabre_DAV_URLUtil::splitPath($uri);
+
+ if (!$this->broadcastEvent('beforeBind',array($uri))) return false;
+
+ $parent = $this->tree->getNodeForPath($dir);
+
+ if (!$this->broadcastEvent('beforeCreateFile',array($uri, &$data, $parent))) return false;
+
+ $etag = $parent->createFile($name,$data);
+ $this->tree->markDirty($dir);
+
+ $this->broadcastEvent('afterBind',array($uri));
+ $this->broadcastEvent('afterCreateFile',array($uri, $parent));
+
+ return true;
+ }
+
+ /**
+ * This method is invoked by sub-systems creating a new directory.
+ *
+ * @param string $uri
+ * @return void
+ */
+ public function createDirectory($uri) {
+
+ $this->createCollection($uri,array('{DAV:}collection'),array());
+
+ }
+
+ /**
+ * Use this method to create a new collection
+ *
+ * The {DAV:}resourcetype is specified using the resourceType array.
+ * At the very least it must contain {DAV:}collection.
+ *
+ * The properties array can contain a list of additional properties.
+ *
+ * @param string $uri The new uri
+ * @param array $resourceType The resourceType(s)
+ * @param array $properties A list of properties
+ * @return array|null
+ */
+ public function createCollection($uri, array $resourceType, array $properties) {
+
+ list($parentUri,$newName) = Sabre_DAV_URLUtil::splitPath($uri);
+
+ // Making sure {DAV:}collection was specified as resourceType
+ if (!in_array('{DAV:}collection', $resourceType)) {
+ throw new Sabre_DAV_Exception_InvalidResourceType('The resourceType for this collection must at least include {DAV:}collection');
+ }
+
+
+ // Making sure the parent exists
+ try {
+
+ $parent = $this->tree->getNodeForPath($parentUri);
+
+ } catch (Sabre_DAV_Exception_NotFound $e) {
+
+ throw new Sabre_DAV_Exception_Conflict('Parent node does not exist');
+
+ }
+
+ // Making sure the parent is a collection
+ if (!$parent instanceof Sabre_DAV_ICollection) {
+ throw new Sabre_DAV_Exception_Conflict('Parent node is not a collection');
+ }
+
+
+
+ // Making sure the child does not already exist
+ try {
+ $parent->getChild($newName);
+
+ // If we got here.. it means there's already a node on that url, and we need to throw a 405
+ throw new Sabre_DAV_Exception_MethodNotAllowed('The resource you tried to create already exists');
+
+ } catch (Sabre_DAV_Exception_NotFound $e) {
+ // This is correct
+ }
+
+
+ if (!$this->broadcastEvent('beforeBind',array($uri))) return;
+
+ // There are 2 modes of operation. The standard collection
+ // creates the directory, and then updates properties
+ // the extended collection can create it directly.
+ if ($parent instanceof Sabre_DAV_IExtendedCollection) {
+
+ $parent->createExtendedCollection($newName, $resourceType, $properties);
+
+ } else {
+
+ // No special resourcetypes are supported
+ if (count($resourceType)>1) {
+ throw new Sabre_DAV_Exception_InvalidResourceType('The {DAV:}resourcetype you specified is not supported here.');
+ }
+
+ $parent->createDirectory($newName);
+ $rollBack = false;
+ $exception = null;
+ $errorResult = null;
+
+ if (count($properties)>0) {
+
+ try {
+
+ $errorResult = $this->updateProperties($uri, $properties);
+ if (!isset($errorResult[200])) {
+ $rollBack = true;
+ }
+
+ } catch (Sabre_DAV_Exception $e) {
+
+ $rollBack = true;
+ $exception = $e;
+
+ }
+
+ }
+
+ if ($rollBack) {
+ if (!$this->broadcastEvent('beforeUnbind',array($uri))) return;
+ $this->tree->delete($uri);
+
+ // Re-throwing exception
+ if ($exception) throw $exception;
+
+ return $errorResult;
+ }
+
+ }
+ $this->tree->markDirty($parentUri);
+ $this->broadcastEvent('afterBind',array($uri));
+
+ }
+
+ /**
+ * This method updates a resource's properties
+ *
+ * The properties array must be a list of properties. Array-keys are
+ * property names in clarknotation, array-values are it's values.
+ * If a property must be deleted, the value should be null.
+ *
+ * Note that this request should either completely succeed, or
+ * completely fail.
+ *
+ * The response is an array with statuscodes for keys, which in turn
+ * contain arrays with propertynames. This response can be used
+ * to generate a multistatus body.
+ *
+ * @param string $uri
+ * @param array $properties
+ * @return array
+ */
+ public function updateProperties($uri, array $properties) {
+
+ // we'll start by grabbing the node, this will throw the appropriate
+ // exceptions if it doesn't.
+ $node = $this->tree->getNodeForPath($uri);
+
+ $result = array(
+ 200 => array(),
+ 403 => array(),
+ 424 => array(),
+ );
+ $remainingProperties = $properties;
+ $hasError = false;
+
+ // Running through all properties to make sure none of them are protected
+ if (!$hasError) foreach($properties as $propertyName => $value) {
+ if(in_array($propertyName, $this->protectedProperties)) {
+ $result[403][$propertyName] = null;
+ unset($remainingProperties[$propertyName]);
+ $hasError = true;
+ }
+ }
+
+ if (!$hasError) {
+ // Allowing plugins to take care of property updating
+ $hasError = !$this->broadcastEvent('updateProperties',array(
+ &$remainingProperties,
+ &$result,
+ $node
+ ));
+ }
+
+ // If the node is not an instance of Sabre_DAV_IProperties, every
+ // property is 403 Forbidden
+ if (!$hasError && count($remainingProperties) && !($node instanceof Sabre_DAV_IProperties)) {
+ $hasError = true;
+ foreach($properties as $propertyName=> $value) {
+ $result[403][$propertyName] = null;
+ }
+ $remainingProperties = array();
+ }
+
+ // Only if there were no errors we may attempt to update the resource
+ if (!$hasError) {
+
+ if (count($remainingProperties)>0) {
+
+ $updateResult = $node->updateProperties($remainingProperties);
+
+ if ($updateResult===true) {
+ // success
+ foreach($remainingProperties as $propertyName=>$value) {
+ $result[200][$propertyName] = null;
+ }
+
+ } elseif ($updateResult===false) {
+ // The node failed to update the properties for an
+ // unknown reason
+ foreach($remainingProperties as $propertyName=>$value) {
+ $result[403][$propertyName] = null;
+ }
+
+ } elseif (is_array($updateResult)) {
+
+ // The node has detailed update information
+ // We need to merge the results with the earlier results.
+ foreach($updateResult as $status => $props) {
+ if (is_array($props)) {
+ if (!isset($result[$status]))
+ $result[$status] = array();
+
+ $result[$status] = array_merge($result[$status], $updateResult[$status]);
+ }
+ }
+
+ } else {
+ throw new Sabre_DAV_Exception('Invalid result from updateProperties');
+ }
+ $remainingProperties = array();
+ }
+
+ }
+
+ foreach($remainingProperties as $propertyName=>$value) {
+ // if there are remaining properties, it must mean
+ // there's a dependency failure
+ $result[424][$propertyName] = null;
+ }
+
+ // Removing empty array values
+ foreach($result as $status=>$props) {
+
+ if (count($props)===0) unset($result[$status]);
+
+ }
+ $result['href'] = $uri;
+ return $result;
+
+ }
+
+ /**
+ * This method checks the main HTTP preconditions.
+ *
+ * Currently these are:
+ * * If-Match
+ * * If-None-Match
+ * * If-Modified-Since
+ * * If-Unmodified-Since
+ *
+ * The method will return true if all preconditions are met
+ * The method will return false, or throw an exception if preconditions
+ * failed. If false is returned the operation should be aborted, and
+ * the appropriate HTTP response headers are already set.
+ *
+ * Normally this method will throw 412 Precondition Failed for failures
+ * related to If-None-Match, If-Match and If-Unmodified Since. It will
+ * set the status to 304 Not Modified for If-Modified_since.
+ *
+ * If the $handleAsGET argument is set to true, it will also return 304
+ * Not Modified for failure of the If-None-Match precondition. This is the
+ * desired behaviour for HTTP GET and HTTP HEAD requests.
+ *
+ * @param bool $handleAsGET
+ * @return bool
+ */
+ public function checkPreconditions($handleAsGET = false) {
+
+ $uri = $this->getRequestUri();
+ $node = null;
+ $lastMod = null;
+ $etag = null;
+
+ if ($ifMatch = $this->httpRequest->getHeader('If-Match')) {
+
+ // If-Match contains an entity tag. Only if the entity-tag
+ // matches we are allowed to make the request succeed.
+ // If the entity-tag is '*' we are only allowed to make the
+ // request succeed if a resource exists at that url.
+ try {
+ $node = $this->tree->getNodeForPath($uri);
+ } catch (Sabre_DAV_Exception_NotFound $e) {
+ throw new Sabre_DAV_Exception_PreconditionFailed('An If-Match header was specified and the resource did not exist','If-Match');
+ }
+
+ // Only need to check entity tags if they are not *
+ if ($ifMatch!=='*') {
+
+ // There can be multiple etags
+ $ifMatch = explode(',',$ifMatch);
+ $haveMatch = false;
+ foreach($ifMatch as $ifMatchItem) {
+
+ // Stripping any extra spaces
+ $ifMatchItem = trim($ifMatchItem,' ');
+
+ $etag = $node->getETag();
+ if ($etag===$ifMatchItem) {
+ $haveMatch = true;
+ } else {
+ // Evolution has a bug where it sometimes prepends the "
+ // with a \. This is our workaround.
+ if (str_replace('\\"','"', $ifMatchItem) === $etag) {
+ $haveMatch = true;
+ }
+ }
+
+ }
+ if (!$haveMatch) {
+ throw new Sabre_DAV_Exception_PreconditionFailed('An If-Match header was specified, but none of the specified the ETags matched.','If-Match');
+ }
+ }
+ }
+
+ if ($ifNoneMatch = $this->httpRequest->getHeader('If-None-Match')) {
+
+ // The If-None-Match header contains an etag.
+ // Only if the ETag does not match the current ETag, the request will succeed
+ // The header can also contain *, in which case the request
+ // will only succeed if the entity does not exist at all.
+ $nodeExists = true;
+ if (!$node) {
+ try {
+ $node = $this->tree->getNodeForPath($uri);
+ } catch (Sabre_DAV_Exception_NotFound $e) {
+ $nodeExists = false;
+ }
+ }
+ if ($nodeExists) {
+ $haveMatch = false;
+ if ($ifNoneMatch==='*') $haveMatch = true;
+ else {
+
+ // There might be multiple etags
+ $ifNoneMatch = explode(',', $ifNoneMatch);
+ $etag = $node->getETag();
+
+ foreach($ifNoneMatch as $ifNoneMatchItem) {
+
+ // Stripping any extra spaces
+ $ifNoneMatchItem = trim($ifNoneMatchItem,' ');
+
+ if ($etag===$ifNoneMatchItem) $haveMatch = true;
+
+ }
+
+ }
+
+ if ($haveMatch) {
+ if ($handleAsGET) {
+ $this->httpResponse->sendStatus(304);
+ return false;
+ } else {
+ throw new Sabre_DAV_Exception_PreconditionFailed('An If-None-Match header was specified, but the ETag matched (or * was specified).','If-None-Match');
+ }
+ }
+ }
+
+ }
+
+ if (!$ifNoneMatch && ($ifModifiedSince = $this->httpRequest->getHeader('If-Modified-Since'))) {
+
+ // The If-Modified-Since header contains a date. We
+ // will only return the entity if it has been changed since
+ // that date. If it hasn't been changed, we return a 304
+ // header
+ // Note that this header only has to be checked if there was no If-None-Match header
+ // as per the HTTP spec.
+ $date = Sabre_HTTP_Util::parseHTTPDate($ifModifiedSince);
+
+ if ($date) {
+ if (is_null($node)) {
+ $node = $this->tree->getNodeForPath($uri);
+ }
+ $lastMod = $node->getLastModified();
+ if ($lastMod) {
+ $lastMod = new DateTime('@' . $lastMod);
+ if ($lastMod <= $date) {
+ $this->httpResponse->sendStatus(304);
+ $this->httpResponse->setHeader('Last-Modified', Sabre_HTTP_Util::toHTTPDate($lastMod));
+ return false;
+ }
+ }
+ }
+ }
+
+ if ($ifUnmodifiedSince = $this->httpRequest->getHeader('If-Unmodified-Since')) {
+
+ // The If-Unmodified-Since will allow allow the request if the
+ // entity has not changed since the specified date.
+ $date = Sabre_HTTP_Util::parseHTTPDate($ifUnmodifiedSince);
+
+ // We must only check the date if it's valid
+ if ($date) {
+ if (is_null($node)) {
+ $node = $this->tree->getNodeForPath($uri);
+ }
+ $lastMod = $node->getLastModified();
+ if ($lastMod) {
+ $lastMod = new DateTime('@' . $lastMod);
+ if ($lastMod > $date) {
+ throw new Sabre_DAV_Exception_PreconditionFailed('An If-Unmodified-Since header was specified, but the entity has been changed since the specified date.','If-Unmodified-Since');
+ }
+ }
+ }
+
+ }
+ return true;
+
+ }
+
+ // }}}
+ // {{{ XML Readers & Writers
+
+
+ /**
+ * Generates a WebDAV propfind response body based on a list of nodes
+ *
+ * @param array $fileProperties The list with nodes
+ * @return string
+ */
+ public function generateMultiStatus(array $fileProperties) {
+
+ $dom = new DOMDocument('1.0','utf-8');
+ //$dom->formatOutput = true;
+ $multiStatus = $dom->createElement('d:multistatus');
+ $dom->appendChild($multiStatus);
+
+ // Adding in default namespaces
+ foreach($this->xmlNamespaces as $namespace=>$prefix) {
+
+ $multiStatus->setAttribute('xmlns:' . $prefix,$namespace);
+
+ }
+
+ foreach($fileProperties as $entry) {
+
+ $href = $entry['href'];
+ unset($entry['href']);
+
+ $response = new Sabre_DAV_Property_Response($href,$entry);
+ $response->serialize($this,$multiStatus);
+
+ }
+
+ return $dom->saveXML();
+
+ }
+
+ /**
+ * This method parses a PropPatch request
+ *
+ * PropPatch changes the properties for a resource. This method
+ * returns a list of properties.
+ *
+ * The keys in the returned array contain the property name (e.g.: {DAV:}displayname,
+ * and the value contains the property value. If a property is to be removed the value
+ * will be null.
+ *
+ * @param string $body xml body
+ * @return array list of properties in need of updating or deletion
+ */
+ public function parsePropPatchRequest($body) {
+
+ //We'll need to change the DAV namespace declaration to something else in order to make it parsable
+ $dom = Sabre_DAV_XMLUtil::loadDOMDocument($body);
+
+ $newProperties = array();
+
+ foreach($dom->firstChild->childNodes as $child) {
+
+ if ($child->nodeType !== XML_ELEMENT_NODE) continue;
+
+ $operation = Sabre_DAV_XMLUtil::toClarkNotation($child);
+
+ if ($operation!=='{DAV:}set' && $operation!=='{DAV:}remove') continue;
+
+ $innerProperties = Sabre_DAV_XMLUtil::parseProperties($child, $this->propertyMap);
+
+ foreach($innerProperties as $propertyName=>$propertyValue) {
+
+ if ($operation==='{DAV:}remove') {
+ $propertyValue = null;
+ }
+
+ $newProperties[$propertyName] = $propertyValue;
+
+ }
+
+ }
+
+ return $newProperties;
+
+ }
+
+ /**
+ * This method parses the PROPFIND request and returns its information
+ *
+ * This will either be a list of properties, or an empty array; in which case
+ * an {DAV:}allprop was requested.
+ *
+ * @param string $body
+ * @return array
+ */
+ public function parsePropFindRequest($body) {
+
+ // If the propfind body was empty, it means IE is requesting 'all' properties
+ if (!$body) return array();
+
+ $dom = Sabre_DAV_XMLUtil::loadDOMDocument($body);
+ $elem = $dom->getElementsByTagNameNS('urn:DAV','propfind')->item(0);
+ return array_keys(Sabre_DAV_XMLUtil::parseProperties($elem));
+
+ }
+
+ // }}}
+
+}
+
diff --git a/3rdparty/Sabre/DAV/ServerPlugin.php b/3rdparty/Sabre/DAV/ServerPlugin.php
new file mode 100755
index 00000000000..131863d13fb
--- /dev/null
+++ b/3rdparty/Sabre/DAV/ServerPlugin.php
@@ -0,0 +1,90 @@
+<?php
+
+/**
+ * The baseclass for all server plugins.
+ *
+ * Plugins can modify or extend the servers behaviour.
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+abstract class Sabre_DAV_ServerPlugin {
+
+ /**
+ * This initializes the plugin.
+ *
+ * This function is called by Sabre_DAV_Server, after
+ * addPlugin is called.
+ *
+ * This method should set up the requires event subscriptions.
+ *
+ * @param Sabre_DAV_Server $server
+ * @return void
+ */
+ abstract public function initialize(Sabre_DAV_Server $server);
+
+ /**
+ * This method should return a list of server-features.
+ *
+ * This is for example 'versioning' and is added to the DAV: header
+ * in an OPTIONS response.
+ *
+ * @return array
+ */
+ public function getFeatures() {
+
+ return array();
+
+ }
+
+ /**
+ * Use this method to tell the server this plugin defines additional
+ * HTTP methods.
+ *
+ * This method is passed a uri. It should only return HTTP methods that are
+ * available for the specified uri.
+ *
+ * @param string $uri
+ * @return array
+ */
+ public function getHTTPMethods($uri) {
+
+ return array();
+
+ }
+
+ /**
+ * 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 get_class($this);
+
+ }
+
+ /**
+ * 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();
+
+ }
+
+}
+
diff --git a/3rdparty/Sabre/DAV/SimpleCollection.php b/3rdparty/Sabre/DAV/SimpleCollection.php
new file mode 100755
index 00000000000..4acf971caa5
--- /dev/null
+++ b/3rdparty/Sabre/DAV/SimpleCollection.php
@@ -0,0 +1,105 @@
+<?php
+
+/**
+ * SimpleCollection
+ *
+ * The SimpleCollection is used to quickly setup static directory structures.
+ * Just create the object with a proper name, and add children to use it.
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_SimpleCollection extends Sabre_DAV_Collection {
+
+ /**
+ * List of childnodes
+ *
+ * @var array
+ */
+ protected $children = array();
+
+ /**
+ * Name of this resource
+ *
+ * @var string
+ */
+ protected $name;
+
+ /**
+ * Creates this node
+ *
+ * The name of the node must be passed, child nodes can also be bassed.
+ * This nodes must be instances of Sabre_DAV_INode
+ *
+ * @param string $name
+ * @param array $children
+ */
+ public function __construct($name,array $children = array()) {
+
+ $this->name = $name;
+ foreach($children as $child) {
+
+ if (!($child instanceof Sabre_DAV_INode)) throw new Sabre_DAV_Exception('Only instances of Sabre_DAV_INode are allowed to be passed in the children argument');
+ $this->addChild($child);
+
+ }
+
+ }
+
+ /**
+ * Adds a new childnode to this collection
+ *
+ * @param Sabre_DAV_INode $child
+ * @return void
+ */
+ public function addChild(Sabre_DAV_INode $child) {
+
+ $this->children[$child->getName()] = $child;
+
+ }
+
+ /**
+ * Returns the name of the collection
+ *
+ * @return string
+ */
+ public function getName() {
+
+ return $this->name;
+
+ }
+
+ /**
+ * Returns a child object, by its name.
+ *
+ * This method makes use of the getChildren method to grab all the child nodes, and compares the name.
+ * Generally its wise to override this, as this can usually be optimized
+ *
+ * @param string $name
+ * @throws Sabre_DAV_Exception_NotFound
+ * @return Sabre_DAV_INode
+ */
+ public function getChild($name) {
+
+ if (isset($this->children[$name])) return $this->children[$name];
+ throw new Sabre_DAV_Exception_NotFound('File not found: ' . $name . ' in \'' . $this->getName() . '\'');
+
+ }
+
+ /**
+ * Returns a list of children for this collection
+ *
+ * @return array
+ */
+ public function getChildren() {
+
+ return array_values($this->children);
+
+ }
+
+
+}
+
diff --git a/3rdparty/Sabre/DAV/SimpleDirectory.php b/3rdparty/Sabre/DAV/SimpleDirectory.php
new file mode 100755
index 00000000000..621222ebc53
--- /dev/null
+++ b/3rdparty/Sabre/DAV/SimpleDirectory.php
@@ -0,0 +1,21 @@
+<?php
+
+/**
+ * SimpleDirectory
+ *
+ * The SimpleDirectory is used to quickly setup static directory structures.
+ * Just create the object with a proper name, and add children to use it.
+ *
+ * This class is now deprecated, use Sabre_DAV_SimpleCollection instead.
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @deprecated Use Sabre_DAV_SimpleCollection instead.
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_SimpleDirectory extends Sabre_DAV_SimpleCollection {
+
+}
+
diff --git a/3rdparty/Sabre/DAV/SimpleFile.php b/3rdparty/Sabre/DAV/SimpleFile.php
new file mode 100755
index 00000000000..58330d6861d
--- /dev/null
+++ b/3rdparty/Sabre/DAV/SimpleFile.php
@@ -0,0 +1,121 @@
+<?php
+
+/**
+ * SimpleFile
+ *
+ * The 'SimpleFile' class is used to easily add read-only immutable files to
+ * the directory structure. One usecase would be to add a 'readme.txt' to a
+ * root of a webserver with some standard content.
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_SimpleFile extends Sabre_DAV_File {
+
+ /**
+ * File contents
+ *
+ * @var string
+ */
+ protected $contents = array();
+
+ /**
+ * Name of this resource
+ *
+ * @var string
+ */
+ protected $name;
+
+ /**
+ * A mimetype, such as 'text/plain' or 'text/html'
+ *
+ * @var string
+ */
+ protected $mimeType;
+
+ /**
+ * Creates this node
+ *
+ * The name of the node must be passed, as well as the contents of the
+ * file.
+ *
+ * @param string $name
+ * @param string $contents
+ * @param string|null $mimeType
+ */
+ public function __construct($name, $contents, $mimeType = null) {
+
+ $this->name = $name;
+ $this->contents = $contents;
+ $this->mimeType = $mimeType;
+
+ }
+
+ /**
+ * Returns the node name for this file.
+ *
+ * This name is used to construct the url.
+ *
+ * @return string
+ */
+ public function getName() {
+
+ return $this->name;
+
+ }
+
+ /**
+ * Returns the data
+ *
+ * This method may either return a string or a readable stream resource
+ *
+ * @return mixed
+ */
+ public function get() {
+
+ return $this->contents;
+
+ }
+
+ /**
+ * Returns the size of the file, in bytes.
+ *
+ * @return int
+ */
+ public function getSize() {
+
+ return strlen($this->contents);
+
+ }
+
+ /**
+ * Returns the ETag for a file
+ *
+ * An ETag is a unique identifier representing the current version of the file. If the file changes, the ETag MUST change.
+ * The ETag is an arbitrary string, but MUST be surrounded by double-quotes.
+ *
+ * Return null if the ETag can not effectively be determined
+ * @return string
+ */
+ public function getETag() {
+
+ return '"' . md5($this->contents) . '"';
+
+ }
+
+ /**
+ * Returns the mime-type for a file
+ *
+ * If null is returned, we'll assume application/octet-stream
+ * @return string
+ */
+ public function getContentType() {
+
+ return $this->mimeType;
+
+ }
+
+}
diff --git a/3rdparty/Sabre/DAV/StringUtil.php b/3rdparty/Sabre/DAV/StringUtil.php
new file mode 100755
index 00000000000..b126a94c825
--- /dev/null
+++ b/3rdparty/Sabre/DAV/StringUtil.php
@@ -0,0 +1,91 @@
+<?php
+
+/**
+ * String utility
+ *
+ * This class is mainly used to implement the 'text-match' filter, used by both
+ * the CalDAV calendar-query REPORT, and CardDAV addressbook-query REPORT.
+ * Because they both need it, it was decided to put it in Sabre_DAV instead.
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_StringUtil {
+
+ /**
+ * Checks if a needle occurs in a haystack ;)
+ *
+ * @param string $haystack
+ * @param string $needle
+ * @param string $collation
+ * @param string $matchType
+ * @return bool
+ */
+ static public function textMatch($haystack, $needle, $collation, $matchType = 'contains') {
+
+ switch($collation) {
+
+ case 'i;ascii-casemap' :
+ // default strtolower takes locale into consideration
+ // we don't want this.
+ $haystack = str_replace(range('a','z'), range('A','Z'), $haystack);
+ $needle = str_replace(range('a','z'), range('A','Z'), $needle);
+ break;
+
+ case 'i;octet' :
+ // Do nothing
+ break;
+
+ case 'i;unicode-casemap' :
+ $haystack = mb_strtoupper($haystack, 'UTF-8');
+ $needle = mb_strtoupper($needle, 'UTF-8');
+ break;
+
+ default :
+ throw new Sabre_DAV_Exception_BadRequest('Collation type: ' . $collation . ' is not supported');
+
+ }
+
+ switch($matchType) {
+
+ case 'contains' :
+ return strpos($haystack, $needle)!==false;
+ case 'equals' :
+ return $haystack === $needle;
+ case 'starts-with' :
+ return strpos($haystack, $needle)===0;
+ case 'ends-with' :
+ return strrpos($haystack, $needle)===strlen($haystack)-strlen($needle);
+ default :
+ throw new Sabre_DAV_Exception_BadRequest('Match-type: ' . $matchType . ' is not supported');
+
+ }
+
+ }
+
+ /**
+ * This method takes an input string, checks if it's not valid UTF-8 and
+ * attempts to convert it to UTF-8 if it's not.
+ *
+ * Note that currently this can only convert ISO-8559-1 to UTF-8 (latin-1),
+ * anything else will likely fail.
+ *
+ * @param string $input
+ * @return string
+ */
+ static public function ensureUTF8($input) {
+
+ $encoding = mb_detect_encoding($input , array('UTF-8','ISO-8859-1'), true);
+
+ if ($encoding === 'ISO-8859-1') {
+ return utf8_encode($input);
+ } else {
+ return $input;
+ }
+
+ }
+
+}
diff --git a/3rdparty/Sabre/DAV/TemporaryFileFilterPlugin.php b/3rdparty/Sabre/DAV/TemporaryFileFilterPlugin.php
new file mode 100755
index 00000000000..36096e67775
--- /dev/null
+++ b/3rdparty/Sabre/DAV/TemporaryFileFilterPlugin.php
@@ -0,0 +1,289 @@
+<?php
+
+/**
+ * Temporary File Filter Plugin
+ *
+ * The purpose of this filter is to intercept some of the garbage files
+ * operation systems and applications tend to generate when mounting
+ * a WebDAV share as a disk.
+ *
+ * It will intercept these files and place them in a separate directory.
+ * these files are not deleted automatically, so it is adviceable to
+ * delete these after they are not accessed for 24 hours.
+ *
+ * Currently it supports:
+ * * OS/X style resource forks and .DS_Store
+ * * desktop.ini and Thumbs.db (windows)
+ * * .*.swp (vim temporary files)
+ * * .dat.* (smultron temporary files)
+ *
+ * Additional patterns can be added, by adding on to the
+ * temporaryFilePatterns property.
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_TemporaryFileFilterPlugin extends Sabre_DAV_ServerPlugin {
+
+ /**
+ * This is the list of patterns we intercept.
+ * If new patterns are added, they must be valid patterns for preg_match.
+ *
+ * @var array
+ */
+ public $temporaryFilePatterns = array(
+ '/^\._(.*)$/', // OS/X resource forks
+ '/^.DS_Store$/', // OS/X custom folder settings
+ '/^desktop.ini$/', // Windows custom folder settings
+ '/^Thumbs.db$/', // Windows thumbnail cache
+ '/^.(.*).swp$/', // ViM temporary files
+ '/^\.dat(.*)$/', // Smultron seems to create these
+ '/^~lock.(.*)#$/', // Windows 7 lockfiles
+ );
+
+ /**
+ * This is the directory where this plugin
+ * will store it's files.
+ *
+ * @var string
+ */
+ private $dataDir;
+
+ /**
+ * A reference to the main Server class
+ *
+ * @var Sabre_DAV_Server
+ */
+ private $server;
+
+ /**
+ * Creates the plugin.
+ *
+ * Make sure you specify a directory for your files. If you don't, we
+ * will use PHP's directory for session-storage instead, and you might
+ * not want that.
+ *
+ * @param string|null $dataDir
+ */
+ public function __construct($dataDir = null) {
+
+ if (!$dataDir) $dataDir = ini_get('session.save_path').'/sabredav/';
+ if (!is_dir($dataDir)) mkdir($dataDir);
+ $this->dataDir = $dataDir;
+
+ }
+
+ /**
+ * Initialize the plugin
+ *
+ * This is called automatically be the Server class after this plugin is
+ * added with Sabre_DAV_Server::addPlugin()
+ *
+ * @param Sabre_DAV_Server $server
+ * @return void
+ */
+ public function initialize(Sabre_DAV_Server $server) {
+
+ $this->server = $server;
+ $server->subscribeEvent('beforeMethod',array($this,'beforeMethod'));
+ $server->subscribeEvent('beforeCreateFile',array($this,'beforeCreateFile'));
+
+ }
+
+ /**
+ * This method is called before any HTTP method handler
+ *
+ * This method intercepts any GET, DELETE, PUT and PROPFIND calls to
+ * filenames that are known to match the 'temporary file' regex.
+ *
+ * @param string $method
+ * @param string $uri
+ * @return bool
+ */
+ public function beforeMethod($method, $uri) {
+
+ if (!$tempLocation = $this->isTempFile($uri))
+ return true;
+
+ switch($method) {
+ case 'GET' :
+ return $this->httpGet($tempLocation);
+ case 'PUT' :
+ return $this->httpPut($tempLocation);
+ case 'PROPFIND' :
+ return $this->httpPropfind($tempLocation, $uri);
+ case 'DELETE' :
+ return $this->httpDelete($tempLocation);
+ }
+ return true;
+
+ }
+
+ /**
+ * This method is invoked if some subsystem creates a new file.
+ *
+ * This is used to deal with HTTP LOCK requests which create a new
+ * file.
+ *
+ * @param string $uri
+ * @param resource $data
+ * @return bool
+ */
+ public function beforeCreateFile($uri,$data) {
+
+ if ($tempPath = $this->isTempFile($uri)) {
+
+ $hR = $this->server->httpResponse;
+ $hR->setHeader('X-Sabre-Temp','true');
+ file_put_contents($tempPath,$data);
+ return false;
+ }
+ return true;
+
+ }
+
+ /**
+ * This method will check if the url matches the temporary file pattern
+ * if it does, it will return an path based on $this->dataDir for the
+ * temporary file storage.
+ *
+ * @param string $path
+ * @return boolean|string
+ */
+ protected function isTempFile($path) {
+
+ // We're only interested in the basename.
+ list(, $tempPath) = Sabre_DAV_URLUtil::splitPath($path);
+
+ foreach($this->temporaryFilePatterns as $tempFile) {
+
+ if (preg_match($tempFile,$tempPath)) {
+ return $this->getDataDir() . '/sabredav_' . md5($path) . '.tempfile';
+ }
+
+ }
+
+ return false;
+
+ }
+
+
+ /**
+ * This method handles the GET method for temporary files.
+ * If the file doesn't exist, it will return false which will kick in
+ * the regular system for the GET method.
+ *
+ * @param string $tempLocation
+ * @return bool
+ */
+ public function httpGet($tempLocation) {
+
+ if (!file_exists($tempLocation)) return true;
+
+ $hR = $this->server->httpResponse;
+ $hR->setHeader('Content-Type','application/octet-stream');
+ $hR->setHeader('Content-Length',filesize($tempLocation));
+ $hR->setHeader('X-Sabre-Temp','true');
+ $hR->sendStatus(200);
+ $hR->sendBody(fopen($tempLocation,'r'));
+ return false;
+
+ }
+
+ /**
+ * This method handles the PUT method.
+ *
+ * @param string $tempLocation
+ * @return bool
+ */
+ public function httpPut($tempLocation) {
+
+ $hR = $this->server->httpResponse;
+ $hR->setHeader('X-Sabre-Temp','true');
+
+ $newFile = !file_exists($tempLocation);
+
+ if (!$newFile && ($this->server->httpRequest->getHeader('If-None-Match'))) {
+ throw new Sabre_DAV_Exception_PreconditionFailed('The resource already exists, and an If-None-Match header was supplied');
+ }
+
+ file_put_contents($tempLocation,$this->server->httpRequest->getBody());
+ $hR->sendStatus($newFile?201:200);
+ return false;
+
+ }
+
+ /**
+ * This method handles the DELETE method.
+ *
+ * If the file didn't exist, it will return false, which will make the
+ * standard HTTP DELETE handler kick in.
+ *
+ * @param string $tempLocation
+ * @return bool
+ */
+ public function httpDelete($tempLocation) {
+
+ if (!file_exists($tempLocation)) return true;
+
+ unlink($tempLocation);
+ $hR = $this->server->httpResponse;
+ $hR->setHeader('X-Sabre-Temp','true');
+ $hR->sendStatus(204);
+ return false;
+
+ }
+
+ /**
+ * This method handles the PROPFIND method.
+ *
+ * It's a very lazy method, it won't bother checking the request body
+ * for which properties were requested, and just sends back a default
+ * set of properties.
+ *
+ * @param string $tempLocation
+ * @param string $uri
+ * @return bool
+ */
+ public function httpPropfind($tempLocation, $uri) {
+
+ if (!file_exists($tempLocation)) return true;
+
+ $hR = $this->server->httpResponse;
+ $hR->setHeader('X-Sabre-Temp','true');
+ $hR->sendStatus(207);
+ $hR->setHeader('Content-Type','application/xml; charset=utf-8');
+
+ $this->server->parsePropFindRequest($this->server->httpRequest->getBody(true));
+
+ $properties = array(
+ 'href' => $uri,
+ 200 => array(
+ '{DAV:}getlastmodified' => new Sabre_DAV_Property_GetLastModified(filemtime($tempLocation)),
+ '{DAV:}getcontentlength' => filesize($tempLocation),
+ '{DAV:}resourcetype' => new Sabre_DAV_Property_ResourceType(null),
+ '{'.Sabre_DAV_Server::NS_SABREDAV.'}tempFile' => true,
+
+ ),
+ );
+
+ $data = $this->server->generateMultiStatus(array($properties));
+ $hR->sendBody($data);
+ return false;
+
+ }
+
+
+ /**
+ * This method returns the directory where the temporary files should be stored.
+ *
+ * @return string
+ */
+ protected function getDataDir()
+ {
+ return $this->dataDir;
+ }
+}
diff --git a/3rdparty/Sabre/DAV/Tree.php b/3rdparty/Sabre/DAV/Tree.php
new file mode 100755
index 00000000000..50216394155
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Tree.php
@@ -0,0 +1,193 @@
+<?php
+
+/**
+ * Abstract tree object
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+abstract class Sabre_DAV_Tree {
+
+ /**
+ * This function must return an INode object for a path
+ * If a Path doesn't exist, thrown a Exception_NotFound
+ *
+ * @param string $path
+ * @throws Sabre_DAV_Exception_NotFound
+ * @return Sabre_DAV_INode
+ */
+ abstract function getNodeForPath($path);
+
+ /**
+ * This function allows you to check if a node exists.
+ *
+ * Implementors of this class should override this method to make
+ * it cheaper.
+ *
+ * @param string $path
+ * @return bool
+ */
+ public function nodeExists($path) {
+
+ try {
+
+ $this->getNodeForPath($path);
+ return true;
+
+ } catch (Sabre_DAV_Exception_NotFound $e) {
+
+ return false;
+
+ }
+
+ }
+
+ /**
+ * Copies a file from path to another
+ *
+ * @param string $sourcePath The source location
+ * @param string $destinationPath The full destination path
+ * @return void
+ */
+ public function copy($sourcePath, $destinationPath) {
+
+ $sourceNode = $this->getNodeForPath($sourcePath);
+
+ // grab the dirname and basename components
+ list($destinationDir, $destinationName) = Sabre_DAV_URLUtil::splitPath($destinationPath);
+
+ $destinationParent = $this->getNodeForPath($destinationDir);
+ $this->copyNode($sourceNode,$destinationParent,$destinationName);
+
+ $this->markDirty($destinationDir);
+
+ }
+
+ /**
+ * Moves a file from one location to another
+ *
+ * @param string $sourcePath The path to the file which should be moved
+ * @param string $destinationPath The full destination path, so not just the destination parent node
+ * @return int
+ */
+ public function move($sourcePath, $destinationPath) {
+
+ list($sourceDir, $sourceName) = Sabre_DAV_URLUtil::splitPath($sourcePath);
+ list($destinationDir, $destinationName) = Sabre_DAV_URLUtil::splitPath($destinationPath);
+
+ if ($sourceDir===$destinationDir) {
+ $renameable = $this->getNodeForPath($sourcePath);
+ $renameable->setName($destinationName);
+ } else {
+ $this->copy($sourcePath,$destinationPath);
+ $this->getNodeForPath($sourcePath)->delete();
+ }
+ $this->markDirty($sourceDir);
+ $this->markDirty($destinationDir);
+
+ }
+
+ /**
+ * Deletes a node from the tree
+ *
+ * @param string $path
+ * @return void
+ */
+ public function delete($path) {
+
+ $node = $this->getNodeForPath($path);
+ $node->delete();
+
+ list($parent) = Sabre_DAV_URLUtil::splitPath($path);
+ $this->markDirty($parent);
+
+ }
+
+ /**
+ * Returns a list of childnodes for a given path.
+ *
+ * @param string $path
+ * @return array
+ */
+ public function getChildren($path) {
+
+ $node = $this->getNodeForPath($path);
+ return $node->getChildren();
+
+ }
+
+ /**
+ * This method is called with every tree update
+ *
+ * Examples of tree updates are:
+ * * node deletions
+ * * node creations
+ * * copy
+ * * move
+ * * renaming nodes
+ *
+ * If Tree classes implement a form of caching, this will allow
+ * them to make sure caches will be expired.
+ *
+ * If a path is passed, it is assumed that the entire subtree is dirty
+ *
+ * @param string $path
+ * @return void
+ */
+ public function markDirty($path) {
+
+
+ }
+
+ /**
+ * copyNode
+ *
+ * @param Sabre_DAV_INode $source
+ * @param Sabre_DAV_ICollection $destinationParent
+ * @param string $destinationName
+ * @return void
+ */
+ protected function copyNode(Sabre_DAV_INode $source,Sabre_DAV_ICollection $destinationParent,$destinationName = null) {
+
+ if (!$destinationName) $destinationName = $source->getName();
+
+ if ($source instanceof Sabre_DAV_IFile) {
+
+ $data = $source->get();
+
+ // If the body was a string, we need to convert it to a stream
+ if (is_string($data)) {
+ $stream = fopen('php://temp','r+');
+ fwrite($stream,$data);
+ rewind($stream);
+ $data = $stream;
+ }
+ $destinationParent->createFile($destinationName,$data);
+ $destination = $destinationParent->getChild($destinationName);
+
+ } elseif ($source instanceof Sabre_DAV_ICollection) {
+
+ $destinationParent->createDirectory($destinationName);
+
+ $destination = $destinationParent->getChild($destinationName);
+ foreach($source->getChildren() as $child) {
+
+ $this->copyNode($child,$destination);
+
+ }
+
+ }
+ if ($source instanceof Sabre_DAV_IProperties && $destination instanceof Sabre_DAV_IProperties) {
+
+ $props = $source->getProperties(array());
+ $destination->updateProperties($props);
+
+ }
+
+ }
+
+}
+
diff --git a/3rdparty/Sabre/DAV/Tree/Filesystem.php b/3rdparty/Sabre/DAV/Tree/Filesystem.php
new file mode 100755
index 00000000000..40580ae366f
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Tree/Filesystem.php
@@ -0,0 +1,123 @@
+<?php
+
+/**
+ * Sabre_DAV_Tree_Filesystem
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_Tree_Filesystem extends Sabre_DAV_Tree {
+
+ /**
+ * Base url on the filesystem.
+ *
+ * @var string
+ */
+ protected $basePath;
+
+ /**
+ * Creates this tree
+ *
+ * Supply the path you'd like to share.
+ *
+ * @param string $basePath
+ */
+ public function __construct($basePath) {
+
+ $this->basePath = $basePath;
+
+ }
+
+ /**
+ * Returns a new node for the given path
+ *
+ * @param string $path
+ * @return Sabre_DAV_FS_Node
+ */
+ public function getNodeForPath($path) {
+
+ $realPath = $this->getRealPath($path);
+ if (!file_exists($realPath)) throw new Sabre_DAV_Exception_NotFound('File at location ' . $realPath . ' not found');
+ if (is_dir($realPath)) {
+ return new Sabre_DAV_FS_Directory($realPath);
+ } else {
+ return new Sabre_DAV_FS_File($realPath);
+ }
+
+ }
+
+ /**
+ * Returns the real filesystem path for a webdav url.
+ *
+ * @param string $publicPath
+ * @return string
+ */
+ protected function getRealPath($publicPath) {
+
+ return rtrim($this->basePath,'/') . '/' . trim($publicPath,'/');
+
+ }
+
+ /**
+ * Copies a file or directory.
+ *
+ * This method must work recursively and delete the destination
+ * if it exists
+ *
+ * @param string $source
+ * @param string $destination
+ * @return void
+ */
+ public function copy($source,$destination) {
+
+ $source = $this->getRealPath($source);
+ $destination = $this->getRealPath($destination);
+ $this->realCopy($source,$destination);
+
+ }
+
+ /**
+ * Used by self::copy
+ *
+ * @param string $source
+ * @param string $destination
+ * @return void
+ */
+ protected function realCopy($source,$destination) {
+
+ if (is_file($source)) {
+ copy($source,$destination);
+ } else {
+ mkdir($destination);
+ foreach(scandir($source) as $subnode) {
+
+ if ($subnode=='.' || $subnode=='..') continue;
+ $this->realCopy($source.'/'.$subnode,$destination.'/'.$subnode);
+
+ }
+ }
+
+ }
+
+ /**
+ * Moves a file or directory recursively.
+ *
+ * If the destination exists, delete it first.
+ *
+ * @param string $source
+ * @param string $destination
+ * @return void
+ */
+ public function move($source,$destination) {
+
+ $source = $this->getRealPath($source);
+ $destination = $this->getRealPath($destination);
+ rename($source,$destination);
+
+ }
+
+}
+
diff --git a/3rdparty/Sabre/DAV/URLUtil.php b/3rdparty/Sabre/DAV/URLUtil.php
new file mode 100755
index 00000000000..794665a44f6
--- /dev/null
+++ b/3rdparty/Sabre/DAV/URLUtil.php
@@ -0,0 +1,121 @@
+<?php
+
+/**
+ * URL utility class
+ *
+ * This class provides methods to deal with encoding and decoding url (percent encoded) strings.
+ *
+ * It was not possible to use PHP's built-in methods for this, because some clients don't like
+ * encoding of certain characters.
+ *
+ * Specifically, it was found that GVFS (gnome's webdav client) does not like encoding of ( and
+ * ). Since these are reserved, but don't have a reserved meaning in url, these characters are
+ * kept as-is.
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_URLUtil {
+
+ /**
+ * Encodes the path of a url.
+ *
+ * slashes (/) are treated as path-separators.
+ *
+ * @param string $path
+ * @return string
+ */
+ static function encodePath($path) {
+
+ return preg_replace_callback('/([^A-Za-z0-9_\-\.~\(\)\/])/',function($match) {
+
+ return '%'.sprintf('%02x',ord($match[0]));
+
+ }, $path);
+
+ }
+
+ /**
+ * Encodes a 1 segment of a path
+ *
+ * Slashes are considered part of the name, and are encoded as %2f
+ *
+ * @param string $pathSegment
+ * @return string
+ */
+ static function encodePathSegment($pathSegment) {
+
+ return preg_replace_callback('/([^A-Za-z0-9_\-\.~\(\)])/',function($match) {
+
+ return '%'.sprintf('%02x',ord($match[0]));
+
+ }, $pathSegment);
+ }
+
+ /**
+ * Decodes a url-encoded path
+ *
+ * @param string $path
+ * @return string
+ */
+ static function decodePath($path) {
+
+ return self::decodePathSegment($path);
+
+ }
+
+ /**
+ * Decodes a url-encoded path segment
+ *
+ * @param string $path
+ * @return string
+ */
+ static function decodePathSegment($path) {
+
+ $path = rawurldecode($path);
+ $encoding = mb_detect_encoding($path, array('UTF-8','ISO-8859-1'));
+
+ switch($encoding) {
+
+ case 'ISO-8859-1' :
+ $path = utf8_encode($path);
+
+ }
+
+ return $path;
+
+ }
+
+ /**
+ * Returns the 'dirname' and 'basename' for a path.
+ *
+ * The reason there is a custom function for this purpose, is because
+ * basename() is locale aware (behaviour changes if C locale or a UTF-8 locale is used)
+ * and we need a method that just operates on UTF-8 characters.
+ *
+ * In addition basename and dirname are platform aware, and will treat backslash (\) as a
+ * directory separator on windows.
+ *
+ * This method returns the 2 components as an array.
+ *
+ * If there is no dirname, it will return an empty string. Any / appearing at the end of the
+ * string is stripped off.
+ *
+ * @param string $path
+ * @return array
+ */
+ static function splitPath($path) {
+
+ $matches = array();
+ if(preg_match('/^(?:(?:(.*)(?:\/+))?([^\/]+))(?:\/?)$/u',$path,$matches)) {
+ return array($matches[1],$matches[2]);
+ } else {
+ return array(null,null);
+ }
+
+ }
+
+}
diff --git a/3rdparty/Sabre/DAV/UUIDUtil.php b/3rdparty/Sabre/DAV/UUIDUtil.php
new file mode 100755
index 00000000000..f0eebe598e5
--- /dev/null
+++ b/3rdparty/Sabre/DAV/UUIDUtil.php
@@ -0,0 +1,64 @@
+<?php
+
+/**
+ * UUID Utility
+ *
+ * This class has static methods to generate and validate UUID's.
+ * UUIDs are used a decent amount within various *DAV standards, so it made
+ * sense to include it.
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_UUIDUtil {
+
+ /**
+ * Returns a pseudo-random v4 UUID
+ *
+ * This function is based on a comment by Andrew Moore on php.net
+ *
+ * @see http://www.php.net/manual/en/function.uniqid.php#94959
+ * @return string
+ */
+ static function getUUID() {
+
+ return sprintf( '%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
+ // 32 bits for "time_low"
+ mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ),
+
+ // 16 bits for "time_mid"
+ mt_rand( 0, 0xffff ),
+
+ // 16 bits for "time_hi_and_version",
+ // four most significant bits holds version number 4
+ mt_rand( 0, 0x0fff ) | 0x4000,
+
+ // 16 bits, 8 bits for "clk_seq_hi_res",
+ // 8 bits for "clk_seq_low",
+ // two most significant bits holds zero and one for variant DCE1.1
+ mt_rand( 0, 0x3fff ) | 0x8000,
+
+ // 48 bits for "node"
+ mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff )
+ );
+ }
+
+ /**
+ * Checks if a string is a valid UUID.
+ *
+ * @param string $uuid
+ * @return bool
+ */
+ static function validateUUID($uuid) {
+
+ return preg_match(
+ '/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/i',
+ $uuid
+ ) == true;
+
+ }
+
+}
diff --git a/3rdparty/Sabre/DAV/Version.php b/3rdparty/Sabre/DAV/Version.php
new file mode 100755
index 00000000000..274646240ab
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Version.php
@@ -0,0 +1,24 @@
+<?php
+
+/**
+ * This class contains the SabreDAV version constants.
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_Version {
+
+ /**
+ * Full version number
+ */
+ const VERSION = '1.6.4';
+
+ /**
+ * Stability : alpha, beta, stable
+ */
+ const STABILITY = 'stable';
+
+}
diff --git a/3rdparty/Sabre/DAV/XMLUtil.php b/3rdparty/Sabre/DAV/XMLUtil.php
new file mode 100755
index 00000000000..60eff3b159a
--- /dev/null
+++ b/3rdparty/Sabre/DAV/XMLUtil.php
@@ -0,0 +1,186 @@
+<?php
+
+/**
+ * XML utilities for WebDAV
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_XMLUtil {
+
+ /**
+ * Returns the 'clark notation' for an element.
+ *
+ * For example, and element encoded as:
+ * <b:myelem xmlns:b="http://www.example.org/" />
+ * will be returned as:
+ * {http://www.example.org}myelem
+ *
+ * This format is used throughout the SabreDAV sourcecode.
+ * Elements encoded with the urn:DAV namespace will
+ * be returned as if they were in the DAV: namespace. This is to avoid
+ * compatibility problems.
+ *
+ * This function will return null if a nodetype other than an Element is passed.
+ *
+ * @param DOMNode $dom
+ * @return string
+ */
+ static function toClarkNotation(DOMNode $dom) {
+
+ if ($dom->nodeType !== XML_ELEMENT_NODE) return null;
+
+ // Mapping back to the real namespace, in case it was dav
+ if ($dom->namespaceURI=='urn:DAV') $ns = 'DAV:'; else $ns = $dom->namespaceURI;
+
+ // Mapping to clark notation
+ return '{' . $ns . '}' . $dom->localName;
+
+ }
+
+ /**
+ * Parses a clark-notation string, and returns the namespace and element
+ * name components.
+ *
+ * If the string was invalid, it will throw an InvalidArgumentException.
+ *
+ * @param string $str
+ * @throws InvalidArgumentException
+ * @return array
+ */
+ static function parseClarkNotation($str) {
+
+ if (!preg_match('/^{([^}]*)}(.*)$/',$str,$matches)) {
+ throw new InvalidArgumentException('\'' . $str . '\' is not a valid clark-notation formatted string');
+ }
+
+ return array(
+ $matches[1],
+ $matches[2]
+ );
+
+ }
+
+ /**
+ * This method takes an XML document (as string) and converts all instances of the
+ * DAV: namespace to urn:DAV
+ *
+ * This is unfortunately needed, because the DAV: namespace violates the xml namespaces
+ * spec, and causes the DOM to throw errors
+ *
+ * @param string $xmlDocument
+ * @return array|string|null
+ */
+ static function convertDAVNamespace($xmlDocument) {
+
+ // This is used to map the DAV: namespace to urn:DAV. This is needed, because the DAV:
+ // namespace is actually a violation of the XML namespaces specification, and will cause errors
+ return preg_replace("/xmlns(:[A-Za-z0-9_]*)?=(\"|\')DAV:(\\2)/","xmlns\\1=\\2urn:DAV\\2",$xmlDocument);
+
+ }
+
+ /**
+ * This method provides a generic way to load a DOMDocument for WebDAV use.
+ *
+ * This method throws a Sabre_DAV_Exception_BadRequest exception for any xml errors.
+ * It does not preserve whitespace, and it converts the DAV: namespace to urn:DAV.
+ *
+ * @param string $xml
+ * @throws Sabre_DAV_Exception_BadRequest
+ * @return DOMDocument
+ */
+ static function loadDOMDocument($xml) {
+
+ if (empty($xml))
+ throw new Sabre_DAV_Exception_BadRequest('Empty XML document sent');
+
+ // The BitKinex client sends xml documents as UTF-16. PHP 5.3.1 (and presumably lower)
+ // does not support this, so we must intercept this and convert to UTF-8.
+ if (substr($xml,0,12) === "\x3c\x00\x3f\x00\x78\x00\x6d\x00\x6c\x00\x20\x00") {
+
+ // Note: the preceeding byte sequence is "<?xml" encoded as UTF_16, without the BOM.
+ $xml = iconv('UTF-16LE','UTF-8',$xml);
+
+ // Because the xml header might specify the encoding, we must also change this.
+ // This regex looks for the string encoding="UTF-16" and replaces it with
+ // encoding="UTF-8".
+ $xml = preg_replace('|<\?xml([^>]*)encoding="UTF-16"([^>]*)>|u','<?xml\1encoding="UTF-8"\2>',$xml);
+
+ }
+
+ // Retaining old error setting
+ $oldErrorSetting = libxml_use_internal_errors(true);
+
+ // Clearing any previous errors
+ libxml_clear_errors();
+
+ $dom = new DOMDocument();
+ $dom->loadXML(self::convertDAVNamespace($xml),LIBXML_NOWARNING | LIBXML_NOERROR);
+
+ // We don't generally care about any whitespace
+ $dom->preserveWhiteSpace = false;
+
+ if ($error = libxml_get_last_error()) {
+ libxml_clear_errors();
+ throw new Sabre_DAV_Exception_BadRequest('The request body had an invalid XML body. (message: ' . $error->message . ', errorcode: ' . $error->code . ', line: ' . $error->line . ')');
+ }
+
+ // Restoring old mechanism for error handling
+ if ($oldErrorSetting===false) libxml_use_internal_errors(false);
+
+ return $dom;
+
+ }
+
+ /**
+ * Parses all WebDAV properties out of a DOM Element
+ *
+ * Generally WebDAV properties are enclosed in {DAV:}prop elements. This
+ * method helps by going through all these and pulling out the actual
+ * propertynames, making them array keys and making the property values,
+ * well.. the array values.
+ *
+ * If no value was given (self-closing element) null will be used as the
+ * value. This is used in for example PROPFIND requests.
+ *
+ * Complex values are supported through the propertyMap argument. The
+ * propertyMap should have the clark-notation properties as it's keys, and
+ * classnames as values.
+ *
+ * When any of these properties are found, the unserialize() method will be
+ * (statically) called. The result of this method is used as the value.
+ *
+ * @param DOMElement $parentNode
+ * @param array $propertyMap
+ * @return array
+ */
+ static function parseProperties(DOMElement $parentNode, array $propertyMap = array()) {
+
+ $propList = array();
+ foreach($parentNode->childNodes as $propNode) {
+
+ if (Sabre_DAV_XMLUtil::toClarkNotation($propNode)!=='{DAV:}prop') continue;
+
+ foreach($propNode->childNodes as $propNodeData) {
+
+ /* If there are no elements in here, we actually get 1 text node, this special case is dedicated to netdrive */
+ if ($propNodeData->nodeType != XML_ELEMENT_NODE) continue;
+
+ $propertyName = Sabre_DAV_XMLUtil::toClarkNotation($propNodeData);
+ if (isset($propertyMap[$propertyName])) {
+ $propList[$propertyName] = call_user_func(array($propertyMap[$propertyName],'unserialize'),$propNodeData);
+ } else {
+ $propList[$propertyName] = $propNodeData->textContent;
+ }
+ }
+
+
+ }
+ return $propList;
+
+ }
+
+}
diff --git a/3rdparty/Sabre/DAV/includes.php b/3rdparty/Sabre/DAV/includes.php
new file mode 100755
index 00000000000..6a4890677ea
--- /dev/null
+++ b/3rdparty/Sabre/DAV/includes.php
@@ -0,0 +1,97 @@
+<?php
+
+/**
+ * Sabre_DAV includes file
+ *
+ * Including this file will automatically include all files from the Sabre_DAV
+ * package.
+ *
+ * This often allows faster loadtimes, as autoload-speed is often quite slow.
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+
+// Begin includes
+include __DIR__ . '/Auth/IBackend.php';
+include __DIR__ . '/Client.php';
+include __DIR__ . '/Exception.php';
+include __DIR__ . '/INode.php';
+include __DIR__ . '/IProperties.php';
+include __DIR__ . '/Locks/Backend/Abstract.php';
+include __DIR__ . '/Locks/Backend/File.php';
+include __DIR__ . '/Locks/Backend/FS.php';
+include __DIR__ . '/Locks/Backend/PDO.php';
+include __DIR__ . '/Locks/LockInfo.php';
+include __DIR__ . '/Node.php';
+include __DIR__ . '/Property/IHref.php';
+include __DIR__ . '/Property.php';
+include __DIR__ . '/Server.php';
+include __DIR__ . '/ServerPlugin.php';
+include __DIR__ . '/StringUtil.php';
+include __DIR__ . '/TemporaryFileFilterPlugin.php';
+include __DIR__ . '/Tree.php';
+include __DIR__ . '/URLUtil.php';
+include __DIR__ . '/UUIDUtil.php';
+include __DIR__ . '/Version.php';
+include __DIR__ . '/XMLUtil.php';
+include __DIR__ . '/Auth/Backend/AbstractBasic.php';
+include __DIR__ . '/Auth/Backend/AbstractDigest.php';
+include __DIR__ . '/Auth/Backend/Apache.php';
+include __DIR__ . '/Auth/Backend/File.php';
+include __DIR__ . '/Auth/Backend/PDO.php';
+include __DIR__ . '/Auth/Plugin.php';
+include __DIR__ . '/Browser/GuessContentType.php';
+include __DIR__ . '/Browser/MapGetToPropFind.php';
+include __DIR__ . '/Browser/Plugin.php';
+include __DIR__ . '/Exception/BadRequest.php';
+include __DIR__ . '/Exception/Conflict.php';
+include __DIR__ . '/Exception/Forbidden.php';
+include __DIR__ . '/Exception/InsufficientStorage.php';
+include __DIR__ . '/Exception/InvalidResourceType.php';
+include __DIR__ . '/Exception/Locked.php';
+include __DIR__ . '/Exception/LockTokenMatchesRequestUri.php';
+include __DIR__ . '/Exception/MethodNotAllowed.php';
+include __DIR__ . '/Exception/NotAuthenticated.php';
+include __DIR__ . '/Exception/NotFound.php';
+include __DIR__ . '/Exception/NotImplemented.php';
+include __DIR__ . '/Exception/PaymentRequired.php';
+include __DIR__ . '/Exception/PreconditionFailed.php';
+include __DIR__ . '/Exception/ReportNotImplemented.php';
+include __DIR__ . '/Exception/RequestedRangeNotSatisfiable.php';
+include __DIR__ . '/Exception/UnsupportedMediaType.php';
+include __DIR__ . '/FS/Node.php';
+include __DIR__ . '/FSExt/Node.php';
+include __DIR__ . '/ICollection.php';
+include __DIR__ . '/IExtendedCollection.php';
+include __DIR__ . '/IFile.php';
+include __DIR__ . '/IQuota.php';
+include __DIR__ . '/Locks/Plugin.php';
+include __DIR__ . '/Mount/Plugin.php';
+include __DIR__ . '/ObjectTree.php';
+include __DIR__ . '/Property/GetLastModified.php';
+include __DIR__ . '/Property/Href.php';
+include __DIR__ . '/Property/HrefList.php';
+include __DIR__ . '/Property/LockDiscovery.php';
+include __DIR__ . '/Property/ResourceType.php';
+include __DIR__ . '/Property/Response.php';
+include __DIR__ . '/Property/ResponseList.php';
+include __DIR__ . '/Property/SupportedLock.php';
+include __DIR__ . '/Property/SupportedReportSet.php';
+include __DIR__ . '/Tree/Filesystem.php';
+include __DIR__ . '/Collection.php';
+include __DIR__ . '/Directory.php';
+include __DIR__ . '/Exception/ConflictingLock.php';
+include __DIR__ . '/Exception/FileNotFound.php';
+include __DIR__ . '/File.php';
+include __DIR__ . '/FS/Directory.php';
+include __DIR__ . '/FS/File.php';
+include __DIR__ . '/FSExt/Directory.php';
+include __DIR__ . '/FSExt/File.php';
+include __DIR__ . '/SimpleCollection.php';
+include __DIR__ . '/SimpleDirectory.php';
+include __DIR__ . '/SimpleFile.php';
+// End includes
diff --git a/3rdparty/Sabre/DAVACL/AbstractPrincipalCollection.php b/3rdparty/Sabre/DAVACL/AbstractPrincipalCollection.php
new file mode 100755
index 00000000000..e05b7749805
--- /dev/null
+++ b/3rdparty/Sabre/DAVACL/AbstractPrincipalCollection.php
@@ -0,0 +1,154 @@
+<?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 specified 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
+ */
+ 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 array
+ */
+ 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_NotFound
+ * @return Sabre_DAV_IPrincipal
+ */
+ public function getChild($name) {
+
+ $principalInfo = $this->principalBackend->getPrincipalByPath($this->principalPrefix . '/' . $name);
+ if (!$principalInfo) throw new Sabre_DAV_Exception_NotFound('Principal with name ' . $name . ' not found');
+ return $this->getChildForPrincipal($principalInfo);
+
+ }
+
+ /**
+ * This method is used to search for principals matching a set of
+ * properties.
+ *
+ * This search is specifically used by RFC3744's principal-property-search
+ * REPORT. You should at least allow searching on
+ * http://sabredav.org/ns}email-address.
+ *
+ * The actual search should be a unicode-non-case-sensitive search. The
+ * keys in searchProperties are the WebDAV property names, while the values
+ * are the property values to search on.
+ *
+ * If multiple properties are being searched on, the search should be
+ * AND'ed.
+ *
+ * This method should simply return a list of 'child names', which may be
+ * used to call $this->getChild in the future.
+ *
+ * @param array $searchProperties
+ * @return array
+ */
+ public function searchPrincipals(array $searchProperties) {
+
+ $result = $this->principalBackend->searchPrincipals($this->principalPrefix, $searchProperties);
+ $r = array();
+
+ foreach($result as $row) {
+ list(, $r[]) = Sabre_DAV_URLUtil::splitPath($row);
+ }
+
+ return $r;
+
+ }
+
+}
diff --git a/3rdparty/Sabre/DAVACL/Exception/AceConflict.php b/3rdparty/Sabre/DAVACL/Exception/AceConflict.php
new file mode 100755
index 00000000000..4b9f93b0036
--- /dev/null
+++ b/3rdparty/Sabre/DAVACL/Exception/AceConflict.php
@@ -0,0 +1,32 @@
+<?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/3rdparty/Sabre/DAVACL/Exception/NeedPrivileges.php b/3rdparty/Sabre/DAVACL/Exception/NeedPrivileges.php
new file mode 100755
index 00000000000..9b055dd9709
--- /dev/null
+++ b/3rdparty/Sabre/DAVACL/Exception/NeedPrivileges.php
@@ -0,0 +1,81 @@
+<?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
+ * @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;
+
+ parent::__construct('User did not have the required privileges (' . implode(',', $privileges) . ') for path "' . $uri . '"');
+
+ }
+
+ /**
+ * 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/3rdparty/Sabre/DAVACL/Exception/NoAbstract.php b/3rdparty/Sabre/DAVACL/Exception/NoAbstract.php
new file mode 100755
index 00000000000..f44e3e32281
--- /dev/null
+++ b/3rdparty/Sabre/DAVACL/Exception/NoAbstract.php
@@ -0,0 +1,32 @@
+<?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/3rdparty/Sabre/DAVACL/Exception/NotRecognizedPrincipal.php b/3rdparty/Sabre/DAVACL/Exception/NotRecognizedPrincipal.php
new file mode 100755
index 00000000000..8d1e38ca1b4
--- /dev/null
+++ b/3rdparty/Sabre/DAVACL/Exception/NotRecognizedPrincipal.php
@@ -0,0 +1,32 @@
+<?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/3rdparty/Sabre/DAVACL/Exception/NotSupportedPrivilege.php b/3rdparty/Sabre/DAVACL/Exception/NotSupportedPrivilege.php
new file mode 100755
index 00000000000..3b5d012d7fa
--- /dev/null
+++ b/3rdparty/Sabre/DAVACL/Exception/NotSupportedPrivilege.php
@@ -0,0 +1,32 @@
+<?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/3rdparty/Sabre/DAVACL/IACL.php b/3rdparty/Sabre/DAVACL/IACL.php
new file mode 100755
index 00000000000..003e6993483
--- /dev/null
+++ b/3rdparty/Sabre/DAVACL/IACL.php
@@ -0,0 +1,73 @@
+<?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);
+
+ /**
+ * Returns the list of supported privileges for this node.
+ *
+ * The returned data structure is a list of nested privileges.
+ * See Sabre_DAVACL_Plugin::getDefaultSupportedPrivilegeSet for a simple
+ * standard structure.
+ *
+ * If null is returned from this method, the default privilege set is used,
+ * which is fine for most common usecases.
+ *
+ * @return array|null
+ */
+ function getSupportedPrivilegeSet();
+
+
+}
diff --git a/3rdparty/Sabre/DAVACL/IPrincipal.php b/3rdparty/Sabre/DAVACL/IPrincipal.php
new file mode 100755
index 00000000000..fc7605bf625
--- /dev/null
+++ b/3rdparty/Sabre/DAVACL/IPrincipal.php
@@ -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 alternative 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/3rdparty/Sabre/DAVACL/IPrincipalBackend.php b/3rdparty/Sabre/DAVACL/IPrincipalBackend.php
new file mode 100755
index 00000000000..e798bf890c0
--- /dev/null
+++ b/3rdparty/Sabre/DAVACL/IPrincipalBackend.php
@@ -0,0 +1,153 @@
+<?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 actually 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);
+
+ /**
+ * Updates one ore more webdav properties on a principal.
+ *
+ * The list of mutations is supplied as an array. Each key in the array is
+ * a propertyname, such as {DAV:}displayname.
+ *
+ * Each value is the actual value to be updated. If a value is null, it
+ * must be deleted.
+ *
+ * This method should be atomic. It must either completely succeed, or
+ * completely fail. Success and failure can simply be returned as 'true' or
+ * 'false'.
+ *
+ * It is also possible to return detailed failure information. In that case
+ * an array such as this should be returned:
+ *
+ * array(
+ * 200 => array(
+ * '{DAV:}prop1' => null,
+ * ),
+ * 201 => array(
+ * '{DAV:}prop2' => null,
+ * ),
+ * 403 => array(
+ * '{DAV:}prop3' => null,
+ * ),
+ * 424 => array(
+ * '{DAV:}prop4' => null,
+ * ),
+ * );
+ *
+ * In this previous example prop1 was successfully updated or deleted, and
+ * prop2 was succesfully created.
+ *
+ * prop3 failed to update due to '403 Forbidden' and because of this prop4
+ * also could not be updated with '424 Failed dependency'.
+ *
+ * This last example was actually incorrect. While 200 and 201 could appear
+ * in 1 response, if there's any error (403) the other properties should
+ * always fail with 423 (failed dependency).
+ *
+ * But anyway, if you don't want to scratch your head over this, just
+ * return true or false.
+ *
+ * @param string $path
+ * @param array $mutations
+ * @return array|bool
+ */
+ function updatePrincipal($path, $mutations);
+
+ /**
+ * This method is used to search for principals matching a set of
+ * properties.
+ *
+ * This search is specifically used by RFC3744's principal-property-search
+ * REPORT. You should at least allow searching on
+ * http://sabredav.org/ns}email-address.
+ *
+ * The actual search should be a unicode-non-case-sensitive search. The
+ * keys in searchProperties are the WebDAV property names, while the values
+ * are the property values to search on.
+ *
+ * If multiple properties are being searched on, the search should be
+ * AND'ed.
+ *
+ * This method should simply return an array with full principal uri's.
+ *
+ * If somebody attempted to search on a property the backend does not
+ * support, you should simply return 0 results.
+ *
+ * You can also just return 0 results if you choose to not support
+ * searching at all, but keep in mind that this may stop certain features
+ * from working.
+ *
+ * @param string $prefixPath
+ * @param array $searchProperties
+ * @return array
+ */
+ function searchPrincipals($prefixPath, array $searchProperties);
+
+ /**
+ * 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/3rdparty/Sabre/DAVACL/Plugin.php b/3rdparty/Sabre/DAVACL/Plugin.php
new file mode 100755
index 00000000000..5c828c6d97b
--- /dev/null
+++ b/3rdparty/Sabre/DAVACL/Plugin.php
@@ -0,0 +1,1348 @@
+<?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';
+
+ /**
+ * This list of properties are the properties a client can search on using
+ * the {DAV:}principal-property-search report.
+ *
+ * The keys are the property names, values are descriptions.
+ *
+ * @var array
+ */
+ public $principalSearchPropertySet = array(
+ '{DAV:}displayname' => 'Display name',
+ '{http://sabredav.org/ns}email-address' => 'Email address',
+ );
+
+ /**
+ * Any principal uri's added here, will automatically be added to the list
+ * of ACL's. They will effectively receive {DAV:}all privileges, as a
+ * protected privilege.
+ *
+ * @var array
+ */
+ public $adminPrincipals = array();
+
+ /**
+ * 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 int $recursion
+ * @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.
+ *
+ * You can either get the list of privileges by a uri (path) or by
+ * specifying a Node.
+ *
+ * @param string|Sabre_DAV_INode $node
+ * @return array
+ */
+ public function getSupportedPrivilegeSet($node) {
+
+ if (is_string($node)) {
+ $node = $this->server->tree->getNodeForPath($node);
+ }
+
+ if ($node instanceof Sabre_DAVACL_IACL) {
+ $result = $node->getSupportedPrivilegeSet();
+
+ if ($result)
+ return $result;
+ }
+
+ return self::getDefaultSupportedPrivilegeSet();
+
+ }
+
+ /**
+ * Returns a fairly standard set of privileges, which may be useful for
+ * other systems to use as a basis.
+ *
+ * @return array
+ */
+ static function getDefaultSupportedPrivilegeSet() {
+
+ 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
+ *
+ * @param string|Sabre_DAV_INode $node
+ * @return array
+ */
+ final public function getFlatPrivilegeSet($node) {
+
+ $privs = $this->getSupportedPrivilegeSet($node);
+
+ $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.
+ *
+ * @param array $priv
+ * @param $concrete
+ * @param array $flat
+ * @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 null;
+ }
+ $acl = $node->getACL();
+ foreach($this->adminPrincipals as $adminPrincipal) {
+ $acl[] = array(
+ 'principal' => $adminPrincipal,
+ 'privilege' => '{DAV:}all',
+ 'protected' => true,
+ );
+ }
+ return $acl;
+
+ }
+
+ /**
+ * 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) {
+
+ $principal = $ace['principal'];
+
+ switch($principal) {
+
+ case '{DAV:}owner' :
+ $owner = $node->getOwner();
+ if ($owner && in_array($owner, $principals)) {
+ $collected[] = $ace;
+ }
+ break;
+
+
+ // 'all' matches for every user
+ case '{DAV:}all' :
+
+ // 'authenticated' matched for every user that's logged in.
+ // Since it's not possible to use ACL while not being logged
+ // in, this is also always true.
+ case '{DAV:}authenticated' :
+ $collected[] = $ace;
+ break;
+
+ // 'unauthenticated' can never occur either, so we simply
+ // ignore these.
+ case '{DAV:}unauthenticated' :
+ break;
+
+ default :
+ if (in_array($ace['principal'], $principals)) {
+ $collected[] = $ace;
+ }
+ break;
+
+ }
+
+
+
+ }
+
+ // Now we deduct all aggregated privileges.
+ $flat = $this->getFlatPrivilegeSet($node);
+
+ $collected2 = array();
+ while(count($collected)) {
+
+ $current = array_pop($collected);
+ $collected2[] = $current['privilege'];
+
+ foreach($flat[$current['privilege']]['aggregates'] as $subPriv) {
+ $collected2[] = $subPriv;
+ $collected[] = $flat[$subPriv];
+ }
+
+ }
+
+ return array_values(array_unique($collected2));
+
+ }
+
+ /**
+ * Principal property search
+ *
+ * This method can search for principals matching certain values in
+ * properties.
+ *
+ * This method will return a list of properties for the matched properties.
+ *
+ * @param array $searchProperties The properties to search on. This is a
+ * key-value list. The keys are property
+ * names, and the values the strings to
+ * match them on.
+ * @param array $requestedProperties This is the list of properties to
+ * return for every match.
+ * @param string $collectionUri The principal collection to search on.
+ * If this is ommitted, the standard
+ * principal collection-set will be used.
+ * @return array This method returns an array structure similar to
+ * Sabre_DAV_Server::getPropertiesForPath. Returned
+ * properties are index by a HTTP status code.
+ *
+ */
+ public function principalSearch(array $searchProperties, array $requestedProperties, $collectionUri = null) {
+
+ if (!is_null($collectionUri)) {
+ $uris = array($collectionUri);
+ } else {
+ $uris = $this->principalCollectionSet;
+ }
+
+ $lookupResults = array();
+ foreach($uris as $uri) {
+
+ $principalCollection = $this->server->tree->getNodeForPath($uri);
+ if (!$principalCollection instanceof Sabre_DAVACL_AbstractPrincipalCollection) {
+ // Not a principal collection, we're simply going to ignore
+ // this.
+ continue;
+ }
+
+ $results = $principalCollection->searchPrincipals($searchProperties);
+ foreach($results as $result) {
+ $lookupResults[] = rtrim($uri,'/') . '/' . $result;
+ }
+
+ }
+
+ $matches = array();
+
+ foreach($lookupResults as $lookupResult) {
+
+ list($matches[]) = $this->server->getPropertiesForPath($lookupResult, $requestedProperties, 0);
+
+ }
+
+ return $matches;
+
+ }
+
+ /**
+ * 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($node));
+
+ }
+ 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));
+ }
+
+ }
+
+ }
+
+ /* The acl-restrictions property contains information on how privileges
+ * must behave.
+ */
+ if (false !== ($index = array_search('{DAV:}acl-restrictions', $requestedProperties))) {
+ unset($requestedProperties[$index]);
+ $returnedProperties[200]['{DAV:}acl-restrictions'] = new Sabre_DAVACL_Property_AclRestrictions();
+ }
+
+ }
+
+ /**
+ * 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 bool
+ */
+ 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 bool
+ */
+ 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 bool
+ */
+ 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($node);
+
+ /* Checking if protected principals from the existing principal set are
+ not overwritten. */
+ foreach($oldAcl as $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 $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_NotFound $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 int $depth
+ * @return array
+ */
+ 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) {
+
+ $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($this->principalSearchPropertySet 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 responsible 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) {
+
+ list($searchProperties, $requestedProperties, $applyToPrincipalCollectionSet) = $this->parsePrincipalPropertySearchReportRequest($dom);
+
+ $uri = null;
+ if (!$applyToPrincipalCollectionSet) {
+ $uri = $this->server->getRequestUri();
+ }
+ $result = $this->principalSearch($searchProperties, $requestedProperties, $uri);
+
+ $xml = $this->server->generateMultiStatus($result);
+ $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/3rdparty/Sabre/DAVACL/Principal.php b/3rdparty/Sabre/DAVACL/Principal.php
new file mode 100755
index 00000000000..51c6658afd6
--- /dev/null
+++ b/3rdparty/Sabre/DAVACL/Principal.php
@@ -0,0 +1,279 @@
+<?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 Sabre_DAVACL_IPrincipalBackend $principalBackend
+ * @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 alternative 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 $groupMembers
+ * @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 string
+ */
+ 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 array
+ */
+ 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.
+ *
+ * @param array $mutations
+ * @see Sabre_DAV_IProperties::updateProperties
+ * @return bool|array
+ */
+ public function updateProperties($mutations) {
+
+ return $this->principalBackend->updatePrincipal($this->principalProperties['uri'], $mutations);
+
+ }
+
+ /**
+ * 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->getPrincipalUrl(),
+ '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');
+
+ }
+
+ /**
+ * Returns the list of supported privileges for this node.
+ *
+ * The returned data structure is a list of nested privileges.
+ * See Sabre_DAVACL_Plugin::getDefaultSupportedPrivilegeSet for a simple
+ * standard structure.
+ *
+ * If null is returned from this method, the default privilege set is used,
+ * which is fine for most common usecases.
+ *
+ * @return array|null
+ */
+ public function getSupportedPrivilegeSet() {
+
+ return null;
+
+ }
+
+}
diff --git a/3rdparty/Sabre/DAVACL/PrincipalBackend/PDO.php b/3rdparty/Sabre/DAVACL/PrincipalBackend/PDO.php
new file mode 100755
index 00000000000..a76b4a9d727
--- /dev/null
+++ b/3rdparty/Sabre/DAVACL/PrincipalBackend/PDO.php
@@ -0,0 +1,427 @@
+<?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;
+
+ /**
+ * A list of additional fields to support
+ *
+ * @var array
+ */
+ protected $fieldMap = array(
+
+ /**
+ * This property can be used to display the users' real name.
+ */
+ '{DAV:}displayname' => array(
+ 'dbField' => 'displayname',
+ ),
+
+ /**
+ * This property is actually used by the CardDAV plugin, where it gets
+ * mapped to {http://calendarserver.orgi/ns/}me-card.
+ *
+ * The reason we don't straight-up use that property, is because
+ * me-card is defined as a property on the users' addressbook
+ * collection.
+ */
+ '{http://sabredav.org/ns}vcard-url' => array(
+ 'dbField' => 'vcardurl',
+ ),
+ /**
+ * This is the users' primary email-address.
+ */
+ '{http://sabredav.org/ns}email-address' => array(
+ 'dbField' => 'email',
+ ),
+ );
+
+ /**
+ * Sets up the backend.
+ *
+ * @param PDO $pdo
+ * @param string $tableName
+ * @param string $groupMembersTableName
+ */
+ 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) {
+
+ $fields = array(
+ 'uri',
+ );
+
+ foreach($this->fieldMap as $key=>$value) {
+ $fields[] = $value['dbField'];
+ }
+ $result = $this->pdo->query('SELECT '.implode(',', $fields).' 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;
+
+ $principal = array(
+ 'uri' => $row['uri'],
+ );
+ foreach($this->fieldMap as $key=>$value) {
+ if ($row[$value['dbField']]) {
+ $principal[$key] = $row[$value['dbField']];
+ }
+ }
+ $principals[] = $principal;
+
+ }
+
+ 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) {
+
+ $fields = array(
+ 'id',
+ 'uri',
+ );
+
+ foreach($this->fieldMap as $key=>$value) {
+ $fields[] = $value['dbField'];
+ }
+ $stmt = $this->pdo->prepare('SELECT '.implode(',', $fields).' FROM '. $this->tableName . ' WHERE uri = ?');
+ $stmt->execute(array($path));
+
+ $row = $stmt->fetch(PDO::FETCH_ASSOC);
+ if (!$row) return;
+
+ $principal = array(
+ 'id' => $row['id'],
+ 'uri' => $row['uri'],
+ );
+ foreach($this->fieldMap as $key=>$value) {
+ if ($row[$value['dbField']]) {
+ $principal[$key] = $row[$value['dbField']];
+ }
+ }
+ return $principal;
+
+ }
+
+ /**
+ * Updates one ore more webdav properties on a principal.
+ *
+ * The list of mutations is supplied as an array. Each key in the array is
+ * a propertyname, such as {DAV:}displayname.
+ *
+ * Each value is the actual value to be updated. If a value is null, it
+ * must be deleted.
+ *
+ * This method should be atomic. It must either completely succeed, or
+ * completely fail. Success and failure can simply be returned as 'true' or
+ * 'false'.
+ *
+ * It is also possible to return detailed failure information. In that case
+ * an array such as this should be returned:
+ *
+ * array(
+ * 200 => array(
+ * '{DAV:}prop1' => null,
+ * ),
+ * 201 => array(
+ * '{DAV:}prop2' => null,
+ * ),
+ * 403 => array(
+ * '{DAV:}prop3' => null,
+ * ),
+ * 424 => array(
+ * '{DAV:}prop4' => null,
+ * ),
+ * );
+ *
+ * In this previous example prop1 was successfully updated or deleted, and
+ * prop2 was succesfully created.
+ *
+ * prop3 failed to update due to '403 Forbidden' and because of this prop4
+ * also could not be updated with '424 Failed dependency'.
+ *
+ * This last example was actually incorrect. While 200 and 201 could appear
+ * in 1 response, if there's any error (403) the other properties should
+ * always fail with 423 (failed dependency).
+ *
+ * But anyway, if you don't want to scratch your head over this, just
+ * return true or false.
+ *
+ * @param string $path
+ * @param array $mutations
+ * @return array|bool
+ */
+ public function updatePrincipal($path, $mutations) {
+
+ $updateAble = array();
+ foreach($mutations as $key=>$value) {
+
+ // We are not aware of this field, we must fail.
+ if (!isset($this->fieldMap[$key])) {
+
+ $response = array(
+ 403 => array(
+ $key => null,
+ ),
+ 424 => array(),
+ );
+
+ // Adding the rest to the response as a 424
+ foreach($mutations as $subKey=>$subValue) {
+ if ($subKey !== $key) {
+ $response[424][$subKey] = null;
+ }
+ }
+ return $response;
+ }
+
+ $updateAble[$this->fieldMap[$key]['dbField']] = $value;
+
+ }
+
+ // No fields to update
+ $query = "UPDATE " . $this->tableName . " SET ";
+
+ $first = true;
+ foreach($updateAble as $key => $value) {
+ if (!$first) {
+ $query.= ', ';
+ }
+ $first = false;
+ $query.= "$key = :$key ";
+ }
+ $query.='WHERE uri = :uri';
+ $stmt = $this->pdo->prepare($query);
+ $updateAble['uri'] = $path;
+ $stmt->execute($updateAble);
+
+ return true;
+
+ }
+
+ /**
+ * This method is used to search for principals matching a set of
+ * properties.
+ *
+ * This search is specifically used by RFC3744's principal-property-search
+ * REPORT. You should at least allow searching on
+ * http://sabredav.org/ns}email-address.
+ *
+ * The actual search should be a unicode-non-case-sensitive search. The
+ * keys in searchProperties are the WebDAV property names, while the values
+ * are the property values to search on.
+ *
+ * If multiple properties are being searched on, the search should be
+ * AND'ed.
+ *
+ * This method should simply return an array with full principal uri's.
+ *
+ * If somebody attempted to search on a property the backend does not
+ * support, you should simply return 0 results.
+ *
+ * You can also just return 0 results if you choose to not support
+ * searching at all, but keep in mind that this may stop certain features
+ * from working.
+ *
+ * @param string $prefixPath
+ * @param array $searchProperties
+ * @return array
+ */
+ public function searchPrincipals($prefixPath, array $searchProperties) {
+
+ $query = 'SELECT uri FROM ' . $this->tableName . ' WHERE 1=1 ';
+ $values = array();
+ foreach($searchProperties as $property => $value) {
+
+ switch($property) {
+
+ case '{DAV:}displayname' :
+ $query.=' AND displayname LIKE ?';
+ $values[] = '%' . $value . '%';
+ break;
+ case '{http://sabredav.org/ns}email-address' :
+ $query.=' AND email LIKE ?';
+ $values[] = '%' . $value . '%';
+ break;
+ default :
+ // Unsupported property
+ return array();
+
+ }
+
+ }
+ $stmt = $this->pdo->prepare($query);
+ $stmt->execute($values);
+
+ $principals = array();
+ while($row = $stmt->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[] = $row['uri'];
+
+ }
+
+ return $principals;
+
+ }
+
+ /**
+ * 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/3rdparty/Sabre/DAVACL/PrincipalCollection.php b/3rdparty/Sabre/DAVACL/PrincipalCollection.php
new file mode 100755
index 00000000000..c3e4cb83f23
--- /dev/null
+++ b/3rdparty/Sabre/DAVACL/PrincipalCollection.php
@@ -0,0 +1,35 @@
+<?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/3rdparty/Sabre/DAVACL/Property/Acl.php b/3rdparty/Sabre/DAVACL/Property/Acl.php
new file mode 100755
index 00000000000..05e1a690b3c
--- /dev/null
+++ b/3rdparty/Sabre/DAVACL/Property/Acl.php
@@ -0,0 +1,209 @@
+<?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;
+
+ /**
+ * Whether 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));
+
+ switch($principal->getType()) {
+ case Sabre_DAVACL_Property_Principal::HREF :
+ $principal = $principal->getHref();
+ break;
+ case Sabre_DAVACL_Property_Principal::AUTHENTICATED :
+ $principal = '{DAV:}authenticated';
+ break;
+ case Sabre_DAVACL_Property_Principal::UNAUTHENTICATED :
+ $principal = '{DAV:}unauthenticated';
+ break;
+ case Sabre_DAVACL_Property_Principal::ALL :
+ $principal = '{DAV:}all';
+ break;
+
+ }
+
+ $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);
+ switch($ace['principal']) {
+ case '{DAV:}authenticated' :
+ $principal->appendChild($doc->createElementNS('DAV:','d:authenticated'));
+ break;
+ case '{DAV:}unauthenticated' :
+ $principal->appendChild($doc->createElementNS('DAV:','d:unauthenticated'));
+ break;
+ case '{DAV:}all' :
+ $principal->appendChild($doc->createElementNS('DAV:','d:all'));
+ break;
+ default:
+ $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/3rdparty/Sabre/DAVACL/Property/AclRestrictions.php b/3rdparty/Sabre/DAVACL/Property/AclRestrictions.php
new file mode 100755
index 00000000000..a8b054956dd
--- /dev/null
+++ b/3rdparty/Sabre/DAVACL/Property/AclRestrictions.php
@@ -0,0 +1,32 @@
+<?php
+
+/**
+ * AclRestrictions property
+ *
+ * This property represents {DAV:}acl-restrictions, as defined in RFC3744.
+ *
+ * @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_AclRestrictions extends Sabre_DAV_Property {
+
+ /**
+ * Serializes the property into a DOMElement
+ *
+ * @param Sabre_DAV_Server $server
+ * @param DOMElement $elem
+ * @return void
+ */
+ public function serialize(Sabre_DAV_Server $server,DOMElement $elem) {
+
+ $doc = $elem->ownerDocument;
+
+ $elem->appendChild($doc->createElementNS('DAV:','d:grant-only'));
+ $elem->appendChild($doc->createElementNS('DAV:','d:no-invert'));
+
+ }
+
+}
diff --git a/3rdparty/Sabre/DAVACL/Property/CurrentUserPrivilegeSet.php b/3rdparty/Sabre/DAVACL/Property/CurrentUserPrivilegeSet.php
new file mode 100755
index 00000000000..94a29640615
--- /dev/null
+++ b/3rdparty/Sabre/DAVACL/Property/CurrentUserPrivilegeSet.php
@@ -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/3rdparty/Sabre/DAVACL/Property/Principal.php b/3rdparty/Sabre/DAVACL/Property/Principal.php
new file mode 100755
index 00000000000..c36328a58e0
--- /dev/null
+++ b/3rdparty/Sabre/DAVACL/Property/Principal.php
@@ -0,0 +1,160 @@
+<?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 principals can be specified with the HREF
+ */
+ const HREF = 3;
+
+ /**
+ * Everybody, basically
+ */
+ const ALL = 4;
+
+ /**
+ * 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|null $href
+ */
+ 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);
+ case '{DAV:}all':
+ return new self(self::ALL);
+ default :
+ throw new Sabre_DAV_Exception_BadRequest('Unexpected element (' . Sabre_DAV_XMLUtil::toClarkNotation($parent) . '). Could not deserialize');
+
+ }
+
+ }
+
+}
diff --git a/3rdparty/Sabre/DAVACL/Property/SupportedPrivilegeSet.php b/3rdparty/Sabre/DAVACL/Property/SupportedPrivilegeSet.php
new file mode 100755
index 00000000000..276d57ae093
--- /dev/null
+++ b/3rdparty/Sabre/DAVACL/Property/SupportedPrivilegeSet.php
@@ -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/3rdparty/Sabre/DAVACL/Version.php b/3rdparty/Sabre/DAVACL/Version.php
new file mode 100755
index 00000000000..9950f748741
--- /dev/null
+++ b/3rdparty/Sabre/DAVACL/Version.php
@@ -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.6.0';
+
+ /**
+ * Stability : alpha, beta, stable
+ */
+ const STABILITY = 'stable';
+
+}
diff --git a/3rdparty/Sabre/DAVACL/includes.php b/3rdparty/Sabre/DAVACL/includes.php
new file mode 100755
index 00000000000..28fa3eed225
--- /dev/null
+++ b/3rdparty/Sabre/DAVACL/includes.php
@@ -0,0 +1,38 @@
+<?php
+
+/**
+ * Sabre_DAVACL includes file
+ *
+ * Including this file will automatically include all files from the
+ * Sabre_DAVACL package.
+ *
+ * This often allows faster loadtimes, as autoload-speed is often quite slow.
+ *
+ * @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
+ */
+
+// Begin includes
+include __DIR__ . '/AbstractPrincipalCollection.php';
+include __DIR__ . '/Exception/AceConflict.php';
+include __DIR__ . '/Exception/NeedPrivileges.php';
+include __DIR__ . '/Exception/NoAbstract.php';
+include __DIR__ . '/Exception/NotRecognizedPrincipal.php';
+include __DIR__ . '/Exception/NotSupportedPrivilege.php';
+include __DIR__ . '/IACL.php';
+include __DIR__ . '/IPrincipal.php';
+include __DIR__ . '/IPrincipalBackend.php';
+include __DIR__ . '/Plugin.php';
+include __DIR__ . '/Principal.php';
+include __DIR__ . '/PrincipalBackend/PDO.php';
+include __DIR__ . '/PrincipalCollection.php';
+include __DIR__ . '/Property/Acl.php';
+include __DIR__ . '/Property/AclRestrictions.php';
+include __DIR__ . '/Property/CurrentUserPrivilegeSet.php';
+include __DIR__ . '/Property/Principal.php';
+include __DIR__ . '/Property/SupportedPrivilegeSet.php';
+include __DIR__ . '/Version.php';
+// End includes
diff --git a/3rdparty/Sabre/HTTP/AWSAuth.php b/3rdparty/Sabre/HTTP/AWSAuth.php
new file mode 100755
index 00000000000..fb8245c8cbf
--- /dev/null
+++ b/3rdparty/Sabre/HTTP/AWSAuth.php
@@ -0,0 +1,227 @@
+<?php
+
+/**
+ * HTTP AWS Authentication handler
+ *
+ * Use this class to leverage amazon's AWS authentication header
+ *
+ * @package Sabre
+ * @subpackage HTTP
+ * @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_HTTP_AWSAuth extends Sabre_HTTP_AbstractAuth {
+
+ /**
+ * The signature supplied by the HTTP client
+ *
+ * @var string
+ */
+ private $signature = null;
+
+ /**
+ * The accesskey supplied by the HTTP client
+ *
+ * @var string
+ */
+ private $accessKey = null;
+
+ /**
+ * An error code, if any
+ *
+ * This value will be filled with one of the ERR_* constants
+ *
+ * @var int
+ */
+ public $errorCode = 0;
+
+ const ERR_NOAWSHEADER = 1;
+ const ERR_MD5CHECKSUMWRONG = 2;
+ const ERR_INVALIDDATEFORMAT = 3;
+ const ERR_REQUESTTIMESKEWED = 4;
+ const ERR_INVALIDSIGNATURE = 5;
+
+ /**
+ * Gathers all information from the headers
+ *
+ * This method needs to be called prior to anything else.
+ *
+ * @return bool
+ */
+ public function init() {
+
+ $authHeader = $this->httpRequest->getHeader('Authorization');
+ $authHeader = explode(' ',$authHeader);
+
+ if ($authHeader[0]!='AWS' || !isset($authHeader[1])) {
+ $this->errorCode = self::ERR_NOAWSHEADER;
+ return false;
+ }
+
+ list($this->accessKey,$this->signature) = explode(':',$authHeader[1]);
+
+ return true;
+
+ }
+
+ /**
+ * Returns the username for the request
+ *
+ * @return string
+ */
+ public function getAccessKey() {
+
+ return $this->accessKey;
+
+ }
+
+ /**
+ * Validates the signature based on the secretKey
+ *
+ * @param string $secretKey
+ * @return bool
+ */
+ public function validate($secretKey) {
+
+ $contentMD5 = $this->httpRequest->getHeader('Content-MD5');
+
+ if ($contentMD5) {
+ // We need to validate the integrity of the request
+ $body = $this->httpRequest->getBody(true);
+ $this->httpRequest->setBody($body,true);
+
+ if ($contentMD5!=base64_encode(md5($body,true))) {
+ // content-md5 header did not match md5 signature of body
+ $this->errorCode = self::ERR_MD5CHECKSUMWRONG;
+ return false;
+ }
+
+ }
+
+ if (!$requestDate = $this->httpRequest->getHeader('x-amz-date'))
+ $requestDate = $this->httpRequest->getHeader('Date');
+
+ if (!$this->validateRFC2616Date($requestDate))
+ return false;
+
+ $amzHeaders = $this->getAmzHeaders();
+
+ $signature = base64_encode(
+ $this->hmacsha1($secretKey,
+ $this->httpRequest->getMethod() . "\n" .
+ $contentMD5 . "\n" .
+ $this->httpRequest->getHeader('Content-type') . "\n" .
+ $requestDate . "\n" .
+ $amzHeaders .
+ $this->httpRequest->getURI()
+ )
+ );
+
+ if ($this->signature != $signature) {
+
+ $this->errorCode = self::ERR_INVALIDSIGNATURE;
+ return false;
+
+ }
+
+ return true;
+
+ }
+
+
+ /**
+ * Returns an HTTP 401 header, forcing login
+ *
+ * This should be called when username and password are incorrect, or not supplied at all
+ *
+ * @return void
+ */
+ public function requireLogin() {
+
+ $this->httpResponse->setHeader('WWW-Authenticate','AWS');
+ $this->httpResponse->sendStatus(401);
+
+ }
+
+ /**
+ * Makes sure the supplied value is a valid RFC2616 date.
+ *
+ * If we would just use strtotime to get a valid timestamp, we have no way of checking if a
+ * user just supplied the word 'now' for the date header.
+ *
+ * This function also makes sure the Date header is within 15 minutes of the operating
+ * system date, to prevent replay attacks.
+ *
+ * @param string $dateHeader
+ * @return bool
+ */
+ protected function validateRFC2616Date($dateHeader) {
+
+ $date = Sabre_HTTP_Util::parseHTTPDate($dateHeader);
+
+ // Unknown format
+ if (!$date) {
+ $this->errorCode = self::ERR_INVALIDDATEFORMAT;
+ return false;
+ }
+
+ $min = new DateTime('-15 minutes');
+ $max = new DateTime('+15 minutes');
+
+ // We allow 15 minutes around the current date/time
+ if ($date > $max || $date < $min) {
+ $this->errorCode = self::ERR_REQUESTTIMESKEWED;
+ return false;
+ }
+
+ return $date;
+
+ }
+
+ /**
+ * Returns a list of AMZ headers
+ *
+ * @return string
+ */
+ protected function getAmzHeaders() {
+
+ $amzHeaders = array();
+ $headers = $this->httpRequest->getHeaders();
+ foreach($headers as $headerName => $headerValue) {
+ if (strpos(strtolower($headerName),'x-amz-')===0) {
+ $amzHeaders[strtolower($headerName)] = str_replace(array("\r\n"),array(' '),$headerValue) . "\n";
+ }
+ }
+ ksort($amzHeaders);
+
+ $headerStr = '';
+ foreach($amzHeaders as $h=>$v) {
+ $headerStr.=$h.':'.$v;
+ }
+
+ return $headerStr;
+
+ }
+
+ /**
+ * Generates an HMAC-SHA1 signature
+ *
+ * @param string $key
+ * @param string $message
+ * @return string
+ */
+ private function hmacsha1($key, $message) {
+
+ $blocksize=64;
+ if (strlen($key)>$blocksize)
+ $key=pack('H*', sha1($key));
+ $key=str_pad($key,$blocksize,chr(0x00));
+ $ipad=str_repeat(chr(0x36),$blocksize);
+ $opad=str_repeat(chr(0x5c),$blocksize);
+ $hmac = pack('H*',sha1(($key^$opad).pack('H*',sha1(($key^$ipad).$message))));
+ return $hmac;
+
+ }
+
+}
diff --git a/3rdparty/Sabre/HTTP/AbstractAuth.php b/3rdparty/Sabre/HTTP/AbstractAuth.php
new file mode 100755
index 00000000000..3bccabcd1c1
--- /dev/null
+++ b/3rdparty/Sabre/HTTP/AbstractAuth.php
@@ -0,0 +1,111 @@
+<?php
+
+/**
+ * HTTP Authentication baseclass
+ *
+ * This class has the common functionality for BasicAuth and DigestAuth
+ *
+ * @package Sabre
+ * @subpackage HTTP
+ * @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_HTTP_AbstractAuth {
+
+ /**
+ * The realm will be displayed in the dialog boxes
+ *
+ * This identifier can be changed through setRealm()
+ *
+ * @var string
+ */
+ protected $realm = 'SabreDAV';
+
+ /**
+ * HTTP response helper
+ *
+ * @var Sabre_HTTP_Response
+ */
+ protected $httpResponse;
+
+
+ /**
+ * HTTP request helper
+ *
+ * @var Sabre_HTTP_Request
+ */
+ protected $httpRequest;
+
+ /**
+ * __construct
+ *
+ */
+ public function __construct() {
+
+ $this->httpResponse = new Sabre_HTTP_Response();
+ $this->httpRequest = new Sabre_HTTP_Request();
+
+ }
+
+ /**
+ * Sets an alternative HTTP response object
+ *
+ * @param Sabre_HTTP_Response $response
+ * @return void
+ */
+ public function setHTTPResponse(Sabre_HTTP_Response $response) {
+
+ $this->httpResponse = $response;
+
+ }
+
+ /**
+ * Sets an alternative HTTP request object
+ *
+ * @param Sabre_HTTP_Request $request
+ * @return void
+ */
+ public function setHTTPRequest(Sabre_HTTP_Request $request) {
+
+ $this->httpRequest = $request;
+
+ }
+
+
+ /**
+ * Sets the realm
+ *
+ * The realm is often displayed in authentication dialog boxes
+ * Commonly an application name displayed here
+ *
+ * @param string $realm
+ * @return void
+ */
+ public function setRealm($realm) {
+
+ $this->realm = $realm;
+
+ }
+
+ /**
+ * Returns the realm
+ *
+ * @return string
+ */
+ public function getRealm() {
+
+ return $this->realm;
+
+ }
+
+ /**
+ * Returns an HTTP 401 header, forcing login
+ *
+ * This should be called when username and password are incorrect, or not supplied at all
+ *
+ * @return void
+ */
+ abstract public function requireLogin();
+
+}
diff --git a/3rdparty/Sabre/HTTP/BasicAuth.php b/3rdparty/Sabre/HTTP/BasicAuth.php
new file mode 100755
index 00000000000..f90ed24f5d8
--- /dev/null
+++ b/3rdparty/Sabre/HTTP/BasicAuth.php
@@ -0,0 +1,67 @@
+<?php
+
+/**
+ * HTTP Basic Authentication handler
+ *
+ * Use this class for easy http authentication setup
+ *
+ * @package Sabre
+ * @subpackage HTTP
+ * @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_HTTP_BasicAuth extends Sabre_HTTP_AbstractAuth {
+
+ /**
+ * Returns the supplied username and password.
+ *
+ * The returned array has two values:
+ * * 0 - username
+ * * 1 - password
+ *
+ * If nothing was supplied, 'false' will be returned
+ *
+ * @return mixed
+ */
+ public function getUserPass() {
+
+ // Apache and mod_php
+ if (($user = $this->httpRequest->getRawServerValue('PHP_AUTH_USER')) && ($pass = $this->httpRequest->getRawServerValue('PHP_AUTH_PW'))) {
+
+ return array($user,$pass);
+
+ }
+
+ // Most other webservers
+ $auth = $this->httpRequest->getHeader('Authorization');
+
+ // Apache could prefix environment variables with REDIRECT_ when urls
+ // are passed through mod_rewrite
+ if (!$auth) {
+ $auth = $this->httpRequest->getRawServerValue('REDIRECT_HTTP_AUTHORIZATION');
+ }
+
+ if (!$auth) return false;
+
+ if (strpos(strtolower($auth),'basic')!==0) return false;
+
+ return explode(':', base64_decode(substr($auth, 6)),2);
+
+ }
+
+ /**
+ * Returns an HTTP 401 header, forcing login
+ *
+ * This should be called when username and password are incorrect, or not supplied at all
+ *
+ * @return void
+ */
+ public function requireLogin() {
+
+ $this->httpResponse->setHeader('WWW-Authenticate','Basic realm="' . $this->realm . '"');
+ $this->httpResponse->sendStatus(401);
+
+ }
+
+}
diff --git a/3rdparty/Sabre/HTTP/DigestAuth.php b/3rdparty/Sabre/HTTP/DigestAuth.php
new file mode 100755
index 00000000000..ee7f05c08ed
--- /dev/null
+++ b/3rdparty/Sabre/HTTP/DigestAuth.php
@@ -0,0 +1,240 @@
+<?php
+
+/**
+ * HTTP Digest Authentication handler
+ *
+ * Use this class for easy http digest authentication.
+ * Instructions:
+ *
+ * 1. Create the object
+ * 2. Call the setRealm() method with the realm you plan to use
+ * 3. Call the init method function.
+ * 4. Call the getUserName() function. This function may return false if no
+ * authentication information was supplied. Based on the username you
+ * should check your internal database for either the associated password,
+ * or the so-called A1 hash of the digest.
+ * 5. Call either validatePassword() or validateA1(). This will return true
+ * or false.
+ * 6. To make sure an authentication prompt is displayed, call the
+ * requireLogin() method.
+ *
+ *
+ * @package Sabre
+ * @subpackage HTTP
+ * @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_HTTP_DigestAuth extends Sabre_HTTP_AbstractAuth {
+
+ /**
+ * These constants are used in setQOP();
+ */
+ const QOP_AUTH = 1;
+ const QOP_AUTHINT = 2;
+
+ protected $nonce;
+ protected $opaque;
+ protected $digestParts;
+ protected $A1;
+ protected $qop = self::QOP_AUTH;
+
+ /**
+ * Initializes the object
+ */
+ public function __construct() {
+
+ $this->nonce = uniqid();
+ $this->opaque = md5($this->realm);
+ parent::__construct();
+
+ }
+
+ /**
+ * Gathers all information from the headers
+ *
+ * This method needs to be called prior to anything else.
+ *
+ * @return void
+ */
+ public function init() {
+
+ $digest = $this->getDigest();
+ $this->digestParts = $this->parseDigest($digest);
+
+ }
+
+ /**
+ * Sets the quality of protection value.
+ *
+ * Possible values are:
+ * Sabre_HTTP_DigestAuth::QOP_AUTH
+ * Sabre_HTTP_DigestAuth::QOP_AUTHINT
+ *
+ * Multiple values can be specified using logical OR.
+ *
+ * QOP_AUTHINT ensures integrity of the request body, but this is not
+ * supported by most HTTP clients. QOP_AUTHINT also requires the entire
+ * request body to be md5'ed, which can put strains on CPU and memory.
+ *
+ * @param int $qop
+ * @return void
+ */
+ public function setQOP($qop) {
+
+ $this->qop = $qop;
+
+ }
+
+ /**
+ * Validates the user.
+ *
+ * The A1 parameter should be md5($username . ':' . $realm . ':' . $password);
+ *
+ * @param string $A1
+ * @return bool
+ */
+ public function validateA1($A1) {
+
+ $this->A1 = $A1;
+ return $this->validate();
+
+ }
+
+ /**
+ * Validates authentication through a password. The actual password must be provided here.
+ * It is strongly recommended not store the password in plain-text and use validateA1 instead.
+ *
+ * @param string $password
+ * @return bool
+ */
+ public function validatePassword($password) {
+
+ $this->A1 = md5($this->digestParts['username'] . ':' . $this->realm . ':' . $password);
+ return $this->validate();
+
+ }
+
+ /**
+ * Returns the username for the request
+ *
+ * @return string
+ */
+ public function getUsername() {
+
+ return $this->digestParts['username'];
+
+ }
+
+ /**
+ * Validates the digest challenge
+ *
+ * @return bool
+ */
+ protected function validate() {
+
+ $A2 = $this->httpRequest->getMethod() . ':' . $this->digestParts['uri'];
+
+ if ($this->digestParts['qop']=='auth-int') {
+ // Making sure we support this qop value
+ if (!($this->qop & self::QOP_AUTHINT)) return false;
+ // We need to add an md5 of the entire request body to the A2 part of the hash
+ $body = $this->httpRequest->getBody(true);
+ $this->httpRequest->setBody($body,true);
+ $A2 .= ':' . md5($body);
+ } else {
+
+ // We need to make sure we support this qop value
+ if (!($this->qop & self::QOP_AUTH)) return false;
+ }
+
+ $A2 = md5($A2);
+
+ $validResponse = md5("{$this->A1}:{$this->digestParts['nonce']}:{$this->digestParts['nc']}:{$this->digestParts['cnonce']}:{$this->digestParts['qop']}:{$A2}");
+
+ return $this->digestParts['response']==$validResponse;
+
+
+ }
+
+ /**
+ * Returns an HTTP 401 header, forcing login
+ *
+ * This should be called when username and password are incorrect, or not supplied at all
+ *
+ * @return void
+ */
+ public function requireLogin() {
+
+ $qop = '';
+ switch($this->qop) {
+ case self::QOP_AUTH : $qop = 'auth'; break;
+ case self::QOP_AUTHINT : $qop = 'auth-int'; break;
+ case self::QOP_AUTH | self::QOP_AUTHINT : $qop = 'auth,auth-int'; break;
+ }
+
+ $this->httpResponse->setHeader('WWW-Authenticate','Digest realm="' . $this->realm . '",qop="'.$qop.'",nonce="' . $this->nonce . '",opaque="' . $this->opaque . '"');
+ $this->httpResponse->sendStatus(401);
+
+ }
+
+
+ /**
+ * This method returns the full digest string.
+ *
+ * It should be compatibile with mod_php format and other webservers.
+ *
+ * If the header could not be found, null will be returned
+ *
+ * @return mixed
+ */
+ public function getDigest() {
+
+ // mod_php
+ $digest = $this->httpRequest->getRawServerValue('PHP_AUTH_DIGEST');
+ if ($digest) return $digest;
+
+ // most other servers
+ $digest = $this->httpRequest->getHeader('Authorization');
+
+ // Apache could prefix environment variables with REDIRECT_ when urls
+ // are passed through mod_rewrite
+ if (!$digest) {
+ $digest = $this->httpRequest->getRawServerValue('REDIRECT_HTTP_AUTHORIZATION');
+ }
+
+ if ($digest && strpos(strtolower($digest),'digest')===0) {
+ return substr($digest,7);
+ } else {
+ return null;
+ }
+
+ }
+
+
+ /**
+ * Parses the different pieces of the digest string into an array.
+ *
+ * This method returns false if an incomplete digest was supplied
+ *
+ * @param string $digest
+ * @return mixed
+ */
+ protected function parseDigest($digest) {
+
+ // protect against missing data
+ $needed_parts = array('nonce'=>1, 'nc'=>1, 'cnonce'=>1, 'qop'=>1, 'username'=>1, 'uri'=>1, 'response'=>1);
+ $data = array();
+
+ preg_match_all('@(\w+)=(?:(?:")([^"]+)"|([^\s,$]+))@', $digest, $matches, PREG_SET_ORDER);
+
+ foreach ($matches as $m) {
+ $data[$m[1]] = $m[2] ? $m[2] : $m[3];
+ unset($needed_parts[$m[1]]);
+ }
+
+ return $needed_parts ? false : $data;
+
+ }
+
+}
diff --git a/3rdparty/Sabre/HTTP/Request.php b/3rdparty/Sabre/HTTP/Request.php
new file mode 100755
index 00000000000..4746ef77704
--- /dev/null
+++ b/3rdparty/Sabre/HTTP/Request.php
@@ -0,0 +1,268 @@
+<?php
+
+/**
+ * HTTP Request information
+ *
+ * This object can be used to easily access information about an HTTP request.
+ * It can additionally be used to create 'mock' requests.
+ *
+ * This class mostly operates independent, but because of the nature of a single
+ * request per run it can operate as a singleton. For more information check out
+ * the behaviour around 'defaultInputStream'.
+ *
+ * @package Sabre
+ * @subpackage HTTP
+ * @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_HTTP_Request {
+
+ /**
+ * PHP's $_SERVER data
+ *
+ * @var array
+ */
+ protected $_SERVER;
+
+ /**
+ * PHP's $_POST data
+ *
+ * @var array
+ */
+ protected $_POST;
+
+ /**
+ * The request body, if any.
+ *
+ * This is stored in the form of a stream resource.
+ *
+ * @var resource
+ */
+ protected $body = null;
+
+ /**
+ * This will be set as the 'default' inputStream for a specific HTTP request
+ * We sometimes need to retain, or rebuild this if we need multiple runs
+ * of parsing the original HTTP request.
+ *
+ * @var resource
+ */
+ static $defaultInputStream=null;
+
+ /**
+ * Sets up the object
+ *
+ * The serverData and postData array can be used to override usage of PHP's
+ * global _SERVER and _POST variable respectively.
+ *
+ * @param array $serverData
+ * @param array $postData
+ */
+ public function __construct(array $serverData = null, array $postData = null) {
+
+ if ($serverData) $this->_SERVER = $serverData;
+ else $this->_SERVER =& $_SERVER;
+
+ if ($postData) $this->_POST = $postData;
+ else $this->_POST =& $_POST;
+
+ }
+
+ /**
+ * Returns the value for a specific http header.
+ *
+ * This method returns null if the header did not exist.
+ *
+ * @param string $name
+ * @return string
+ */
+ public function getHeader($name) {
+
+ $name = strtoupper(str_replace(array('-'),array('_'),$name));
+ if (isset($this->_SERVER['HTTP_' . $name])) {
+ return $this->_SERVER['HTTP_' . $name];
+ }
+
+ // There's a few headers that seem to end up in the top-level
+ // server array.
+ switch($name) {
+ case 'CONTENT_TYPE' :
+ case 'CONTENT_LENGTH' :
+ if (isset($this->_SERVER[$name])) {
+ return $this->_SERVER[$name];
+ }
+ break;
+
+ }
+ return;
+
+ }
+
+ /**
+ * Returns all (known) HTTP headers.
+ *
+ * All headers are converted to lower-case, and additionally all underscores
+ * are automatically converted to dashes
+ *
+ * @return array
+ */
+ public function getHeaders() {
+
+ $hdrs = array();
+ foreach($this->_SERVER as $key=>$value) {
+
+ switch($key) {
+ case 'CONTENT_LENGTH' :
+ case 'CONTENT_TYPE' :
+ $hdrs[strtolower(str_replace('_','-',$key))] = $value;
+ break;
+ default :
+ if (strpos($key,'HTTP_')===0) {
+ $hdrs[substr(strtolower(str_replace('_','-',$key)),5)] = $value;
+ }
+ break;
+ }
+
+ }
+
+ return $hdrs;
+
+ }
+
+ /**
+ * Returns the HTTP request method
+ *
+ * This is for example POST or GET
+ *
+ * @return string
+ */
+ public function getMethod() {
+
+ return $this->_SERVER['REQUEST_METHOD'];
+
+ }
+
+ /**
+ * Returns the requested uri
+ *
+ * @return string
+ */
+ public function getUri() {
+
+ return $this->_SERVER['REQUEST_URI'];
+
+ }
+
+ /**
+ * Will return protocol + the hostname + the uri
+ *
+ * @return string
+ */
+ public function getAbsoluteUri() {
+
+ // Checking if the request was made through HTTPS. The last in line is for IIS
+ $protocol = isset($this->_SERVER['HTTPS']) && ($this->_SERVER['HTTPS']) && ($this->_SERVER['HTTPS']!='off');
+ return ($protocol?'https':'http') . '://' . $this->getHeader('Host') . $this->getUri();
+
+ }
+
+ /**
+ * Returns everything after the ? from the current url
+ *
+ * @return string
+ */
+ public function getQueryString() {
+
+ return isset($this->_SERVER['QUERY_STRING'])?$this->_SERVER['QUERY_STRING']:'';
+
+ }
+
+ /**
+ * Returns the HTTP request body body
+ *
+ * This method returns a readable stream resource.
+ * If the asString parameter is set to true, a string is sent instead.
+ *
+ * @param bool asString
+ * @return resource
+ */
+ public function getBody($asString = false) {
+
+ if (is_null($this->body)) {
+ if (!is_null(self::$defaultInputStream)) {
+ $this->body = self::$defaultInputStream;
+ } else {
+ $this->body = fopen('php://input','r');
+ self::$defaultInputStream = $this->body;
+ }
+ }
+ if ($asString) {
+ $body = stream_get_contents($this->body);
+ return $body;
+ } else {
+ return $this->body;
+ }
+
+ }
+
+ /**
+ * Sets the contents of the HTTP request body
+ *
+ * This method can either accept a string, or a readable stream resource.
+ *
+ * If the setAsDefaultInputStream is set to true, it means for this run of the
+ * script the supplied body will be used instead of php://input.
+ *
+ * @param mixed $body
+ * @param bool $setAsDefaultInputStream
+ * @return void
+ */
+ public function setBody($body,$setAsDefaultInputStream = false) {
+
+ if(is_resource($body)) {
+ $this->body = $body;
+ } else {
+
+ $stream = fopen('php://temp','r+');
+ fputs($stream,$body);
+ rewind($stream);
+ // String is assumed
+ $this->body = $stream;
+ }
+ if ($setAsDefaultInputStream) {
+ self::$defaultInputStream = $this->body;
+ }
+
+ }
+
+ /**
+ * Returns PHP's _POST variable.
+ *
+ * The reason this is in a method is so it can be subclassed and
+ * overridden.
+ *
+ * @return array
+ */
+ public function getPostVars() {
+
+ return $this->_POST;
+
+ }
+
+ /**
+ * Returns a specific item from the _SERVER array.
+ *
+ * Do not rely on this feature, it is for internal use only.
+ *
+ * @param string $field
+ * @return string
+ */
+ public function getRawServerValue($field) {
+
+ return isset($this->_SERVER[$field])?$this->_SERVER[$field]:null;
+
+ }
+
+}
+
diff --git a/3rdparty/Sabre/HTTP/Response.php b/3rdparty/Sabre/HTTP/Response.php
new file mode 100755
index 00000000000..ffe9bda2082
--- /dev/null
+++ b/3rdparty/Sabre/HTTP/Response.php
@@ -0,0 +1,157 @@
+<?php
+
+/**
+ * Sabre_HTTP_Response
+ *
+ * @package Sabre
+ * @subpackage HTTP
+ * @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_HTTP_Response {
+
+ /**
+ * Returns a full HTTP status message for an HTTP status code
+ *
+ * @param int $code
+ * @return string
+ */
+ public function getStatusMessage($code) {
+
+ $msg = array(
+ 100 => 'Continue',
+ 101 => 'Switching Protocols',
+ 102 => 'Processing',
+ 200 => 'OK',
+ 201 => 'Created',
+ 202 => 'Accepted',
+ 203 => 'Non-Authorative Information',
+ 204 => 'No Content',
+ 205 => 'Reset Content',
+ 206 => 'Partial Content',
+ 207 => 'Multi-Status', // RFC 4918
+ 208 => 'Already Reported', // RFC 5842
+ 226 => 'IM Used', // RFC 3229
+ 300 => 'Multiple Choices',
+ 301 => 'Moved Permanently',
+ 302 => 'Found',
+ 303 => 'See Other',
+ 304 => 'Not Modified',
+ 305 => 'Use Proxy',
+ 306 => 'Reserved',
+ 307 => 'Temporary Redirect',
+ 400 => 'Bad request',
+ 401 => 'Unauthorized',
+ 402 => 'Payment Required',
+ 403 => 'Forbidden',
+ 404 => 'Not Found',
+ 405 => 'Method Not Allowed',
+ 406 => 'Not Acceptable',
+ 407 => 'Proxy Authentication Required',
+ 408 => 'Request Timeout',
+ 409 => 'Conflict',
+ 410 => 'Gone',
+ 411 => 'Length Required',
+ 412 => 'Precondition failed',
+ 413 => 'Request Entity Too Large',
+ 414 => 'Request-URI Too Long',
+ 415 => 'Unsupported Media Type',
+ 416 => 'Requested Range Not Satisfiable',
+ 417 => 'Expectation Failed',
+ 418 => 'I\'m a teapot', // RFC 2324
+ 422 => 'Unprocessable Entity', // RFC 4918
+ 423 => 'Locked', // RFC 4918
+ 424 => 'Failed Dependency', // RFC 4918
+ 426 => 'Upgrade required',
+ 428 => 'Precondition required', // draft-nottingham-http-new-status
+ 429 => 'Too Many Requests', // draft-nottingham-http-new-status
+ 431 => 'Request Header Fields Too Large', // draft-nottingham-http-new-status
+ 500 => 'Internal Server Error',
+ 501 => 'Not Implemented',
+ 502 => 'Bad Gateway',
+ 503 => 'Service Unavailable',
+ 504 => 'Gateway Timeout',
+ 505 => 'HTTP Version not supported',
+ 506 => 'Variant Also Negotiates',
+ 507 => 'Insufficient Storage', // RFC 4918
+ 508 => 'Loop Detected', // RFC 5842
+ 509 => 'Bandwidth Limit Exceeded', // non-standard
+ 510 => 'Not extended',
+ 511 => 'Network Authentication Required', // draft-nottingham-http-new-status
+ );
+
+ return 'HTTP/1.1 ' . $code . ' ' . $msg[$code];
+
+ }
+
+ /**
+ * Sends an HTTP status header to the client
+ *
+ * @param int $code HTTP status code
+ * @return bool
+ */
+ public function sendStatus($code) {
+
+ if (!headers_sent())
+ return header($this->getStatusMessage($code));
+ else return false;
+
+ }
+
+ /**
+ * Sets an HTTP header for the response
+ *
+ * @param string $name
+ * @param string $value
+ * @param bool $replace
+ * @return bool
+ */
+ public function setHeader($name, $value, $replace = true) {
+
+ $value = str_replace(array("\r","\n"),array('\r','\n'),$value);
+ if (!headers_sent())
+ return header($name . ': ' . $value, $replace);
+ else return false;
+
+ }
+
+ /**
+ * Sets a bunch of HTTP Headers
+ *
+ * headersnames are specified as keys, value in the array value
+ *
+ * @param array $headers
+ * @return void
+ */
+ public function setHeaders(array $headers) {
+
+ foreach($headers as $key=>$value)
+ $this->setHeader($key, $value);
+
+ }
+
+ /**
+ * Sends the entire response body
+ *
+ * This method can accept either an open filestream, or a string.
+ *
+ * @param mixed $body
+ * @return void
+ */
+ public function sendBody($body) {
+
+ if (is_resource($body)) {
+
+ fpassthru($body);
+
+ } else {
+
+ // We assume a string
+ echo $body;
+
+ }
+
+ }
+
+}
diff --git a/3rdparty/Sabre/HTTP/Util.php b/3rdparty/Sabre/HTTP/Util.php
new file mode 100755
index 00000000000..67bdd489e1e
--- /dev/null
+++ b/3rdparty/Sabre/HTTP/Util.php
@@ -0,0 +1,82 @@
+<?php
+
+/**
+ * HTTP utility methods
+ *
+ * @package Sabre
+ * @subpackage HTTP
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @author Paul Voegler
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_HTTP_Util {
+
+ /**
+ * Parses a RFC2616-compatible date string
+ *
+ * This method returns false if the date is invalid
+ *
+ * @param string $dateHeader
+ * @return bool|DateTime
+ */
+ static function parseHTTPDate($dateHeader) {
+
+ //RFC 2616 section 3.3.1 Full Date
+ //Only the format is checked, valid ranges are checked by strtotime below
+ $month = '(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)';
+ $weekday = '(Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday)';
+ $wkday = '(Mon|Tue|Wed|Thu|Fri|Sat|Sun)';
+ $time = '[0-2]\d(\:[0-5]\d){2}';
+ $date3 = $month . ' ([1-3]\d| \d)';
+ $date2 = '[0-3]\d\-' . $month . '\-\d\d';
+ //4-digit year cannot begin with 0 - unix timestamp begins in 1970
+ $date1 = '[0-3]\d ' . $month . ' [1-9]\d{3}';
+
+ //ANSI C's asctime() format
+ //4-digit year cannot begin with 0 - unix timestamp begins in 1970
+ $asctime_date = $wkday . ' ' . $date3 . ' ' . $time . ' [1-9]\d{3}';
+ //RFC 850, obsoleted by RFC 1036
+ $rfc850_date = $weekday . ', ' . $date2 . ' ' . $time . ' GMT';
+ //RFC 822, updated by RFC 1123
+ $rfc1123_date = $wkday . ', ' . $date1 . ' ' . $time . ' GMT';
+ //allowed date formats by RFC 2616
+ $HTTP_date = "($rfc1123_date|$rfc850_date|$asctime_date)";
+
+ //allow for space around the string and strip it
+ $dateHeader = trim($dateHeader, ' ');
+ if (!preg_match('/^' . $HTTP_date . '$/', $dateHeader))
+ return false;
+
+ //append implicit GMT timezone to ANSI C time format
+ if (strpos($dateHeader, ' GMT') === false)
+ $dateHeader .= ' GMT';
+
+
+ $realDate = strtotime($dateHeader);
+ //strtotime can return -1 or false in case of error
+ if ($realDate !== false && $realDate >= 0)
+ return new DateTime('@' . $realDate, new DateTimeZone('UTC'));
+
+ }
+
+ /**
+ * Transforms a DateTime object to HTTP's most common date format.
+ *
+ * We're serializing it as the RFC 1123 date, which, for HTTP must be
+ * specified as GMT.
+ *
+ * @param DateTime $dateTime
+ * @return string
+ */
+ static function toHTTPDate(DateTime $dateTime) {
+
+ // We need to clone it, as we don't want to affect the existing
+ // DateTime.
+ $dateTime = clone $dateTime;
+ $dateTime->setTimeZone(new DateTimeZone('GMT'));
+ return $dateTime->format('D, d M Y H:i:s \G\M\T');
+
+ }
+
+}
diff --git a/3rdparty/Sabre/HTTP/Version.php b/3rdparty/Sabre/HTTP/Version.php
new file mode 100755
index 00000000000..e6b4f7e5358
--- /dev/null
+++ b/3rdparty/Sabre/HTTP/Version.php
@@ -0,0 +1,24 @@
+<?php
+
+/**
+ * This class contains the Sabre_HTTP version constants.
+ *
+ * @package Sabre
+ * @subpackage HTTP
+ * @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_HTTP_Version {
+
+ /**
+ * Full version number
+ */
+ const VERSION = '1.6.4';
+
+ /**
+ * Stability : alpha, beta, stable
+ */
+ const STABILITY = 'stable';
+
+}
diff --git a/3rdparty/Sabre/HTTP/includes.php b/3rdparty/Sabre/HTTP/includes.php
new file mode 100755
index 00000000000..9d34bf3a8be
--- /dev/null
+++ b/3rdparty/Sabre/HTTP/includes.php
@@ -0,0 +1,27 @@
+<?php
+
+/**
+ * Sabre_HTTP includes file
+ *
+ * Including this file will automatically include all files from the Sabre_HTTP
+ * package.
+ *
+ * This often allows faster loadtimes, as autoload-speed is often quite slow.
+ *
+ * @package Sabre
+ * @subpackage HTTP
+ * @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
+ */
+
+// Begin includes
+include __DIR__ . '/AbstractAuth.php';
+include __DIR__ . '/AWSAuth.php';
+include __DIR__ . '/BasicAuth.php';
+include __DIR__ . '/DigestAuth.php';
+include __DIR__ . '/Request.php';
+include __DIR__ . '/Response.php';
+include __DIR__ . '/Util.php';
+include __DIR__ . '/Version.php';
+// End includes
diff --git a/3rdparty/Sabre/VObject/Component.php b/3rdparty/Sabre/VObject/Component.php
new file mode 100755
index 00000000000..b78a26133fa
--- /dev/null
+++ b/3rdparty/Sabre/VObject/Component.php
@@ -0,0 +1,365 @@
+<?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();
+
+ /**
+ * If coponents are added to this map, they will be automatically mapped
+ * to their respective classes, if parsed by the reader or constructed with
+ * the 'create' method.
+ *
+ * @var array
+ */
+ static public $classMap = array(
+ 'VCALENDAR' => 'Sabre_VObject_Component_VCalendar',
+ 'VEVENT' => 'Sabre_VObject_Component_VEvent',
+ 'VTODO' => 'Sabre_VObject_Component_VTodo',
+ 'VJOURNAL' => 'Sabre_VObject_Component_VJournal',
+ 'VALARM' => 'Sabre_VObject_Component_VAlarm',
+ );
+
+ /**
+ * Creates the new component by name, but in addition will also see if
+ * there's a class mapped to the property name.
+ *
+ * @param string $name
+ * @param string $value
+ * @return Sabre_VObject_Component
+ */
+ static public function create($name, $value = null) {
+
+ $name = strtoupper($name);
+
+ if (isset(self::$classMap[$name])) {
+ return new self::$classMap[$name]($name, $value);
+ } else {
+ return new self($name, $value);
+ }
+
+ }
+
+ /**
+ * 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";
+
+ /**
+ * Gives a component a 'score' for sorting purposes.
+ *
+ * This is solely used by the childrenSort method.
+ *
+ * A higher score means the item will be higher in the list
+ *
+ * @param Sabre_VObject_Node $n
+ * @return int
+ */
+ $sortScore = function($n) {
+
+ if ($n instanceof Sabre_VObject_Component) {
+ // We want to encode VTIMEZONE first, this is a personal
+ // preference.
+ if ($n->name === 'VTIMEZONE') {
+ return 1;
+ } else {
+ return 0;
+ }
+ } else {
+ // VCARD version 4.0 wants the VERSION property to appear first
+ if ($n->name === 'VERSION') {
+ return 3;
+ } else {
+ return 2;
+ }
+ }
+
+ };
+
+ usort($this->children, function($a, $b) use ($sortScore) {
+
+ $sA = $sortScore($a);
+ $sB = $sortScore($b);
+
+ if ($sA === $sB) return 0;
+
+ return ($sA > $sB) ? -1 : 1;
+
+ });
+
+ foreach($this->children as $child) $str.=$child->serialize();
+ $str.= "END:" . $this->name . "\r\n";
+
+ return $str;
+
+ }
+
+ /**
+ * Adds a new component 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 = Sabre_VObject_Property::create($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;
+
+ }
+
+ /**
+ * This method only returns a list of sub-components. Properties are
+ * ignored.
+ *
+ * @return array
+ */
+ public function getComponents() {
+
+ $result = array();
+ foreach($this->children as $child) {
+ if ($child instanceof Sabre_VObject_Component) {
+ $result[] = $child;
+ }
+ }
+
+ return $result;
+
+ }
+
+ /* Magic property accessors {{{ */
+
+ /**
+ * Using 'get' you will either get a property or component,
+ *
+ * If there were no child-elements found with the specified name,
+ * null is returned.
+ *
+ * @param string $name
+ * @return Sabre_VObject_Property
+ */
+ public function __get($name) {
+
+ $matches = $this->select($name);
+ if (count($matches)===0) {
+ return null;
+ } else {
+ $firstMatch = current($matches);
+ /** @var $firstMatch Sabre_VObject_Property */
+ $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 = Sabre_VObject_Property::create($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;
+
+ }
+
+ }
+
+ /* }}} */
+
+ /**
+ * This method is automatically called when the object is cloned.
+ * Specifically, this will ensure all child elements are also cloned.
+ *
+ * @return void
+ */
+ public function __clone() {
+
+ foreach($this->children as $key=>$child) {
+ $this->children[$key] = clone $child;
+ $this->children[$key]->parent = $this;
+ }
+
+ }
+
+}
diff --git a/3rdparty/Sabre/VObject/Component/VAlarm.php b/3rdparty/Sabre/VObject/Component/VAlarm.php
new file mode 100755
index 00000000000..ebb4a9b18f6
--- /dev/null
+++ b/3rdparty/Sabre/VObject/Component/VAlarm.php
@@ -0,0 +1,102 @@
+<?php
+
+/**
+ * VAlarm component
+ *
+ * This component contains some additional functionality specific for VALARMs.
+ *
+ * @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_VAlarm extends Sabre_VObject_Component {
+
+ /**
+ * Returns a DateTime object when this alarm is going to trigger.
+ *
+ * This ignores repeated alarm, only the first trigger is returned.
+ *
+ * @return DateTime
+ */
+ public function getEffectiveTriggerTime() {
+
+ $trigger = $this->TRIGGER;
+ if(!isset($trigger['VALUE']) || strtoupper($trigger['VALUE']) === 'DURATION') {
+ $triggerDuration = Sabre_VObject_DateTimeParser::parseDuration($this->TRIGGER);
+ $related = (isset($trigger['RELATED']) && strtoupper($trigger['RELATED']) == 'END') ? 'END' : 'START';
+
+ $parentComponent = $this->parent;
+ if ($related === 'START') {
+ $effectiveTrigger = clone $parentComponent->DTSTART->getDateTime();
+ $effectiveTrigger->add($triggerDuration);
+ } else {
+ if ($parentComponent->name === 'VTODO') {
+ $endProp = 'DUE';
+ } elseif ($parentComponent->name === 'VEVENT') {
+ $endProp = 'DTEND';
+ } else {
+ throw new Sabre_DAV_Exception('time-range filters on VALARM components are only supported when they are a child of VTODO or VEVENT');
+ }
+
+ if (isset($parentComponent->$endProp)) {
+ $effectiveTrigger = clone $parentComponent->$endProp->getDateTime();
+ $effectiveTrigger->add($triggerDuration);
+ } elseif (isset($parentComponent->DURATION)) {
+ $effectiveTrigger = clone $parentComponent->DTSTART->getDateTime();
+ $duration = Sabre_VObject_DateTimeParser::parseDuration($parentComponent->DURATION);
+ $effectiveTrigger->add($duration);
+ $effectiveTrigger->add($triggerDuration);
+ } else {
+ $effectiveTrigger = clone $parentComponent->DTSTART->getDateTime();
+ $effectiveTrigger->add($triggerDuration);
+ }
+ }
+ } else {
+ $effectiveTrigger = $trigger->getDateTime();
+ }
+ return $effectiveTrigger;
+
+ }
+
+ /**
+ * Returns true or false depending on if the event falls in the specified
+ * time-range. This is used for filtering purposes.
+ *
+ * The rules used to determine if an event falls within the specified
+ * time-range is based on the CalDAV specification.
+ *
+ * @param DateTime $start
+ * @param DateTime $end
+ * @return bool
+ */
+ public function isInTimeRange(DateTime $start, DateTime $end) {
+
+ $effectiveTrigger = $this->getEffectiveTriggerTime();
+
+ if (isset($this->DURATION)) {
+ $duration = Sabre_VObject_DateTimeParser::parseDuration($this->DURATION);
+ $repeat = (string)$this->repeat;
+ if (!$repeat) {
+ $repeat = 1;
+ }
+
+ $period = new DatePeriod($effectiveTrigger, $duration, (int)$repeat);
+
+ foreach($period as $occurrence) {
+
+ if ($start <= $occurrence && $end > $occurrence) {
+ return true;
+ }
+ }
+ return false;
+ } else {
+ return ($start <= $effectiveTrigger && $end > $effectiveTrigger);
+ }
+
+ }
+
+}
+
+?>
diff --git a/3rdparty/Sabre/VObject/Component/VCalendar.php b/3rdparty/Sabre/VObject/Component/VCalendar.php
new file mode 100755
index 00000000000..f3be29afdbb
--- /dev/null
+++ b/3rdparty/Sabre/VObject/Component/VCalendar.php
@@ -0,0 +1,133 @@
+<?php
+
+/**
+ * The VCalendar component
+ *
+ * This component adds functionality to a component, specific for a VCALENDAR.
+ *
+ * @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_VCalendar extends Sabre_VObject_Component {
+
+ /**
+ * Returns a list of all 'base components'. For instance, if an Event has
+ * a recurrence rule, and one instance is overridden, the overridden event
+ * will have the same UID, but will be excluded from this list.
+ *
+ * VTIMEZONE components will always be excluded.
+ *
+ * @param string $componentName filter by component name
+ * @return array
+ */
+ public function getBaseComponents($componentName = null) {
+
+ $components = array();
+ foreach($this->children as $component) {
+
+ if (!$component instanceof Sabre_VObject_Component)
+ continue;
+
+ if (isset($component->{'RECURRENCE-ID'}))
+ continue;
+
+ if ($componentName && $component->name !== strtoupper($componentName))
+ continue;
+
+ if ($component->name === 'VTIMEZONE')
+ continue;
+
+ $components[] = $component;
+
+ }
+
+ return $components;
+
+ }
+
+ /**
+ * If this calendar object, has events with recurrence rules, this method
+ * can be used to expand the event into multiple sub-events.
+ *
+ * Each event will be stripped from it's recurrence information, and only
+ * the instances of the event in the specified timerange will be left
+ * alone.
+ *
+ * In addition, this method will cause timezone information to be stripped,
+ * and normalized to UTC.
+ *
+ * This method will alter the VCalendar. This cannot be reversed.
+ *
+ * This functionality is specifically used by the CalDAV standard. It is
+ * possible for clients to request expand events, if they are rather simple
+ * clients and do not have the possibility to calculate recurrences.
+ *
+ * @param DateTime $start
+ * @param DateTime $end
+ * @return void
+ */
+ public function expand(DateTime $start, DateTime $end) {
+
+ $newEvents = array();
+
+ foreach($this->select('VEVENT') as $key=>$vevent) {
+
+ if (isset($vevent->{'RECURRENCE-ID'})) {
+ unset($this->children[$key]);
+ continue;
+ }
+
+
+ if (!$vevent->rrule) {
+ unset($this->children[$key]);
+ if ($vevent->isInTimeRange($start, $end)) {
+ $newEvents[] = $vevent;
+ }
+ continue;
+ }
+
+ $uid = (string)$vevent->uid;
+ if (!$uid) {
+ throw new LogicException('Event did not have a UID!');
+ }
+
+ $it = new Sabre_VObject_RecurrenceIterator($this, $vevent->uid);
+ $it->fastForward($start);
+
+ while($it->valid() && $it->getDTStart() < $end) {
+
+ if ($it->getDTEnd() > $start) {
+
+ $newEvents[] = $it->getEventObject();
+
+ }
+ $it->next();
+
+ }
+ unset($this->children[$key]);
+
+ }
+
+ foreach($newEvents as $newEvent) {
+
+ foreach($newEvent->children as $child) {
+ if ($child instanceof Sabre_VObject_Property_DateTime &&
+ $child->getDateType() == Sabre_VObject_Property_DateTime::LOCALTZ) {
+ $child->setDateTime($child->getDateTime(),Sabre_VObject_Property_DateTime::UTC);
+ }
+ }
+
+ $this->add($newEvent);
+
+ }
+
+ // Removing all VTIMEZONE components
+ unset($this->VTIMEZONE);
+
+ }
+
+}
+
diff --git a/3rdparty/Sabre/VObject/Component/VEvent.php b/3rdparty/Sabre/VObject/Component/VEvent.php
new file mode 100755
index 00000000000..d6b910874d0
--- /dev/null
+++ b/3rdparty/Sabre/VObject/Component/VEvent.php
@@ -0,0 +1,71 @@
+<?php
+
+/**
+ * VEvent component
+ *
+ * This component contains some additional functionality specific for VEVENT's.
+ *
+ * @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_VEvent extends Sabre_VObject_Component {
+
+ /**
+ * Returns true or false depending on if the event falls in the specified
+ * time-range. This is used for filtering purposes.
+ *
+ * The rules used to determine if an event falls within the specified
+ * time-range is based on the CalDAV specification.
+ *
+ * @param DateTime $start
+ * @param DateTime $end
+ * @return bool
+ */
+ public function isInTimeRange(DateTime $start, DateTime $end) {
+
+ if ($this->RRULE) {
+ $it = new Sabre_VObject_RecurrenceIterator($this);
+ $it->fastForward($start);
+
+ // We fast-forwarded to a spot where the end-time of the
+ // recurrence instance exceeded the start of the requested
+ // time-range.
+ //
+ // If the starttime of the recurrence did not exceed the
+ // end of the time range as well, we have a match.
+ return ($it->getDTStart() < $end && $it->getDTEnd() > $start);
+
+ }
+
+ $effectiveStart = $this->DTSTART->getDateTime();
+ if (isset($this->DTEND)) {
+
+ // The DTEND property is considered non inclusive. So for a 3 day
+ // event in july, dtstart and dtend would have to be July 1st and
+ // July 4th respectively.
+ //
+ // See:
+ // http://tools.ietf.org/html/rfc5545#page-54
+ $effectiveEnd = $this->DTEND->getDateTime();
+
+ } elseif (isset($this->DURATION)) {
+ $effectiveEnd = clone $effectiveStart;
+ $effectiveEnd->add( Sabre_VObject_DateTimeParser::parseDuration($this->DURATION) );
+ } elseif ($this->DTSTART->getDateType() == Sabre_VObject_Element_DateTime::DATE) {
+ $effectiveEnd = clone $effectiveStart;
+ $effectiveEnd->modify('+1 day');
+ } else {
+ $effectiveEnd = clone $effectiveStart;
+ }
+ return (
+ ($start <= $effectiveEnd) && ($end > $effectiveStart)
+ );
+
+ }
+
+}
+
+?>
diff --git a/3rdparty/Sabre/VObject/Component/VJournal.php b/3rdparty/Sabre/VObject/Component/VJournal.php
new file mode 100755
index 00000000000..22b3ec921e5
--- /dev/null
+++ b/3rdparty/Sabre/VObject/Component/VJournal.php
@@ -0,0 +1,46 @@
+<?php
+
+/**
+ * VJournal component
+ *
+ * This component contains some additional functionality specific for VJOURNALs.
+ *
+ * @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_VJournal extends Sabre_VObject_Component {
+
+ /**
+ * Returns true or false depending on if the event falls in the specified
+ * time-range. This is used for filtering purposes.
+ *
+ * The rules used to determine if an event falls within the specified
+ * time-range is based on the CalDAV specification.
+ *
+ * @param DateTime $start
+ * @param DateTime $end
+ * @return bool
+ */
+ public function isInTimeRange(DateTime $start, DateTime $end) {
+
+ $dtstart = isset($this->DTSTART)?$this->DTSTART->getDateTime():null;
+ if ($dtstart) {
+ $effectiveEnd = clone $dtstart;
+ if ($this->DTSTART->getDateType() == Sabre_VObject_Element_DateTime::DATE) {
+ $effectiveEnd->modify('+1 day');
+ }
+
+ return ($start <= $effectiveEnd && $end > $dtstart);
+
+ }
+ return false;
+
+
+ }
+
+}
+
+?>
diff --git a/3rdparty/Sabre/VObject/Component/VTodo.php b/3rdparty/Sabre/VObject/Component/VTodo.php
new file mode 100755
index 00000000000..79d06298d7f
--- /dev/null
+++ b/3rdparty/Sabre/VObject/Component/VTodo.php
@@ -0,0 +1,68 @@
+<?php
+
+/**
+ * VTodo component
+ *
+ * This component contains some additional functionality specific for VTODOs.
+ *
+ * @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_VTodo extends Sabre_VObject_Component {
+
+ /**
+ * Returns true or false depending on if the event falls in the specified
+ * time-range. This is used for filtering purposes.
+ *
+ * The rules used to determine if an event falls within the specified
+ * time-range is based on the CalDAV specification.
+ *
+ * @param DateTime $start
+ * @param DateTime $end
+ * @return bool
+ */
+ public function isInTimeRange(DateTime $start, DateTime $end) {
+
+ $dtstart = isset($this->DTSTART)?$this->DTSTART->getDateTime():null;
+ $duration = isset($this->DURATION)?Sabre_VObject_DateTimeParser::parseDuration($this->DURATION):null;
+ $due = isset($this->DUE)?$this->DUE->getDateTime():null;
+ $completed = isset($this->COMPLETED)?$this->COMPLETED->getDateTime():null;
+ $created = isset($this->CREATED)?$this->CREATED->getDateTime():null;
+
+ if ($dtstart) {
+ if ($duration) {
+ $effectiveEnd = clone $dtstart;
+ $effectiveEnd->add($duration);
+ return $start <= $effectiveEnd && $end > $dtstart;
+ } elseif ($due) {
+ return
+ ($start < $due || $start <= $dtstart) &&
+ ($end > $dtstart || $end >= $due);
+ } else {
+ return $start <= $dtstart && $end > $dtstart;
+ }
+ }
+ if ($due) {
+ return ($start < $due && $end >= $due);
+ }
+ if ($completed && $created) {
+ return
+ ($start <= $created || $start <= $completed) &&
+ ($end >= $created || $end >= $completed);
+ }
+ if ($completed) {
+ return ($start <= $completed && $end >= $completed);
+ }
+ if ($created) {
+ return ($end > $created);
+ }
+ return true;
+
+ }
+
+}
+
+?>
diff --git a/3rdparty/Sabre/VObject/DateTimeParser.php b/3rdparty/Sabre/VObject/DateTimeParser.php
new file mode 100755
index 00000000000..23a4bb69916
--- /dev/null
+++ b/3rdparty/Sabre/VObject/DateTimeParser.php
@@ -0,0 +1,181 @@
+<?php
+
+/**
+ * DateTimeParser
+ *
+ * This class is responsible for parsing the several different date and time
+ * formats iCalendar and vCards have.
+ *
+ * @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_DateTimeParser {
+
+ /**
+ * Parses an iCalendar (rfc5545) formatted datetime and returns a DateTime object
+ *
+ * Specifying a reference timezone is optional. It will only be used
+ * if the non-UTC format is used. The argument is used as a reference, the
+ * returned DateTime object will still be in the UTC timezone.
+ *
+ * @param string $dt
+ * @param DateTimeZone $tz
+ * @return DateTime
+ */
+ static public function parseDateTime($dt,DateTimeZone $tz = null) {
+
+ // Format is YYYYMMDD + "T" + hhmmss
+ $result = preg_match('/^([1-3][0-9]{3})([0-1][0-9])([0-3][0-9])T([0-2][0-9])([0-5][0-9])([0-5][0-9])([Z]?)$/',$dt,$matches);
+
+ if (!$result) {
+ throw new Sabre_DAV_Exception_BadRequest('The supplied iCalendar datetime value is incorrect: ' . $dt);
+ }
+
+ if ($matches[7]==='Z' || is_null($tz)) {
+ $tz = new DateTimeZone('UTC');
+ }
+ $date = new DateTime($matches[1] . '-' . $matches[2] . '-' . $matches[3] . ' ' . $matches[4] . ':' . $matches[5] .':' . $matches[6], $tz);
+
+ // Still resetting the timezone, to normalize everything to UTC
+ $date->setTimeZone(new DateTimeZone('UTC'));
+ return $date;
+
+ }
+
+ /**
+ * Parses an iCalendar (rfc5545) formatted date and returns a DateTime object
+ *
+ * @param string $date
+ * @return DateTime
+ */
+ static public function parseDate($date) {
+
+ // Format is YYYYMMDD
+ $result = preg_match('/^([1-3][0-9]{3})([0-1][0-9])([0-3][0-9])$/',$date,$matches);
+
+ if (!$result) {
+ throw new Sabre_DAV_Exception_BadRequest('The supplied iCalendar date value is incorrect: ' . $date);
+ }
+
+ $date = new DateTime($matches[1] . '-' . $matches[2] . '-' . $matches[3], new DateTimeZone('UTC'));
+ return $date;
+
+ }
+
+ /**
+ * Parses an iCalendar (RFC5545) formatted duration value.
+ *
+ * This method will either return a DateTimeInterval object, or a string
+ * suitable for strtotime or DateTime::modify.
+ *
+ * @param string $duration
+ * @param bool $asString
+ * @return DateInterval|string
+ */
+ static public function parseDuration($duration, $asString = false) {
+
+ $result = preg_match('/^(?P<plusminus>\+|-)?P((?P<week>\d+)W)?((?P<day>\d+)D)?(T((?P<hour>\d+)H)?((?P<minute>\d+)M)?((?P<second>\d+)S)?)?$/', $duration, $matches);
+ if (!$result) {
+ throw new Sabre_DAV_Exception_BadRequest('The supplied iCalendar duration value is incorrect: ' . $duration);
+ }
+
+ if (!$asString) {
+ $invert = false;
+ if ($matches['plusminus']==='-') {
+ $invert = true;
+ }
+
+
+ $parts = array(
+ 'week',
+ 'day',
+ 'hour',
+ 'minute',
+ 'second',
+ );
+ foreach($parts as $part) {
+ $matches[$part] = isset($matches[$part])&&$matches[$part]?(int)$matches[$part]:0;
+ }
+
+
+ // We need to re-construct the $duration string, because weeks and
+ // days are not supported by DateInterval in the same string.
+ $duration = 'P';
+ $days = $matches['day'];
+ if ($matches['week']) {
+ $days+=$matches['week']*7;
+ }
+ if ($days)
+ $duration.=$days . 'D';
+
+ if ($matches['minute'] || $matches['second'] || $matches['hour']) {
+ $duration.='T';
+
+ if ($matches['hour'])
+ $duration.=$matches['hour'].'H';
+
+ if ($matches['minute'])
+ $duration.=$matches['minute'].'M';
+
+ if ($matches['second'])
+ $duration.=$matches['second'].'S';
+
+ }
+
+ if ($duration==='P') {
+ $duration = 'PT0S';
+ }
+ $iv = new DateInterval($duration);
+ if ($invert) $iv->invert = true;
+
+ return $iv;
+
+ }
+
+
+
+ $parts = array(
+ 'week',
+ 'day',
+ 'hour',
+ 'minute',
+ 'second',
+ );
+
+ $newDur = '';
+ foreach($parts as $part) {
+ if (isset($matches[$part]) && $matches[$part]) {
+ $newDur.=' '.$matches[$part] . ' ' . $part . 's';
+ }
+ }
+
+ $newDur = ($matches['plusminus']==='-'?'-':'+') . trim($newDur);
+ if ($newDur === '+') { $newDur = '+0 seconds'; };
+ return $newDur;
+
+ }
+
+ /**
+ * Parses either a Date or DateTime, or Duration value.
+ *
+ * @param string $date
+ * @param DateTimeZone|string $referenceTZ
+ * @return DateTime|DateInterval
+ */
+ static public function parse($date, $referenceTZ = null) {
+
+ if ($date[0]==='P' || ($date[0]==='-' && $date[1]==='P')) {
+ return self::parseDuration($date);
+ } elseif (strlen($date)===8) {
+ return self::parseDate($date);
+ } else {
+ return self::parseDateTime($date, $referenceTZ);
+ }
+
+ }
+
+
+}
diff --git a/3rdparty/Sabre/VObject/Element.php b/3rdparty/Sabre/VObject/Element.php
new file mode 100755
index 00000000000..e20ff0b353c
--- /dev/null
+++ b/3rdparty/Sabre/VObject/Element.php
@@ -0,0 +1,16 @@
+<?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 {
+
+ public $parent = null;
+
+}
diff --git a/3rdparty/Sabre/VObject/Element/DateTime.php b/3rdparty/Sabre/VObject/Element/DateTime.php
new file mode 100755
index 00000000000..5e5eb7ab6f2
--- /dev/null
+++ b/3rdparty/Sabre/VObject/Element/DateTime.php
@@ -0,0 +1,37 @@
+<?php
+
+/**
+ * DateTime property
+ *
+ * this class got renamed to Sabre_VObject_Property_DateTime
+ *
+ * @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
+ * @deprecated
+ */
+class Sabre_VObject_Element_DateTime extends Sabre_VObject_Property_DateTime {
+
+ /**
+ * 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;
+
+}
diff --git a/3rdparty/Sabre/VObject/Element/MultiDateTime.php b/3rdparty/Sabre/VObject/Element/MultiDateTime.php
new file mode 100755
index 00000000000..8a12ced94a8
--- /dev/null
+++ b/3rdparty/Sabre/VObject/Element/MultiDateTime.php
@@ -0,0 +1,17 @@
+<?php
+
+/**
+ * Multi-DateTime property
+ *
+ * This class got renamed to Sabre_VObject_Property_MultiDateTime
+ *
+ * @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
+ * @deprecated
+ */
+class Sabre_VObject_Element_MultiDateTime extends Sabre_VObject_Property_MultiDateTime {
+
+}
diff --git a/3rdparty/Sabre/VObject/ElementList.php b/3rdparty/Sabre/VObject/ElementList.php
new file mode 100755
index 00000000000..7e508db20f0
--- /dev/null
+++ b/3rdparty/Sabre/VObject/ElementList.php
@@ -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/3rdparty/Sabre/VObject/FreeBusyGenerator.php b/3rdparty/Sabre/VObject/FreeBusyGenerator.php
new file mode 100755
index 00000000000..1c96a64a004
--- /dev/null
+++ b/3rdparty/Sabre/VObject/FreeBusyGenerator.php
@@ -0,0 +1,297 @@
+<?php
+
+/**
+ * This class helps with generating FREEBUSY reports based on existing sets of
+ * objects.
+ *
+ * It only looks at VEVENT and VFREEBUSY objects from the sourcedata, and
+ * generates a single VFREEBUSY object.
+ *
+ * VFREEBUSY components are described in RFC5545, The rules for what should
+ * go in a single freebusy report is taken from RFC4791, section 7.10.
+ *
+ * @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_FreeBusyGenerator {
+
+ /**
+ * Input objects
+ *
+ * @var array
+ */
+ protected $objects;
+
+ /**
+ * Start of range
+ *
+ * @var DateTime|null
+ */
+ protected $start;
+
+ /**
+ * End of range
+ *
+ * @var DateTime|null
+ */
+ protected $end;
+
+ /**
+ * VCALENDAR object
+ *
+ * @var Sabre_VObject_Component
+ */
+ protected $baseObject;
+
+ /**
+ * Sets the VCALENDAR object.
+ *
+ * If this is set, it will not be generated for you. You are responsible
+ * for setting things like the METHOD, CALSCALE, VERSION, etc..
+ *
+ * The VFREEBUSY object will be automatically added though.
+ *
+ * @param Sabre_VObject_Component $vcalendar
+ * @return void
+ */
+ public function setBaseObject(Sabre_VObject_Component $vcalendar) {
+
+ $this->baseObject = $vcalendar;
+
+ }
+
+ /**
+ * Sets the input objects
+ *
+ * Every object must either be a string or a Sabre_VObject_Component.
+ *
+ * @param array $objects
+ * @return void
+ */
+ public function setObjects(array $objects) {
+
+ $this->objects = array();
+ foreach($objects as $object) {
+
+ if (is_string($object)) {
+ $this->objects[] = Sabre_VObject_Reader::read($object);
+ } elseif ($object instanceof Sabre_VObject_Component) {
+ $this->objects[] = $object;
+ } else {
+ throw new InvalidArgumentException('You can only pass strings or Sabre_VObject_Component arguments to setObjects');
+ }
+
+ }
+
+ }
+
+ /**
+ * Sets the time range
+ *
+ * Any freebusy object falling outside of this time range will be ignored.
+ *
+ * @param DateTime $start
+ * @param DateTime $end
+ * @return void
+ */
+ public function setTimeRange(DateTime $start = null, DateTime $end = null) {
+
+ $this->start = $start;
+ $this->end = $end;
+
+ }
+
+ /**
+ * Parses the input data and returns a correct VFREEBUSY object, wrapped in
+ * a VCALENDAR.
+ *
+ * @return Sabre_VObject_Component
+ */
+ public function getResult() {
+
+ $busyTimes = array();
+
+ foreach($this->objects as $object) {
+
+ foreach($object->getBaseComponents() as $component) {
+
+ switch($component->name) {
+
+ case 'VEVENT' :
+
+ $FBTYPE = 'BUSY';
+ if (isset($component->TRANSP) && (strtoupper($component->TRANSP) === 'TRANSPARENT')) {
+ break;
+ }
+ if (isset($component->STATUS)) {
+ $status = strtoupper($component->STATUS);
+ if ($status==='CANCELLED') {
+ break;
+ }
+ if ($status==='TENTATIVE') {
+ $FBTYPE = 'BUSY-TENTATIVE';
+ }
+ }
+
+ $times = array();
+
+ if ($component->RRULE) {
+
+ $iterator = new Sabre_VObject_RecurrenceIterator($object, (string)$component->uid);
+ if ($this->start) {
+ $iterator->fastForward($this->start);
+ }
+
+ $maxRecurrences = 200;
+
+ while($iterator->valid() && --$maxRecurrences) {
+
+ $startTime = $iterator->getDTStart();
+ if ($this->end && $startTime > $this->end) {
+ break;
+ }
+ $times[] = array(
+ $iterator->getDTStart(),
+ $iterator->getDTEnd(),
+ );
+
+ $iterator->next();
+
+ }
+
+ } else {
+
+ $startTime = $component->DTSTART->getDateTime();
+ if ($this->end && $startTime > $this->end) {
+ break;
+ }
+ $endTime = null;
+ if (isset($component->DTEND)) {
+ $endTime = $component->DTEND->getDateTime();
+ } elseif (isset($component->DURATION)) {
+ $duration = Sabre_VObject_DateTimeParser::parseDuration((string)$component->DURATION);
+ $endTime = clone $startTime;
+ $endTime->add($duration);
+ } elseif ($component->DTSTART->getDateType() === Sabre_VObject_Property_DateTime::DATE) {
+ $endTime = clone $startTime;
+ $endTime->modify('+1 day');
+ } else {
+ // The event had no duration (0 seconds)
+ break;
+ }
+
+ $times[] = array($startTime, $endTime);
+
+ }
+
+ foreach($times as $time) {
+
+ if ($this->end && $time[0] > $this->end) break;
+ if ($this->start && $time[1] < $this->start) break;
+
+ $busyTimes[] = array(
+ $time[0],
+ $time[1],
+ $FBTYPE,
+ );
+ }
+ break;
+
+ case 'VFREEBUSY' :
+ foreach($component->FREEBUSY as $freebusy) {
+
+ $fbType = isset($freebusy['FBTYPE'])?strtoupper($freebusy['FBTYPE']):'BUSY';
+
+ // Skipping intervals marked as 'free'
+ if ($fbType==='FREE')
+ continue;
+
+ $values = explode(',', $freebusy);
+ foreach($values as $value) {
+ list($startTime, $endTime) = explode('/', $value);
+ $startTime = Sabre_VObject_DateTimeParser::parseDateTime($startTime);
+
+ if (substr($endTime,0,1)==='P' || substr($endTime,0,2)==='-P') {
+ $duration = Sabre_VObject_DateTimeParser::parseDuration($endTime);
+ $endTime = clone $startTime;
+ $endTime->add($duration);
+ } else {
+ $endTime = Sabre_VObject_DateTimeParser::parseDateTime($endTime);
+ }
+
+ if($this->start && $this->start > $endTime) continue;
+ if($this->end && $this->end < $startTime) continue;
+ $busyTimes[] = array(
+ $startTime,
+ $endTime,
+ $fbType
+ );
+
+ }
+
+
+ }
+ break;
+
+
+
+ }
+
+
+ }
+
+ }
+
+ if ($this->baseObject) {
+ $calendar = $this->baseObject;
+ } else {
+ $calendar = new Sabre_VObject_Component('VCALENDAR');
+ $calendar->version = '2.0';
+ if (Sabre_DAV_Server::$exposeVersion) {
+ $calendar->prodid = '-//SabreDAV//Sabre VObject ' . Sabre_VObject_Version::VERSION . '//EN';
+ } else {
+ $calendar->prodid = '-//SabreDAV//Sabre VObject//EN';
+ }
+ $calendar->calscale = 'GREGORIAN';
+ }
+
+ $vfreebusy = new Sabre_VObject_Component('VFREEBUSY');
+ $calendar->add($vfreebusy);
+
+ if ($this->start) {
+ $dtstart = new Sabre_VObject_Property_DateTime('DTSTART');
+ $dtstart->setDateTime($this->start,Sabre_VObject_Property_DateTime::UTC);
+ $vfreebusy->add($dtstart);
+ }
+ if ($this->end) {
+ $dtend = new Sabre_VObject_Property_DateTime('DTEND');
+ $dtend->setDateTime($this->start,Sabre_VObject_Property_DateTime::UTC);
+ $vfreebusy->add($dtend);
+ }
+ $dtstamp = new Sabre_VObject_Property_DateTime('DTSTAMP');
+ $dtstamp->setDateTime(new DateTime('now'), Sabre_VObject_Property_DateTime::UTC);
+ $vfreebusy->add($dtstamp);
+
+ foreach($busyTimes as $busyTime) {
+
+ $busyTime[0]->setTimeZone(new DateTimeZone('UTC'));
+ $busyTime[1]->setTimeZone(new DateTimeZone('UTC'));
+
+ $prop = new Sabre_VObject_Property(
+ 'FREEBUSY',
+ $busyTime[0]->format('Ymd\\THis\\Z') . '/' . $busyTime[1]->format('Ymd\\THis\\Z')
+ );
+ $prop['FBTYPE'] = $busyTime[2];
+ $vfreebusy->add($prop);
+
+ }
+
+ return $calendar;
+
+ }
+
+}
+
diff --git a/3rdparty/Sabre/VObject/Node.php b/3rdparty/Sabre/VObject/Node.php
new file mode 100755
index 00000000000..d89e01b56c6
--- /dev/null
+++ b/3rdparty/Sabre/VObject/Node.php
@@ -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
+ */
+ public $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/3rdparty/Sabre/VObject/Parameter.php b/3rdparty/Sabre/VObject/Parameter.php
new file mode 100755
index 00000000000..2e39af5f78a
--- /dev/null
+++ b/3rdparty/Sabre/VObject/Parameter.php
@@ -0,0 +1,84 @@
+<?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() {
+
+ if (is_null($this->value)) {
+ return $this->name;
+ }
+ $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/3rdparty/Sabre/VObject/ParseException.php b/3rdparty/Sabre/VObject/ParseException.php
new file mode 100755
index 00000000000..1b5e95bf16e
--- /dev/null
+++ b/3rdparty/Sabre/VObject/ParseException.php
@@ -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/3rdparty/Sabre/VObject/Property.php b/3rdparty/Sabre/VObject/Property.php
new file mode 100755
index 00000000000..ce74fe3865b
--- /dev/null
+++ b/3rdparty/Sabre/VObject/Property.php
@@ -0,0 +1,348 @@
+<?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;
+
+ /**
+ * If properties are added to this map, they will be automatically mapped
+ * to their respective classes, if parsed by the reader or constructed with
+ * the 'create' method.
+ *
+ * @var array
+ */
+ static public $classMap = array(
+ 'COMPLETED' => 'Sabre_VObject_Property_DateTime',
+ 'CREATED' => 'Sabre_VObject_Property_DateTime',
+ 'DTEND' => 'Sabre_VObject_Property_DateTime',
+ 'DTSTAMP' => 'Sabre_VObject_Property_DateTime',
+ 'DTSTART' => 'Sabre_VObject_Property_DateTime',
+ 'DUE' => 'Sabre_VObject_Property_DateTime',
+ 'EXDATE' => 'Sabre_VObject_Property_MultiDateTime',
+ 'LAST-MODIFIED' => 'Sabre_VObject_Property_DateTime',
+ 'RECURRENCE-ID' => 'Sabre_VObject_Property_DateTime',
+ 'TRIGGER' => 'Sabre_VObject_Property_DateTime',
+ );
+
+ /**
+ * Creates the new property by name, but in addition will also see if
+ * there's a class mapped to the property name.
+ *
+ * @param string $name
+ * @param string $value
+ * @return void
+ */
+ static public function create($name, $value = null) {
+
+ $name = strtoupper($name);
+ $shortName = $name;
+ $group = null;
+ if (strpos($shortName,'.')!==false) {
+ list($group, $shortName) = explode('.', $shortName);
+ }
+
+ if (isset(self::$classMap[$shortName])) {
+ return new self::$classMap[$shortName]($name, $value);
+ } else {
+ return new self($name, $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) && !is_null($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);
+ $name = strtoupper($name);
+
+ 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 (string)$this->value;
+
+ }
+
+ /**
+ * This method is automatically called when the object is cloned.
+ * Specifically, this will ensure all child elements are also cloned.
+ *
+ * @return void
+ */
+ public function __clone() {
+
+ foreach($this->parameters as $key=>$child) {
+ $this->parameters[$key] = clone $child;
+ $this->parameters[$key]->parent = $this;
+ }
+
+ }
+
+}
diff --git a/3rdparty/Sabre/VObject/Property/DateTime.php b/3rdparty/Sabre/VObject/Property/DateTime.php
new file mode 100755
index 00000000000..fe2372caa81
--- /dev/null
+++ b/3rdparty/Sabre/VObject/Property/DateTime.php
@@ -0,0 +1,260 @@
+<?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_Property_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 {
+ // tzid an Olson identifier?
+ $tz = new DateTimeZone($tzid->value);
+ } catch (Exception $e) {
+
+ // Not an Olson id, we're going to try to find the information
+ // through the time zone name map.
+ $newtzid = Sabre_VObject_WindowsTimezoneMap::lookup($tzid->value);
+ if (is_null($newtzid)) {
+
+ // Not a well known time zone name either, 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'})) {
+ $newtzid = (string)$vtimezone->{'X-LIC-LOCATION'};
+ } else {
+ // No libical location specified. As a last resort we could
+ // try matching $vtimezone's DST rules against all known
+ // time zones returned by DateTimeZone::list*
+
+ // TODO
+ }
+ }
+ }
+ }
+ }
+
+ try {
+ $tz = new DateTimeZone($newtzid);
+ } catch (Exception $e) {
+ // If all else fails, we use the default PHP timezone
+ $tz = new DateTimeZone(date_default_timezone_get());
+ }
+ }
+ $dt = new DateTime($dateStr, $tz);
+ $dt->setTimeZone($tz);
+
+ return array(
+ self::LOCALTZ,
+ $dt
+ );
+
+ }
+
+}
diff --git a/3rdparty/Sabre/VObject/Property/MultiDateTime.php b/3rdparty/Sabre/VObject/Property/MultiDateTime.php
new file mode 100755
index 00000000000..ae53ab6a617
--- /dev/null
+++ b/3rdparty/Sabre/VObject/Property/MultiDateTime.php
@@ -0,0 +1,166 @@
+<?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_Property_MultiDateTime extends Sabre_VObject_Property {
+
+ /**
+ * DateTime representation
+ *
+ * @var DateTime[]
+ */
+ protected $dateTimes;
+
+ /**
+ * dateType
+ *
+ * This is one of the Sabre_VObject_Property_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_Property_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_Property_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_Property_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_Property_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_Property_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_Property_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_Property_DateTime::parseData($val, $this);
+ $dts[] = $dt;
+ $this->dateType = $type;
+ }
+ $this->dateTimes = $dts;
+ return $this->dateType;
+
+ }
+
+}
diff --git a/3rdparty/Sabre/VObject/Reader.php b/3rdparty/Sabre/VObject/Reader.php
new file mode 100755
index 00000000000..eea73fa3dce
--- /dev/null
+++ b/3rdparty/Sabre/VObject/Reader.php
@@ -0,0 +1,183 @@
+<?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 refactoring 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 {
+
+ /**
+ * 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.
+ *
+ * This method receives the full array of lines. The array pointer is used
+ * to traverse.
+ *
+ * @param array $lines
+ * @return Sabre_VObject_Element
+ */
+ static private function readLine(&$lines) {
+
+ $line = current($lines);
+ $lineNr = key($lines);
+ next($lines);
+
+ // Components
+ if (stripos($line,"BEGIN:")===0) {
+
+ $componentName = strtoupper(substr($line,6));
+ $obj = Sabre_VObject_Component::create($componentName);
+
+ $nextLine = current($lines);
+
+ while(stripos($nextLine,"END:")!==0) {
+
+ $obj->add(self::readLine($lines));
+
+ $nextLine = current($lines);
+
+ if ($nextLine===false)
+ throw new Sabre_VObject_ParseException('Invalid VObject. Document ended prematurely.');
+
+ }
+
+ // Checking component name of the 'END:' line.
+ if (substr($nextLine,4)!==$obj->name) {
+ throw new Sabre_VObject_ParseException('Invalid VObject, expected: "END:' . $obj->name . '" got: "' . $nextLine . '"');
+ }
+ next($lines);
+
+ return $obj;
+
+ }
+
+ // Properties
+ //$result = preg_match('/(?P<name>[A-Z0-9-]+)(?:;(?P<parameters>^(?<!:):))(.*)$/',$line,$matches);
+
+
+ $token = '[A-Z0-9-\.]+';
+ $parameters = "(?:;(?P<parameters>([^:^\"]|\"([^\"]*)\")*))?";
+ $regex = "/^(?P<name>$token)$parameters:(?P<value>.*)$/i";
+
+ $result = preg_match($regex,$line,$matches);
+
+ if (!$result) {
+ throw new Sabre_VObject_ParseException('Invalid VObject, line ' . ($lineNr+1) . ' did not follow the icalendar/vcard format');
+ }
+
+ $propertyName = strtoupper($matches['name']);
+ $propertyValue = preg_replace_callback('#(\\\\(\\\\|N|n|;|,))#',function($matches) {
+ if ($matches[2]==='n' || $matches[2]==='N') {
+ return "\n";
+ } else {
+ return $matches[2];
+ }
+ }, $matches['value']);
+
+ $obj = Sabre_VObject_Property::create($propertyName, $propertyValue);
+
+ if ($matches['parameters']) {
+
+ foreach(self::readParameters($matches['parameters']) as $param) {
+ $obj->add($param);
+ }
+
+ }
+
+ return $obj;
+
+
+ }
+
+ /**
+ * Reads a parameter list from a property
+ *
+ * This method returns an array of Sabre_VObject_Parameter
+ *
+ * @param string $parameters
+ * @return array
+ */
+ static private function readParameters($parameters) {
+
+ $token = '[A-Z0-9-]+';
+
+ $paramValue = '(?P<paramValue>[^\"^;]*|"[^"]*")';
+
+ $regex = "/(?<=^|;)(?P<paramName>$token)(=$paramValue(?=$|;))?/i";
+ preg_match_all($regex, $parameters, $matches, PREG_SET_ORDER);
+
+ $params = array();
+ foreach($matches as $match) {
+
+ $value = isset($match['paramValue'])?$match['paramValue']:null;
+
+ if (isset($value[0])) {
+ // Stripping quotes, if needed
+ if ($value[0] === '"') $value = substr($value,1,strlen($value)-2);
+ } else {
+ $value = '';
+ }
+
+ $value = preg_replace_callback('#(\\\\(\\\\|N|n|;|,))#',function($matches) {
+ if ($matches[2]==='n' || $matches[2]==='N') {
+ return "\n";
+ } else {
+ return $matches[2];
+ }
+ }, $value);
+
+ $params[] = new Sabre_VObject_Parameter($match['paramName'], $value);
+
+ }
+
+ return $params;
+
+ }
+
+
+}
diff --git a/3rdparty/Sabre/VObject/RecurrenceIterator.php b/3rdparty/Sabre/VObject/RecurrenceIterator.php
new file mode 100755
index 00000000000..740270dd8f0
--- /dev/null
+++ b/3rdparty/Sabre/VObject/RecurrenceIterator.php
@@ -0,0 +1,1043 @@
+<?php
+
+/**
+ * This class is used to determine new for a recurring event, when the next
+ * events occur.
+ *
+ * This iterator may loop infinitely in the future, therefore it is important
+ * that if you use this class, you set hard limits for the amount of iterations
+ * you want to handle.
+ *
+ * Note that currently there is not full support for the entire iCalendar
+ * specification, as it's very complex and contains a lot of permutations
+ * that's not yet used very often in software.
+ *
+ * For the focus has been on features as they actually appear in Calendaring
+ * software, but this may well get expanded as needed / on demand
+ *
+ * The following RRULE properties are supported
+ * * UNTIL
+ * * INTERVAL
+ * * COUNT
+ * * FREQ=DAILY
+ * * BYDAY
+ * * FREQ=WEEKLY
+ * * BYDAY
+ * * WKST
+ * * FREQ=MONTHLY
+ * * BYMONTHDAY
+ * * BYDAY
+ * * BYSETPOS
+ * * FREQ=YEARLY
+ * * BYMONTH
+ * * BYMONTHDAY (only if BYMONTH is also set)
+ * * BYDAY (only if BYMONTH is also set)
+ *
+ * Anything beyond this is 'undefined', which means that it may get ignored, or
+ * you may get unexpected results. The effect is that in some applications the
+ * specified recurrence may look incorrect, or is missing.
+ *
+ * @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_RecurrenceIterator implements Iterator {
+
+ /**
+ * The initial event date
+ *
+ * @var DateTime
+ */
+ public $startDate;
+
+ /**
+ * The end-date of the initial event
+ *
+ * @var DateTime
+ */
+ public $endDate;
+
+ /**
+ * The 'current' recurrence.
+ *
+ * This will be increased for every iteration.
+ *
+ * @var DateTime
+ */
+ public $currentDate;
+
+
+ /**
+ * List of dates that are excluded from the rules.
+ *
+ * This list contains the items that have been overriden by the EXDATE
+ * property.
+ *
+ * @var array
+ */
+ public $exceptionDates = array();
+
+ /**
+ * Base event
+ *
+ * @var Sabre_VObject_Component_VEvent
+ */
+ public $baseEvent;
+
+ /**
+ * List of dates that are overridden by other events.
+ * Similar to $overriddenEvents, but this just contains the original dates.
+ *
+ * @var array
+ */
+ public $overriddenDates = array();
+
+ /**
+ * list of events that are 'overridden'.
+ *
+ * This is an array of Sabre_VObject_Component_VEvent objects.
+ *
+ * @var array
+ */
+ public $overriddenEvents = array();
+
+
+ /**
+ * Frequency is one of: secondly, minutely, hourly, daily, weekly, monthly,
+ * yearly.
+ *
+ * @var string
+ */
+ public $frequency;
+
+ /**
+ * The last instance of this recurrence, inclusively
+ *
+ * @var DateTime|null
+ */
+ public $until;
+
+ /**
+ * The number of recurrences, or 'null' if infinitely recurring.
+ *
+ * @var int
+ */
+ public $count;
+
+ /**
+ * The interval.
+ *
+ * If for example frequency is set to daily, interval = 2 would mean every
+ * 2 days.
+ *
+ * @var int
+ */
+ public $interval = 1;
+
+ /**
+ * Which seconds to recur.
+ *
+ * This is an array of integers (between 0 and 60)
+ *
+ * @var array
+ */
+ public $bySecond;
+
+ /**
+ * Which minutes to recur
+ *
+ * This is an array of integers (between 0 and 59)
+ *
+ * @var array
+ */
+ public $byMinute;
+
+ /**
+ * Which hours to recur
+ *
+ * This is an array of integers (between 0 and 23)
+ *
+ * @var array
+ */
+ public $byHour;
+
+ /**
+ * Which weekdays to recur.
+ *
+ * This is an array of weekdays
+ *
+ * This may also be preceeded by a positive or negative integer. If present,
+ * this indicates the nth occurrence of a specific day within the monthly or
+ * yearly rrule. For instance, -2TU indicates the second-last tuesday of
+ * the month, or year.
+ *
+ * @var array
+ */
+ public $byDay;
+
+ /**
+ * Which days of the month to recur
+ *
+ * This is an array of days of the months (1-31). The value can also be
+ * negative. -5 for instance means the 5th last day of the month.
+ *
+ * @var array
+ */
+ public $byMonthDay;
+
+ /**
+ * Which days of the year to recur.
+ *
+ * This is an array with days of the year (1 to 366). The values can also
+ * be negative. For instance, -1 will always represent the last day of the
+ * year. (December 31st).
+ *
+ * @var array
+ */
+ public $byYearDay;
+
+ /**
+ * Which week numbers to recur.
+ *
+ * This is an array of integers from 1 to 53. The values can also be
+ * negative. -1 will always refer to the last week of the year.
+ *
+ * @var array
+ */
+ public $byWeekNo;
+
+ /**
+ * Which months to recur
+ *
+ * This is an array of integers from 1 to 12.
+ *
+ * @var array
+ */
+ public $byMonth;
+
+ /**
+ * Which items in an existing st to recur.
+ *
+ * These numbers work together with an existing by* rule. It specifies
+ * exactly which items of the existing by-rule to filter.
+ *
+ * Valid values are 1 to 366 and -1 to -366. As an example, this can be
+ * used to recur the last workday of the month.
+ *
+ * This would be done by setting frequency to 'monthly', byDay to
+ * 'MO,TU,WE,TH,FR' and bySetPos to -1.
+ *
+ * @var array
+ */
+ public $bySetPos;
+
+ /**
+ * When a week starts
+ *
+ * @var string
+ */
+ public $weekStart = 'MO';
+
+ /**
+ * The current item in the list
+ *
+ * @var int
+ */
+ public $counter = 0;
+
+ /**
+ * Simple mapping from iCalendar day names to day numbers
+ *
+ * @var array
+ */
+ private $dayMap = array(
+ 'SU' => 0,
+ 'MO' => 1,
+ 'TU' => 2,
+ 'WE' => 3,
+ 'TH' => 4,
+ 'FR' => 5,
+ 'SA' => 6,
+ );
+
+ /**
+ * Mappings between the day number and english day name.
+ *
+ * @var array
+ */
+ private $dayNames = array(
+ 0 => 'Sunday',
+ 1 => 'Monday',
+ 2 => 'Tuesday',
+ 3 => 'Wednesday',
+ 4 => 'Thursday',
+ 5 => 'Friday',
+ 6 => 'Saturday',
+ );
+
+ /**
+ * If the current iteration of the event is an overriden event, this
+ * property will hold the VObject
+ *
+ * @var Sabre_Component_VObject
+ */
+ private $currentOverriddenEvent;
+
+ /**
+ * This property may contain the date of the next not-overridden event.
+ * This date is calculated sometimes a bit early, before overridden events
+ * are evaluated.
+ *
+ * @var DateTime
+ */
+ private $nextDate;
+
+ /**
+ * Creates the iterator
+ *
+ * You should pass a VCALENDAR component, as well as the UID of the event
+ * we're going to traverse.
+ *
+ * @param Sabre_VObject_Component $vcal
+ * @param string|null $uid
+ */
+ public function __construct(Sabre_VObject_Component $vcal, $uid=null) {
+
+ if (is_null($uid)) {
+ if ($vcal->name === 'VCALENDAR') {
+ throw new InvalidArgumentException('If you pass a VCALENDAR object, you must pass a uid argument as well');
+ }
+ $components = array($vcal);
+ $uid = (string)$vcal->uid;
+ } else {
+ $components = $vcal->select('VEVENT');
+ }
+ foreach($components as $component) {
+ if ((string)$component->uid == $uid) {
+ if (isset($component->{'RECURRENCE-ID'})) {
+ $this->overriddenEvents[$component->DTSTART->getDateTime()->getTimeStamp()] = $component;
+ $this->overriddenDates[] = $component->{'RECURRENCE-ID'}->getDateTime();
+ } else {
+ $this->baseEvent = $component;
+ }
+ }
+ }
+ if (!$this->baseEvent) {
+ throw new InvalidArgumentException('Could not find a base event with uid: ' . $uid);
+ }
+
+ $this->startDate = clone $this->baseEvent->DTSTART->getDateTime();
+
+ $this->endDate = null;
+ if (isset($this->baseEvent->DTEND)) {
+ $this->endDate = clone $this->baseEvent->DTEND->getDateTime();
+ } else {
+ $this->endDate = clone $this->startDate;
+ if (isset($this->baseEvent->DURATION)) {
+ $this->endDate->add(Sabre_VObject_DateTimeParser::parse($this->baseEvent->DURATION->value));
+ } elseif ($this->baseEvent->DTSTART->getDateType()===Sabre_VObject_Property_DateTime::DATE) {
+ $this->endDate->modify('+1 day');
+ }
+ }
+ $this->currentDate = clone $this->startDate;
+
+ $rrule = (string)$this->baseEvent->RRULE;
+
+ $parts = explode(';', $rrule);
+
+ foreach($parts as $part) {
+
+ list($key, $value) = explode('=', $part, 2);
+
+ switch(strtoupper($key)) {
+
+ case 'FREQ' :
+ if (!in_array(
+ strtolower($value),
+ array('secondly','minutely','hourly','daily','weekly','monthly','yearly')
+ )) {
+ throw new InvalidArgumentException('Unknown value for FREQ=' . strtoupper($value));
+
+ }
+ $this->frequency = strtolower($value);
+ break;
+
+ case 'UNTIL' :
+ $this->until = Sabre_VObject_DateTimeParser::parse($value);
+ break;
+
+ case 'COUNT' :
+ $this->count = (int)$value;
+ break;
+
+ case 'INTERVAL' :
+ $this->interval = (int)$value;
+ break;
+
+ case 'BYSECOND' :
+ $this->bySecond = explode(',', $value);
+ break;
+
+ case 'BYMINUTE' :
+ $this->byMinute = explode(',', $value);
+ break;
+
+ case 'BYHOUR' :
+ $this->byHour = explode(',', $value);
+ break;
+
+ case 'BYDAY' :
+ $this->byDay = explode(',', strtoupper($value));
+ break;
+
+ case 'BYMONTHDAY' :
+ $this->byMonthDay = explode(',', $value);
+ break;
+
+ case 'BYYEARDAY' :
+ $this->byYearDay = explode(',', $value);
+ break;
+
+ case 'BYWEEKNO' :
+ $this->byWeekNo = explode(',', $value);
+ break;
+
+ case 'BYMONTH' :
+ $this->byMonth = explode(',', $value);
+ break;
+
+ case 'BYSETPOS' :
+ $this->bySetPos = explode(',', $value);
+ break;
+
+ case 'WKST' :
+ $this->weekStart = strtoupper($value);
+ break;
+
+ }
+
+ }
+
+ // Parsing exception dates
+ if (isset($this->baseEvent->EXDATE)) {
+ foreach($this->baseEvent->EXDATE as $exDate) {
+
+ foreach(explode(',', (string)$exDate) as $exceptionDate) {
+
+ $this->exceptionDates[] =
+ Sabre_VObject_DateTimeParser::parse($exceptionDate, $this->startDate->getTimeZone());
+
+ }
+
+ }
+
+ }
+
+ }
+
+ /**
+ * Returns the current item in the list
+ *
+ * @return DateTime
+ */
+ public function current() {
+
+ if (!$this->valid()) return null;
+ return clone $this->currentDate;
+
+ }
+
+ /**
+ * This method returns the startdate for the current iteration of the
+ * event.
+ *
+ * @return DateTime
+ */
+ public function getDtStart() {
+
+ if (!$this->valid()) return null;
+ return clone $this->currentDate;
+
+ }
+
+ /**
+ * This method returns the enddate for the current iteration of the
+ * event.
+ *
+ * @return DateTime
+ */
+ public function getDtEnd() {
+
+ if (!$this->valid()) return null;
+ $dtEnd = clone $this->currentDate;
+ $dtEnd->add( $this->startDate->diff( $this->endDate ) );
+ return clone $dtEnd;
+
+ }
+
+ /**
+ * Returns a VEVENT object with the updated start and end date.
+ *
+ * Any recurrence information is removed, and this function may return an
+ * 'overridden' event instead.
+ *
+ * This method always returns a cloned instance.
+ *
+ * @return void
+ */
+ public function getEventObject() {
+
+ if ($this->currentOverriddenEvent) {
+ return clone $this->currentOverriddenEvent;
+ }
+ $event = clone $this->baseEvent;
+ unset($event->RRULE);
+ unset($event->EXDATE);
+ unset($event->RDATE);
+ unset($event->EXRULE);
+
+ $event->DTSTART->setDateTime($this->getDTStart(), $event->DTSTART->getDateType());
+ if (isset($event->DTEND)) {
+ $event->DTEND->setDateTime($this->getDtEnd(), $event->DTSTART->getDateType());
+ }
+ if ($this->counter > 0) {
+ $event->{'RECURRENCE-ID'} = (string)$event->DTSTART;
+ }
+
+ return $event;
+
+ }
+
+ /**
+ * Returns the current item number
+ *
+ * @return int
+ */
+ public function key() {
+
+ return $this->counter;
+
+ }
+
+ /**
+ * Whether or not there is a 'next item'
+ *
+ * @return bool
+ */
+ public function valid() {
+
+ if (!is_null($this->count)) {
+ return $this->counter < $this->count;
+ }
+ if (!is_null($this->until)) {
+ return $this->currentDate <= $this->until;
+ }
+ return true;
+
+ }
+
+ /**
+ * Resets the iterator
+ *
+ * @return void
+ */
+ public function rewind() {
+
+ $this->currentDate = clone $this->startDate;
+ $this->counter = 0;
+
+ }
+
+ /**
+ * This method allows you to quickly go to the next occurrence after the
+ * specified date.
+ *
+ * Note that this checks the current 'endDate', not the 'stardDate'. This
+ * means that if you forward to January 1st, the iterator will stop at the
+ * first event that ends *after* January 1st.
+ *
+ * @param DateTime $dt
+ * @return void
+ */
+ public function fastForward(DateTime $dt) {
+
+ while($this->valid() && $this->getDTEnd() <= $dt) {
+ $this->next();
+ }
+
+ }
+
+ /**
+ * Goes on to the next iteration
+ *
+ * @return void
+ */
+ public function next() {
+
+ /*
+ if (!is_null($this->count) && $this->counter >= $this->count) {
+ $this->currentDate = null;
+ }*/
+
+
+ $previousStamp = $this->currentDate->getTimeStamp();
+
+ while(true) {
+
+ $this->currentOverriddenEvent = null;
+
+ // If we have a next date 'stored', we use that
+ if ($this->nextDate) {
+ $this->currentDate = $this->nextDate;
+ $currentStamp = $this->currentDate->getTimeStamp();
+ $this->nextDate = null;
+ } else {
+
+ // Otherwise, we calculate it
+ switch($this->frequency) {
+
+ case 'daily' :
+ $this->nextDaily();
+ break;
+
+ case 'weekly' :
+ $this->nextWeekly();
+ break;
+
+ case 'monthly' :
+ $this->nextMonthly();
+ break;
+
+ case 'yearly' :
+ $this->nextYearly();
+ break;
+
+ }
+ $currentStamp = $this->currentDate->getTimeStamp();
+
+ // Checking exception dates
+ foreach($this->exceptionDates as $exceptionDate) {
+ if ($this->currentDate == $exceptionDate) {
+ $this->counter++;
+ continue 2;
+ }
+ }
+ foreach($this->overriddenDates as $overriddenDate) {
+ if ($this->currentDate == $overriddenDate) {
+ continue 2;
+ }
+ }
+
+ }
+
+ // Checking overriden events
+ foreach($this->overriddenEvents as $index=>$event) {
+ if ($index > $previousStamp && $index <= $currentStamp) {
+
+ // We're moving the 'next date' aside, for later use.
+ $this->nextDate = clone $this->currentDate;
+
+ $this->currentDate = $event->DTSTART->getDateTime();
+ $this->currentOverriddenEvent = $event;
+
+ break;
+ }
+ }
+
+ break;
+
+ }
+
+ /*
+ if (!is_null($this->until)) {
+ if($this->currentDate > $this->until) {
+ $this->currentDate = null;
+ }
+ }*/
+
+ $this->counter++;
+
+ }
+
+ /**
+ * Does the processing for advancing the iterator for daily frequency.
+ *
+ * @return void
+ */
+ protected function nextDaily() {
+
+ if (!$this->byDay) {
+ $this->currentDate->modify('+' . $this->interval . ' days');
+ return;
+ }
+
+ $recurrenceDays = array();
+ foreach($this->byDay as $byDay) {
+
+ // The day may be preceeded with a positive (+n) or
+ // negative (-n) integer. However, this does not make
+ // sense in 'weekly' so we ignore it here.
+ $recurrenceDays[] = $this->dayMap[substr($byDay,-2)];
+
+ }
+
+ do {
+
+ $this->currentDate->modify('+' . $this->interval . ' days');
+
+ // Current day of the week
+ $currentDay = $this->currentDate->format('w');
+
+ } while (!in_array($currentDay, $recurrenceDays));
+
+ }
+
+ /**
+ * Does the processing for advancing the iterator for weekly frequency.
+ *
+ * @return void
+ */
+ protected function nextWeekly() {
+
+ if (!$this->byDay) {
+ $this->currentDate->modify('+' . $this->interval . ' weeks');
+ return;
+ }
+
+ $recurrenceDays = array();
+ foreach($this->byDay as $byDay) {
+
+ // The day may be preceeded with a positive (+n) or
+ // negative (-n) integer. However, this does not make
+ // sense in 'weekly' so we ignore it here.
+ $recurrenceDays[] = $this->dayMap[substr($byDay,-2)];
+
+ }
+
+ // Current day of the week
+ $currentDay = $this->currentDate->format('w');
+
+ // First day of the week:
+ $firstDay = $this->dayMap[$this->weekStart];
+
+ $time = array(
+ $this->currentDate->format('H'),
+ $this->currentDate->format('i'),
+ $this->currentDate->format('s')
+ );
+
+ // Increasing the 'current day' until we find our next
+ // occurrence.
+ while(true) {
+
+ $currentDay++;
+
+ if ($currentDay>6) {
+ $currentDay = 0;
+ }
+
+ // We need to roll over to the next week
+ if ($currentDay === $firstDay) {
+ $this->currentDate->modify('+' . $this->interval . ' weeks');
+
+ // We need to go to the first day of this week, but only if we
+ // are not already on this first day of this week.
+ if($this->currentDate->format('w') != $firstDay) {
+ $this->currentDate->modify('last ' . $this->dayNames[$this->dayMap[$this->weekStart]]);
+ $this->currentDate->setTime($time[0],$time[1],$time[2]);
+ }
+ }
+
+ // We have a match
+ if (in_array($currentDay ,$recurrenceDays)) {
+ $this->currentDate->modify($this->dayNames[$currentDay]);
+ $this->currentDate->setTime($time[0],$time[1],$time[2]);
+ break;
+ }
+
+ }
+
+ }
+
+ /**
+ * Does the processing for advancing the iterator for monthly frequency.
+ *
+ * @return void
+ */
+ protected function nextMonthly() {
+
+ $currentDayOfMonth = $this->currentDate->format('j');
+ if (!$this->byMonthDay && !$this->byDay) {
+
+ // If the current day is higher than the 28th, rollover can
+ // occur to the next month. We Must skip these invalid
+ // entries.
+ if ($currentDayOfMonth < 29) {
+ $this->currentDate->modify('+' . $this->interval . ' months');
+ } else {
+ $increase = 0;
+ do {
+ $increase++;
+ $tempDate = clone $this->currentDate;
+ $tempDate->modify('+ ' . ($this->interval*$increase) . ' months');
+ } while ($tempDate->format('j') != $currentDayOfMonth);
+ $this->currentDate = $tempDate;
+ }
+ return;
+ }
+
+ while(true) {
+
+ $occurrences = $this->getMonthlyOccurrences();
+
+ foreach($occurrences as $occurrence) {
+
+ // The first occurrence thats higher than the current
+ // day of the month wins.
+ if ($occurrence > $currentDayOfMonth) {
+ break 2;
+ }
+
+ }
+
+ // If we made it all the way here, it means there were no
+ // valid occurrences, and we need to advance to the next
+ // month.
+ $this->currentDate->modify('first day of this month');
+ $this->currentDate->modify('+ ' . $this->interval . ' months');
+
+ // This goes to 0 because we need to start counting at hte
+ // beginning.
+ $currentDayOfMonth = 0;
+
+ }
+
+ $this->currentDate->setDate($this->currentDate->format('Y'), $this->currentDate->format('n'), $occurrence);
+
+ }
+
+ /**
+ * Does the processing for advancing the iterator for yearly frequency.
+ *
+ * @return void
+ */
+ protected function nextYearly() {
+
+ $currentMonth = $this->currentDate->format('n');
+ $currentYear = $this->currentDate->format('Y');
+ $currentDayOfMonth = $this->currentDate->format('j');
+
+ // No sub-rules, so we just advance by year
+ if (!$this->byMonth) {
+
+ // Unless it was a leap day!
+ if ($currentMonth==2 && $currentDayOfMonth==29) {
+
+ $counter = 0;
+ do {
+ $counter++;
+ // Here we increase the year count by the interval, until
+ // we hit a date that's also in a leap year.
+ //
+ // We could just find the next interval that's dividable by
+ // 4, but that would ignore the rule that there's no leap
+ // year every year that's dividable by a 100, but not by
+ // 400. (1800, 1900, 2100). So we just rely on the datetime
+ // functions instead.
+ $nextDate = clone $this->currentDate;
+ $nextDate->modify('+ ' . ($this->interval*$counter) . ' years');
+ } while ($nextDate->format('n')!=2);
+ $this->currentDate = $nextDate;
+
+ return;
+
+ }
+
+ // The easiest form
+ $this->currentDate->modify('+' . $this->interval . ' years');
+ return;
+
+ }
+
+ $currentMonth = $this->currentDate->format('n');
+ $currentYear = $this->currentDate->format('Y');
+ $currentDayOfMonth = $this->currentDate->format('j');
+
+ $advancedToNewMonth = false;
+
+ // If we got a byDay or getMonthDay filter, we must first expand
+ // further.
+ if ($this->byDay || $this->byMonthDay) {
+
+ while(true) {
+
+ $occurrences = $this->getMonthlyOccurrences();
+
+ foreach($occurrences as $occurrence) {
+
+ // The first occurrence that's higher than the current
+ // day of the month wins.
+ // If we advanced to the next month or year, the first
+ // occurence is always correct.
+ if ($occurrence > $currentDayOfMonth || $advancedToNewMonth) {
+ break 2;
+ }
+
+ }
+
+ // If we made it here, it means we need to advance to
+ // the next month or year.
+ $currentDayOfMonth = 1;
+ $advancedToNewMonth = true;
+ do {
+
+ $currentMonth++;
+ if ($currentMonth>12) {
+ $currentYear+=$this->interval;
+ $currentMonth = 1;
+ }
+ } while (!in_array($currentMonth, $this->byMonth));
+
+ $this->currentDate->setDate($currentYear, $currentMonth, $currentDayOfMonth);
+
+ }
+
+ // If we made it here, it means we got a valid occurrence
+ $this->currentDate->setDate($currentYear, $currentMonth, $occurrence);
+ return;
+
+ } else {
+
+ // These are the 'byMonth' rules, if there are no byDay or
+ // byMonthDay sub-rules.
+ do {
+
+ $currentMonth++;
+ if ($currentMonth>12) {
+ $currentYear+=$this->interval;
+ $currentMonth = 1;
+ }
+ } while (!in_array($currentMonth, $this->byMonth));
+ $this->currentDate->setDate($currentYear, $currentMonth, $currentDayOfMonth);
+
+ return;
+
+ }
+
+ }
+
+ /**
+ * Returns all the occurrences for a monthly frequency with a 'byDay' or
+ * 'byMonthDay' expansion for the current month.
+ *
+ * The returned list is an array of integers with the day of month (1-31).
+ *
+ * @return array
+ */
+ protected function getMonthlyOccurrences() {
+
+ $startDate = clone $this->currentDate;
+
+ $byDayResults = array();
+
+ // Our strategy is to simply go through the byDays, advance the date to
+ // that point and add it to the results.
+ if ($this->byDay) foreach($this->byDay as $day) {
+
+ $dayName = $this->dayNames[$this->dayMap[substr($day,-2)]];
+
+ // Dayname will be something like 'wednesday'. Now we need to find
+ // all wednesdays in this month.
+ $dayHits = array();
+
+ $checkDate = clone $startDate;
+ $checkDate->modify('first day of this month');
+ $checkDate->modify($dayName);
+
+ do {
+ $dayHits[] = $checkDate->format('j');
+ $checkDate->modify('next ' . $dayName);
+ } while ($checkDate->format('n') === $startDate->format('n'));
+
+ // So now we have 'all wednesdays' for month. It is however
+ // possible that the user only really wanted the 1st, 2nd or last
+ // wednesday.
+ if (strlen($day)>2) {
+ $offset = (int)substr($day,0,-2);
+
+ if ($offset>0) {
+ // It is possible that the day does not exist, such as a
+ // 5th or 6th wednesday of the month.
+ if (isset($dayHits[$offset-1])) {
+ $byDayResults[] = $dayHits[$offset-1];
+ }
+ } else {
+
+ // if it was negative we count from the end of the array
+ $byDayResults[] = $dayHits[count($dayHits) + $offset];
+ }
+ } else {
+ // There was no counter (first, second, last wednesdays), so we
+ // just need to add the all to the list).
+ $byDayResults = array_merge($byDayResults, $dayHits);
+
+ }
+
+ }
+
+ $byMonthDayResults = array();
+ if ($this->byMonthDay) foreach($this->byMonthDay as $monthDay) {
+
+ // Removing values that are out of range for this month
+ if ($monthDay > $startDate->format('t') ||
+ $monthDay < 0-$startDate->format('t')) {
+ continue;
+ }
+ if ($monthDay>0) {
+ $byMonthDayResults[] = $monthDay;
+ } else {
+ // Negative values
+ $byMonthDayResults[] = $startDate->format('t') + 1 + $monthDay;
+ }
+ }
+
+ // If there was just byDay or just byMonthDay, they just specify our
+ // (almost) final list. If both were provided, then byDay limits the
+ // list.
+ if ($this->byMonthDay && $this->byDay) {
+ $result = array_intersect($byMonthDayResults, $byDayResults);
+ } elseif ($this->byMonthDay) {
+ $result = $byMonthDayResults;
+ } else {
+ $result = $byDayResults;
+ }
+ $result = array_unique($result);
+ sort($result, SORT_NUMERIC);
+
+ // The last thing that needs checking is the BYSETPOS. If it's set, it
+ // means only certain items in the set survive the filter.
+ if (!$this->bySetPos) {
+ return $result;
+ }
+
+ $filteredResult = array();
+ foreach($this->bySetPos as $setPos) {
+
+ if ($setPos<0) {
+ $setPos = count($result)-($setPos+1);
+ }
+ if (isset($result[$setPos-1])) {
+ $filteredResult[] = $result[$setPos-1];
+ }
+ }
+
+ sort($filteredResult, SORT_NUMERIC);
+ return $filteredResult;
+
+ }
+
+
+}
+
diff --git a/3rdparty/Sabre/VObject/Version.php b/3rdparty/Sabre/VObject/Version.php
new file mode 100755
index 00000000000..9ee03d87118
--- /dev/null
+++ b/3rdparty/Sabre/VObject/Version.php
@@ -0,0 +1,24 @@
+<?php
+
+/**
+ * This class contains the version number for the VObject package
+ *
+ * @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_Version {
+
+ /**
+ * Full version number
+ */
+ const VERSION = '1.3.4';
+
+ /**
+ * Stability : alpha, beta, stable
+ */
+ const STABILITY = 'stable';
+
+}
diff --git a/3rdparty/Sabre/VObject/WindowsTimezoneMap.php b/3rdparty/Sabre/VObject/WindowsTimezoneMap.php
new file mode 100755
index 00000000000..5e1cc5d479b
--- /dev/null
+++ b/3rdparty/Sabre/VObject/WindowsTimezoneMap.php
@@ -0,0 +1,128 @@
+<?php
+
+/**
+ * Time zone name translation
+ *
+ * This file translates well-known time zone names into "Olson database" time zone names.
+ *
+ * @package Sabre
+ * @subpackage VObject
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Frank Edelhaeuser (fedel@users.sourceforge.net)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_VObject_WindowsTimezoneMap {
+
+ protected static $map = array(
+
+ // from http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/zone_tzid.html
+ // snapshot taken on 2012/01/16
+
+ // windows
+ 'AUS Central Standard Time'=>'Australia/Darwin',
+ 'AUS Eastern Standard Time'=>'Australia/Sydney',
+ 'Afghanistan Standard Time'=>'Asia/Kabul',
+ 'Alaskan Standard Time'=>'America/Anchorage',
+ 'Arab Standard Time'=>'Asia/Riyadh',
+ 'Arabian Standard Time'=>'Asia/Dubai',
+ 'Arabic Standard Time'=>'Asia/Baghdad',
+ 'Argentina Standard Time'=>'America/Buenos_Aires',
+ 'Armenian Standard Time'=>'Asia/Yerevan',
+ 'Atlantic Standard Time'=>'America/Halifax',
+ 'Azerbaijan Standard Time'=>'Asia/Baku',
+ 'Azores Standard Time'=>'Atlantic/Azores',
+ 'Bangladesh Standard Time'=>'Asia/Dhaka',
+ 'Canada Central Standard Time'=>'America/Regina',
+ 'Cape Verde Standard Time'=>'Atlantic/Cape_Verde',
+ 'Caucasus Standard Time'=>'Asia/Yerevan',
+ 'Cen. Australia Standard Time'=>'Australia/Adelaide',
+ 'Central America Standard Time'=>'America/Guatemala',
+ 'Central Asia Standard Time'=>'Asia/Almaty',
+ 'Central Brazilian Standard Time'=>'America/Cuiaba',
+ 'Central Europe Standard Time'=>'Europe/Budapest',
+ 'Central European Standard Time'=>'Europe/Warsaw',
+ 'Central Pacific Standard Time'=>'Pacific/Guadalcanal',
+ 'Central Standard Time'=>'America/Chicago',
+ 'Central Standard Time (Mexico)'=>'America/Mexico_City',
+ 'China Standard Time'=>'Asia/Shanghai',
+ 'Dateline Standard Time'=>'Etc/GMT+12',
+ 'E. Africa Standard Time'=>'Africa/Nairobi',
+ 'E. Australia Standard Time'=>'Australia/Brisbane',
+ 'E. Europe Standard Time'=>'Europe/Minsk',
+ 'E. South America Standard Time'=>'America/Sao_Paulo',
+ 'Eastern Standard Time'=>'America/New_York',
+ 'Egypt Standard Time'=>'Africa/Cairo',
+ 'Ekaterinburg Standard Time'=>'Asia/Yekaterinburg',
+ 'FLE Standard Time'=>'Europe/Kiev',
+ 'Fiji Standard Time'=>'Pacific/Fiji',
+ 'GMT Standard Time'=>'Europe/London',
+ 'GTB Standard Time'=>'Europe/Istanbul',
+ 'Georgian Standard Time'=>'Asia/Tbilisi',
+ 'Greenland Standard Time'=>'America/Godthab',
+ 'Greenwich Standard Time'=>'Atlantic/Reykjavik',
+ 'Hawaiian Standard Time'=>'Pacific/Honolulu',
+ 'India Standard Time'=>'Asia/Calcutta',
+ 'Iran Standard Time'=>'Asia/Tehran',
+ 'Israel Standard Time'=>'Asia/Jerusalem',
+ 'Jordan Standard Time'=>'Asia/Amman',
+ 'Kamchatka Standard Time'=>'Asia/Kamchatka',
+ 'Korea Standard Time'=>'Asia/Seoul',
+ 'Magadan Standard Time'=>'Asia/Magadan',
+ 'Mauritius Standard Time'=>'Indian/Mauritius',
+ 'Mexico Standard Time'=>'America/Mexico_City',
+ 'Mexico Standard Time 2'=>'America/Chihuahua',
+ 'Mid-Atlantic Standard Time'=>'Etc/GMT+2',
+ 'Middle East Standard Time'=>'Asia/Beirut',
+ 'Montevideo Standard Time'=>'America/Montevideo',
+ 'Morocco Standard Time'=>'Africa/Casablanca',
+ 'Mountain Standard Time'=>'America/Denver',
+ 'Mountain Standard Time (Mexico)'=>'America/Chihuahua',
+ 'Myanmar Standard Time'=>'Asia/Rangoon',
+ 'N. Central Asia Standard Time'=>'Asia/Novosibirsk',
+ 'Namibia Standard Time'=>'Africa/Windhoek',
+ 'Nepal Standard Time'=>'Asia/Katmandu',
+ 'New Zealand Standard Time'=>'Pacific/Auckland',
+ 'Newfoundland Standard Time'=>'America/St_Johns',
+ 'North Asia East Standard Time'=>'Asia/Irkutsk',
+ 'North Asia Standard Time'=>'Asia/Krasnoyarsk',
+ 'Pacific SA Standard Time'=>'America/Santiago',
+ 'Pacific Standard Time'=>'America/Los_Angeles',
+ 'Pacific Standard Time (Mexico)'=>'America/Santa_Isabel',
+ 'Pakistan Standard Time'=>'Asia/Karachi',
+ 'Paraguay Standard Time'=>'America/Asuncion',
+ 'Romance Standard Time'=>'Europe/Paris',
+ 'Russian Standard Time'=>'Europe/Moscow',
+ 'SA Eastern Standard Time'=>'America/Cayenne',
+ 'SA Pacific Standard Time'=>'America/Bogota',
+ 'SA Western Standard Time'=>'America/La_Paz',
+ 'SE Asia Standard Time'=>'Asia/Bangkok',
+ 'Samoa Standard Time'=>'Pacific/Apia',
+ 'Singapore Standard Time'=>'Asia/Singapore',
+ 'South Africa Standard Time'=>'Africa/Johannesburg',
+ 'Sri Lanka Standard Time'=>'Asia/Colombo',
+ 'Syria Standard Time'=>'Asia/Damascus',
+ 'Taipei Standard Time'=>'Asia/Taipei',
+ 'Tasmania Standard Time'=>'Australia/Hobart',
+ 'Tokyo Standard Time'=>'Asia/Tokyo',
+ 'Tonga Standard Time'=>'Pacific/Tongatapu',
+ 'US Eastern Standard Time'=>'America/Indianapolis',
+ 'US Mountain Standard Time'=>'America/Phoenix',
+ 'UTC'=>'Etc/GMT',
+ 'UTC+12'=>'Etc/GMT-12',
+ 'UTC-02'=>'Etc/GMT+2',
+ 'UTC-11'=>'Etc/GMT+11',
+ 'Ulaanbaatar Standard Time'=>'Asia/Ulaanbaatar',
+ 'Venezuela Standard Time'=>'America/Caracas',
+ 'Vladivostok Standard Time'=>'Asia/Vladivostok',
+ 'W. Australia Standard Time'=>'Australia/Perth',
+ 'W. Central Africa Standard Time'=>'Africa/Lagos',
+ 'W. Europe Standard Time'=>'Europe/Berlin',
+ 'West Asia Standard Time'=>'Asia/Tashkent',
+ 'West Pacific Standard Time'=>'Pacific/Port_Moresby',
+ 'Yakutsk Standard Time'=>'Asia/Yakutsk',
+ );
+
+ static public function lookup($tzid) {
+ return isset(self::$map[$tzid]) ? self::$map[$tzid] : null;
+ }
+}
diff --git a/3rdparty/Sabre/VObject/includes.php b/3rdparty/Sabre/VObject/includes.php
new file mode 100755
index 00000000000..0177a8f1ba6
--- /dev/null
+++ b/3rdparty/Sabre/VObject/includes.php
@@ -0,0 +1,41 @@
+<?php
+
+/**
+ * Sabre_VObject includes file
+ *
+ * Including this file will automatically include all files from the VObject
+ * package.
+ *
+ * This often allows faster loadtimes, as autoload-speed is often quite slow.
+ *
+ * @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
+ */
+
+// Begin includes
+include __DIR__ . '/DateTimeParser.php';
+include __DIR__ . '/ElementList.php';
+include __DIR__ . '/FreeBusyGenerator.php';
+include __DIR__ . '/Node.php';
+include __DIR__ . '/Parameter.php';
+include __DIR__ . '/ParseException.php';
+include __DIR__ . '/Reader.php';
+include __DIR__ . '/RecurrenceIterator.php';
+include __DIR__ . '/Version.php';
+include __DIR__ . '/WindowsTimezoneMap.php';
+include __DIR__ . '/Element.php';
+include __DIR__ . '/Property.php';
+include __DIR__ . '/Component.php';
+include __DIR__ . '/Property/DateTime.php';
+include __DIR__ . '/Property/MultiDateTime.php';
+include __DIR__ . '/Component/VAlarm.php';
+include __DIR__ . '/Component/VCalendar.php';
+include __DIR__ . '/Component/VEvent.php';
+include __DIR__ . '/Component/VJournal.php';
+include __DIR__ . '/Component/VTodo.php';
+include __DIR__ . '/Element/DateTime.php';
+include __DIR__ . '/Element/MultiDateTime.php';
+// End includes
diff --git a/3rdparty/Sabre/autoload.php b/3rdparty/Sabre/autoload.php
new file mode 100755
index 00000000000..c7b537d83d3
--- /dev/null
+++ b/3rdparty/Sabre/autoload.php
@@ -0,0 +1,31 @@
+<?php
+
+/**
+ * SabreDAV's PHP autoloader
+ *
+ * If you love the autoloader, and don't care as much about performance, this
+ * file register a new autoload function using spl_autoload_register.
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+
+/**
+ * @param string $className
+ * @return void
+ */
+function Sabre_autoload($className) {
+
+ if(strpos($className,'Sabre_')===0) {
+
+ include dirname(__FILE__) . '/' . str_replace('_','/',substr($className,6)) . '.php';
+
+ }
+
+}
+
+spl_autoload_register('Sabre_autoload');
+