diff options
Diffstat (limited to '3rdparty/Sabre/CalDAV')
25 files changed, 4295 insertions, 0 deletions
diff --git a/3rdparty/Sabre/CalDAV/Backend/Abstract.php b/3rdparty/Sabre/CalDAV/Backend/Abstract.php new file mode 100755 index 00000000000..7aba1d69ffe --- /dev/null +++ b/3rdparty/Sabre/CalDAV/Backend/Abstract.php @@ -0,0 +1,168 @@ +<?php + +/** + * Abstract Calendaring backend. Extend this class to create your own backends. + * + * @package Sabre + * @subpackage CalDAV + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. + * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License + */ +abstract class Sabre_CalDAV_Backend_Abstract { + + /** + * Returns a list of calendars for a principal. + * + * Every project is an array with the following keys: + * * id, a unique id that will be used by other functions to modify the + * calendar. This can be the same as the uri or a database key. + * * uri, which the basename of the uri with which the calendar is + * accessed. + * * principaluri. The owner of the calendar. Almost always the same as + * principalUri passed to this method. + * + * Furthermore it can contain webdav properties in clark notation. A very + * common one is '{DAV:}displayname'. + * + * @param string $principalUri + * @return array + */ + abstract function getCalendarsForUser($principalUri); + + /** + * Creates a new calendar for a principal. + * + * If the creation was a success, an id must be returned that can be used to reference + * this calendar in other methods, such as updateCalendar. + * + * @param string $principalUri + * @param string $calendarUri + * @param array $properties + * @return void + */ + abstract function createCalendar($principalUri,$calendarUri,array $properties); + + /** + * Updates properties for a calendar. + * + * The mutations array uses the propertyName in clark-notation as key, + * and the array value for the property value. In the case a property + * should be deleted, the property value will be null. + * + * This method must be atomic. If one property cannot be changed, the + * entire operation must fail. + * + * If the operation was successful, true can be returned. + * If the operation failed, false can be returned. + * + * Deletion of a non-existent property is always successful. + * + * Lastly, it is optional to return detailed information about any + * failures. In this case an array should be returned with the following + * structure: + * + * array( + * 403 => array( + * '{DAV:}displayname' => null, + * ), + * 424 => array( + * '{DAV:}owner' => null, + * ) + * ) + * + * In this example it was forbidden to update {DAV:}displayname. + * (403 Forbidden), which in turn also caused {DAV:}owner to fail + * (424 Failed Dependency) because the request needs to be atomic. + * + * @param string $calendarId + * @param array $mutations + * @return bool|array + */ + public function updateCalendar($calendarId, array $mutations) { + + return false; + + } + + /** + * Delete a calendar and all it's objects + * + * @param string $calendarId + * @return void + */ + abstract function deleteCalendar($calendarId); + + /** + * Returns all calendar objects within a calendar. + * + * Every item contains an array with the following keys: + * * id - unique identifier which will be used for subsequent updates + * * calendardata - The iCalendar-compatible calendar data + * * uri - a unique key which will be used to construct the uri. This can be any arbitrary string. + * * lastmodified - a timestamp of the last modification time + * * etag - An arbitrary string, surrounded by double-quotes. (e.g.: + * ' "abcdef"') + * * calendarid - The calendarid as it was passed to this function. + * * size - The size of the calendar objects, in bytes. + * + * Note that the etag is optional, but it's highly encouraged to return for + * speed reasons. + * + * The calendardata is also optional. If it's not returned + * 'getCalendarObject' will be called later, which *is* expected to return + * calendardata. + * + * If neither etag or size are specified, the calendardata will be + * used/fetched to determine these numbers. If both are specified the + * amount of times this is needed is reduced by a great degree. + * + * @param string $calendarId + * @return array + */ + abstract function getCalendarObjects($calendarId); + + /** + * Returns information from a single calendar object, based on it's object + * uri. + * + * The returned array must have the same keys as getCalendarObjects. The + * 'calendardata' object is required here though, while it's not required + * for getCalendarObjects. + * + * @param string $calendarId + * @param string $objectUri + * @return array + */ + abstract function getCalendarObject($calendarId,$objectUri); + + /** + * Creates a new calendar object. + * + * @param string $calendarId + * @param string $objectUri + * @param string $calendarData + * @return void + */ + abstract function createCalendarObject($calendarId,$objectUri,$calendarData); + + /** + * Updates an existing calendarobject, based on it's uri. + * + * @param string $calendarId + * @param string $objectUri + * @param string $calendarData + * @return void + */ + abstract function updateCalendarObject($calendarId,$objectUri,$calendarData); + + /** + * Deletes an existing calendar object. + * + * @param string $calendarId + * @param string $objectUri + * @return void + */ + abstract function deleteCalendarObject($calendarId,$objectUri); + +} diff --git a/3rdparty/Sabre/CalDAV/Backend/PDO.php b/3rdparty/Sabre/CalDAV/Backend/PDO.php new file mode 100755 index 00000000000..ddacf940c74 --- /dev/null +++ b/3rdparty/Sabre/CalDAV/Backend/PDO.php @@ -0,0 +1,396 @@ +<?php + +/** + * PDO CalDAV backend + * + * This backend is used to store calendar-data in a PDO database, such as + * sqlite or MySQL + * + * @package Sabre + * @subpackage CalDAV + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. + * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License + */ +class Sabre_CalDAV_Backend_PDO extends Sabre_CalDAV_Backend_Abstract { + + /** + * pdo + * + * @var PDO + */ + protected $pdo; + + /** + * The table name that will be used for calendars + * + * @var string + */ + protected $calendarTableName; + + /** + * The table name that will be used for calendar objects + * + * @var string + */ + protected $calendarObjectTableName; + + /** + * List of CalDAV properties, and how they map to database fieldnames + * + * Add your own properties by simply adding on to this array + * + * @var array + */ + public $propertyMap = array( + '{DAV:}displayname' => 'displayname', + '{urn:ietf:params:xml:ns:caldav}calendar-description' => 'description', + '{urn:ietf:params:xml:ns:caldav}calendar-timezone' => 'timezone', + '{http://apple.com/ns/ical/}calendar-order' => 'calendarorder', + '{http://apple.com/ns/ical/}calendar-color' => 'calendarcolor', + ); + + /** + * Creates the backend + * + * @param PDO $pdo + * @param string $calendarTableName + * @param string $calendarObjectTableName + */ + public function __construct(PDO $pdo, $calendarTableName = 'calendars', $calendarObjectTableName = 'calendarobjects') { + + $this->pdo = $pdo; + $this->calendarTableName = $calendarTableName; + $this->calendarObjectTableName = $calendarObjectTableName; + + } + + /** + * Returns a list of calendars for a principal. + * + * Every project is an array with the following keys: + * * id, a unique id that will be used by other functions to modify the + * calendar. This can be the same as the uri or a database key. + * * uri, which the basename of the uri with which the calendar is + * accessed. + * * principaluri. The owner of the calendar. Almost always the same as + * principalUri passed to this method. + * + * Furthermore it can contain webdav properties in clark notation. A very + * common one is '{DAV:}displayname'. + * + * @param string $principalUri + * @return array + */ + public function getCalendarsForUser($principalUri) { + + $fields = array_values($this->propertyMap); + $fields[] = 'id'; + $fields[] = 'uri'; + $fields[] = 'ctag'; + $fields[] = 'components'; + $fields[] = 'principaluri'; + + // Making fields a comma-delimited list + $fields = implode(', ', $fields); + $stmt = $this->pdo->prepare("SELECT " . $fields . " FROM ".$this->calendarTableName." WHERE principaluri = ? ORDER BY calendarorder ASC"); + $stmt->execute(array($principalUri)); + + $calendars = array(); + while($row = $stmt->fetch(PDO::FETCH_ASSOC)) { + + $components = array(); + if ($row['components']) { + $components = explode(',',$row['components']); + } + + $calendar = array( + 'id' => $row['id'], + 'uri' => $row['uri'], + 'principaluri' => $row['principaluri'], + '{' . Sabre_CalDAV_Plugin::NS_CALENDARSERVER . '}getctag' => $row['ctag']?$row['ctag']:'0', + '{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}supported-calendar-component-set' => new Sabre_CalDAV_Property_SupportedCalendarComponentSet($components), + ); + + + foreach($this->propertyMap as $xmlName=>$dbName) { + $calendar[$xmlName] = $row[$dbName]; + } + + $calendars[] = $calendar; + + } + + return $calendars; + + } + + /** + * Creates a new calendar for a principal. + * + * If the creation was a success, an id must be returned that can be used to reference + * this calendar in other methods, such as updateCalendar + * + * @param string $principalUri + * @param string $calendarUri + * @param array $properties + * @return string + */ + public function createCalendar($principalUri, $calendarUri, array $properties) { + + $fieldNames = array( + 'principaluri', + 'uri', + 'ctag', + ); + $values = array( + ':principaluri' => $principalUri, + ':uri' => $calendarUri, + ':ctag' => 1, + ); + + // Default value + $sccs = '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set'; + $fieldNames[] = 'components'; + if (!isset($properties[$sccs])) { + $values[':components'] = 'VEVENT,VTODO'; + } else { + if (!($properties[$sccs] instanceof Sabre_CalDAV_Property_SupportedCalendarComponentSet)) { + throw new Sabre_DAV_Exception('The ' . $sccs . ' property must be of type: Sabre_CalDAV_Property_SupportedCalendarComponentSet'); + } + $values[':components'] = implode(',',$properties[$sccs]->getValue()); + } + + foreach($this->propertyMap as $xmlName=>$dbName) { + if (isset($properties[$xmlName])) { + + $values[':' . $dbName] = $properties[$xmlName]; + $fieldNames[] = $dbName; + } + } + + $stmt = $this->pdo->prepare("INSERT INTO ".$this->calendarTableName." (".implode(', ', $fieldNames).") VALUES (".implode(', ',array_keys($values)).")"); + $stmt->execute($values); + + return $this->pdo->lastInsertId(); + + } + + /** + * Updates properties for a calendar. + * + * The mutations array uses the propertyName in clark-notation as key, + * and the array value for the property value. In the case a property + * should be deleted, the property value will be null. + * + * This method must be atomic. If one property cannot be changed, the + * entire operation must fail. + * + * If the operation was successful, true can be returned. + * If the operation failed, false can be returned. + * + * Deletion of a non-existent property is always successful. + * + * Lastly, it is optional to return detailed information about any + * failures. In this case an array should be returned with the following + * structure: + * + * array( + * 403 => array( + * '{DAV:}displayname' => null, + * ), + * 424 => array( + * '{DAV:}owner' => null, + * ) + * ) + * + * In this example it was forbidden to update {DAV:}displayname. + * (403 Forbidden), which in turn also caused {DAV:}owner to fail + * (424 Failed Dependency) because the request needs to be atomic. + * + * @param string $calendarId + * @param array $mutations + * @return bool|array + */ + public function updateCalendar($calendarId, array $mutations) { + + $newValues = array(); + $result = array( + 200 => array(), // Ok + 403 => array(), // Forbidden + 424 => array(), // Failed Dependency + ); + + $hasError = false; + + foreach($mutations as $propertyName=>$propertyValue) { + + // We don't know about this property. + if (!isset($this->propertyMap[$propertyName])) { + $hasError = true; + $result[403][$propertyName] = null; + unset($mutations[$propertyName]); + continue; + } + + $fieldName = $this->propertyMap[$propertyName]; + $newValues[$fieldName] = $propertyValue; + + } + + // If there were any errors we need to fail the request + if ($hasError) { + // Properties has the remaining properties + foreach($mutations as $propertyName=>$propertyValue) { + $result[424][$propertyName] = null; + } + + // Removing unused statuscodes for cleanliness + foreach($result as $status=>$properties) { + if (is_array($properties) && count($properties)===0) unset($result[$status]); + } + + return $result; + + } + + // Success + + // Now we're generating the sql query. + $valuesSql = array(); + foreach($newValues as $fieldName=>$value) { + $valuesSql[] = $fieldName . ' = ?'; + } + $valuesSql[] = 'ctag = ctag + 1'; + + $stmt = $this->pdo->prepare("UPDATE " . $this->calendarTableName . " SET " . implode(', ',$valuesSql) . " WHERE id = ?"); + $newValues['id'] = $calendarId; + $stmt->execute(array_values($newValues)); + + return true; + + } + + /** + * Delete a calendar and all it's objects + * + * @param string $calendarId + * @return void + */ + public function deleteCalendar($calendarId) { + + $stmt = $this->pdo->prepare('DELETE FROM '.$this->calendarObjectTableName.' WHERE calendarid = ?'); + $stmt->execute(array($calendarId)); + + $stmt = $this->pdo->prepare('DELETE FROM '.$this->calendarTableName.' WHERE id = ?'); + $stmt->execute(array($calendarId)); + + } + + /** + * Returns all calendar objects within a calendar. + * + * Every item contains an array with the following keys: + * * id - unique identifier which will be used for subsequent updates + * * calendardata - The iCalendar-compatible calendar data + * * uri - a unique key which will be used to construct the uri. This can be any arbitrary string. + * * lastmodified - a timestamp of the last modification time + * * etag - An arbitrary string, surrounded by double-quotes. (e.g.: + * ' "abcdef"') + * * calendarid - The calendarid as it was passed to this function. + * * size - The size of the calendar objects, in bytes. + * + * Note that the etag is optional, but it's highly encouraged to return for + * speed reasons. + * + * The calendardata is also optional. If it's not returned + * 'getCalendarObject' will be called later, which *is* expected to return + * calendardata. + * + * If neither etag or size are specified, the calendardata will be + * used/fetched to determine these numbers. If both are specified the + * amount of times this is needed is reduced by a great degree. + * + * @param string $calendarId + * @return array + */ + public function getCalendarObjects($calendarId) { + + $stmt = $this->pdo->prepare('SELECT * FROM '.$this->calendarObjectTableName.' WHERE calendarid = ?'); + $stmt->execute(array($calendarId)); + return $stmt->fetchAll(); + + } + + /** + * Returns information from a single calendar object, based on it's object + * uri. + * + * The returned array must have the same keys as getCalendarObjects. The + * 'calendardata' object is required here though, while it's not required + * for getCalendarObjects. + * + * @param string $calendarId + * @param string $objectUri + * @return array + */ + public function getCalendarObject($calendarId,$objectUri) { + + $stmt = $this->pdo->prepare('SELECT * FROM '.$this->calendarObjectTableName.' WHERE calendarid = ? AND uri = ?'); + $stmt->execute(array($calendarId, $objectUri)); + return $stmt->fetch(); + + } + + /** + * Creates a new calendar object. + * + * @param string $calendarId + * @param string $objectUri + * @param string $calendarData + * @return void + */ + public function createCalendarObject($calendarId,$objectUri,$calendarData) { + + $stmt = $this->pdo->prepare('INSERT INTO '.$this->calendarObjectTableName.' (calendarid, uri, calendardata, lastmodified) VALUES (?,?,?,?)'); + $stmt->execute(array($calendarId,$objectUri,$calendarData,time())); + $stmt = $this->pdo->prepare('UPDATE '.$this->calendarTableName.' SET ctag = ctag + 1 WHERE id = ?'); + $stmt->execute(array($calendarId)); + + } + + /** + * Updates an existing calendarobject, based on it's uri. + * + * @param string $calendarId + * @param string $objectUri + * @param string $calendarData + * @return void + */ + public function updateCalendarObject($calendarId,$objectUri,$calendarData) { + + $stmt = $this->pdo->prepare('UPDATE '.$this->calendarObjectTableName.' SET calendardata = ?, lastmodified = ? WHERE calendarid = ? AND uri = ?'); + $stmt->execute(array($calendarData,time(),$calendarId,$objectUri)); + $stmt = $this->pdo->prepare('UPDATE '.$this->calendarTableName.' SET ctag = ctag + 1 WHERE id = ?'); + $stmt->execute(array($calendarId)); + + } + + /** + * Deletes an existing calendar object. + * + * @param string $calendarId + * @param string $objectUri + * @return void + */ + public function deleteCalendarObject($calendarId,$objectUri) { + + $stmt = $this->pdo->prepare('DELETE FROM '.$this->calendarObjectTableName.' WHERE calendarid = ? AND uri = ?'); + $stmt->execute(array($calendarId,$objectUri)); + $stmt = $this->pdo->prepare('UPDATE '. $this->calendarTableName .' SET ctag = ctag + 1 WHERE id = ?'); + $stmt->execute(array($calendarId)); + + } + + +} diff --git a/3rdparty/Sabre/CalDAV/Calendar.php b/3rdparty/Sabre/CalDAV/Calendar.php new file mode 100755 index 00000000000..623df2dd1b8 --- /dev/null +++ b/3rdparty/Sabre/CalDAV/Calendar.php @@ -0,0 +1,343 @@ +<?php + +/** + * This object represents a CalDAV calendar. + * + * A calendar can contain multiple TODO and or Events. These are represented + * as Sabre_CalDAV_CalendarObject objects. + * + * @package Sabre + * @subpackage CalDAV + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. + * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License + */ +class Sabre_CalDAV_Calendar implements Sabre_CalDAV_ICalendar, Sabre_DAV_IProperties, Sabre_DAVACL_IACL { + + /** + * This is an array with calendar information + * + * @var array + */ + protected $calendarInfo; + + /** + * CalDAV backend + * + * @var Sabre_CalDAV_Backend_Abstract + */ + protected $caldavBackend; + + /** + * Principal backend + * + * @var Sabre_DAVACL_IPrincipalBackend + */ + protected $principalBackend; + + /** + * Constructor + * + * @param Sabre_DAVACL_IPrincipalBackend $principalBackend + * @param Sabre_CalDAV_Backend_Abstract $caldavBackend + * @param array $calendarInfo + */ + public function __construct(Sabre_DAVACL_IPrincipalBackend $principalBackend, Sabre_CalDAV_Backend_Abstract $caldavBackend, $calendarInfo) { + + $this->caldavBackend = $caldavBackend; + $this->principalBackend = $principalBackend; + $this->calendarInfo = $calendarInfo; + + + } + + /** + * Returns the name of the calendar + * + * @return string + */ + public function getName() { + + return $this->calendarInfo['uri']; + + } + + /** + * Updates properties such as the display name and description + * + * @param array $mutations + * @return array + */ + public function updateProperties($mutations) { + + return $this->caldavBackend->updateCalendar($this->calendarInfo['id'],$mutations); + + } + + /** + * Returns the list of properties + * + * @param array $requestedProperties + * @return array + */ + public function getProperties($requestedProperties) { + + $response = array(); + + foreach($requestedProperties as $prop) switch($prop) { + + case '{urn:ietf:params:xml:ns:caldav}supported-calendar-data' : + $response[$prop] = new Sabre_CalDAV_Property_SupportedCalendarData(); + break; + case '{urn:ietf:params:xml:ns:caldav}supported-collation-set' : + $response[$prop] = new Sabre_CalDAV_Property_SupportedCollationSet(); + break; + case '{DAV:}owner' : + $response[$prop] = new Sabre_DAVACL_Property_Principal(Sabre_DAVACL_Property_Principal::HREF,$this->calendarInfo['principaluri']); + break; + default : + if (isset($this->calendarInfo[$prop])) $response[$prop] = $this->calendarInfo[$prop]; + break; + + } + return $response; + + } + + /** + * Returns a calendar object + * + * The contained calendar objects are for example Events or Todo's. + * + * @param string $name + * @return Sabre_DAV_ICalendarObject + */ + public function getChild($name) { + + $obj = $this->caldavBackend->getCalendarObject($this->calendarInfo['id'],$name); + if (!$obj) throw new Sabre_DAV_Exception_NotFound('Calendar object not found'); + return new Sabre_CalDAV_CalendarObject($this->caldavBackend,$this->calendarInfo,$obj); + + } + + /** + * Returns the full list of calendar objects + * + * @return array + */ + public function getChildren() { + + $objs = $this->caldavBackend->getCalendarObjects($this->calendarInfo['id']); + $children = array(); + foreach($objs as $obj) { + $children[] = new Sabre_CalDAV_CalendarObject($this->caldavBackend,$this->calendarInfo,$obj); + } + return $children; + + } + + /** + * Checks if a child-node exists. + * + * @param string $name + * @return bool + */ + public function childExists($name) { + + $obj = $this->caldavBackend->getCalendarObject($this->calendarInfo['id'],$name); + if (!$obj) + return false; + else + return true; + + } + + /** + * Creates a new directory + * + * We actually block this, as subdirectories are not allowed in calendars. + * + * @param string $name + * @return void + */ + public function createDirectory($name) { + + throw new Sabre_DAV_Exception_MethodNotAllowed('Creating collections in calendar objects is not allowed'); + + } + + /** + * Creates a new file + * + * The contents of the new file must be a valid ICalendar string. + * + * @param string $name + * @param resource $calendarData + * @return string|null + */ + public function createFile($name,$calendarData = null) { + + if (is_resource($calendarData)) { + $calendarData = stream_get_contents($calendarData); + } + return $this->caldavBackend->createCalendarObject($this->calendarInfo['id'],$name,$calendarData); + + } + + /** + * Deletes the calendar. + * + * @return void + */ + public function delete() { + + $this->caldavBackend->deleteCalendar($this->calendarInfo['id']); + + } + + /** + * Renames the calendar. Note that most calendars use the + * {DAV:}displayname to display a name to display a name. + * + * @param string $newName + * @return void + */ + public function setName($newName) { + + throw new Sabre_DAV_Exception_MethodNotAllowed('Renaming calendars is not yet supported'); + + } + + /** + * Returns the last modification date as a unix timestamp. + * + * @return void + */ + public function getLastModified() { + + return null; + + } + + /** + * Returns the owner principal + * + * This must be a url to a principal, or null if there's no owner + * + * @return string|null + */ + public function getOwner() { + + return $this->calendarInfo['principaluri']; + + } + + /** + * Returns a group principal + * + * This must be a url to a principal, or null if there's no owner + * + * @return string|null + */ + public function getGroup() { + + return null; + + } + + /** + * Returns a list of ACE's for this node. + * + * Each ACE has the following properties: + * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are + * currently the only supported privileges + * * 'principal', a url to the principal who owns the node + * * 'protected' (optional), indicating that this ACE is not allowed to + * be updated. + * + * @return array + */ + public function getACL() { + + return array( + array( + 'privilege' => '{DAV:}read', + 'principal' => $this->calendarInfo['principaluri'], + 'protected' => true, + ), + array( + 'privilege' => '{DAV:}write', + 'principal' => $this->calendarInfo['principaluri'], + 'protected' => true, + ), + array( + 'privilege' => '{DAV:}read', + 'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-write', + 'protected' => true, + ), + array( + 'privilege' => '{DAV:}write', + 'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-write', + 'protected' => true, + ), + array( + 'privilege' => '{DAV:}read', + 'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-read', + 'protected' => true, + ), + array( + 'privilege' => '{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}read-free-busy', + 'principal' => '{DAV:}authenticated', + 'protected' => true, + ), + + ); + + } + + /** + * Updates the ACL + * + * This method will receive a list of new ACE's. + * + * @param array $acl + * @return void + */ + public function setACL(array $acl) { + + throw new Sabre_DAV_Exception_MethodNotAllowed('Changing ACL is not yet supported'); + + } + + /** + * Returns the list of supported privileges for this node. + * + * The returned data structure is a list of nested privileges. + * See Sabre_DAVACL_Plugin::getDefaultSupportedPrivilegeSet for a simple + * standard structure. + * + * If null is returned from this method, the default privilege set is used, + * which is fine for most common usecases. + * + * @return array|null + */ + public function getSupportedPrivilegeSet() { + + $default = Sabre_DAVACL_Plugin::getDefaultSupportedPrivilegeSet(); + + // We need to inject 'read-free-busy' in the tree, aggregated under + // {DAV:}read. + foreach($default['aggregates'] as &$agg) { + + if ($agg['privilege'] !== '{DAV:}read') continue; + + $agg['aggregates'][] = array( + 'privilege' => '{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}read-free-busy', + ); + + } + return $default; + + } + +} diff --git a/3rdparty/Sabre/CalDAV/CalendarObject.php b/3rdparty/Sabre/CalDAV/CalendarObject.php new file mode 100755 index 00000000000..72f0a578d16 --- /dev/null +++ b/3rdparty/Sabre/CalDAV/CalendarObject.php @@ -0,0 +1,273 @@ +<?php + +/** + * The CalendarObject represents a single VEVENT or VTODO within a Calendar. + * + * @package Sabre + * @subpackage CalDAV + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. + * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License + */ +class Sabre_CalDAV_CalendarObject extends Sabre_DAV_File implements Sabre_CalDAV_ICalendarObject, Sabre_DAVACL_IACL { + + /** + * Sabre_CalDAV_Backend_Abstract + * + * @var array + */ + protected $caldavBackend; + + /** + * Array with information about this CalendarObject + * + * @var array + */ + protected $objectData; + + /** + * Array with information about the containing calendar + * + * @var array + */ + protected $calendarInfo; + + /** + * Constructor + * + * @param Sabre_CalDAV_Backend_Abstract $caldavBackend + * @param array $calendarInfo + * @param array $objectData + */ + public function __construct(Sabre_CalDAV_Backend_Abstract $caldavBackend,array $calendarInfo,array $objectData) { + + $this->caldavBackend = $caldavBackend; + + if (!isset($objectData['calendarid'])) { + throw new InvalidArgumentException('The objectData argument must contain a \'calendarid\' property'); + } + if (!isset($objectData['uri'])) { + throw new InvalidArgumentException('The objectData argument must contain an \'uri\' property'); + } + + $this->calendarInfo = $calendarInfo; + $this->objectData = $objectData; + + } + + /** + * Returns the uri for this object + * + * @return string + */ + public function getName() { + + return $this->objectData['uri']; + + } + + /** + * Returns the ICalendar-formatted object + * + * @return string + */ + public function get() { + + // Pre-populating the 'calendardata' is optional, if we don't have it + // already we fetch it from the backend. + if (!isset($this->objectData['calendardata'])) { + $this->objectData = $this->caldavBackend->getCalendarObject($this->objectData['calendarid'], $this->objectData['uri']); + } + return $this->objectData['calendardata']; + + } + + /** + * Updates the ICalendar-formatted object + * + * @param string $calendarData + * @return void + */ + public function put($calendarData) { + + if (is_resource($calendarData)) { + $calendarData = stream_get_contents($calendarData); + } + $etag = $this->caldavBackend->updateCalendarObject($this->calendarInfo['id'],$this->objectData['uri'],$calendarData); + $this->objectData['calendardata'] = $calendarData; + $this->objectData['etag'] = $etag; + + return $etag; + + } + + /** + * Deletes the calendar object + * + * @return void + */ + public function delete() { + + $this->caldavBackend->deleteCalendarObject($this->calendarInfo['id'],$this->objectData['uri']); + + } + + /** + * Returns the mime content-type + * + * @return string + */ + public function getContentType() { + + return 'text/calendar'; + + } + + /** + * Returns an ETag for this object. + * + * The ETag is an arbitrary string, but MUST be surrounded by double-quotes. + * + * @return string + */ + public function getETag() { + + if (isset($this->objectData['etag'])) { + return $this->objectData['etag']; + } else { + return '"' . md5($this->get()). '"'; + } + + } + + /** + * Returns the last modification date as a unix timestamp + * + * @return time + */ + public function getLastModified() { + + return $this->objectData['lastmodified']; + + } + + /** + * Returns the size of this object in bytes + * + * @return int + */ + public function getSize() { + + if (array_key_exists('size',$this->objectData)) { + return $this->objectData['size']; + } else { + return strlen($this->get()); + } + + } + + /** + * Returns the owner principal + * + * This must be a url to a principal, or null if there's no owner + * + * @return string|null + */ + public function getOwner() { + + return $this->calendarInfo['principaluri']; + + } + + /** + * Returns a group principal + * + * This must be a url to a principal, or null if there's no owner + * + * @return string|null + */ + public function getGroup() { + + return null; + + } + + /** + * Returns a list of ACE's for this node. + * + * Each ACE has the following properties: + * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are + * currently the only supported privileges + * * 'principal', a url to the principal who owns the node + * * 'protected' (optional), indicating that this ACE is not allowed to + * be updated. + * + * @return array + */ + public function getACL() { + + return array( + array( + 'privilege' => '{DAV:}read', + 'principal' => $this->calendarInfo['principaluri'], + 'protected' => true, + ), + array( + 'privilege' => '{DAV:}write', + 'principal' => $this->calendarInfo['principaluri'], + 'protected' => true, + ), + array( + 'privilege' => '{DAV:}read', + 'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-write', + 'protected' => true, + ), + array( + 'privilege' => '{DAV:}write', + 'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-write', + 'protected' => true, + ), + array( + 'privilege' => '{DAV:}read', + 'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-read', + 'protected' => true, + ), + + ); + + } + + /** + * Updates the ACL + * + * This method will receive a list of new ACE's. + * + * @param array $acl + * @return void + */ + public function setACL(array $acl) { + + throw new Sabre_DAV_Exception_MethodNotAllowed('Changing ACL is not yet supported'); + + } + + /** + * Returns the list of supported privileges for this node. + * + * The returned data structure is a list of nested privileges. + * See Sabre_DAVACL_Plugin::getDefaultSupportedPrivilegeSet for a simple + * standard structure. + * + * If null is returned from this method, the default privilege set is used, + * which is fine for most common usecases. + * + * @return array|null + */ + public function getSupportedPrivilegeSet() { + + return null; + + } + +} + diff --git a/3rdparty/Sabre/CalDAV/CalendarQueryParser.php b/3rdparty/Sabre/CalDAV/CalendarQueryParser.php new file mode 100755 index 00000000000..bd0d343382f --- /dev/null +++ b/3rdparty/Sabre/CalDAV/CalendarQueryParser.php @@ -0,0 +1,296 @@ +<?php + +/** + * Parses the calendar-query report request body. + * + * Whoever designed this format, and the CalDAV equivalent even more so, + * has no feel for design. + * + * @package Sabre + * @subpackage CalDAV + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. + * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License + */ +class Sabre_CalDAV_CalendarQueryParser { + + /** + * List of requested properties the client wanted + * + * @var array + */ + public $requestedProperties; + + /** + * List of property/component filters. + * + * @var array + */ + public $filters; + + /** + * This property will contain null if CALDAV:expand was not specified, + * otherwise it will contain an array with 2 elements (start, end). Each + * contain a DateTime object. + * + * If expand is specified, recurring calendar objects are to be expanded + * into their individual components, and only the components that fall + * within the specified time-range are to be returned. + * + * For more details, see rfc4791, section 9.6.5. + * + * @var null|array + */ + public $expand; + + /** + * DOM Document + * + * @var DOMDocument + */ + protected $dom; + + /** + * DOM XPath object + * + * @var DOMXPath + */ + protected $xpath; + + /** + * Creates the parser + * + * @param DOMDocument $dom + */ + public function __construct(DOMDocument $dom) { + + $this->dom = $dom; + + $this->xpath = new DOMXPath($dom); + $this->xpath->registerNameSpace('cal',Sabre_CalDAV_Plugin::NS_CALDAV); + $this->xpath->registerNameSpace('dav','urn:DAV'); + + } + + /** + * Parses the request. + * + * @return void + */ + public function parse() { + + $filterNode = null; + + $filter = $this->xpath->query('/cal:calendar-query/cal:filter'); + if ($filter->length !== 1) { + throw new Sabre_DAV_Exception_BadRequest('Only one filter element is allowed'); + } + + $compFilters = $this->parseCompFilters($filter->item(0)); + if (count($compFilters)!==1) { + throw new Sabre_DAV_Exception_BadRequest('There must be exactly 1 top-level comp-filter.'); + } + + $this->filters = $compFilters[0]; + $this->requestedProperties = array_keys(Sabre_DAV_XMLUtil::parseProperties($this->dom->firstChild)); + + $expand = $this->xpath->query('/cal:calendar-query/dav:prop/cal:calendar-data/cal:expand'); + if ($expand->length>0) { + $this->expand = $this->parseExpand($expand->item(0)); + } + + + } + + /** + * Parses all the 'comp-filter' elements from a node + * + * @param DOMElement $parentNode + * @return array + */ + protected function parseCompFilters(DOMElement $parentNode) { + + $compFilterNodes = $this->xpath->query('cal:comp-filter', $parentNode); + $result = array(); + + for($ii=0; $ii < $compFilterNodes->length; $ii++) { + + $compFilterNode = $compFilterNodes->item($ii); + + $compFilter = array(); + $compFilter['name'] = $compFilterNode->getAttribute('name'); + $compFilter['is-not-defined'] = $this->xpath->query('cal:is-not-defined', $compFilterNode)->length>0; + $compFilter['comp-filters'] = $this->parseCompFilters($compFilterNode); + $compFilter['prop-filters'] = $this->parsePropFilters($compFilterNode); + $compFilter['time-range'] = $this->parseTimeRange($compFilterNode); + + if ($compFilter['time-range'] && !in_array($compFilter['name'],array( + 'VEVENT', + 'VTODO', + 'VJOURNAL', + 'VFREEBUSY', + 'VALARM', + ))) { + throw new Sabre_DAV_Exception_BadRequest('The time-range filter is not defined for the ' . $compFilter['name'] . ' component'); + }; + + $result[] = $compFilter; + + } + + return $result; + + } + + /** + * Parses all the prop-filter elements from a node + * + * @param DOMElement $parentNode + * @return array + */ + protected function parsePropFilters(DOMElement $parentNode) { + + $propFilterNodes = $this->xpath->query('cal:prop-filter', $parentNode); + $result = array(); + + for ($ii=0; $ii < $propFilterNodes->length; $ii++) { + + $propFilterNode = $propFilterNodes->item($ii); + $propFilter = array(); + $propFilter['name'] = $propFilterNode->getAttribute('name'); + $propFilter['is-not-defined'] = $this->xpath->query('cal:is-not-defined', $propFilterNode)->length>0; + $propFilter['param-filters'] = $this->parseParamFilters($propFilterNode); + $propFilter['text-match'] = $this->parseTextMatch($propFilterNode); + $propFilter['time-range'] = $this->parseTimeRange($propFilterNode); + + $result[] = $propFilter; + + } + + return $result; + + } + + /** + * Parses the param-filter element + * + * @param DOMElement $parentNode + * @return array + */ + protected function parseParamFilters(DOMElement $parentNode) { + + $paramFilterNodes = $this->xpath->query('cal:param-filter', $parentNode); + $result = array(); + + for($ii=0;$ii<$paramFilterNodes->length;$ii++) { + + $paramFilterNode = $paramFilterNodes->item($ii); + $paramFilter = array(); + $paramFilter['name'] = $paramFilterNode->getAttribute('name'); + $paramFilter['is-not-defined'] = $this->xpath->query('cal:is-not-defined', $paramFilterNode)->length>0; + $paramFilter['text-match'] = $this->parseTextMatch($paramFilterNode); + + $result[] = $paramFilter; + + } + + return $result; + + } + + /** + * Parses the text-match element + * + * @param DOMElement $parentNode + * @return array|null + */ + protected function parseTextMatch(DOMElement $parentNode) { + + $textMatchNodes = $this->xpath->query('cal:text-match', $parentNode); + + if ($textMatchNodes->length === 0) + return null; + + $textMatchNode = $textMatchNodes->item(0); + $negateCondition = $textMatchNode->getAttribute('negate-condition'); + $negateCondition = $negateCondition==='yes'; + $collation = $textMatchNode->getAttribute('collation'); + if (!$collation) $collation = 'i;ascii-casemap'; + + return array( + 'negate-condition' => $negateCondition, + 'collation' => $collation, + 'value' => $textMatchNode->nodeValue + ); + + } + + /** + * Parses the time-range element + * + * @param DOMElement $parentNode + * @return array|null + */ + protected function parseTimeRange(DOMElement $parentNode) { + + $timeRangeNodes = $this->xpath->query('cal:time-range', $parentNode); + if ($timeRangeNodes->length === 0) { + return null; + } + + $timeRangeNode = $timeRangeNodes->item(0); + + if ($start = $timeRangeNode->getAttribute('start')) { + $start = Sabre_VObject_DateTimeParser::parseDateTime($start); + } else { + $start = null; + } + if ($end = $timeRangeNode->getAttribute('end')) { + $end = Sabre_VObject_DateTimeParser::parseDateTime($end); + } else { + $end = null; + } + + if (!is_null($start) && !is_null($end) && $end <= $start) { + throw new Sabre_DAV_Exception_BadRequest('The end-date must be larger than the start-date in the time-range filter'); + } + + return array( + 'start' => $start, + 'end' => $end, + ); + + } + + /** + * Parses the CALDAV:expand element + * + * @param DOMElement $parentNode + * @return void + */ + protected function parseExpand(DOMElement $parentNode) { + + $start = $parentNode->getAttribute('start'); + if(!$start) { + throw new Sabre_DAV_Exception_BadRequest('The "start" attribute is required for the CALDAV:expand element'); + } + $start = Sabre_VObject_DateTimeParser::parseDateTime($start); + + $end = $parentNode->getAttribute('end'); + if(!$end) { + throw new Sabre_DAV_Exception_BadRequest('The "end" attribute is required for the CALDAV:expand element'); + } + $end = Sabre_VObject_DateTimeParser::parseDateTime($end); + + if ($end <= $start) { + throw new Sabre_DAV_Exception_BadRequest('The end-date must be larger than the start-date in the expand element.'); + } + + return array( + 'start' => $start, + 'end' => $end, + ); + + } + +} diff --git a/3rdparty/Sabre/CalDAV/CalendarQueryValidator.php b/3rdparty/Sabre/CalDAV/CalendarQueryValidator.php new file mode 100755 index 00000000000..1bb6b5d53fa --- /dev/null +++ b/3rdparty/Sabre/CalDAV/CalendarQueryValidator.php @@ -0,0 +1,347 @@ +<?php + +/** + * CalendarQuery Validator + * + * This class is responsible for checking if an iCalendar object matches a set + * of filters. The main function to do this is 'validate'. + * + * This is used to determine which icalendar objects should be returned for a + * calendar-query REPORT request. + * + * @package Sabre + * @subpackage CalDAV + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. + * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License + */ +class Sabre_CalDAV_CalendarQueryValidator { + + /** + * Verify if a list of filters applies to the calendar data object + * + * The list of filters must be formatted as parsed by Sabre_CalDAV_CalendarQueryParser + * + * @param Sabre_VObject_Component $vObject + * @param array $filters + * @return bool + */ + public function validate(Sabre_VObject_Component $vObject,array $filters) { + + // The top level object is always a component filter. + // We'll parse it manually, as it's pretty simple. + if ($vObject->name !== $filters['name']) { + return false; + } + + return + $this->validateCompFilters($vObject, $filters['comp-filters']) && + $this->validatePropFilters($vObject, $filters['prop-filters']); + + + } + + /** + * This method checks the validity of comp-filters. + * + * A list of comp-filters needs to be specified. Also the parent of the + * component we're checking should be specified, not the component to check + * itself. + * + * @param Sabre_VObject_Component $parent + * @param array $filters + * @return bool + */ + protected function validateCompFilters(Sabre_VObject_Component $parent, array $filters) { + + foreach($filters as $filter) { + + $isDefined = isset($parent->$filter['name']); + + if ($filter['is-not-defined']) { + + if ($isDefined) { + return false; + } else { + continue; + } + + } + if (!$isDefined) { + return false; + } + + if ($filter['time-range']) { + foreach($parent->$filter['name'] as $subComponent) { + if ($this->validateTimeRange($subComponent, $filter['time-range']['start'], $filter['time-range']['end'])) { + continue 2; + } + } + return false; + } + + if (!$filter['comp-filters'] && !$filter['prop-filters']) { + continue; + } + + // If there are sub-filters, we need to find at least one component + // for which the subfilters hold true. + foreach($parent->$filter['name'] as $subComponent) { + + if ( + $this->validateCompFilters($subComponent, $filter['comp-filters']) && + $this->validatePropFilters($subComponent, $filter['prop-filters'])) { + // We had a match, so this comp-filter succeeds + continue 2; + } + + } + + // If we got here it means there were sub-comp-filters or + // sub-prop-filters and there was no match. This means this filter + // needs to return false. + return false; + + } + + // If we got here it means we got through all comp-filters alive so the + // filters were all true. + return true; + + } + + /** + * This method checks the validity of prop-filters. + * + * A list of prop-filters needs to be specified. Also the parent of the + * property we're checking should be specified, not the property to check + * itself. + * + * @param Sabre_VObject_Component $parent + * @param array $filters + * @return bool + */ + protected function validatePropFilters(Sabre_VObject_Component $parent, array $filters) { + + foreach($filters as $filter) { + + $isDefined = isset($parent->$filter['name']); + + if ($filter['is-not-defined']) { + + if ($isDefined) { + return false; + } else { + continue; + } + + } + if (!$isDefined) { + return false; + } + + if ($filter['time-range']) { + foreach($parent->$filter['name'] as $subComponent) { + if ($this->validateTimeRange($subComponent, $filter['time-range']['start'], $filter['time-range']['end'])) { + continue 2; + } + } + return false; + } + + if (!$filter['param-filters'] && !$filter['text-match']) { + continue; + } + + // If there are sub-filters, we need to find at least one property + // for which the subfilters hold true. + foreach($parent->$filter['name'] as $subComponent) { + + if( + $this->validateParamFilters($subComponent, $filter['param-filters']) && + (!$filter['text-match'] || $this->validateTextMatch($subComponent, $filter['text-match'])) + ) { + // We had a match, so this prop-filter succeeds + continue 2; + } + + } + + // If we got here it means there were sub-param-filters or + // text-match filters and there was no match. This means the + // filter needs to return false. + return false; + + } + + // If we got here it means we got through all prop-filters alive so the + // filters were all true. + return true; + + } + + /** + * This method checks the validity of param-filters. + * + * A list of param-filters needs to be specified. Also the parent of the + * parameter we're checking should be specified, not the parameter to check + * itself. + * + * @param Sabre_VObject_Property $parent + * @param array $filters + * @return bool + */ + protected function validateParamFilters(Sabre_VObject_Property $parent, array $filters) { + + foreach($filters as $filter) { + + $isDefined = isset($parent[$filter['name']]); + + if ($filter['is-not-defined']) { + + if ($isDefined) { + return false; + } else { + continue; + } + + } + if (!$isDefined) { + return false; + } + + if (!$filter['text-match']) { + continue; + } + + // If there are sub-filters, we need to find at least one parameter + // for which the subfilters hold true. + foreach($parent[$filter['name']] as $subParam) { + + if($this->validateTextMatch($subParam,$filter['text-match'])) { + // We had a match, so this param-filter succeeds + continue 2; + } + + } + + // If we got here it means there was a text-match filter and there + // were no matches. This means the filter needs to return false. + return false; + + } + + // If we got here it means we got through all param-filters alive so the + // filters were all true. + return true; + + } + + /** + * This method checks the validity of a text-match. + * + * A single text-match should be specified as well as the specific property + * or parameter we need to validate. + * + * @param Sabre_VObject_Node $parent + * @param array $textMatch + * @return bool + */ + protected function validateTextMatch(Sabre_VObject_Node $parent, array $textMatch) { + + $value = (string)$parent; + + $isMatching = Sabre_DAV_StringUtil::textMatch($value, $textMatch['value'], $textMatch['collation']); + + return ($textMatch['negate-condition'] xor $isMatching); + + } + + /** + * Validates if a component matches the given time range. + * + * This is all based on the rules specified in rfc4791, which are quite + * complex. + * + * @param Sabre_VObject_Node $component + * @param DateTime $start + * @param DateTime $end + * @return bool + */ + protected function validateTimeRange(Sabre_VObject_Node $component, $start, $end) { + + if (is_null($start)) { + $start = new DateTime('1900-01-01'); + } + if (is_null($end)) { + $end = new DateTime('3000-01-01'); + } + + switch($component->name) { + + case 'VEVENT' : + case 'VTODO' : + case 'VJOURNAL' : + + return $component->isInTimeRange($start, $end); + + case 'VALARM' : + + // If the valarm is wrapped in a recurring event, we need to + // expand the recursions, and validate each. + // + // Our datamodel doesn't easily allow us to do this straight + // in the VALARM component code, so this is a hack, and an + // expensive one too. + if ($component->parent->name === 'VEVENT' && $component->parent->RRULE) { + // Fire up the iterator! + $it = new Sabre_VObject_RecurrenceIterator($component->parent->parent, (string)$component->parent->UID); + while($it->valid()) { + $expandedEvent = $it->getEventObject(); + + // We need to check from these expanded alarms, which + // one is the first to trigger. Based on this, we can + // determine if we can 'give up' expanding events. + $firstAlarm = null; + foreach($expandedEvent->VALARM as $expandedAlarm) { + $effectiveTrigger = $expandedAlarm->getEffectiveTriggerTime(); + if (!$firstAlarm || $effectiveTrigger < $firstAlarm) { + $firstAlarm = $effectiveTrigger; + } + if ($expandedAlarm->isInTimeRange($start, $end)) { + return true; + } + + } + if ($firstAlarm > $end) { + return false; + } + $it->next(); + } + return false; + } else { + return $component->isInTimeRange($start, $end); + } + + case 'VFREEBUSY' : + throw new Sabre_DAV_Exception_NotImplemented('time-range filters are currently not supported on ' . $component->name . ' components'); + + case 'COMPLETED' : + case 'CREATED' : + case 'DTEND' : + case 'DTSTAMP' : + case 'DTSTART' : + case 'DUE' : + case 'LAST-MODIFIED' : + return ($start <= $component->getDateTime() && $end >= $component->getDateTime()); + + + + default : + throw new Sabre_DAV_Exception_BadRequest('You cannot create a time-range filter on a ' . $component->name . ' component'); + + } + + } + +} diff --git a/3rdparty/Sabre/CalDAV/CalendarRootNode.php b/3rdparty/Sabre/CalDAV/CalendarRootNode.php new file mode 100755 index 00000000000..3907913cc78 --- /dev/null +++ b/3rdparty/Sabre/CalDAV/CalendarRootNode.php @@ -0,0 +1,75 @@ +<?php + +/** + * Users collection + * + * This object is responsible for generating a collection of users. + * + * @package Sabre + * @subpackage CalDAV + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. + * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License + */ +class Sabre_CalDAV_CalendarRootNode extends Sabre_DAVACL_AbstractPrincipalCollection { + + /** + * CalDAV backend + * + * @var Sabre_CalDAV_Backend_Abstract + */ + protected $caldavBackend; + + /** + * Constructor + * + * This constructor needs both an authentication and a caldav backend. + * + * By default this class will show a list of calendar collections for + * principals in the 'principals' collection. If your main principals are + * actually located in a different path, use the $principalPrefix argument + * to override this. + * + * + * @param Sabre_DAVACL_IPrincipalBackend $principalBackend + * @param Sabre_CalDAV_Backend_Abstract $caldavBackend + * @param string $principalPrefix + */ + public function __construct(Sabre_DAVACL_IPrincipalBackend $principalBackend,Sabre_CalDAV_Backend_Abstract $caldavBackend, $principalPrefix = 'principals') { + + parent::__construct($principalBackend, $principalPrefix); + $this->caldavBackend = $caldavBackend; + + } + + /** + * Returns the nodename + * + * We're overriding this, because the default will be the 'principalPrefix', + * and we want it to be Sabre_CalDAV_Plugin::CALENDAR_ROOT + * + * @return string + */ + public function getName() { + + return Sabre_CalDAV_Plugin::CALENDAR_ROOT; + + } + + /** + * This method returns a node for a principal. + * + * The passed array contains principal information, and is guaranteed to + * at least contain a uri item. Other properties may or may not be + * supplied by the authentication backend. + * + * @param array $principal + * @return Sabre_DAV_INode + */ + public function getChildForPrincipal(array $principal) { + + return new Sabre_CalDAV_UserCalendars($this->principalBackend, $this->caldavBackend, $principal['uri']); + + } + +} diff --git a/3rdparty/Sabre/CalDAV/ICSExportPlugin.php b/3rdparty/Sabre/CalDAV/ICSExportPlugin.php new file mode 100755 index 00000000000..ec42b406b2f --- /dev/null +++ b/3rdparty/Sabre/CalDAV/ICSExportPlugin.php @@ -0,0 +1,139 @@ +<?php + +/** + * ICS Exporter + * + * This plugin adds the ability to export entire calendars as .ics files. + * This is useful for clients that don't support CalDAV yet. They often do + * support ics files. + * + * @package Sabre + * @subpackage CalDAV + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. + * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License + */ +class Sabre_CalDAV_ICSExportPlugin extends Sabre_DAV_ServerPlugin { + + /** + * Reference to Server class + * + * @var Sabre_DAV_Server + */ + private $server; + + /** + * Initializes the plugin and registers event handlers + * + * @param Sabre_DAV_Server $server + * @return void + */ + public function initialize(Sabre_DAV_Server $server) { + + $this->server = $server; + $this->server->subscribeEvent('beforeMethod',array($this,'beforeMethod'), 90); + + } + + /** + * 'beforeMethod' event handles. This event handles intercepts GET requests ending + * with ?export + * + * @param string $method + * @param string $uri + * @return bool + */ + public function beforeMethod($method, $uri) { + + if ($method!='GET') return; + if ($this->server->httpRequest->getQueryString()!='export') return; + + // splitting uri + list($uri) = explode('?',$uri,2); + + $node = $this->server->tree->getNodeForPath($uri); + + if (!($node instanceof Sabre_CalDAV_Calendar)) return; + + // Checking ACL, if available. + if ($aclPlugin = $this->server->getPlugin('acl')) { + $aclPlugin->checkPrivileges($uri, '{DAV:}read'); + } + + $this->server->httpResponse->setHeader('Content-Type','text/calendar'); + $this->server->httpResponse->sendStatus(200); + + $nodes = $this->server->getPropertiesForPath($uri, array( + '{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}calendar-data', + ),1); + + $this->server->httpResponse->sendBody($this->generateICS($nodes)); + + // Returning false to break the event chain + return false; + + } + + /** + * Merges all calendar objects, and builds one big ics export + * + * @param array $nodes + * @return string + */ + public function generateICS(array $nodes) { + + $calendar = new Sabre_VObject_Component('vcalendar'); + $calendar->version = '2.0'; + if (Sabre_DAV_Server::$exposeVersion) { + $calendar->prodid = '-//SabreDAV//SabreDAV ' . Sabre_DAV_Version::VERSION . '//EN'; + } else { + $calendar->prodid = '-//SabreDAV//SabreDAV//EN'; + } + $calendar->calscale = 'GREGORIAN'; + + $collectedTimezones = array(); + + $timezones = array(); + $objects = array(); + + foreach($nodes as $node) { + + if (!isset($node[200]['{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}calendar-data'])) { + continue; + } + $nodeData = $node[200]['{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}calendar-data']; + + $nodeComp = Sabre_VObject_Reader::read($nodeData); + + foreach($nodeComp->children() as $child) { + + switch($child->name) { + case 'VEVENT' : + case 'VTODO' : + case 'VJOURNAL' : + $objects[] = $child; + break; + + // VTIMEZONE is special, because we need to filter out the duplicates + case 'VTIMEZONE' : + // Naively just checking tzid. + if (in_array((string)$child->TZID, $collectedTimezones)) continue; + + $timezones[] = $child; + $collectedTimezones[] = $child->TZID; + break; + + } + + } + + } + + foreach($timezones as $tz) $calendar->add($tz); + foreach($objects as $obj) $calendar->add($obj); + + return $calendar->serialize(); + + } + +} diff --git a/3rdparty/Sabre/CalDAV/ICalendar.php b/3rdparty/Sabre/CalDAV/ICalendar.php new file mode 100755 index 00000000000..15d51ebcf79 --- /dev/null +++ b/3rdparty/Sabre/CalDAV/ICalendar.php @@ -0,0 +1,18 @@ +<?php + +/** + * Calendar interface + * + * Implement this interface to allow a node to be recognized as an calendar. + * + * @package Sabre + * @subpackage CalDAV + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. + * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License + */ +interface Sabre_CalDAV_ICalendar extends Sabre_DAV_ICollection { + + + +} diff --git a/3rdparty/Sabre/CalDAV/ICalendarObject.php b/3rdparty/Sabre/CalDAV/ICalendarObject.php new file mode 100755 index 00000000000..280f982a310 --- /dev/null +++ b/3rdparty/Sabre/CalDAV/ICalendarObject.php @@ -0,0 +1,20 @@ +<?php + +/** + * CalendarObject interface +/** + * Extend the ICalendarObject interface to allow your custom nodes to be picked up as + * CalendarObjects. + * + * Calendar objects are resources such as Events, Todo's or Journals. + * + * @package Sabre + * @subpackage CalDAV + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. + * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License + */ +interface Sabre_CalDAV_ICalendarObject extends Sabre_DAV_IFile { + +} + diff --git a/3rdparty/Sabre/CalDAV/Plugin.php b/3rdparty/Sabre/CalDAV/Plugin.php new file mode 100755 index 00000000000..d7d1d970518 --- /dev/null +++ b/3rdparty/Sabre/CalDAV/Plugin.php @@ -0,0 +1,829 @@ +<?php + +/** + * CalDAV plugin + * + * This plugin provides functionality added by CalDAV (RFC 4791) + * It implements new reports, and the MKCALENDAR method. + * + * @package Sabre + * @subpackage CalDAV + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. + * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License + */ +class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin { + + /** + * This is the official CalDAV namespace + */ + const NS_CALDAV = 'urn:ietf:params:xml:ns:caldav'; + + /** + * This is the namespace for the proprietary calendarserver extensions + */ + const NS_CALENDARSERVER = 'http://calendarserver.org/ns/'; + + /** + * The following constants are used to differentiate + * the various filters for the calendar-query report + */ + const FILTER_COMPFILTER = 1; + const FILTER_TIMERANGE = 3; + const FILTER_PROPFILTER = 4; + const FILTER_PARAMFILTER = 5; + const FILTER_TEXTMATCH = 6; + + /** + * The hardcoded root for calendar objects. It is unfortunate + * that we're stuck with it, but it will have to do for now + */ + const CALENDAR_ROOT = 'calendars'; + + /** + * Reference to server object + * + * @var Sabre_DAV_Server + */ + private $server; + + /** + * The email handler for invites and other scheduling messages. + * + * @var Sabre_CalDAV_Schedule_IMip + */ + protected $imipHandler; + + /** + * Sets the iMIP handler. + * + * iMIP = The email transport of iCalendar scheduling messages. Setting + * this is optional, but if you want the server to allow invites to be sent + * out, you must set a handler. + * + * Specifically iCal will plain assume that the server supports this. If + * the server doesn't, iCal will display errors when inviting people to + * events. + * + * @param Sabre_CalDAV_Schedule_IMip $imipHandler + * @return void + */ + public function setIMipHandler(Sabre_CalDAV_Schedule_IMip $imipHandler) { + + $this->imipHandler = $imipHandler; + + } + + /** + * Use this method to tell the server this plugin defines additional + * HTTP methods. + * + * This method is passed a uri. It should only return HTTP methods that are + * available for the specified uri. + * + * @param string $uri + * @return array + */ + public function getHTTPMethods($uri) { + + // The MKCALENDAR is only available on unmapped uri's, whose + // parents extend IExtendedCollection + list($parent, $name) = Sabre_DAV_URLUtil::splitPath($uri); + + $node = $this->server->tree->getNodeForPath($parent); + + if ($node instanceof Sabre_DAV_IExtendedCollection) { + try { + $node->getChild($name); + } catch (Sabre_DAV_Exception_NotFound $e) { + return array('MKCALENDAR'); + } + } + return array(); + + } + + /** + * Returns a list of features for the DAV: HTTP header. + * + * @return array + */ + public function getFeatures() { + + return array('calendar-access', 'calendar-proxy'); + + } + + /** + * Returns a plugin name. + * + * Using this name other plugins will be able to access other plugins + * using Sabre_DAV_Server::getPlugin + * + * @return string + */ + public function getPluginName() { + + return 'caldav'; + + } + + /** + * Returns a list of reports this plugin supports. + * + * This will be used in the {DAV:}supported-report-set property. + * Note that you still need to subscribe to the 'report' event to actually + * implement them + * + * @param string $uri + * @return array + */ + public function getSupportedReportSet($uri) { + + $node = $this->server->tree->getNodeForPath($uri); + + $reports = array(); + if ($node instanceof Sabre_CalDAV_ICalendar || $node instanceof Sabre_CalDAV_ICalendarObject) { + $reports[] = '{' . self::NS_CALDAV . '}calendar-multiget'; + $reports[] = '{' . self::NS_CALDAV . '}calendar-query'; + } + if ($node instanceof Sabre_CalDAV_ICalendar) { + $reports[] = '{' . self::NS_CALDAV . '}free-busy-query'; + } + return $reports; + + } + + /** + * Initializes the plugin + * + * @param Sabre_DAV_Server $server + * @return void + */ + public function initialize(Sabre_DAV_Server $server) { + + $this->server = $server; + + $server->subscribeEvent('unknownMethod',array($this,'unknownMethod')); + //$server->subscribeEvent('unknownMethod',array($this,'unknownMethod2'),1000); + $server->subscribeEvent('report',array($this,'report')); + $server->subscribeEvent('beforeGetProperties',array($this,'beforeGetProperties')); + $server->subscribeEvent('onHTMLActionsPanel', array($this,'htmlActionsPanel')); + $server->subscribeEvent('onBrowserPostAction', array($this,'browserPostAction')); + $server->subscribeEvent('beforeWriteContent', array($this, 'beforeWriteContent')); + $server->subscribeEvent('beforeCreateFile', array($this, 'beforeCreateFile')); + + $server->xmlNamespaces[self::NS_CALDAV] = 'cal'; + $server->xmlNamespaces[self::NS_CALENDARSERVER] = 'cs'; + + $server->propertyMap['{' . self::NS_CALDAV . '}supported-calendar-component-set'] = 'Sabre_CalDAV_Property_SupportedCalendarComponentSet'; + + $server->resourceTypeMapping['Sabre_CalDAV_ICalendar'] = '{urn:ietf:params:xml:ns:caldav}calendar'; + $server->resourceTypeMapping['Sabre_CalDAV_Schedule_IOutbox'] = '{urn:ietf:params:xml:ns:caldav}schedule-outbox'; + $server->resourceTypeMapping['Sabre_CalDAV_Principal_ProxyRead'] = '{http://calendarserver.org/ns/}calendar-proxy-read'; + $server->resourceTypeMapping['Sabre_CalDAV_Principal_ProxyWrite'] = '{http://calendarserver.org/ns/}calendar-proxy-write'; + + array_push($server->protectedProperties, + + '{' . self::NS_CALDAV . '}supported-calendar-component-set', + '{' . self::NS_CALDAV . '}supported-calendar-data', + '{' . self::NS_CALDAV . '}max-resource-size', + '{' . self::NS_CALDAV . '}min-date-time', + '{' . self::NS_CALDAV . '}max-date-time', + '{' . self::NS_CALDAV . '}max-instances', + '{' . self::NS_CALDAV . '}max-attendees-per-instance', + '{' . self::NS_CALDAV . '}calendar-home-set', + '{' . self::NS_CALDAV . '}supported-collation-set', + '{' . self::NS_CALDAV . '}calendar-data', + + // scheduling extension + '{' . self::NS_CALDAV . '}schedule-inbox-URL', + '{' . self::NS_CALDAV . '}schedule-outbox-URL', + '{' . self::NS_CALDAV . '}calendar-user-address-set', + '{' . self::NS_CALDAV . '}calendar-user-type', + + // CalendarServer extensions + '{' . self::NS_CALENDARSERVER . '}getctag', + '{' . self::NS_CALENDARSERVER . '}calendar-proxy-read-for', + '{' . self::NS_CALENDARSERVER . '}calendar-proxy-write-for' + + ); + } + + /** + * This function handles support for the MKCALENDAR method + * + * @param string $method + * @param string $uri + * @return bool + */ + public function unknownMethod($method, $uri) { + + switch ($method) { + case 'MKCALENDAR' : + $this->httpMkCalendar($uri); + // false is returned to stop the propagation of the + // unknownMethod event. + return false; + case 'POST' : + // Checking if we're talking to an outbox + try { + $node = $this->server->tree->getNodeForPath($uri); + } catch (Sabre_DAV_Exception_NotFound $e) { + return; + } + if (!$node instanceof Sabre_CalDAV_Schedule_IOutbox) + return; + + $this->outboxRequest($node); + return false; + + } + + } + + /** + * This functions handles REPORT requests specific to CalDAV + * + * @param string $reportName + * @param DOMNode $dom + * @return bool + */ + public function report($reportName,$dom) { + + switch($reportName) { + case '{'.self::NS_CALDAV.'}calendar-multiget' : + $this->calendarMultiGetReport($dom); + return false; + case '{'.self::NS_CALDAV.'}calendar-query' : + $this->calendarQueryReport($dom); + return false; + case '{'.self::NS_CALDAV.'}free-busy-query' : + $this->freeBusyQueryReport($dom); + return false; + + } + + + } + + /** + * This function handles the MKCALENDAR HTTP method, which creates + * a new calendar. + * + * @param string $uri + * @return void + */ + public function httpMkCalendar($uri) { + + // Due to unforgivable bugs in iCal, we're completely disabling MKCALENDAR support + // for clients matching iCal in the user agent + //$ua = $this->server->httpRequest->getHeader('User-Agent'); + //if (strpos($ua,'iCal/')!==false) { + // throw new Sabre_DAV_Exception_Forbidden('iCal has major bugs in it\'s RFC3744 support. Therefore we are left with no other choice but disabling this feature.'); + //} + + $body = $this->server->httpRequest->getBody(true); + $properties = array(); + + if ($body) { + + $dom = Sabre_DAV_XMLUtil::loadDOMDocument($body); + + foreach($dom->firstChild->childNodes as $child) { + + if (Sabre_DAV_XMLUtil::toClarkNotation($child)!=='{DAV:}set') continue; + foreach(Sabre_DAV_XMLUtil::parseProperties($child,$this->server->propertyMap) as $k=>$prop) { + $properties[$k] = $prop; + } + + } + } + + $resourceType = array('{DAV:}collection','{urn:ietf:params:xml:ns:caldav}calendar'); + + $this->server->createCollection($uri,$resourceType,$properties); + + $this->server->httpResponse->sendStatus(201); + $this->server->httpResponse->setHeader('Content-Length',0); + } + + /** + * beforeGetProperties + * + * This method handler is invoked before any after properties for a + * resource are fetched. This allows us to add in any CalDAV specific + * properties. + * + * @param string $path + * @param Sabre_DAV_INode $node + * @param array $requestedProperties + * @param array $returnedProperties + * @return void + */ + public function beforeGetProperties($path, Sabre_DAV_INode $node, &$requestedProperties, &$returnedProperties) { + + if ($node instanceof Sabre_DAVACL_IPrincipal) { + + // calendar-home-set property + $calHome = '{' . self::NS_CALDAV . '}calendar-home-set'; + if (in_array($calHome,$requestedProperties)) { + $principalId = $node->getName(); + $calendarHomePath = self::CALENDAR_ROOT . '/' . $principalId . '/'; + unset($requestedProperties[$calHome]); + $returnedProperties[200][$calHome] = new Sabre_DAV_Property_Href($calendarHomePath); + } + + // schedule-outbox-URL property + $scheduleProp = '{' . self::NS_CALDAV . '}schedule-outbox-URL'; + if (in_array($scheduleProp,$requestedProperties)) { + $principalId = $node->getName(); + $outboxPath = self::CALENDAR_ROOT . '/' . $principalId . '/outbox'; + unset($requestedProperties[$scheduleProp]); + $returnedProperties[200][$scheduleProp] = new Sabre_DAV_Property_Href($outboxPath); + } + + // calendar-user-address-set property + $calProp = '{' . self::NS_CALDAV . '}calendar-user-address-set'; + if (in_array($calProp,$requestedProperties)) { + + $addresses = $node->getAlternateUriSet(); + $addresses[] = $this->server->getBaseUri() . $node->getPrincipalUrl(); + unset($requestedProperties[$calProp]); + $returnedProperties[200][$calProp] = new Sabre_DAV_Property_HrefList($addresses, false); + + } + + // These two properties are shortcuts for ical to easily find + // other principals this principal has access to. + $propRead = '{' . self::NS_CALENDARSERVER . '}calendar-proxy-read-for'; + $propWrite = '{' . self::NS_CALENDARSERVER . '}calendar-proxy-write-for'; + if (in_array($propRead,$requestedProperties) || in_array($propWrite,$requestedProperties)) { + + $membership = $node->getGroupMembership(); + $readList = array(); + $writeList = array(); + + foreach($membership as $group) { + + $groupNode = $this->server->tree->getNodeForPath($group); + + // If the node is either ap proxy-read or proxy-write + // group, we grab the parent principal and add it to the + // list. + if ($groupNode instanceof Sabre_CalDAV_Principal_ProxyRead) { + list($readList[]) = Sabre_DAV_URLUtil::splitPath($group); + } + if ($groupNode instanceof Sabre_CalDAV_Principal_ProxyWrite) { + list($writeList[]) = Sabre_DAV_URLUtil::splitPath($group); + } + + } + if (in_array($propRead,$requestedProperties)) { + unset($requestedProperties[$propRead]); + $returnedProperties[200][$propRead] = new Sabre_DAV_Property_HrefList($readList); + } + if (in_array($propWrite,$requestedProperties)) { + unset($requestedProperties[$propWrite]); + $returnedProperties[200][$propWrite] = new Sabre_DAV_Property_HrefList($writeList); + } + + } + + } // instanceof IPrincipal + + + if ($node instanceof Sabre_CalDAV_ICalendarObject) { + // The calendar-data property is not supposed to be a 'real' + // property, but in large chunks of the spec it does act as such. + // Therefore we simply expose it as a property. + $calDataProp = '{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}calendar-data'; + if (in_array($calDataProp, $requestedProperties)) { + unset($requestedProperties[$calDataProp]); + $val = $node->get(); + if (is_resource($val)) + $val = stream_get_contents($val); + + // Taking out \r to not screw up the xml output + $returnedProperties[200][$calDataProp] = str_replace("\r","", $val); + + } + } + + } + + /** + * This function handles the calendar-multiget REPORT. + * + * This report is used by the client to fetch the content of a series + * of urls. Effectively avoiding a lot of redundant requests. + * + * @param DOMNode $dom + * @return void + */ + public function calendarMultiGetReport($dom) { + + $properties = array_keys(Sabre_DAV_XMLUtil::parseProperties($dom->firstChild)); + $hrefElems = $dom->getElementsByTagNameNS('urn:DAV','href'); + + $xpath = new DOMXPath($dom); + $xpath->registerNameSpace('cal',Sabre_CalDAV_Plugin::NS_CALDAV); + $xpath->registerNameSpace('dav','urn:DAV'); + + $expand = $xpath->query('/cal:calendar-multiget/dav:prop/cal:calendar-data/cal:expand'); + if ($expand->length>0) { + $expandElem = $expand->item(0); + $start = $expandElem->getAttribute('start'); + $end = $expandElem->getAttribute('end'); + if(!$start || !$end) { + throw new Sabre_DAV_Exception_BadRequest('The "start" and "end" attributes are required for the CALDAV:expand element'); + } + $start = Sabre_VObject_DateTimeParser::parseDateTime($start); + $end = Sabre_VObject_DateTimeParser::parseDateTime($end); + + if ($end <= $start) { + throw new Sabre_DAV_Exception_BadRequest('The end-date must be larger than the start-date in the expand element.'); + } + + $expand = true; + + } else { + + $expand = false; + + } + + foreach($hrefElems as $elem) { + $uri = $this->server->calculateUri($elem->nodeValue); + list($objProps) = $this->server->getPropertiesForPath($uri,$properties); + + if ($expand && isset($objProps[200]['{' . self::NS_CALDAV . '}calendar-data'])) { + $vObject = Sabre_VObject_Reader::read($objProps[200]['{' . self::NS_CALDAV . '}calendar-data']); + $vObject->expand($start, $end); + $objProps[200]['{' . self::NS_CALDAV . '}calendar-data'] = $vObject->serialize(); + } + + $propertyList[]=$objProps; + + } + + $this->server->httpResponse->sendStatus(207); + $this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8'); + $this->server->httpResponse->sendBody($this->server->generateMultiStatus($propertyList)); + + } + + /** + * This function handles the calendar-query REPORT + * + * This report is used by clients to request calendar objects based on + * complex conditions. + * + * @param DOMNode $dom + * @return void + */ + public function calendarQueryReport($dom) { + + $parser = new Sabre_CalDAV_CalendarQueryParser($dom); + $parser->parse(); + + $requestedCalendarData = true; + $requestedProperties = $parser->requestedProperties; + + if (!in_array('{urn:ietf:params:xml:ns:caldav}calendar-data', $requestedProperties)) { + + // We always retrieve calendar-data, as we need it for filtering. + $requestedProperties[] = '{urn:ietf:params:xml:ns:caldav}calendar-data'; + + // If calendar-data wasn't explicitly requested, we need to remove + // it after processing. + $requestedCalendarData = false; + } + + // These are the list of nodes that potentially match the requirement + $candidateNodes = $this->server->getPropertiesForPath( + $this->server->getRequestUri(), + $requestedProperties, + $this->server->getHTTPDepth(0) + ); + + $verifiedNodes = array(); + + $validator = new Sabre_CalDAV_CalendarQueryValidator(); + + foreach($candidateNodes as $node) { + + // If the node didn't have a calendar-data property, it must not be a calendar object + if (!isset($node[200]['{urn:ietf:params:xml:ns:caldav}calendar-data'])) + continue; + + $vObject = Sabre_VObject_Reader::read($node[200]['{urn:ietf:params:xml:ns:caldav}calendar-data']); + if ($validator->validate($vObject,$parser->filters)) { + + if (!$requestedCalendarData) { + unset($node[200]['{urn:ietf:params:xml:ns:caldav}calendar-data']); + } + if ($parser->expand) { + $vObject->expand($parser->expand['start'], $parser->expand['end']); + $node[200]['{' . self::NS_CALDAV . '}calendar-data'] = $vObject->serialize(); + } + $verifiedNodes[] = $node; + } + + } + + $this->server->httpResponse->sendStatus(207); + $this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8'); + $this->server->httpResponse->sendBody($this->server->generateMultiStatus($verifiedNodes)); + + } + + /** + * This method is responsible for parsing the request and generating the + * response for the CALDAV:free-busy-query REPORT. + * + * @param DOMNode $dom + * @return void + */ + protected function freeBusyQueryReport(DOMNode $dom) { + + $start = null; + $end = null; + + foreach($dom->firstChild->childNodes as $childNode) { + + $clark = Sabre_DAV_XMLUtil::toClarkNotation($childNode); + if ($clark == '{' . self::NS_CALDAV . '}time-range') { + $start = $childNode->getAttribute('start'); + $end = $childNode->getAttribute('end'); + break; + } + + } + if ($start) { + $start = Sabre_VObject_DateTimeParser::parseDateTime($start); + } + if ($end) { + $end = Sabre_VObject_DateTimeParser::parseDateTime($end); + } + + if (!$start && !$end) { + throw new Sabre_DAV_Exception_BadRequest('The freebusy report must have a time-range filter'); + } + $acl = $this->server->getPlugin('acl'); + + if (!$acl) { + throw new Sabre_DAV_Exception('The ACL plugin must be loaded for free-busy queries to work'); + } + $uri = $this->server->getRequestUri(); + $acl->checkPrivileges($uri,'{' . self::NS_CALDAV . '}read-free-busy'); + + $calendar = $this->server->tree->getNodeForPath($uri); + if (!$calendar instanceof Sabre_CalDAV_ICalendar) { + throw new Sabre_DAV_Exception_NotImplemented('The free-busy-query REPORT is only implemented on calendars'); + } + + $objects = array_map(function($child) { + $obj = $child->get(); + if (is_resource($obj)) { + $obj = stream_get_contents($obj); + } + return $obj; + }, $calendar->getChildren()); + + $generator = new Sabre_VObject_FreeBusyGenerator(); + $generator->setObjects($objects); + $generator->setTimeRange($start, $end); + $result = $generator->getResult(); + $result = $result->serialize(); + + $this->server->httpResponse->sendStatus(200); + $this->server->httpResponse->setHeader('Content-Type', 'text/calendar'); + $this->server->httpResponse->setHeader('Content-Length', strlen($result)); + $this->server->httpResponse->sendBody($result); + + } + + /** + * This method is triggered before a file gets updated with new content. + * + * This plugin uses this method to ensure that CalDAV objects receive + * valid calendar data. + * + * @param string $path + * @param Sabre_DAV_IFile $node + * @param resource $data + * @return void + */ + public function beforeWriteContent($path, Sabre_DAV_IFile $node, &$data) { + + if (!$node instanceof Sabre_CalDAV_ICalendarObject) + return; + + $this->validateICalendar($data); + + } + + /** + * This method is triggered before a new file is created. + * + * This plugin uses this method to ensure that newly created calendar + * objects contain valid calendar data. + * + * @param string $path + * @param resource $data + * @param Sabre_DAV_ICollection $parentNode + * @return void + */ + public function beforeCreateFile($path, &$data, Sabre_DAV_ICollection $parentNode) { + + if (!$parentNode instanceof Sabre_CalDAV_Calendar) + return; + + $this->validateICalendar($data); + + } + + /** + * Checks if the submitted iCalendar data is in fact, valid. + * + * An exception is thrown if it's not. + * + * @param resource|string $data + * @return void + */ + protected function validateICalendar(&$data) { + + // If it's a stream, we convert it to a string first. + if (is_resource($data)) { + $data = stream_get_contents($data); + } + + // Converting the data to unicode, if needed. + $data = Sabre_DAV_StringUtil::ensureUTF8($data); + + try { + + $vobj = Sabre_VObject_Reader::read($data); + + } catch (Sabre_VObject_ParseException $e) { + + throw new Sabre_DAV_Exception_UnsupportedMediaType('This resource only supports valid iCalendar 2.0 data. Parse error: ' . $e->getMessage()); + + } + + } + + /** + * This method handles POST requests to the schedule-outbox + * + * @param Sabre_CalDAV_Schedule_IOutbox $outboxNode + * @return void + */ + public function outboxRequest(Sabre_CalDAV_Schedule_IOutbox $outboxNode) { + + $originator = $this->server->httpRequest->getHeader('Originator'); + $recipients = $this->server->httpRequest->getHeader('Recipient'); + + if (!$originator) { + throw new Sabre_DAV_Exception_BadRequest('The Originator: header must be specified when making POST requests'); + } + if (!$recipients) { + throw new Sabre_DAV_Exception_BadRequest('The Recipient: header must be specified when making POST requests'); + } + + if (!preg_match('/^mailto:(.*)@(.*)$/', $originator)) { + throw new Sabre_DAV_Exception_BadRequest('Originator must start with mailto: and must be valid email address'); + } + $originator = substr($originator,7); + + $recipients = explode(',',$recipients); + foreach($recipients as $k=>$recipient) { + + $recipient = trim($recipient); + if (!preg_match('/^mailto:(.*)@(.*)$/', $recipient)) { + throw new Sabre_DAV_Exception_BadRequest('Recipients must start with mailto: and must be valid email address'); + } + $recipient = substr($recipient, 7); + $recipients[$k] = $recipient; + } + + // We need to make sure that 'originator' matches one of the email + // addresses of the selected principal. + $principal = $outboxNode->getOwner(); + $props = $this->server->getProperties($principal,array( + '{' . self::NS_CALDAV . '}calendar-user-address-set', + )); + + $addresses = array(); + if (isset($props['{' . self::NS_CALDAV . '}calendar-user-address-set'])) { + $addresses = $props['{' . self::NS_CALDAV . '}calendar-user-address-set']->getHrefs(); + } + + if (!in_array('mailto:' . $originator, $addresses)) { + throw new Sabre_DAV_Exception_Forbidden('The addresses specified in the Originator header did not match any addresses in the owners calendar-user-address-set header'); + } + + try { + $vObject = Sabre_VObject_Reader::read($this->server->httpRequest->getBody(true)); + } catch (Sabre_VObject_ParseException $e) { + throw new Sabre_DAV_Exception_BadRequest('The request body must be a valid iCalendar object. Parse error: ' . $e->getMessage()); + } + + // Checking for the object type + $componentType = null; + foreach($vObject->getComponents() as $component) { + if ($component->name !== 'VTIMEZONE') { + $componentType = $component->name; + break; + } + } + if (is_null($componentType)) { + throw new Sabre_DAV_Exception_BadRequest('We expected at least one VTODO, VJOURNAL, VFREEBUSY or VEVENT component'); + } + + // Validating the METHOD + $method = strtoupper((string)$vObject->METHOD); + if (!$method) { + throw new Sabre_DAV_Exception_BadRequest('A METHOD property must be specified in iTIP messages'); + } + + if (in_array($method, array('REQUEST','REPLY','ADD','CANCEL')) && $componentType==='VEVENT') { + $this->iMIPMessage($originator, $recipients, $vObject); + $this->server->httpResponse->sendStatus(200); + $this->server->httpResponse->sendBody('Messages sent'); + } else { + throw new Sabre_DAV_Exception_NotImplemented('This iTIP method is currently not implemented'); + } + + } + + /** + * Sends an iMIP message by email. + * + * @param string $originator + * @param array $recipients + * @param Sabre_VObject_Component $vObject + * @return void + */ + protected function iMIPMessage($originator, array $recipients, Sabre_VObject_Component $vObject) { + + if (!$this->imipHandler) { + throw new Sabre_DAV_Exception_NotImplemented('No iMIP handler is setup on this server.'); + } + $this->imipHandler->sendMessage($originator, $recipients, $vObject); + + } + + /** + * This method is used to generate HTML output for the + * Sabre_DAV_Browser_Plugin. This allows us to generate an interface users + * can use to create new calendars. + * + * @param Sabre_DAV_INode $node + * @param string $output + * @return bool + */ + public function htmlActionsPanel(Sabre_DAV_INode $node, &$output) { + + if (!$node instanceof Sabre_CalDAV_UserCalendars) + return; + + $output.= '<tr><td colspan="2"><form method="post" action=""> + <h3>Create new calendar</h3> + <input type="hidden" name="sabreAction" value="mkcalendar" /> + <label>Name (uri):</label> <input type="text" name="name" /><br /> + <label>Display name:</label> <input type="text" name="{DAV:}displayname" /><br /> + <input type="submit" value="create" /> + </form> + </td></tr>'; + + return false; + + } + + /** + * This method allows us to intercept the 'mkcalendar' sabreAction. This + * action enables the user to create new calendars from the browser plugin. + * + * @param string $uri + * @param string $action + * @param array $postVars + * @return bool + */ + public function browserPostAction($uri, $action, array $postVars) { + + if ($action!=='mkcalendar') + return; + + $resourceType = array('{DAV:}collection','{urn:ietf:params:xml:ns:caldav}calendar'); + $properties = array(); + if (isset($postVars['{DAV:}displayname'])) { + $properties['{DAV:}displayname'] = $postVars['{DAV:}displayname']; + } + $this->server->createCollection($uri . '/' . $postVars['name'],$resourceType,$properties); + return false; + + } + +} diff --git a/3rdparty/Sabre/CalDAV/Principal/Collection.php b/3rdparty/Sabre/CalDAV/Principal/Collection.php new file mode 100755 index 00000000000..abbefa5567a --- /dev/null +++ b/3rdparty/Sabre/CalDAV/Principal/Collection.php @@ -0,0 +1,31 @@ +<?php + +/** + * Principal collection + * + * This is an alternative collection to the standard ACL principal collection. + * This collection adds support for the calendar-proxy-read and + * calendar-proxy-write sub-principals, as defined by the caldav-proxy + * specification. + * + * @package Sabre + * @subpackage CalDAV + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. + * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License + */ +class Sabre_CalDAV_Principal_Collection extends Sabre_DAVACL_AbstractPrincipalCollection { + + /** + * Returns a child object based on principal information + * + * @param array $principalInfo + * @return Sabre_CalDAV_Principal_User + */ + public function getChildForPrincipal(array $principalInfo) { + + return new Sabre_CalDAV_Principal_User($this->principalBackend, $principalInfo); + + } + +} diff --git a/3rdparty/Sabre/CalDAV/Principal/ProxyRead.php b/3rdparty/Sabre/CalDAV/Principal/ProxyRead.php new file mode 100755 index 00000000000..4b3f035634a --- /dev/null +++ b/3rdparty/Sabre/CalDAV/Principal/ProxyRead.php @@ -0,0 +1,178 @@ +<?php + +/** + * ProxyRead principal + * + * This class represents a principal group, hosted under the main principal. + * This is needed to implement 'Calendar delegation' support. This class is + * instantiated by Sabre_CalDAV_Principal_User. + * + * @package Sabre + * @subpackage CalDAV + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. + * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License + */ +class Sabre_CalDAV_Principal_ProxyRead implements Sabre_DAVACL_IPrincipal { + + /** + * Principal information from the parent principal. + * + * @var array + */ + protected $principalInfo; + + /** + * Principal backend + * + * @var Sabre_DAVACL_IPrincipalBackend + */ + protected $principalBackend; + + /** + * Creates the object. + * + * Note that you MUST supply the parent principal information. + * + * @param Sabre_DAVACL_IPrincipalBackend $principalBackend + * @param array $principalInfo + */ + public function __construct(Sabre_DAVACL_IPrincipalBackend $principalBackend, array $principalInfo) { + + $this->principalInfo = $principalInfo; + $this->principalBackend = $principalBackend; + + } + + /** + * Returns this principals name. + * + * @return string + */ + public function getName() { + + return 'calendar-proxy-read'; + + } + + /** + * Returns the last modification time + * + * @return null + */ + public function getLastModified() { + + return null; + + } + + /** + * Deletes the current node + * + * @throws Sabre_DAV_Exception_Forbidden + * @return void + */ + public function delete() { + + throw new Sabre_DAV_Exception_Forbidden('Permission denied to delete node'); + + } + + /** + * Renames the node + * + * @throws Sabre_DAV_Exception_Forbidden + * @param string $name The new name + * @return void + */ + public function setName($name) { + + throw new Sabre_DAV_Exception_Forbidden('Permission denied to rename file'); + + } + + + /** + * Returns a list of alternative urls for a principal + * + * This can for example be an email address, or ldap url. + * + * @return array + */ + public function getAlternateUriSet() { + + return array(); + + } + + /** + * Returns the full principal url + * + * @return string + */ + public function getPrincipalUrl() { + + return $this->principalInfo['uri'] . '/' . $this->getName(); + + } + + /** + * Returns the list of group members + * + * If this principal is a group, this function should return + * all member principal uri's for the group. + * + * @return array + */ + public function getGroupMemberSet() { + + return $this->principalBackend->getGroupMemberSet($this->getPrincipalUrl()); + + } + + /** + * Returns the list of groups this principal is member of + * + * If this principal is a member of a (list of) groups, this function + * should return a list of principal uri's for it's members. + * + * @return array + */ + public function getGroupMembership() { + + return $this->principalBackend->getGroupMembership($this->getPrincipalUrl()); + + } + + /** + * Sets a list of group members + * + * If this principal is a group, this method sets all the group members. + * The list of members is always overwritten, never appended to. + * + * This method should throw an exception if the members could not be set. + * + * @param array $principals + * @return void + */ + public function setGroupMemberSet(array $principals) { + + $this->principalBackend->setGroupMemberSet($this->getPrincipalUrl(), $principals); + + } + + /** + * Returns the displayname + * + * This should be a human readable name for the principal. + * If none is available, return the nodename. + * + * @return string + */ + public function getDisplayName() { + + return $this->getName(); + + } + +} diff --git a/3rdparty/Sabre/CalDAV/Principal/ProxyWrite.php b/3rdparty/Sabre/CalDAV/Principal/ProxyWrite.php new file mode 100755 index 00000000000..dd0c2e86edd --- /dev/null +++ b/3rdparty/Sabre/CalDAV/Principal/ProxyWrite.php @@ -0,0 +1,178 @@ +<?php + +/** + * ProxyWrite principal + * + * This class represents a principal group, hosted under the main principal. + * This is needed to implement 'Calendar delegation' support. This class is + * instantiated by Sabre_CalDAV_Principal_User. + * + * @package Sabre + * @subpackage CalDAV + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. + * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License + */ +class Sabre_CalDAV_Principal_ProxyWrite implements Sabre_DAVACL_IPrincipal { + + /** + * Parent principal information + * + * @var array + */ + protected $principalInfo; + + /** + * Principal Backend + * + * @var Sabre_DAVACL_IPrincipalBackend + */ + protected $principalBackend; + + /** + * Creates the object + * + * Note that you MUST supply the parent principal information. + * + * @param Sabre_DAVACL_IPrincipalBackend $principalBackend + * @param array $principalInfo + */ + public function __construct(Sabre_DAVACL_IPrincipalBackend $principalBackend, array $principalInfo) { + + $this->principalInfo = $principalInfo; + $this->principalBackend = $principalBackend; + + } + + /** + * Returns this principals name. + * + * @return string + */ + public function getName() { + + return 'calendar-proxy-write'; + + } + + /** + * Returns the last modification time + * + * @return null + */ + public function getLastModified() { + + return null; + + } + + /** + * Deletes the current node + * + * @throws Sabre_DAV_Exception_Forbidden + * @return void + */ + public function delete() { + + throw new Sabre_DAV_Exception_Forbidden('Permission denied to delete node'); + + } + + /** + * Renames the node + * + * @throws Sabre_DAV_Exception_Forbidden + * @param string $name The new name + * @return void + */ + public function setName($name) { + + throw new Sabre_DAV_Exception_Forbidden('Permission denied to rename file'); + + } + + + /** + * Returns a list of alternative urls for a principal + * + * This can for example be an email address, or ldap url. + * + * @return array + */ + public function getAlternateUriSet() { + + return array(); + + } + + /** + * Returns the full principal url + * + * @return string + */ + public function getPrincipalUrl() { + + return $this->principalInfo['uri'] . '/' . $this->getName(); + + } + + /** + * Returns the list of group members + * + * If this principal is a group, this function should return + * all member principal uri's for the group. + * + * @return array + */ + public function getGroupMemberSet() { + + return $this->principalBackend->getGroupMemberSet($this->getPrincipalUrl()); + + } + + /** + * Returns the list of groups this principal is member of + * + * If this principal is a member of a (list of) groups, this function + * should return a list of principal uri's for it's members. + * + * @return array + */ + public function getGroupMembership() { + + return $this->principalBackend->getGroupMembership($this->getPrincipalUrl()); + + } + + /** + * Sets a list of group members + * + * If this principal is a group, this method sets all the group members. + * The list of members is always overwritten, never appended to. + * + * This method should throw an exception if the members could not be set. + * + * @param array $principals + * @return void + */ + public function setGroupMemberSet(array $principals) { + + $this->principalBackend->setGroupMemberSet($this->getPrincipalUrl(), $principals); + + } + + /** + * Returns the displayname + * + * This should be a human readable name for the principal. + * If none is available, return the nodename. + * + * @return string + */ + public function getDisplayName() { + + return $this->getName(); + + } + +} diff --git a/3rdparty/Sabre/CalDAV/Principal/User.php b/3rdparty/Sabre/CalDAV/Principal/User.php new file mode 100755 index 00000000000..8453b877a73 --- /dev/null +++ b/3rdparty/Sabre/CalDAV/Principal/User.php @@ -0,0 +1,132 @@ +<?php + +/** + * CalDAV principal + * + * This is a standard user-principal for CalDAV. This principal is also a + * collection and returns the caldav-proxy-read and caldav-proxy-write child + * principals. + * + * @package Sabre + * @subpackage CalDAV + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. + * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License + */ +class Sabre_CalDAV_Principal_User extends Sabre_DAVACL_Principal implements Sabre_DAV_ICollection { + + /** + * Creates a new file in the directory + * + * @param string $name Name of the file + * @param resource $data Initial payload, passed as a readable stream resource. + * @throws Sabre_DAV_Exception_Forbidden + * @return void + */ + public function createFile($name, $data = null) { + + throw new Sabre_DAV_Exception_Forbidden('Permission denied to create file (filename ' . $name . ')'); + + } + + /** + * Creates a new subdirectory + * + * @param string $name + * @throws Sabre_DAV_Exception_Forbidden + * @return void + */ + public function createDirectory($name) { + + throw new Sabre_DAV_Exception_Forbidden('Permission denied to create directory'); + + } + + /** + * Returns a specific child node, referenced by its name + * + * @param string $name + * @return Sabre_DAV_INode + */ + public function getChild($name) { + + $principal = $this->principalBackend->getPrincipalByPath($this->getPrincipalURL() . '/' . $name); + if (!$principal) { + throw new Sabre_DAV_Exception_NotFound('Node with name ' . $name . ' was not found'); + } + if ($name === 'calendar-proxy-read') + return new Sabre_CalDAV_Principal_ProxyRead($this->principalBackend, $this->principalProperties); + + if ($name === 'calendar-proxy-write') + return new Sabre_CalDAV_Principal_ProxyWrite($this->principalBackend, $this->principalProperties); + + throw new Sabre_DAV_Exception_NotFound('Node with name ' . $name . ' was not found'); + + } + + /** + * Returns an array with all the child nodes + * + * @return Sabre_DAV_INode[] + */ + public function getChildren() { + + $r = array(); + if ($this->principalBackend->getPrincipalByPath($this->getPrincipalURL() . '/calendar-proxy-read')) { + $r[] = new Sabre_CalDAV_Principal_ProxyRead($this->principalBackend, $this->principalProperties); + } + if ($this->principalBackend->getPrincipalByPath($this->getPrincipalURL() . '/calendar-proxy-write')) { + $r[] = new Sabre_CalDAV_Principal_ProxyWrite($this->principalBackend, $this->principalProperties); + } + + return $r; + + } + + /** + * Returns whether or not the child node exists + * + * @param string $name + * @return bool + */ + public function childExists($name) { + + try { + $this->getChild($name); + return true; + } catch (Sabre_DAV_Exception_NotFound $e) { + return false; + } + + } + + /** + * Returns a list of ACE's for this node. + * + * Each ACE has the following properties: + * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are + * currently the only supported privileges + * * 'principal', a url to the principal who owns the node + * * 'protected' (optional), indicating that this ACE is not allowed to + * be updated. + * + * @return array + */ + public function getACL() { + + $acl = parent::getACL(); + $acl[] = array( + 'privilege' => '{DAV:}read', + 'principal' => $this->principalProperties['uri'] . '/calendar-proxy-read', + 'protected' => true, + ); + $acl[] = array( + 'privilege' => '{DAV:}read', + 'principal' => $this->principalProperties['uri'] . '/calendar-proxy-write', + 'protected' => true, + ); + return $acl; + + } + +} diff --git a/3rdparty/Sabre/CalDAV/Property/SupportedCalendarComponentSet.php b/3rdparty/Sabre/CalDAV/Property/SupportedCalendarComponentSet.php new file mode 100755 index 00000000000..2ea078d7dac --- /dev/null +++ b/3rdparty/Sabre/CalDAV/Property/SupportedCalendarComponentSet.php @@ -0,0 +1,85 @@ +<?php + +/** + * Supported component set property + * + * This property is a representation of the supported-calendar_component-set + * property in the CalDAV namespace. It simply requires an array of components, + * such as VEVENT, VTODO + * + * @package Sabre + * @subpackage CalDAV + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. + * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License + */ +class Sabre_CalDAV_Property_SupportedCalendarComponentSet extends Sabre_DAV_Property { + + /** + * List of supported components, such as "VEVENT, VTODO" + * + * @var array + */ + private $components; + + /** + * Creates the property + * + * @param array $components + */ + public function __construct(array $components) { + + $this->components = $components; + + } + + /** + * Returns the list of supported components + * + * @return array + */ + public function getValue() { + + return $this->components; + + } + + /** + * Serializes the property in a DOMDocument + * + * @param Sabre_DAV_Server $server + * @param DOMElement $node + * @return void + */ + public function serialize(Sabre_DAV_Server $server,DOMElement $node) { + + $doc = $node->ownerDocument; + foreach($this->components as $component) { + + $xcomp = $doc->createElement('cal:comp'); + $xcomp->setAttribute('name',$component); + $node->appendChild($xcomp); + + } + + } + + /** + * Unserializes the DOMElement back into a Property class. + * + * @param DOMElement $node + * @return Sabre_CalDAV_Property_SupportedCalendarComponentSet + */ + static function unserialize(DOMElement $node) { + + $components = array(); + foreach($node->childNodes as $childNode) { + if (Sabre_DAV_XMLUtil::toClarkNotation($childNode)==='{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}comp') { + $components[] = $childNode->getAttribute('name'); + } + } + return new self($components); + + } + +} diff --git a/3rdparty/Sabre/CalDAV/Property/SupportedCalendarData.php b/3rdparty/Sabre/CalDAV/Property/SupportedCalendarData.php new file mode 100755 index 00000000000..1d848dd5cf6 --- /dev/null +++ b/3rdparty/Sabre/CalDAV/Property/SupportedCalendarData.php @@ -0,0 +1,38 @@ +<?php + +/** + * Supported-calendar-data property + * + * This property is a representation of the supported-calendar-data property + * in the CalDAV namespace. SabreDAV only has support for text/calendar;2.0 + * so the value is currently hardcoded. + * + * @package Sabre + * @subpackage CalDAV + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. + * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License + */ +class Sabre_CalDAV_Property_SupportedCalendarData extends Sabre_DAV_Property { + + /** + * Serializes the property in a DOMDocument + * + * @param Sabre_DAV_Server $server + * @param DOMElement $node + * @return void + */ + public function serialize(Sabre_DAV_Server $server,DOMElement $node) { + + $doc = $node->ownerDocument; + + $prefix = isset($server->xmlNamespaces[Sabre_CalDAV_Plugin::NS_CALDAV])?$server->xmlNamespaces[Sabre_CalDAV_Plugin::NS_CALDAV]:'cal'; + + $caldata = $doc->createElement($prefix . ':calendar-data'); + $caldata->setAttribute('content-type','text/calendar'); + $caldata->setAttribute('version','2.0'); + + $node->appendChild($caldata); + } + +} diff --git a/3rdparty/Sabre/CalDAV/Property/SupportedCollationSet.php b/3rdparty/Sabre/CalDAV/Property/SupportedCollationSet.php new file mode 100755 index 00000000000..24e84d4c17d --- /dev/null +++ b/3rdparty/Sabre/CalDAV/Property/SupportedCollationSet.php @@ -0,0 +1,44 @@ +<?php + +/** + * supported-collation-set property + * + * This property is a representation of the supported-collation-set property + * in the CalDAV namespace. + * + * @package Sabre + * @subpackage CalDAV + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. + * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License + */ +class Sabre_CalDAV_Property_SupportedCollationSet extends Sabre_DAV_Property { + + /** + * Serializes the property in a DOM document + * + * @param Sabre_DAV_Server $server + * @param DOMElement $node + * @return void + */ + public function serialize(Sabre_DAV_Server $server,DOMElement $node) { + + $doc = $node->ownerDocument; + + $prefix = $node->lookupPrefix('urn:ietf:params:xml:ns:caldav'); + if (!$prefix) $prefix = 'cal'; + + $node->appendChild( + $doc->createElement($prefix . ':supported-collation','i;ascii-casemap') + ); + $node->appendChild( + $doc->createElement($prefix . ':supported-collation','i;octet') + ); + $node->appendChild( + $doc->createElement($prefix . ':supported-collation','i;unicode-casemap') + ); + + + } + +} diff --git a/3rdparty/Sabre/CalDAV/Schedule/IMip.php b/3rdparty/Sabre/CalDAV/Schedule/IMip.php new file mode 100755 index 00000000000..37e75fcc4a7 --- /dev/null +++ b/3rdparty/Sabre/CalDAV/Schedule/IMip.php @@ -0,0 +1,104 @@ +<?php + +/** + * iMIP handler. + * + * This class is responsible for sending out iMIP messages. iMIP is the + * email-based transport for iTIP. iTIP deals with scheduling operations for + * iCalendar objects. + * + * If you want to customize the email that gets sent out, you can do so by + * extending this class and overriding the sendMessage method. + * + * @package Sabre + * @subpackage CalDAV + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. + * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License + */ +class Sabre_CalDAV_Schedule_IMip { + + /** + * Email address used in From: header. + * + * @var string + */ + protected $senderEmail; + + /** + * Creates the email handler. + * + * @param string $senderEmail. The 'senderEmail' is the email that shows up + * in the 'From:' address. This should + * generally be some kind of no-reply email + * address you own. + */ + public function __construct($senderEmail) { + + $this->senderEmail = $senderEmail; + + } + + /** + * Sends one or more iTip messages through email. + * + * @param string $originator + * @param array $recipients + * @param Sabre_VObject_Component $vObject + * @return void + */ + public function sendMessage($originator, array $recipients, Sabre_VObject_Component $vObject) { + + foreach($recipients as $recipient) { + + $to = $recipient; + $replyTo = $originator; + $subject = 'SabreDAV iTIP message'; + + switch(strtoupper($vObject->METHOD)) { + case 'REPLY' : + $subject = 'Response for: ' . $vObject->VEVENT->SUMMARY; + break; + case 'REQUEST' : + $subject = 'Invitation for: ' .$vObject->VEVENT->SUMMARY; + break; + case 'CANCEL' : + $subject = 'Cancelled event: ' . $vObject->VEVENT->SUMMARY; + break; + } + + $headers = array(); + $headers[] = 'Reply-To: ' . $replyTo; + $headers[] = 'From: ' . $this->senderEmail; + $headers[] = 'Content-Type: text/calendar; method=' . (string)$vObject->method . '; charset=utf-8'; + if (Sabre_DAV_Server::$exposeVersion) { + $headers[] = 'X-Sabre-Version: ' . Sabre_DAV_Version::VERSION . '-' . Sabre_DAV_Version::STABILITY; + } + + $vcalBody = $vObject->serialize(); + + $this->mail($to, $subject, $vcalBody, $headers); + + } + + } + + /** + * This function is reponsible for sending the actual email. + * + * @param string $to Recipient email address + * @param string $subject Subject of the email + * @param string $body iCalendar body + * @param array $headers List of headers + * @return void + */ + protected function mail($to, $subject, $body, array $headers) { + + mail($to, $subject, $body, implode("\r\n", $headers)); + + } + + +} + +?> diff --git a/3rdparty/Sabre/CalDAV/Schedule/IOutbox.php b/3rdparty/Sabre/CalDAV/Schedule/IOutbox.php new file mode 100755 index 00000000000..46d77514bc0 --- /dev/null +++ b/3rdparty/Sabre/CalDAV/Schedule/IOutbox.php @@ -0,0 +1,16 @@ +<?php + +/** + * Implement this interface to have a node be recognized as a CalDAV scheduling + * outbox. + * + * @package Sabre + * @subpackage CalDAV + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. + * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License + */ +interface Sabre_CalDAV_Schedule_IOutbox extends Sabre_DAV_ICollection, Sabre_DAVACL_IACL { + + +} diff --git a/3rdparty/Sabre/CalDAV/Schedule/Outbox.php b/3rdparty/Sabre/CalDAV/Schedule/Outbox.php new file mode 100755 index 00000000000..014c37230d1 --- /dev/null +++ b/3rdparty/Sabre/CalDAV/Schedule/Outbox.php @@ -0,0 +1,152 @@ +<?php + +/** + * The CalDAV scheduling outbox + * + * The outbox is mainly used as an endpoint in the tree for a client to do + * free-busy requests. This functionality is completely handled by the + * Scheduling plugin, so this object is actually mostly static. + * + * @package Sabre + * @subpackage CalDAV + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. + * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License + */ +class Sabre_CalDAV_Schedule_Outbox extends Sabre_DAV_Directory implements Sabre_CalDAV_Schedule_IOutbox { + + /** + * The principal Uri + * + * @var string + */ + protected $principalUri; + + /** + * Constructor + * + * @param string $principalUri + */ + public function __construct($principalUri) { + + $this->principalUri = $principalUri; + + } + + /** + * Returns the name of the node. + * + * This is used to generate the url. + * + * @return string + */ + public function getName() { + + return 'outbox'; + + } + + /** + * Returns an array with all the child nodes + * + * @return Sabre_DAV_INode[] + */ + public function getChildren() { + + return array(); + + } + + /** + * Returns the owner principal + * + * This must be a url to a principal, or null if there's no owner + * + * @return string|null + */ + public function getOwner() { + + return $this->principalUri; + + } + + /** + * Returns a group principal + * + * This must be a url to a principal, or null if there's no owner + * + * @return string|null + */ + public function getGroup() { + + return null; + + } + + /** + * Returns a list of ACE's for this node. + * + * Each ACE has the following properties: + * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are + * currently the only supported privileges + * * 'principal', a url to the principal who owns the node + * * 'protected' (optional), indicating that this ACE is not allowed to + * be updated. + * + * @return array + */ + public function getACL() { + + return array( + array( + 'privilege' => '{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}schedule-query-freebusy', + 'principal' => $this->getOwner(), + 'protected' => true, + ), + array( + 'privilege' => '{DAV:}read', + 'principal' => $this->getOwner(), + 'protected' => true, + ), + ); + + } + + /** + * Updates the ACL + * + * This method will receive a list of new ACE's. + * + * @param array $acl + * @return void + */ + public function setACL(array $acl) { + + throw new Sabre_DAV_Exception_MethodNotAllowed('You\'re not allowed to update the ACL'); + + } + + /** + * Returns the list of supported privileges for this node. + * + * The returned data structure is a list of nested privileges. + * See Sabre_DAVACL_Plugin::getDefaultSupportedPrivilegeSet for a simple + * standard structure. + * + * If null is returned from this method, the default privilege set is used, + * which is fine for most common usecases. + * + * @return array|null + */ + public function getSupportedPrivilegeSet() { + + $default = Sabre_DAVACL_Plugin::getDefaultSupportedPrivilegeSet(); + $default['aggregates'][] = array( + 'privilege' => '{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}schedule-query-freebusy', + ); + + return $default; + + } + +} diff --git a/3rdparty/Sabre/CalDAV/Server.php b/3rdparty/Sabre/CalDAV/Server.php new file mode 100755 index 00000000000..325e3d80a7f --- /dev/null +++ b/3rdparty/Sabre/CalDAV/Server.php @@ -0,0 +1,68 @@ +<?php + +/** + * CalDAV server + * + * Deprecated! Warning: This class is now officially deprecated + * + * This script is a convenience script. It quickly sets up a WebDAV server + * with caldav and ACL support, and it creates the root 'principals' and + * 'calendars' collections. + * + * Note that if you plan to do anything moderately complex, you are advised to + * not subclass this server, but use Sabre_DAV_Server directly instead. This + * class is nothing more than an 'easy setup'. + * + * @package Sabre + * @subpackage CalDAV + * @deprecated Don't use this class anymore, it will be removed in version 1.7. + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. + * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License + */ +class Sabre_CalDAV_Server extends Sabre_DAV_Server { + + /** + * The authentication realm + * + * Note that if this changes, the hashes in the auth backend must also + * be recalculated. + * + * @var string + */ + public $authRealm = 'SabreDAV'; + + /** + * Sets up the object. A PDO object must be passed to setup all the backends. + * + * @param PDO $pdo + */ + public function __construct(PDO $pdo) { + + /* Backends */ + $authBackend = new Sabre_DAV_Auth_Backend_PDO($pdo); + $calendarBackend = new Sabre_CalDAV_Backend_PDO($pdo); + $principalBackend = new Sabre_DAVACL_PrincipalBackend_PDO($pdo); + + /* Directory structure */ + $tree = array( + new Sabre_CalDAV_Principal_Collection($principalBackend), + new Sabre_CalDAV_CalendarRootNode($principalBackend, $calendarBackend), + ); + + /* Initializing server */ + parent::__construct($tree); + + /* Server Plugins */ + $authPlugin = new Sabre_DAV_Auth_Plugin($authBackend,$this->authRealm); + $this->addPlugin($authPlugin); + + $aclPlugin = new Sabre_DAVACL_Plugin(); + $this->addPlugin($aclPlugin); + + $caldavPlugin = new Sabre_CalDAV_Plugin(); + $this->addPlugin($caldavPlugin); + + } + +} diff --git a/3rdparty/Sabre/CalDAV/UserCalendars.php b/3rdparty/Sabre/CalDAV/UserCalendars.php new file mode 100755 index 00000000000..b8d3f0573fa --- /dev/null +++ b/3rdparty/Sabre/CalDAV/UserCalendars.php @@ -0,0 +1,298 @@ +<?php + +/** + * The UserCalenders class contains all calendars associated to one user + * + * @package Sabre + * @subpackage CalDAV + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. + * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License + */ +class Sabre_CalDAV_UserCalendars implements Sabre_DAV_IExtendedCollection, Sabre_DAVACL_IACL { + + /** + * Principal backend + * + * @var Sabre_DAVACL_IPrincipalBackend + */ + protected $principalBackend; + + /** + * CalDAV backend + * + * @var Sabre_CalDAV_Backend_Abstract + */ + protected $caldavBackend; + + /** + * Principal information + * + * @var array + */ + protected $principalInfo; + + /** + * Constructor + * + * @param Sabre_DAVACL_IPrincipalBackend $principalBackend + * @param Sabre_CalDAV_Backend_Abstract $caldavBackend + * @param mixed $userUri + */ + public function __construct(Sabre_DAVACL_IPrincipalBackend $principalBackend, Sabre_CalDAV_Backend_Abstract $caldavBackend, $userUri) { + + $this->principalBackend = $principalBackend; + $this->caldavBackend = $caldavBackend; + $this->principalInfo = $principalBackend->getPrincipalByPath($userUri); + + } + + /** + * Returns the name of this object + * + * @return string + */ + public function getName() { + + list(,$name) = Sabre_DAV_URLUtil::splitPath($this->principalInfo['uri']); + return $name; + + } + + /** + * Updates the name of this object + * + * @param string $name + * @return void + */ + public function setName($name) { + + throw new Sabre_DAV_Exception_Forbidden(); + + } + + /** + * Deletes this object + * + * @return void + */ + public function delete() { + + throw new Sabre_DAV_Exception_Forbidden(); + + } + + /** + * Returns the last modification date + * + * @return int + */ + public function getLastModified() { + + return null; + + } + + /** + * Creates a new file under this object. + * + * This is currently not allowed + * + * @param string $filename + * @param resource $data + * @return void + */ + public function createFile($filename, $data=null) { + + throw new Sabre_DAV_Exception_MethodNotAllowed('Creating new files in this collection is not supported'); + + } + + /** + * Creates a new directory under this object. + * + * This is currently not allowed. + * + * @param string $filename + * @return void + */ + public function createDirectory($filename) { + + throw new Sabre_DAV_Exception_MethodNotAllowed('Creating new collections in this collection is not supported'); + + } + + /** + * Returns a single calendar, by name + * + * @param string $name + * @todo needs optimizing + * @return Sabre_CalDAV_Calendar + */ + public function getChild($name) { + + foreach($this->getChildren() as $child) { + if ($name==$child->getName()) + return $child; + + } + throw new Sabre_DAV_Exception_NotFound('Calendar with name \'' . $name . '\' could not be found'); + + } + + /** + * Checks if a calendar exists. + * + * @param string $name + * @todo needs optimizing + * @return bool + */ + public function childExists($name) { + + foreach($this->getChildren() as $child) { + if ($name==$child->getName()) + return true; + + } + return false; + + } + + /** + * Returns a list of calendars + * + * @return array + */ + public function getChildren() { + + $calendars = $this->caldavBackend->getCalendarsForUser($this->principalInfo['uri']); + $objs = array(); + foreach($calendars as $calendar) { + $objs[] = new Sabre_CalDAV_Calendar($this->principalBackend, $this->caldavBackend, $calendar); + } + $objs[] = new Sabre_CalDAV_Schedule_Outbox($this->principalInfo['uri']); + return $objs; + + } + + /** + * Creates a new calendar + * + * @param string $name + * @param array $resourceType + * @param array $properties + * @return void + */ + public function createExtendedCollection($name, array $resourceType, array $properties) { + + if (!in_array('{urn:ietf:params:xml:ns:caldav}calendar',$resourceType) || count($resourceType)!==2) { + throw new Sabre_DAV_Exception_InvalidResourceType('Unknown resourceType for this collection'); + } + $this->caldavBackend->createCalendar($this->principalInfo['uri'], $name, $properties); + + } + + /** + * Returns the owner principal + * + * This must be a url to a principal, or null if there's no owner + * + * @return string|null + */ + public function getOwner() { + + return $this->principalInfo['uri']; + + } + + /** + * Returns a group principal + * + * This must be a url to a principal, or null if there's no owner + * + * @return string|null + */ + public function getGroup() { + + return null; + + } + + /** + * Returns a list of ACE's for this node. + * + * Each ACE has the following properties: + * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are + * currently the only supported privileges + * * 'principal', a url to the principal who owns the node + * * 'protected' (optional), indicating that this ACE is not allowed to + * be updated. + * + * @return array + */ + public function getACL() { + + return array( + array( + 'privilege' => '{DAV:}read', + 'principal' => $this->principalInfo['uri'], + 'protected' => true, + ), + array( + 'privilege' => '{DAV:}write', + 'principal' => $this->principalInfo['uri'], + 'protected' => true, + ), + array( + 'privilege' => '{DAV:}read', + 'principal' => $this->principalInfo['uri'] . '/calendar-proxy-write', + 'protected' => true, + ), + array( + 'privilege' => '{DAV:}write', + 'principal' => $this->principalInfo['uri'] . '/calendar-proxy-write', + 'protected' => true, + ), + array( + 'privilege' => '{DAV:}read', + 'principal' => $this->principalInfo['uri'] . '/calendar-proxy-read', + 'protected' => true, + ), + + ); + + } + + /** + * Updates the ACL + * + * This method will receive a list of new ACE's. + * + * @param array $acl + * @return void + */ + public function setACL(array $acl) { + + throw new Sabre_DAV_Exception_MethodNotAllowed('Changing ACL is not yet supported'); + + } + + /** + * Returns the list of supported privileges for this node. + * + * The returned data structure is a list of nested privileges. + * See Sabre_DAVACL_Plugin::getDefaultSupportedPrivilegeSet for a simple + * standard structure. + * + * If null is returned from this method, the default privilege set is used, + * which is fine for most common usecases. + * + * @return array|null + */ + public function getSupportedPrivilegeSet() { + + return null; + + } + +} diff --git a/3rdparty/Sabre/CalDAV/Version.php b/3rdparty/Sabre/CalDAV/Version.php new file mode 100755 index 00000000000..939e903c89f --- /dev/null +++ b/3rdparty/Sabre/CalDAV/Version.php @@ -0,0 +1,24 @@ +<?php + +/** + * This class contains the Sabre_CalDAV version constants. + * + * @package Sabre + * @subpackage CalDAV + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. + * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License + */ +class Sabre_CalDAV_Version { + + /** + * Full version number + */ + const VERSION = '1.6.2'; + + /** + * Stability : alpha, beta, stable + */ + const STABILITY = 'stable'; + +} diff --git a/3rdparty/Sabre/CalDAV/includes.php b/3rdparty/Sabre/CalDAV/includes.php new file mode 100755 index 00000000000..1ecb870a0e1 --- /dev/null +++ b/3rdparty/Sabre/CalDAV/includes.php @@ -0,0 +1,43 @@ +<?php + +/** + * Sabre_CalDAV includes file + * + * Including this file will automatically include all files from the + * Sabre_CalDAV package. + * + * This often allows faster loadtimes, as autoload-speed is often quite slow. + * + * @package Sabre + * @subpackage CalDAV + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. + * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License + */ + +// Begin includes +include __DIR__ . '/Backend/Abstract.php'; +include __DIR__ . '/Backend/PDO.php'; +include __DIR__ . '/CalendarQueryParser.php'; +include __DIR__ . '/CalendarQueryValidator.php'; +include __DIR__ . '/CalendarRootNode.php'; +include __DIR__ . '/ICalendar.php'; +include __DIR__ . '/ICalendarObject.php'; +include __DIR__ . '/ICSExportPlugin.php'; +include __DIR__ . '/Plugin.php'; +include __DIR__ . '/Principal/Collection.php'; +include __DIR__ . '/Principal/ProxyRead.php'; +include __DIR__ . '/Principal/ProxyWrite.php'; +include __DIR__ . '/Principal/User.php'; +include __DIR__ . '/Property/SupportedCalendarComponentSet.php'; +include __DIR__ . '/Property/SupportedCalendarData.php'; +include __DIR__ . '/Property/SupportedCollationSet.php'; +include __DIR__ . '/Schedule/IMip.php'; +include __DIR__ . '/Schedule/IOutbox.php'; +include __DIR__ . '/Schedule/Outbox.php'; +include __DIR__ . '/Server.php'; +include __DIR__ . '/UserCalendars.php'; +include __DIR__ . '/Version.php'; +include __DIR__ . '/Calendar.php'; +include __DIR__ . '/CalendarObject.php'; +// End includes |