summaryrefslogtreecommitdiffstats
path: root/3rdparty/Sabre/CalDAV
diff options
context:
space:
mode:
Diffstat (limited to '3rdparty/Sabre/CalDAV')
-rw-r--r--3rdparty/Sabre/CalDAV/Backend/Abstract.php166
-rw-r--r--3rdparty/Sabre/CalDAV/Backend/PDO.php387
-rw-r--r--3rdparty/Sabre/CalDAV/Calendar.php316
-rw-r--r--3rdparty/Sabre/CalDAV/CalendarObject.php257
-rw-r--r--3rdparty/Sabre/CalDAV/CalendarRootNode.php75
-rw-r--r--3rdparty/Sabre/CalDAV/Exception/InvalidICalendarObject.php18
-rw-r--r--3rdparty/Sabre/CalDAV/ICSExportPlugin.php124
-rw-r--r--3rdparty/Sabre/CalDAV/ICalendarUtil.php157
-rw-r--r--3rdparty/Sabre/CalDAV/Plugin.php788
-rw-r--r--3rdparty/Sabre/CalDAV/Principal/Collection.php31
-rw-r--r--3rdparty/Sabre/CalDAV/Principal/ProxyRead.php178
-rw-r--r--3rdparty/Sabre/CalDAV/Principal/ProxyWrite.php178
-rw-r--r--3rdparty/Sabre/CalDAV/Principal/User.php122
-rw-r--r--3rdparty/Sabre/CalDAV/Property/SupportedCalendarComponentSet.php85
-rw-r--r--3rdparty/Sabre/CalDAV/Property/SupportedCalendarData.php38
-rw-r--r--3rdparty/Sabre/CalDAV/Property/SupportedCollationSet.php44
-rw-r--r--3rdparty/Sabre/CalDAV/Server.php65
-rw-r--r--3rdparty/Sabre/CalDAV/UserCalendars.php280
-rw-r--r--3rdparty/Sabre/CalDAV/Version.php24
-rw-r--r--3rdparty/Sabre/CalDAV/XMLUtil.php208
20 files changed, 3541 insertions, 0 deletions
diff --git a/3rdparty/Sabre/CalDAV/Backend/Abstract.php b/3rdparty/Sabre/CalDAV/Backend/Abstract.php
new file mode 100644
index 00000000000..7ac282f44a0
--- /dev/null
+++ b/3rdparty/Sabre/CalDAV/Backend/Abstract.php
@@ -0,0 +1,166 @@
+<?php
+
+/**
+ * Abstract Calendaring backend. Extend this class to create your own backends.
+ *
+ * @package Sabre
+ * @subpackage CalDAV
+ * @copyright Copyright (C) 2007-2011 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.
+ *
+ * This function must return a server-wide unique id that can be used
+ * later to reference the calendar.
+ *
+ * @param string $principalUri
+ * @param string $calendarUri
+ * @param array $properties
+ * @return string|int
+ */
+ abstract function createCalendar($principalUri,$calendarUri,array $properties);
+
+ /**
+ * Updates properties on this node,
+ *
+ * The properties array uses the propertyName in clark-notation as key,
+ * and the array value for the property value. In the case a property
+ * should be deleted, the property value will be null.
+ *
+ * This method must be atomic. If one property cannot be changed, the
+ * entire operation must fail.
+ *
+ * If the operation was successful, true can be returned.
+ * If the operation failed, false can be returned.
+ *
+ * Deletion of a non-existant property is always succesful.
+ *
+ * Lastly, it is optional to return detailed information about any
+ * failures. In this case an array should be returned with the following
+ * structure:
+ *
+ * array(
+ * 403 => array(
+ * '{DAV:}displayname' => null,
+ * ),
+ * 424 => array(
+ * '{DAV:}owner' => null,
+ * )
+ * )
+ *
+ * In this example it was forbidden to update {DAV:}displayname.
+ * (403 Forbidden), which in turn also caused {DAV:}owner to fail
+ * (424 Failed Dependency) because the request needs to be atomic.
+ *
+ * @param string $calendarId
+ * @param array $properties
+ * @return bool|array
+ */
+ public function updateCalendar($calendarId, array $properties) {
+
+ 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 object.
+ *
+ * Every item contains an array with the following keys:
+ * * id - unique identifier which will be used for subsequent updates
+ * * calendardata - The iCalendar-compatible calnedar data
+ * * uri - a unique key which will be used to construct the uri. This can be any arbitrary string.
+ * * lastmodified - a timestamp of the last modification time
+ * * etag - An arbitrary string, surrounded by double-quotes. (e.g.:
+ * ' "abcdef"')
+ * * calendarid - The calendarid as it was passed to this function.
+ *
+ * Note that the etag is optional, but it's highly encouraged to return for
+ * speed reasons.
+ *
+ * The calendardata is also optional. If it's not returned
+ * 'getCalendarObject' will be called later, which *is* expected to return
+ * calendardata.
+ *
+ * @param string $calendarId
+ * @return array
+ */
+ 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 100644
index 00000000000..d1785bb8f8f
--- /dev/null
+++ b/3rdparty/Sabre/CalDAV/Backend/PDO.php
@@ -0,0 +1,387 @@
+<?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-2011 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
+ */
+ 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 = ?");
+ $stmt->execute(array($principalUri));
+
+ $calendars = array();
+ while($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+
+ $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 mixed
+ */
+ public function createCalendar($principalUri,$calendarUri, array $properties) {
+
+ $fieldNames = array(
+ 'principaluri',
+ 'uri',
+ 'ctag',
+ );
+ $values = array(
+ ':principaluri' => $principalUri,
+ ':uri' => $calendarUri,
+ ':ctag' => 1,
+ );
+
+ // Default value
+ $sccs = '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set';
+ $fieldNames[] = 'components';
+ if (!isset($properties[$sccs])) {
+ $values[':components'] = 'VEVENT,VTODO';
+ } else {
+ if (!($properties[$sccs] instanceof Sabre_CalDAV_Property_SupportedCalendarComponentSet)) {
+ throw new Sabre_DAV_Exception('The ' . $sccs . ' property must be of type: Sabre_CalDAV_Property_SupportedCalendarComponentSet');
+ }
+ $values[':components'] = implode(',',$properties[$sccs]->getValue());
+ }
+
+ foreach($this->propertyMap as $xmlName=>$dbName) {
+ if (isset($properties[$xmlName])) {
+
+ $myValue = $properties[$xmlName];
+ $values[':' . $dbName] = $properties[$xmlName];
+ $fieldNames[] = $dbName;
+ }
+ }
+
+ $stmt = $this->pdo->prepare("INSERT INTO `".$this->calendarTableName."` (".implode(', ', $fieldNames).") VALUES (".implode(', ',array_keys($values)).")");
+ $stmt->execute($values);
+
+ return $this->pdo->lastInsertId();
+
+ }
+
+ /**
+ * Updates a calendars properties
+ *
+ * The properties array uses the propertyName in clark-notation as key,
+ * and the array value for the property value. In the case a property
+ * should be deleted, the property value will be null.
+ *
+ * This method must be atomic. If one property cannot be changed, the
+ * entire operation must fail.
+ *
+ * If the operation was successful, true can be returned.
+ * If the operation failed, false can be returned.
+ *
+ * Deletion of a non-existant property is always succesful.
+ *
+ * Lastly, it is optional to return detailed information about any
+ * failures. In this case an array should be returned with the following
+ * structure:
+ *
+ * array(
+ * 403 => array(
+ * '{DAV:}displayname' => null,
+ * ),
+ * 424 => array(
+ * '{DAV:}owner' => null,
+ * )
+ * )
+ *
+ * In this example it was forbidden to update {DAV:}displayname.
+ * (403 Forbidden), which in turn also caused {DAV:}owner to fail
+ * (424 Failed Dependency) because the request needs to be atomic.
+ *
+ * @param string $calendarId
+ * @param array $properties
+ * @return bool|array
+ */
+ public function updateCalendar($calendarId, array $properties) {
+
+ $newValues = array();
+ $result = array(
+ 200 => array(), // Ok
+ 403 => array(), // Forbidden
+ 424 => array(), // Failed Dependency
+ );
+
+ $hasError = false;
+
+ foreach($properties as $propertyName=>$propertyValue) {
+
+ // We don't know about this property.
+ if (!isset($this->propertyMap[$propertyName])) {
+ $hasError = true;
+ $result[403][$propertyName] = null;
+ unset($properties[$propertyName]);
+ continue;
+ }
+
+ $fieldName = $this->propertyMap[$propertyName];
+ $newValues[$fieldName] = $propertyValue;
+
+ }
+
+ // If there were any errors we need to fail the request
+ if ($hasError) {
+ // Properties has the remaining properties
+ foreach($properties as $propertyName=>$propertyValue) {
+ $result[424][$propertyName] = null;
+ }
+
+ // Removing unused statuscodes for cleanliness
+ foreach($result as $status=>$properties) {
+ if (is_array($properties) && count($properties)===0) unset($result[$status]);
+ }
+
+ return $result;
+
+ }
+
+ // Success
+
+ // Now we're generating the sql query.
+ $valuesSql = array();
+ foreach($newValues as $fieldName=>$value) {
+ $valuesSql[] = $fieldName . ' = ?';
+ }
+ $valuesSql[] = '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 object.
+ *
+ * Every item contains an array with the following keys:
+ * * id - unique identifier which will be used for subsequent updates
+ * * calendardata - The iCalendar-compatible calnedar data
+ * * uri - a unique key which will be used to construct the uri. This can be any arbitrary string.
+ * * lastmodified - a timestamp of the last modification time
+ * * etag - An arbitrary string, surrounded by double-quotes. (e.g.:
+ * ' "abcdef"')
+ * * calendarid - The calendarid as it was passed to this function.
+ *
+ * Note that the etag is optional, but it's highly encouraged to return for
+ * speed reasons.
+ *
+ * The calendardata is also optional. If it's not returned
+ * 'getCalendarObject' will be called later, which *is* expected to return
+ * calendardata.
+ *
+ * @param string $calendarId
+ * @return array
+ */
+ public function getCalendarObjects($calendarId) {
+
+ $stmt = $this->pdo->prepare('SELECT * FROM `'.$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 100644
index 00000000000..a50aef12b4f
--- /dev/null
+++ b/3rdparty/Sabre/CalDAV/Calendar.php
@@ -0,0 +1,316 @@
+<?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-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_CalDAV_Calendar implements Sabre_DAV_ICollection, Sabre_DAV_IProperties, 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_CalDAV_Backend_Abstract $caldavBackend
+ * @param array $calendarInfo
+ * @return void
+ */
+ 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 $properties
+ * @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_FileNotFound('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 void
+ */
+ public function createFile($name,$calendarData = null) {
+
+ $calendarData = stream_get_contents($calendarData);
+
+ $supportedComponents = $this->calendarInfo['{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}supported-calendar-component-set'];
+ if ($supportedComponents) {
+ $supportedComponents = $supportedComponents->getValue();
+ } else {
+ $supportedComponents = null;
+ }
+ Sabre_CalDAV_ICalendarUtil::validateICalendarObject($calendarData, $supportedComponents);
+
+ $this->caldavBackend->createCalendarObject($this->calendarInfo['id'],$name,$calendarData);
+
+ }
+
+ /**
+ * 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,
+ ),
+
+ );
+
+ }
+
+ /**
+ * Updates the ACL
+ *
+ * This method will receive a list of new ACE's.
+ *
+ * @param array $acl
+ * @return void
+ */
+ public function setACL(array $acl) {
+
+ throw new Sabre_DAV_Exception_MethodNotAllowed('Changing ACL is not yet supported');
+
+ }
+
+
+
+}
diff --git a/3rdparty/Sabre/CalDAV/CalendarObject.php b/3rdparty/Sabre/CalDAV/CalendarObject.php
new file mode 100644
index 00000000000..b5c4e49b838
--- /dev/null
+++ b/3rdparty/Sabre/CalDAV/CalendarObject.php
@@ -0,0 +1,257 @@
+<?php
+
+/**
+ * The CalendarObject represents a single VEVENT or VTODO within a Calendar.
+ *
+ * @package Sabre
+ * @subpackage CalDAV
+ * @copyright Copyright (C) 2007-2011 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_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);
+
+ $supportedComponents = $this->calendarInfo['{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}supported-calendar-component-set'];
+ if ($supportedComponents) {
+ $supportedComponents = $supportedComponents->getValue();
+ } else {
+ $supportedComponents = null;
+ }
+ Sabre_CalDAV_ICalendarUtil::validateICalendarObject($calendarData, $supportedComponents);
+
+ $this->caldavBackend->updateCalendarObject($this->calendarInfo['id'],$this->objectData['uri'],$calendarData);
+ $this->objectData['calendardata'] = $calendarData;
+
+ }
+
+ /**
+ * 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 arbritrary 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() {
+
+ return strlen($this->objectData['calendardata']);
+
+ }
+
+ /**
+ * 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');
+
+ }
+
+
+}
+
diff --git a/3rdparty/Sabre/CalDAV/CalendarRootNode.php b/3rdparty/Sabre/CalDAV/CalendarRootNode.php
new file mode 100644
index 00000000000..69669a9d7fd
--- /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-2011 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 void
+ */
+ 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/Exception/InvalidICalendarObject.php b/3rdparty/Sabre/CalDAV/Exception/InvalidICalendarObject.php
new file mode 100644
index 00000000000..656b7286a66
--- /dev/null
+++ b/3rdparty/Sabre/CalDAV/Exception/InvalidICalendarObject.php
@@ -0,0 +1,18 @@
+<?php
+
+/**
+ * InvalidICalendarObject
+ *
+ * This exception is thrown when an attempt is made to create or update
+ * an invalid ICalendar object
+ *
+ * @package Sabre
+ * @subpackage CalDAV
+ * @copyright Copyright (C) 2007-2011 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_Exception_InvalidICalendarObject extends Sabre_DAV_Exception_PreconditionFailed {
+
+
+}
diff --git a/3rdparty/Sabre/CalDAV/ICSExportPlugin.php b/3rdparty/Sabre/CalDAV/ICSExportPlugin.php
new file mode 100644
index 00000000000..4e2c6eb93d2
--- /dev/null
+++ b/3rdparty/Sabre/CalDAV/ICSExportPlugin.php
@@ -0,0 +1,124 @@
+<?php
+
+/**
+ * ICS Exporter
+ *
+ * This plugin adds the ability to export entire calendars as .ics files.
+ * This is useful for clients that don't support CalDAV yet. They often do
+ * support ics files.
+ *
+ * @package Sabre
+ * @subpackage CalDAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_CalDAV_ICSExportPlugin extends Sabre_DAV_ServerPlugin {
+
+ /**
+ * Reference to Server class
+ *
+ * @var Sabre_DAV_Server
+ */
+ private $server;
+
+ /**
+ * Initializes the plugin and registers event handlers
+ *
+ * @param Sabre_DAV_Server $server
+ * @return void
+ */
+ public function initialize(Sabre_DAV_Server $server) {
+
+ $this->server = $server;
+ $this->server->subscribeEvent('beforeMethod',array($this,'beforeMethod'), 90);
+
+ }
+
+ /**
+ * 'beforeMethod' event handles. This event handles intercepts GET requests ending
+ * with ?export
+ *
+ * @param string $method
+ * @param string $uri
+ * @return void
+ */
+ public function beforeMethod($method, $uri) {
+
+ if ($method!='GET') return;
+ if ($this->server->httpRequest->getQueryString()!='export') return;
+
+ // splitting uri
+ list($uri) = explode('?',$uri,2);
+
+ $node = $this->server->tree->getNodeForPath($uri);
+
+ if (!($node instanceof Sabre_CalDAV_Calendar)) return;
+
+ $this->server->httpResponse->setHeader('Content-Type','text/calendar');
+ $this->server->httpResponse->sendStatus(200);
+ $this->server->httpResponse->sendBody($this->generateICS($this->server->tree->getChildren($uri)));
+
+ // Returning false to break the event chain
+ return false;
+
+ }
+
+ /**
+ * Merges all calendar objects, and builds one big ics export
+ *
+ * @param array $nodes
+ * @return void
+ */
+ public function generateICS(array $nodes) {
+
+ $calendar = new Sabre_VObject_Component('vcalendar');
+ $calendar->version = '2.0';
+ $calendar->prodid = '-//SabreDAV//SabreDAV ' . Sabre_DAV_Version::VERSION . '//EN';
+ $calendar->calscale = 'GREGORIAN';
+
+ $collectedTimezones = array();
+
+ $timezones = array();
+ $objects = array();
+
+ foreach($nodes as $node) {
+
+ $nodeData = $node->get();
+ $nodeComp = Sabre_VObject_Reader::read($nodeData);
+
+ foreach($nodeComp->children() as $child) {
+
+ switch($child->name) {
+ case 'VEVENT' :
+ case 'VTODO' :
+ case 'VJOURNAL' :
+ $objects[] = $child;
+ break;
+
+ // VTIMEZONE is special, because we need to filter out the duplicates
+ case 'VTIMEZONE' :
+ // Naively just checking tzid.
+ if (in_array((string)$child->TZID, $collectedTimezones)) continue;
+
+ $timezones[] = $child;
+ $collectedTimezones[] = $child->TZID;
+ break;
+
+
+ }
+
+
+ }
+
+
+ }
+
+ foreach($timezones as $tz) $calendar->add($tz);
+ foreach($objects as $obj) $calendar->add($obj);
+
+ return $calendar->serialize();
+
+ }
+
+}
diff --git a/3rdparty/Sabre/CalDAV/ICalendarUtil.php b/3rdparty/Sabre/CalDAV/ICalendarUtil.php
new file mode 100644
index 00000000000..699abfdd6f5
--- /dev/null
+++ b/3rdparty/Sabre/CalDAV/ICalendarUtil.php
@@ -0,0 +1,157 @@
+<?php
+
+/**
+ * This class contains several utilities related to the ICalendar (rfc2445) format
+ *
+ * This class is now deprecated, and won't be further maintained. Please use
+ * the Sabre_VObject package for your ics parsing needs.
+ *
+ * @package Sabre
+ * @subpackage CalDAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ * @deprecated Use Sabre_VObject instead.
+ */
+class Sabre_CalDAV_ICalendarUtil {
+
+ /**
+ * Validates an ICalendar object
+ *
+ * This method makes sure this ICalendar object is properly formatted.
+ * If we can't parse it, we'll throw exceptions.
+ *
+ * @param string $icalData
+ * @param array $allowedComponents
+ * @return bool
+ */
+ static function validateICalendarObject($icalData, array $allowedComponents = null) {
+
+ $xcal = simplexml_load_string(self::toXCal($icalData));
+ if (!$xcal) throw new Sabre_CalDAV_Exception_InvalidICalendarObject('Invalid calendarobject format');
+
+ $xcal->registerXPathNameSpace('cal','urn:ietf:params:xml:ns:xcal');
+
+ // Check if there's only 1 component
+ $components = array('vevent','vtodo','vjournal','vfreebusy');
+ $componentsFound = array();
+
+ foreach($components as $component) {
+ $test = $xcal->xpath('/cal:iCalendar/cal:vcalendar/cal:' . $component);
+ if (is_array($test)) $componentsFound = array_merge($componentsFound, $test);
+ }
+ if (count($componentsFound)<1) {
+ throw new Sabre_CalDAV_Exception_InvalidICalendarObject('One VEVENT, VTODO, VJOURNAL or VFREEBUSY must be specified. 0 found.');
+ }
+ $component = $componentsFound[0];
+
+ if (is_null($allowedComponents)) return true;
+
+ // Check if the component is allowed
+ $name = $component->getName();
+ if (!in_array(strtoupper($name),$allowedComponents)) {
+ throw new Sabre_CalDAV_Exception_InvalidICalendarObject(strtoupper($name) . ' is not allowed in this calendar.');
+ }
+
+ if (count($xcal->xpath('/cal:iCalendar/cal:vcalendar/cal:method'))>0) {
+ throw new Sabre_CalDAV_Exception_InvalidICalendarObject('The METHOD property is not allowed in calendar objects');
+ }
+
+ return true;
+
+ }
+
+ /**
+ * Converts ICalendar data to XML.
+ *
+ * Properties are converted to lowercase xml elements. Parameters are;
+ * converted to attributes. BEGIN:VEVENT is converted to <vevent> and
+ * END:VEVENT </vevent> as well as other components.
+ *
+ * It's a very loose parser. If any line does not conform to the spec, it
+ * will simply be ignored. It will try to detect if \r\n or \n line endings
+ * are used.
+ *
+ * @todo Currently quoted attributes are not parsed correctly.
+ * @see http://tools.ietf.org/html/draft-royer-calsch-xcal-03
+ * @param string $icalData
+ * @return string.
+ */
+ static function toXCAL($icalData) {
+
+ // Detecting line endings
+ $lb="\r\n";
+ if (strpos($icalData,"\r\n")!==false) $lb = "\r\n";
+ elseif (strpos($icalData,"\n")!==false) $lb = "\n";
+
+ // Splitting up items per line
+ $lines = explode($lb,$icalData);
+
+ // Properties can be folded over 2 lines. In this case the second
+ // line will be preceeded by a space or tab.
+ $lines2 = array();
+ foreach($lines as $line) {
+
+ if (!$line) continue;
+ if ($line[0]===" " || $line[0]==="\t") {
+ $lines2[count($lines2)-1].=substr($line,1);
+ continue;
+ }
+
+ $lines2[]=$line;
+
+ }
+
+ $xml = '<?xml version="1.0"?>' . "\n";
+ $xml.= "<iCalendar xmlns=\"urn:ietf:params:xml:ns:xcal\">\n";
+
+ $spaces = 2;
+ foreach($lines2 as $line) {
+
+ $matches = array();
+ // This matches PROPERTYNAME;ATTRIBUTES:VALUE
+ if (!preg_match('/^([^:^;]*)(?:;([^:]*))?:(.*)$/',$line,$matches))
+ continue;
+
+ $propertyName = strtolower($matches[1]);
+ $attributes = $matches[2];
+ $value = $matches[3];
+
+ // If the line was in the format BEGIN:COMPONENT or END:COMPONENT, we need to special case it.
+ if ($propertyName === 'begin') {
+ $xml.=str_repeat(" ",$spaces);
+ $xml.='<' . strtolower($value) . ">\n";
+ $spaces+=2;
+ continue;
+ } elseif ($propertyName === 'end') {
+ $spaces-=2;
+ $xml.=str_repeat(" ",$spaces);
+ $xml.='</' . strtolower($value) . ">\n";
+ continue;
+ }
+
+ $xml.=str_repeat(" ",$spaces);
+ $xml.='<' . $propertyName;
+ if ($attributes) {
+ // There can be multiple attributes
+ $attributes = explode(';',$attributes);
+ foreach($attributes as $att) {
+
+ list($attName,$attValue) = explode('=',$att,2);
+ $attName = strtolower($attName);
+ if ($attName === 'language') $attName='xml:lang';
+ $xml.=' ' . $attName . '="' . htmlspecialchars($attValue) . '"';
+
+ }
+ }
+
+ $xml.='>'. htmlspecialchars(trim($value)) . '</' . $propertyName . ">\n";
+
+ }
+ $xml.="</iCalendar>";
+ return $xml;
+
+ }
+
+}
+
diff --git a/3rdparty/Sabre/CalDAV/Plugin.php b/3rdparty/Sabre/CalDAV/Plugin.php
new file mode 100644
index 00000000000..640595e74ba
--- /dev/null
+++ b/3rdparty/Sabre/CalDAV/Plugin.php
@@ -0,0 +1,788 @@
+<?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-2011 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;
+
+ /**
+ * 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_FileNotFound $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);
+ if ($node instanceof Sabre_CalDAV_Calendar || $node instanceof Sabre_CalDAV_CalendarObject) {
+ return array(
+ '{' . self::NS_CALDAV . '}calendar-multiget',
+ '{' . self::NS_CALDAV . '}calendar-query',
+ );
+ }
+ return array();
+
+ }
+
+ /**
+ * 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->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_Calendar'] = '{urn:ietf:params:xml:ns:caldav}calendar';
+ $server->resourceTypeMapping['Sabre_CalDAV_Principal_ProxyRead'] = '{http://calendarserver.org/ns/}calendar-proxy-read';
+ $server->resourceTypeMapping['Sabre_CalDAV_Principal_ProxyWrite'] = '{http://calendarserver.org/ns/}calendar-proxy-write';
+
+ array_push($server->protectedProperties,
+
+ '{' . self::NS_CALDAV . '}supported-calendar-component-set',
+ '{' . 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 . '}calendar-user-address-set',
+
+ // 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
+ * @return bool
+ */
+ public function unknownMethod($method, $uri) {
+
+ if ($method!=='MKCALENDAR') return;
+
+ $this->httpMkCalendar($uri);
+ // false is returned to stop the unknownMethod event
+ 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;
+
+ }
+
+
+ }
+
+ /**
+ * 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);
+ }
+
+ // 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_CalendarObject) {
+ // 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');
+ foreach($hrefElems as $elem) {
+ $uri = $this->server->calculateUri($elem->nodeValue);
+ list($objProps) = $this->server->getPropertiesForPath($uri,$properties);
+ $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) {
+
+ $requestedProperties = array_keys(Sabre_DAV_XMLUtil::parseProperties($dom->firstChild));
+
+ $filterNode = $dom->getElementsByTagNameNS('urn:ietf:params:xml:ns:caldav','filter');
+ if ($filterNode->length!==1) {
+ throw new Sabre_DAV_Exception_BadRequest('The calendar-query report must have a filter element');
+ }
+ $filters = Sabre_CalDAV_XMLUtil::parseCalendarQueryFilters($filterNode->item(0));
+
+ $requestedCalendarData = true;
+
+ 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();
+
+ 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;
+
+ if ($this->validateFilters($node[200]['{urn:ietf:params:xml:ns:caldav}calendar-data'],$filters)) {
+
+ if (!$requestedCalendarData) {
+ unset($node[200]['{urn:ietf:params:xml:ns:caldav}calendar-data']);
+ }
+ $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));
+
+ }
+
+
+ /**
+ * Verify if a list of filters applies to the calendar data object
+ *
+ * The calendarData object must be a valid iCalendar blob. The list of
+ * filters must be formatted as parsed by Sabre_CalDAV_Plugin::parseCalendarQueryFilters
+ *
+ * @param string $calendarData
+ * @param array $filters
+ * @return bool
+ */
+ public function validateFilters($calendarData,$filters) {
+
+ // We are converting the calendar object to an XML structure
+ // This makes it far easier to parse
+ $xCalendarData = Sabre_CalDAV_ICalendarUtil::toXCal($calendarData);
+ $xml = simplexml_load_string($xCalendarData);
+ $xml->registerXPathNamespace('c','urn:ietf:params:xml:ns:xcal');
+
+ foreach($filters as $xpath=>$filter) {
+
+ // if-not-defined comes first
+ if (isset($filter['is-not-defined'])) {
+ if (!$xml->xpath($xpath))
+ continue;
+ else
+ return false;
+
+ }
+
+ $elem = $xml->xpath($xpath);
+
+ if (!$elem) return false;
+ $elem = $elem[0];
+
+ if (isset($filter['time-range'])) {
+
+ switch($elem->getName()) {
+ case 'vevent' :
+ $result = $this->validateTimeRangeFilterForEvent($xml,$xpath,$filter);
+ if ($result===false) return false;
+ break;
+ case 'vtodo' :
+ $result = $this->validateTimeRangeFilterForTodo($xml,$xpath,$filter);
+ if ($result===false) return false;
+ break;
+ case 'vjournal' :
+ case 'vfreebusy' :
+ case 'valarm' :
+ // TODO: not implemented
+ break;
+
+ /*
+
+ case 'vjournal' :
+ $result = $this->validateTimeRangeFilterForJournal($xml,$xpath,$filter);
+ if ($result===false) return false;
+ break;
+ case 'vfreebusy' :
+ $result = $this->validateTimeRangeFilterForFreeBusy($xml,$xpath,$filter);
+ if ($result===false) return false;
+ break;
+ case 'valarm' :
+ $result = $this->validateTimeRangeFilterForAlarm($xml,$xpath,$filter);
+ if ($result===false) return false;
+ break;
+
+ */
+
+ }
+
+ }
+
+ if (isset($filter['text-match'])) {
+ $currentString = (string)$elem;
+
+ $isMatching = Sabre_DAV_StringUtil::textMatch($currentString, $filter['text-match']['value'], $filter['text-match']['collation']);
+ if ($filter['text-match']['negate-condition'] && $isMatching) return false;
+ if (!$filter['text-match']['negate-condition'] && !$isMatching) return false;
+
+ }
+
+ }
+ return true;
+
+ }
+
+ /**
+ * Checks whether a time-range filter matches an event.
+ *
+ * @param SimpleXMLElement $xml Event as xml object
+ * @param string $currentXPath XPath to check
+ * @param array $currentFilter Filter information
+ * @return void
+ */
+ private function validateTimeRangeFilterForEvent(SimpleXMLElement $xml,$currentXPath,array $currentFilter) {
+
+ // Grabbing the DTSTART property
+ $xdtstart = $xml->xpath($currentXPath.'/c:dtstart');
+ if (!count($xdtstart)) {
+ throw new Sabre_DAV_Exception_BadRequest('DTSTART property missing from calendar object');
+ }
+
+ // The dtstart can be both a date, or datetime property
+ if ((string)$xdtstart[0]['value']==='DATE' || strlen((string)$xdtstart[0])===8) {
+ $isDateTime = false;
+ } else {
+ $isDateTime = true;
+ }
+
+ // Determining the timezone
+ if ($tzid = (string)$xdtstart[0]['tzid']) {
+ $tz = new DateTimeZone($tzid);
+ } else {
+ $tz = null;
+ }
+ if ($isDateTime) {
+ $dtstart = Sabre_CalDAV_XMLUtil::parseICalendarDateTime((string)$xdtstart[0],$tz);
+ } else {
+ $dtstart = Sabre_CalDAV_XMLUtil::parseICalendarDate((string)$xdtstart[0]);
+ }
+
+
+ // Grabbing the DTEND property
+ $xdtend = $xml->xpath($currentXPath.'/c:dtend');
+ $dtend = null;
+
+ if (count($xdtend)) {
+ // Determining the timezone
+ if ($tzid = (string)$xdtend[0]['tzid']) {
+ $tz = new DateTimeZone($tzid);
+ } else {
+ $tz = null;
+ }
+
+ // Since the VALUE prameter of both DTSTART and DTEND must be the same
+ // we can assume we don't need to check the VALUE paramter of DTEND.
+ if ($isDateTime) {
+ $dtend = Sabre_CalDAV_XMLUtil::parseICalendarDateTime((string)$xdtend[0],$tz);
+ } else {
+ $dtend = Sabre_CalDAV_XMLUtil::parseICalendarDate((string)$xdtend[0],$tz);
+ }
+
+ }
+
+ if (is_null($dtend)) {
+ // The DTEND property was not found. We will first see if the event has a duration
+ // property
+
+ $xduration = $xml->xpath($currentXPath.'/c:duration');
+ if (count($xduration)) {
+ $duration = Sabre_CalDAV_XMLUtil::parseICalendarDuration((string)$xduration[0]);
+
+ // Making sure that the duration is bigger than 0 seconds.
+ $tempDT = clone $dtstart;
+ $tempDT->modify($duration);
+ if ($tempDT > $dtstart) {
+
+ // use DTEND = DTSTART + DURATION
+ $dtend = $tempDT;
+ } else {
+ // use DTEND = DTSTART
+ $dtend = $dtstart;
+ }
+
+ }
+ }
+
+ if (is_null($dtend)) {
+ if ($isDateTime) {
+ // DTEND = DTSTART
+ $dtend = $dtstart;
+ } else {
+ // DTEND = DTSTART + 1 DAY
+ $dtend = clone $dtstart;
+ $dtend->modify('+1 day');
+ }
+ }
+ // TODO: we need to properly parse RRULE's, but it's very difficult.
+ // For now, we're always returning events if they have an RRULE at all.
+ $rrule = $xml->xpath($currentXPath.'/c:rrule');
+ $hasRrule = (count($rrule))>0;
+
+ if (!is_null($currentFilter['time-range']['start']) && $currentFilter['time-range']['start'] >= $dtend) return false;
+ if (!is_null($currentFilter['time-range']['end']) && $currentFilter['time-range']['end'] <= $dtstart && !$hasRrule) return false;
+ return true;
+
+ }
+
+ private function validateTimeRangeFilterForTodo(SimpleXMLElement $xml,$currentXPath,array $filter) {
+
+ // Gathering all relevant elements
+
+ $dtStart = null;
+ $duration = null;
+ $due = null;
+ $completed = null;
+ $created = null;
+
+ $xdt = $xml->xpath($currentXPath.'/c:dtstart');
+ if (count($xdt)) {
+ // The dtstart can be both a date, or datetime property
+ if ((string)$xdt[0]['value']==='DATE') {
+ $isDateTime = false;
+ } else {
+ $isDateTime = true;
+ }
+
+ // Determining the timezone
+ if ($tzid = (string)$xdt[0]['tzid']) {
+ $tz = new DateTimeZone($tzid);
+ } else {
+ $tz = null;
+ }
+ if ($isDateTime) {
+ $dtStart = Sabre_CalDAV_XMLUtil::parseICalendarDateTime((string)$xdt[0],$tz);
+ } else {
+ $dtStart = Sabre_CalDAV_XMLUtil::parseICalendarDate((string)$xdt[0]);
+ }
+ }
+
+ // Only need to grab duration if dtStart is set
+ if (!is_null($dtStart)) {
+
+ $xduration = $xml->xpath($currentXPath.'/c:duration');
+ if (count($xduration)) {
+ $duration = Sabre_CalDAV_XMLUtil::parseICalendarDuration((string)$xduration[0]);
+ }
+
+ }
+
+ if (!is_null($dtStart) && !is_null($duration)) {
+
+ // Comparision from RFC 4791:
+ // (start <= DTSTART+DURATION) AND ((end > DTSTART) OR (end >= DTSTART+DURATION))
+
+ $end = clone $dtStart;
+ $end->modify($duration);
+
+ if( (is_null($filter['time-range']['start']) || $filter['time-range']['start'] <= $end) &&
+ (is_null($filter['time-range']['end']) || $filter['time-range']['end'] > $dtStart || $filter['time-range']['end'] >= $end) ) {
+ return true;
+ } else {
+ return false;
+ }
+
+ }
+
+ // Need to grab the DUE property
+ $xdt = $xml->xpath($currentXPath.'/c:due');
+ if (count($xdt)) {
+ // The due property can be both a date, or datetime property
+ if ((string)$xdt[0]['value']==='DATE') {
+ $isDateTime = false;
+ } else {
+ $isDateTime = true;
+ }
+ // Determining the timezone
+ if ($tzid = (string)$xdt[0]['tzid']) {
+ $tz = new DateTimeZone($tzid);
+ } else {
+ $tz = null;
+ }
+ if ($isDateTime) {
+ $due = Sabre_CalDAV_XMLUtil::parseICalendarDateTime((string)$xdt[0],$tz);
+ } else {
+ $due = Sabre_CalDAV_XMLUtil::parseICalendarDate((string)$xdt[0]);
+ }
+ }
+
+ if (!is_null($dtStart) && !is_null($due)) {
+
+ // Comparision from RFC 4791:
+ // ((start < DUE) OR (start <= DTSTART)) AND ((end > DTSTART) OR (end >= DUE))
+
+ if( (is_null($filter['time-range']['start']) || $filter['time-range']['start'] < $due || $filter['time-range']['start'] < $dtstart) &&
+ (is_null($filter['time-range']['end']) || $filter['time-range']['end'] >= $due) ) {
+ return true;
+ } else {
+ return false;
+ }
+
+ }
+
+ if (!is_null($dtStart)) {
+
+ // Comparision from RFC 4791
+ // (start <= DTSTART) AND (end > DTSTART)
+ if ( (is_null($filter['time-range']['start']) || $filter['time-range']['start'] <= $dtStart) &&
+ (is_null($filter['time-range']['end']) || $filter['time-range']['end'] > $dtStart) ) {
+ return true;
+ } else {
+ return false;
+ }
+
+ }
+
+ if (!is_null($due)) {
+
+ // Comparison from RFC 4791
+ // (start < DUE) AND (end >= DUE)
+ if ( (is_null($filter['time-range']['start']) || $filter['time-range']['start'] < $due) &&
+ (is_null($filter['time-range']['end']) || $filter['time-range']['end'] >= $due) ) {
+ return true;
+ } else {
+ return false;
+ }
+
+ }
+ // Need to grab the COMPLETED property
+ $xdt = $xml->xpath($currentXPath.'/c:completed');
+ if (count($xdt)) {
+ $completed = Sabre_CalDAV_XMLUtil::parseICalendarDateTime((string)$xdt[0]);
+ }
+ // Need to grab the CREATED property
+ $xdt = $xml->xpath($currentXPath.'/c:created');
+ if (count($xdt)) {
+ $created = Sabre_CalDAV_XMLUtil::parseICalendarDateTime((string)$xdt[0]);
+ }
+
+ if (!is_null($completed) && !is_null($created)) {
+ // Comparison from RFC 4791
+ // ((start <= CREATED) OR (start <= COMPLETED)) AND ((end >= CREATED) OR (end >= COMPLETED))
+ if( (is_null($filter['time-range']['start']) || $filter['time-range']['start'] <= $created || $filter['time-range']['start'] <= $completed) &&
+ (is_null($filter['time-range']['end']) || $filter['time-range']['end'] >= $created || $filter['time-range']['end'] >= $completed)) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ if (!is_null($completed)) {
+ // Comparison from RFC 4791
+ // (start <= COMPLETED) AND (end >= COMPLETED)
+ if( (is_null($filter['time-range']['start']) || $filter['time-range']['start'] <= $completed) &&
+ (is_null($filter['time-range']['end']) || $filter['time-range']['end'] >= $completed)) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ if (!is_null($created)) {
+ // Comparison from RFC 4791
+ // (end > CREATED)
+ if( (is_null($filter['time-range']['end']) || $filter['time-range']['end'] > $created) ) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ // Everything else is TRUE
+ return true;
+
+ }
+
+}
diff --git a/3rdparty/Sabre/CalDAV/Principal/Collection.php b/3rdparty/Sabre/CalDAV/Principal/Collection.php
new file mode 100644
index 00000000000..13435b2448e
--- /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-2011 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 100644
index 00000000000..f531d85d1ff
--- /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-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_CalDAV_Principal_ProxyRead implements Sabre_DAVACL_IPrincipal {
+
+ /**
+ * Principal information from the parent principal.
+ *
+ * @var array
+ */
+ protected $principalInfo;
+
+ /**
+ * Principal backend
+ *
+ * @var Sabre_DAVACL_IPrincipalBackend
+ */
+ protected $principalBackend;
+
+ /**
+ * Creates the object.
+ *
+ * Note that you MUST supply the parent principal information.
+ *
+ * @param Sabre_DAVACL_IPrincipalBackend $principalBackend
+ * @param array $principalInfo
+ */
+ public function __construct(Sabre_DAVACL_IPrincipalBackend $principalBackend, array $principalInfo) {
+
+ $this->principalInfo = $principalInfo;
+ $this->principalBackend = $principalBackend;
+
+ }
+
+ /**
+ * Returns this principals name.
+ *
+ * @return string
+ */
+ public function getName() {
+
+ return 'calendar-proxy-read';
+
+ }
+
+ /**
+ * Returns the last modification time
+ *
+ * @return null
+ */
+ public function getLastModified() {
+
+ return null;
+
+ }
+
+ /**
+ * Deletes the current node
+ *
+ * @throws Sabre_DAV_Exception_Forbidden
+ * @return void
+ */
+ public function delete() {
+
+ throw new Sabre_DAV_Exception_Forbidden('Permission denied to delete node');
+
+ }
+
+ /**
+ * Renames the node
+ *
+ * @throws Sabre_DAV_Exception_Forbidden
+ * @param string $name The new name
+ * @return void
+ */
+ public function setName($name) {
+
+ throw new Sabre_DAV_Exception_Forbidden('Permission denied to rename file');
+
+ }
+
+
+ /**
+ * Returns a list of altenative urls for a principal
+ *
+ * This can for example be an email address, or ldap url.
+ *
+ * @return array
+ */
+ public function getAlternateUriSet() {
+
+ return array();
+
+ }
+
+ /**
+ * Returns the full principal url
+ *
+ * @return string
+ */
+ public function getPrincipalUrl() {
+
+ return $this->principalInfo['uri'] . '/' . $this->getName();
+
+ }
+
+ /**
+ * Returns the list of group members
+ *
+ * If this principal is a group, this function should return
+ * all member principal uri's for the group.
+ *
+ * @return array
+ */
+ public function getGroupMemberSet() {
+
+ return $this->principalBackend->getGroupMemberSet($this->getPrincipalUrl());
+
+ }
+
+ /**
+ * Returns the list of groups this principal is member of
+ *
+ * If this principal is a member of a (list of) groups, this function
+ * should return a list of principal uri's for it's members.
+ *
+ * @return array
+ */
+ public function getGroupMembership() {
+
+ return $this->principalBackend->getGroupMembership($this->getPrincipalUrl());
+
+ }
+
+ /**
+ * Sets a list of group members
+ *
+ * If this principal is a group, this method sets all the group members.
+ * The list of members is always overwritten, never appended to.
+ *
+ * This method should throw an exception if the members could not be set.
+ *
+ * @param array $principals
+ * @return void
+ */
+ public function setGroupMemberSet(array $principals) {
+
+ $this->principalBackend->setGroupMemberSet($this->getPrincipalUrl(), $principals);
+
+ }
+
+ /**
+ * Returns the displayname
+ *
+ * This should be a human readable name for the principal.
+ * If none is available, return the nodename.
+ *
+ * @return string
+ */
+ public function getDisplayName() {
+
+ return $this->getName();
+
+ }
+
+}
diff --git a/3rdparty/Sabre/CalDAV/Principal/ProxyWrite.php b/3rdparty/Sabre/CalDAV/Principal/ProxyWrite.php
new file mode 100644
index 00000000000..4d8face2060
--- /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-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_CalDAV_Principal_ProxyWrite implements Sabre_DAVACL_IPrincipal {
+
+ /**
+ * Parent principal information
+ *
+ * @var array
+ */
+ protected $principalInfo;
+
+ /**
+ * Principal Backend
+ *
+ * @var Sabre_DAVACL_IPrincipalBackend
+ */
+ protected $principalBackend;
+
+ /**
+ * Creates the object
+ *
+ * Note that you MUST supply the parent principal information.
+ *
+ * @param Sabre_DAVACL_IPrincipalBackend $principalBackend
+ * @param array $principalInfo
+ */
+ public function __construct(Sabre_DAVACL_IPrincipalBackend $principalBackend, array $principalInfo) {
+
+ $this->principalInfo = $principalInfo;
+ $this->principalBackend = $principalBackend;
+
+ }
+
+ /**
+ * Returns this principals name.
+ *
+ * @return string
+ */
+ public function getName() {
+
+ return 'calendar-proxy-write';
+
+ }
+
+ /**
+ * Returns the last modification time
+ *
+ * @return null
+ */
+ public function getLastModified() {
+
+ return null;
+
+ }
+
+ /**
+ * Deletes the current node
+ *
+ * @throws Sabre_DAV_Exception_Forbidden
+ * @return void
+ */
+ public function delete() {
+
+ throw new Sabre_DAV_Exception_Forbidden('Permission denied to delete node');
+
+ }
+
+ /**
+ * Renames the node
+ *
+ * @throws Sabre_DAV_Exception_Forbidden
+ * @param string $name The new name
+ * @return void
+ */
+ public function setName($name) {
+
+ throw new Sabre_DAV_Exception_Forbidden('Permission denied to rename file');
+
+ }
+
+
+ /**
+ * Returns a list of altenative urls for a principal
+ *
+ * This can for example be an email address, or ldap url.
+ *
+ * @return array
+ */
+ public function getAlternateUriSet() {
+
+ return array();
+
+ }
+
+ /**
+ * Returns the full principal url
+ *
+ * @return string
+ */
+ public function getPrincipalUrl() {
+
+ return $this->principalInfo['uri'] . '/' . $this->getName();
+
+ }
+
+ /**
+ * Returns the list of group members
+ *
+ * If this principal is a group, this function should return
+ * all member principal uri's for the group.
+ *
+ * @return array
+ */
+ public function getGroupMemberSet() {
+
+ return $this->principalBackend->getGroupMemberSet($this->getPrincipalUrl());
+
+ }
+
+ /**
+ * Returns the list of groups this principal is member of
+ *
+ * If this principal is a member of a (list of) groups, this function
+ * should return a list of principal uri's for it's members.
+ *
+ * @return array
+ */
+ public function getGroupMembership() {
+
+ return $this->principalBackend->getGroupMembership($this->getPrincipalUrl());
+
+ }
+
+ /**
+ * Sets a list of group members
+ *
+ * If this principal is a group, this method sets all the group members.
+ * The list of members is always overwritten, never appended to.
+ *
+ * This method should throw an exception if the members could not be set.
+ *
+ * @param array $principals
+ * @return void
+ */
+ public function setGroupMemberSet(array $principals) {
+
+ $this->principalBackend->setGroupMemberSet($this->getPrincipalUrl(), $principals);
+
+ }
+
+ /**
+ * Returns the displayname
+ *
+ * This should be a human readable name for the principal.
+ * If none is available, return the nodename.
+ *
+ * @return string
+ */
+ public function getDisplayName() {
+
+ return $this->getName();
+
+ }
+
+}
diff --git a/3rdparty/Sabre/CalDAV/Principal/User.php b/3rdparty/Sabre/CalDAV/Principal/User.php
new file mode 100644
index 00000000000..034629b89b3
--- /dev/null
+++ b/3rdparty/Sabre/CalDAV/Principal/User.php
@@ -0,0 +1,122 @@
+<?php
+
+/**
+ * CalDAV principal
+ *
+ * This is a standard user-principal for CalDAV. This principal is also a
+ * collection and returns the caldav-proxy-read and caldav-proxy-write child
+ * principals.
+ *
+ * @package Sabre
+ * @subpackage CalDAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_CalDAV_Principal_User extends Sabre_DAVACL_Principal implements Sabre_DAV_ICollection {
+
+ /**
+ * Creates a new file in the directory
+ *
+ * @param string $name Name of the file
+ * @param resource $data Initial payload, passed as a readable stream resource.
+ * @throws Sabre_DAV_Exception_Forbidden
+ * @return void
+ */
+ public function createFile($name, $data = null) {
+
+ throw new Sabre_DAV_Exception_Forbidden('Permission denied to create file (filename ' . $name . ')');
+
+ }
+
+ /**
+ * Creates a new subdirectory
+ *
+ * @param string $name
+ * @throws Sabre_DAV_Exception_Forbidden
+ * @return void
+ */
+ public function createDirectory($name) {
+
+ throw new Sabre_DAV_Exception_Forbidden('Permission denied to create directory');
+
+ }
+
+ /**
+ * Returns a specific child node, referenced by its name
+ *
+ * @param string $name
+ * @return Sabre_DAV_INode
+ */
+ public function getChild($name) {
+
+ if ($name === 'calendar-proxy-read')
+ return new Sabre_CalDAV_Principal_ProxyRead($this->principalBackend, $this->principalProperties);
+
+ if ($name === 'calendar-proxy-write')
+ return new Sabre_CalDAV_Principal_ProxyWrite($this->principalBackend, $this->principalProperties);
+
+ throw new Sabre_DAV_Exception_FileNotFound('Node with name ' . $name . ' was not found');
+
+ }
+
+ /**
+ * Returns an array with all the child nodes
+ *
+ * @return Sabre_DAV_INode[]
+ */
+ public function getChildren() {
+
+ return array(
+ new Sabre_CalDAV_Principal_ProxyRead($this->principalBackend, $this->principalProperties),
+ new Sabre_CalDAV_Principal_ProxyWrite($this->principalBackend, $this->principalProperties),
+ );
+
+ }
+
+ /**
+ * Checks if a child-node with the specified name exists
+ *
+ * @return bool
+ */
+ public function childExists($name) {
+
+ return $name === 'calendar-proxy-read' || $name === 'calendar-proxy-write';
+
+ }
+
+ /**
+ * Returns a list of ACE's for this node.
+ *
+ * Each ACE has the following properties:
+ * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are
+ * currently the only supported privileges
+ * * 'principal', a url to the principal who owns the node
+ * * 'protected' (optional), indicating that this ACE is not allowed to
+ * be updated.
+ *
+ * @return array
+ */
+ public function getACL() {
+
+ return array(
+ array(
+ 'privilege' => '{DAV:}read',
+ 'principal' => $this->principalProperties['uri'],
+ 'protected' => true,
+ ),
+ array(
+ 'privilege' => '{DAV:}read',
+ 'principal' => $this->principalProperties['uri'] . '/calendar-proxy-read',
+ 'protected' => true,
+ ),
+ array(
+ 'privilege' => '{DAV:}read',
+ 'principal' => $this->principalProperties['uri'] . '/calendar-proxy-write',
+ 'protected' => true,
+ ),
+ );
+
+ }
+
+}
diff --git a/3rdparty/Sabre/CalDAV/Property/SupportedCalendarComponentSet.php b/3rdparty/Sabre/CalDAV/Property/SupportedCalendarComponentSet.php
new file mode 100644
index 00000000000..1bbaca6b8a7
--- /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-2011 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 void
+ */
+ 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 100644
index 00000000000..5010ee6d525
--- /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-2011 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 100644
index 00000000000..e585e9db3d8
--- /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-2011 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/Server.php b/3rdparty/Sabre/CalDAV/Server.php
new file mode 100644
index 00000000000..969d69c6279
--- /dev/null
+++ b/3rdparty/Sabre/CalDAV/Server.php
@@ -0,0 +1,65 @@
+<?php
+
+/**
+ * CalDAV server
+ *
+ * This script is a convenience script. It quickly sets up a WebDAV server
+ * with caldav and ACL support, and it creates the root 'principals' and
+ * 'calendars' collections.
+ *
+ * Note that if you plan to do anything moderately complex, you are advised to
+ * not subclass this server, but use Sabre_DAV_Server directly instead. This
+ * class is nothing more than an 'easy setup'.
+ *
+ * @package Sabre
+ * @subpackage CalDAV
+ * @copyright Copyright (C) 2007-2011 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 100644
index 00000000000..f52d65e9a73
--- /dev/null
+++ b/3rdparty/Sabre/CalDAV/UserCalendars.php
@@ -0,0 +1,280 @@
+<?php
+
+/**
+ * The UserCalenders class contains all calendars associated to one user
+ *
+ * @package Sabre
+ * @subpackage CalDAV
+ * @copyright Copyright (C) 2007-2011 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_FileNotFound('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);
+ }
+ return $objs;
+
+ }
+
+ /**
+ * Creates a new calendar
+ *
+ * @param string $name
+ * @param string $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');
+
+ }
+
+
+
+}
diff --git a/3rdparty/Sabre/CalDAV/Version.php b/3rdparty/Sabre/CalDAV/Version.php
new file mode 100644
index 00000000000..5ecc0cebb37
--- /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-2011 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.5.0';
+
+ /**
+ * Stability : alpha, beta, stable
+ */
+ const STABILITY = 'stable';
+
+}
diff --git a/3rdparty/Sabre/CalDAV/XMLUtil.php b/3rdparty/Sabre/CalDAV/XMLUtil.php
new file mode 100644
index 00000000000..bf349a36aae
--- /dev/null
+++ b/3rdparty/Sabre/CalDAV/XMLUtil.php
@@ -0,0 +1,208 @@
+<?php
+
+/**
+ * XML utilities for CalDAV
+ *
+ * This class contains a few static methods used for parsing certain CalDAV
+ * requests.
+ *
+ * @package Sabre
+ * @subpackage CalDAV
+ * @copyright Copyright (C) 2007-2011 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_XMLUtil {
+
+ /**
+ * This function parses the calendar-query report request body
+ *
+ * The body is quite complicated, so we're turning it into a PHP
+ * array.
+ *
+ * The resulting associative array has xpath expressions as keys.
+ * By default the xpath expressions should simply be checked for existance
+ * The xpath expressions can point to elements or attributes.
+ *
+ * The array values can contain a number of items, which alters the query
+ * filter.
+ *
+ * * time-range. Must also check if the todo or event falls within the
+ * specified timerange. How this is interpreted depends on
+ * the type of object (VTODO, VEVENT, VJOURNAL, etc)
+ * * is-not-defined
+ * Instead of checking if the attribute or element exist,
+ * we must check if it doesn't.
+ * * text-match
+ * Checks if the value of the attribute or element matches
+ * the specified value. This is actually another array with
+ * the 'collation', 'value' and 'negate-condition' items.
+ *
+ * Refer to the CalDAV spec for more information.
+ *
+ * @param DOMNode $domNode
+ * @param string $basePath used for recursive calls.
+ * @param array $filters used for recursive calls.
+ * @return array
+ */
+ static public function parseCalendarQueryFilters($domNode,$basePath = '/c:iCalendar', &$filters = array()) {
+
+ foreach($domNode->childNodes as $child) {
+
+ switch(Sabre_DAV_XMLUtil::toClarkNotation($child)) {
+
+ case '{urn:ietf:params:xml:ns:caldav}comp-filter' :
+ case '{urn:ietf:params:xml:ns:caldav}prop-filter' :
+
+ $filterName = $basePath . '/' . 'c:' . strtolower($child->getAttribute('name'));
+ $filters[$filterName] = array();
+
+ self::parseCalendarQueryFilters($child, $filterName,$filters);
+ break;
+
+ case '{urn:ietf:params:xml:ns:caldav}time-range' :
+
+ if ($start = $child->getAttribute('start')) {
+ $start = self::parseICalendarDateTime($start);
+ } else {
+ $start = null;
+ }
+ if ($end = $child->getAttribute('end')) {
+ $end = self::parseICalendarDateTime($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');
+ }
+
+ $filters[$basePath]['time-range'] = array(
+ 'start' => $start,
+ 'end' => $end
+ );
+ break;
+
+ case '{urn:ietf:params:xml:ns:caldav}is-not-defined' :
+ $filters[$basePath]['is-not-defined'] = true;
+ break;
+
+ case '{urn:ietf:params:xml:ns:caldav}param-filter' :
+
+ $filterName = $basePath . '/@' . strtolower($child->getAttribute('name'));
+ $filters[$filterName] = array();
+ self::parseCalendarQueryFilters($child, $filterName, $filters);
+ break;
+
+ case '{urn:ietf:params:xml:ns:caldav}text-match' :
+
+ $collation = $child->getAttribute('collation');
+ if (!$collation) $collation = 'i;ascii-casemap';
+
+ $filters[$basePath]['text-match'] = array(
+ 'collation' => ($collation == 'default'?'i;ascii-casemap':$collation),
+ 'negate-condition' => $child->getAttribute('negate-condition')==='yes',
+ 'value' => $child->nodeValue,
+ );
+ break;
+
+ }
+
+ }
+
+ return $filters;
+
+ }
+
+ /**
+ * 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 parseICalendarDateTime($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 datetime and returns a DateTime object
+ *
+ * @param string $date
+ * @param DateTimeZone $tz
+ * @return DateTime
+ */
+ static public function parseICalendarDate($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 and returns a string suitable
+ * for strtotime or DateTime::modify.
+ *
+ * NOTE: When we require PHP 5.3 this can be replaced by the DateTimeInterval object, which
+ * supports ISO 8601 Intervals, which is a superset of ICalendar durations.
+ *
+ * For now though, we're just gonna live with this messy system
+ *
+ * @param string $duration
+ * @return string
+ */
+ static public function parseICalendarDuration($duration) {
+
+ $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);
+ }
+
+ $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);
+ return $newDur;
+
+ }
+
+}