diff options
author | Susinthiran Sithamparanathan <chesusin@gmail.com> | 2012-10-17 12:25:34 +0200 |
---|---|---|
committer | Susinthiran Sithamparanathan <chesusin@gmail.com> | 2012-10-17 16:17:36 +0200 |
commit | b2b84f3a6f3c98005f80c6c7c558a33b4ea36193 (patch) | |
tree | 1ae0c571e25ab7322e9a942dfdcc593f67e98ad6 | |
parent | 45dec77d92c9a7804489bee34b218bbe8c85f7c5 (diff) | |
download | nextcloud-server-b2b84f3a6f3c98005f80c6c7c558a33b4ea36193.tar.gz nextcloud-server-b2b84f3a6f3c98005f80c6c7c558a33b4ea36193.zip |
Update Sabre to version 1.7.1
216 files changed, 6471 insertions, 1142 deletions
diff --git a/3rdparty/Sabre.includes.php b/3rdparty/Sabre.includes.php deleted file mode 100755 index c1334373663..00000000000 --- a/3rdparty/Sabre.includes.php +++ /dev/null @@ -1,26 +0,0 @@ -<?php - -/** - * Library include file - * - * This file is deprecated, don't use it! - * Instead, use the specific includes files that are in the sub-packages. - * - * Sabre/DAV/includes.php - * Sabre/HTTP/includes.php - * - * etc.. - * - * This file contains all includes to the rest of the SabreDAV library - * Make sure the lib/ directory is in PHP's include_path. - * - * @package Sabre - * @deprecated Don't use this file, it will be remove in a future version - * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. - * @author Evert Pot (http://www.rooftopsolutions.nl/) - * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License - */ - -include 'Sabre/HTTP/includes.php'; -include 'Sabre/DAV/includes.php'; - diff --git a/3rdparty/Sabre/CalDAV/Backend/Abstract.php b/3rdparty/Sabre/CalDAV/Backend/Abstract.php index 7aba1d69ffe..88e5b4a1a07 100755..100644 --- a/3rdparty/Sabre/CalDAV/Backend/Abstract.php +++ b/3rdparty/Sabre/CalDAV/Backend/Abstract.php @@ -1,47 +1,19 @@ <?php +use Sabre\VObject; + /** * Abstract Calendaring backend. Extend this class to create your own backends. * + * Checkout the BackendInterface for all the methods that must be implemented. + * * @package Sabre * @subpackage CalDAV * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. - * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @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); +abstract class Sabre_CalDAV_Backend_Abstract implements Sabre_CalDAV_Backend_BackendInterface { /** * Updates properties for a calendar. @@ -75,7 +47,7 @@ abstract class Sabre_CalDAV_Backend_Abstract { * (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 mixed $calendarId * @param array $mutations * @return bool|array */ @@ -86,83 +58,97 @@ abstract class Sabre_CalDAV_Backend_Abstract { } /** - * Delete a calendar and all it's objects + * Performs a calendar-query on the contents of this calendar. * - * @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 calendar-query is defined in RFC4791 : CalDAV. Using the + * calendar-query it is possible for a client to request a specific set of + * object, based on contents of iCalendar properties, date-ranges and + * iCalendar component types (VTODO, VEVENT). + * + * This method should just return a list of (relative) urls that match this + * query. + * + * The list of filters are specified as an array. The exact array is + * documented by Sabre_CalDAV_CalendarQueryParser. + * + * Note that it is extremely likely that getCalendarObject for every path + * returned from this method will be called almost immediately after. You + * may want to anticipate this to speed up these requests. * - * The returned array must have the same keys as getCalendarObjects. The - * 'calendardata' object is required here though, while it's not required - * for getCalendarObjects. + * This method provides a default implementation, which parses *all* the + * iCalendar objects in the specified calendar. * - * @param string $calendarId - * @param string $objectUri + * This default may well be good enough for personal use, and calendars + * that aren't very large. But if you anticipate high usage, big calendars + * or high loads, you are strongly adviced to optimize certain paths. + * + * The best way to do so is override this method and to optimize + * specifically for 'common filters'. + * + * Requests that are extremely common are: + * * requests for just VEVENTS + * * requests for just VTODO + * * requests with a time-range-filter on either VEVENT or VTODO. + * + * ..and combinations of these requests. It may not be worth it to try to + * handle every possible situation and just rely on the (relatively + * easy to use) CalendarQueryValidator to handle the rest. + * + * Note that especially time-range-filters may be difficult to parse. A + * time-range filter specified on a VEVENT must for instance also handle + * recurrence rules correctly. + * A good example of how to interprete all these filters can also simply + * be found in Sabre_CalDAV_CalendarQueryFilter. This class is as correct + * as possible, so it gives you a good idea on what type of stuff you need + * to think of. + * + * @param mixed $calendarId + * @param array $filters * @return array */ - abstract function getCalendarObject($calendarId,$objectUri); + public function calendarQuery($calendarId, array $filters) { - /** - * Creates a new calendar object. - * - * @param string $calendarId - * @param string $objectUri - * @param string $calendarData - * @return void - */ - abstract function createCalendarObject($calendarId,$objectUri,$calendarData); + $result = array(); + $objects = $this->getCalendarObjects($calendarId); - /** - * 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); + $validator = new Sabre_CalDAV_CalendarQueryValidator(); + + foreach($objects as $object) { + + if ($this->validateFilterForObject($object, $filters)) { + $result[] = $object['uri']; + } + + } + + return $result; + + } /** - * Deletes an existing calendar object. + * This method validates if a filters (as passed to calendarQuery) matches + * the given object. * - * @param string $calendarId - * @param string $objectUri - * @return void + * @param array $object + * @param array $filter + * @return bool */ - abstract function deleteCalendarObject($calendarId,$objectUri); + protected function validateFilterForObject(array $object, array $filters) { + + // Unfortunately, setting the 'calendardata' here is optional. If + // it was excluded, we actually need another call to get this as + // well. + if (!isset($object['calendardata'])) { + $object = $this->getCalendarObject($object['calendarid'], $object['uri']); + } + + $data = is_resource($object['calendardata'])?stream_get_contents($object['calendardata']):$object['calendardata']; + $vObject = VObject\Reader::read($data); + + $validator = new Sabre_CalDAV_CalendarQueryValidator(); + return $validator->validate($vObject, $filters); + + } + } diff --git a/3rdparty/Sabre/CalDAV/Backend/BackendInterface.php b/3rdparty/Sabre/CalDAV/Backend/BackendInterface.php new file mode 100644 index 00000000000..881538ab60e --- /dev/null +++ b/3rdparty/Sabre/CalDAV/Backend/BackendInterface.php @@ -0,0 +1,231 @@ +<?php + +/** + * Every CalDAV backend must at least implement this interface. + * + * @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_Backend_BackendInterface { + + /** + * 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); + + /** + * 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 + */ + public 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 mixed $calendarId + * @param array $mutations + * @return bool|array + */ + public function updateCalendar($calendarId, array $mutations); + + /** + * Delete a calendar and all it's objects + * + * @param mixed $calendarId + * @return void + */ + public 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 mixed $calendarId + * @return array + */ + public 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 mixed $calendarId + * @param string $objectUri + * @return array + */ + public function getCalendarObject($calendarId,$objectUri); + + /** + * Creates a new calendar object. + * + * It is possible return an etag from this function, which will be used in + * the response to this PUT request. Note that the ETag must be surrounded + * by double-quotes. + * + * However, you should only really return this ETag if you don't mangle the + * calendar-data. If the result of a subsequent GET to this object is not + * the exact same as this request body, you should omit the ETag. + * + * @param mixed $calendarId + * @param string $objectUri + * @param string $calendarData + * @return string|null + */ + public function createCalendarObject($calendarId,$objectUri,$calendarData); + + /** + * Updates an existing calendarobject, based on it's uri. + * + * It is possible return an etag from this function, which will be used in + * the response to this PUT request. Note that the ETag must be surrounded + * by double-quotes. + * + * However, you should only really return this ETag if you don't mangle the + * calendar-data. If the result of a subsequent GET to this object is not + * the exact same as this request body, you should omit the ETag. + * + * @param mixed $calendarId + * @param string $objectUri + * @param string $calendarData + * @return string|null + */ + public function updateCalendarObject($calendarId,$objectUri,$calendarData); + + /** + * Deletes an existing calendar object. + * + * @param mixed $calendarId + * @param string $objectUri + * @return void + */ + public function deleteCalendarObject($calendarId,$objectUri); + + /** + * Performs a calendar-query on the contents of this calendar. + * + * The calendar-query is defined in RFC4791 : CalDAV. Using the + * calendar-query it is possible for a client to request a specific set of + * object, based on contents of iCalendar properties, date-ranges and + * iCalendar component types (VTODO, VEVENT). + * + * This method should just return a list of (relative) urls that match this + * query. + * + * The list of filters are specified as an array. The exact array is + * documented by Sabre_CalDAV_CalendarQueryParser. + * + * Note that it is extremely likely that getCalendarObject for every path + * returned from this method will be called almost immediately after. You + * may want to anticipate this to speed up these requests. + * + * This method provides a default implementation, which parses *all* the + * iCalendar objects in the specified calendar. + * + * This default may well be good enough for personal use, and calendars + * that aren't very large. But if you anticipate high usage, big calendars + * or high loads, you are strongly adviced to optimize certain paths. + * + * The best way to do so is override this method and to optimize + * specifically for 'common filters'. + * + * Requests that are extremely common are: + * * requests for just VEVENTS + * * requests for just VTODO + * * requests with a time-range-filter on either VEVENT or VTODO. + * + * ..and combinations of these requests. It may not be worth it to try to + * handle every possible situation and just rely on the (relatively + * easy to use) CalendarQueryValidator to handle the rest. + * + * Note that especially time-range-filters may be difficult to parse. A + * time-range filter specified on a VEVENT must for instance also handle + * recurrence rules correctly. + * A good example of how to interprete all these filters can also simply + * be found in Sabre_CalDAV_CalendarQueryFilter. This class is as correct + * as possible, so it gives you a good idea on what type of stuff you need + * to think of. + * + * @param mixed $calendarId + * @param array $filters + * @return array + */ + public function calendarQuery($calendarId, array $filters); + +} diff --git a/3rdparty/Sabre/CalDAV/Backend/NotificationSupport.php b/3rdparty/Sabre/CalDAV/Backend/NotificationSupport.php new file mode 100644 index 00000000000..d5a1409d9b9 --- /dev/null +++ b/3rdparty/Sabre/CalDAV/Backend/NotificationSupport.php @@ -0,0 +1,47 @@ +<?php + +/** + * Adds caldav notification support to a backend. + * + * Note: This feature is experimental, and may change in between different + * SabreDAV versions. + * + * Notifications are defined at: + * http://svn.calendarserver.org/repository/calendarserver/CalendarServer/trunk/doc/Extensions/caldav-notifications.txt + * + * These notifications are basically a list of server-generated notifications + * displayed to the user. Users can dismiss notifications by deleting them. + * + * The primary usecase is to allow for calendar-sharing. + * + * @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_Backend_NotificationSupport extends Sabre_CalDAV_Backend_BackendInterface { + + /** + * Returns a list of notifications for a given principal url. + * + * The returned array should only consist of implementations of + * Sabre_CalDAV_Notifications_INotificationType. + * + * @param string $principalUri + * @return array + */ + public function getNotificationsForPrincipal($principalUri); + + /** + * This deletes a specific notifcation. + * + * This may be called by a client once it deems a notification handled. + * + * @param string $principalUri + * @param Sabre_CalDAV_Notifications_INotificationType $notification + * @return void + */ + public function deleteNotification($principalUri, Sabre_CalDAV_Notifications_INotificationType $notification); + +} diff --git a/3rdparty/Sabre/CalDAV/Backend/PDO.php b/3rdparty/Sabre/CalDAV/Backend/PDO.php index ddacf940c74..447f5f590cf 100755..100644 --- a/3rdparty/Sabre/CalDAV/Backend/PDO.php +++ b/3rdparty/Sabre/CalDAV/Backend/PDO.php @@ -1,5 +1,7 @@ <?php +use Sabre\VObject; + /** * PDO CalDAV backend * @@ -9,12 +11,22 @@ * @package Sabre * @subpackage CalDAV * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. - * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @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 { /** + * We need to specify a max date, because we need to stop *somewhere* + * + * On 32 bit system the maximum for a signed integer is 2147483647, so + * MAX_DATE cannot be higher than date('Y-m-d', 2147483647) which results + * in 2038-01-19 to avoid problems when the date is converted + * to a unix timestamp. + */ + const MAX_DATE = '2038-01-01'; + + /** * pdo * * @var PDO @@ -37,8 +49,9 @@ class Sabre_CalDAV_Backend_PDO extends Sabre_CalDAV_Backend_Abstract { /** * List of CalDAV properties, and how they map to database fieldnames + * Add your own properties by simply adding on to this array. * - * Add your own properties by simply adding on to this array + * Note that only string-based properties are supported here. * * @var array */ @@ -90,6 +103,7 @@ class Sabre_CalDAV_Backend_PDO extends Sabre_CalDAV_Backend_Abstract { $fields[] = 'ctag'; $fields[] = 'components'; $fields[] = 'principaluri'; + $fields[] = 'transparent'; // Making fields a comma-delimited list $fields = implode(', ', $fields); @@ -110,6 +124,7 @@ class Sabre_CalDAV_Backend_PDO extends Sabre_CalDAV_Backend_Abstract { '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), + '{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}schedule-calendar-transp' => new Sabre_CalDAV_Property_ScheduleCalendarTransp($row['transparent']?'transparent':'opaque'), ); @@ -142,11 +157,13 @@ class Sabre_CalDAV_Backend_PDO extends Sabre_CalDAV_Backend_Abstract { 'principaluri', 'uri', 'ctag', + 'transparent', ); $values = array( ':principaluri' => $principalUri, ':uri' => $calendarUri, ':ctag' => 1, + ':transparent' => 0, ); // Default value @@ -160,6 +177,10 @@ class Sabre_CalDAV_Backend_PDO extends Sabre_CalDAV_Backend_Abstract { } $values[':components'] = implode(',',$properties[$sccs]->getValue()); } + $transp = '{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}schedule-calendar-transp'; + if (isset($properties[$transp])) { + $values[':transparent'] = $properties[$transp]->getValue()==='transparent'; + } foreach($this->propertyMap as $xmlName=>$dbName) { if (isset($properties[$xmlName])) { @@ -225,17 +246,25 @@ class Sabre_CalDAV_Backend_PDO extends Sabre_CalDAV_Backend_Abstract { 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; + switch($propertyName) { + case '{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}schedule-calendar-transp' : + $fieldName = 'transparent'; + $newValues[$fieldName] = $propertyValue->getValue()==='transparent'; + break; + default : + // Checking the property map + if (!isset($this->propertyMap[$propertyName])) { + // We don't know about this property. + $hasError = true; + $result[403][$propertyName] = null; + unset($mutations[$propertyName]); + continue; + } + + $fieldName = $this->propertyMap[$propertyName]; + $newValues[$fieldName] = $propertyValue; } - $fieldName = $this->propertyMap[$propertyName]; - $newValues[$fieldName] = $propertyValue; - } // If there were any errors we need to fail the request @@ -316,9 +345,22 @@ class Sabre_CalDAV_Backend_PDO extends Sabre_CalDAV_Backend_Abstract { */ public function getCalendarObjects($calendarId) { - $stmt = $this->pdo->prepare('SELECT * FROM '.$this->calendarObjectTableName.' WHERE calendarid = ?'); + $stmt = $this->pdo->prepare('SELECT id, uri, lastmodified, etag, calendarid, size FROM '.$this->calendarObjectTableName.' WHERE calendarid = ?'); $stmt->execute(array($calendarId)); - return $stmt->fetchAll(); + + $result = array(); + foreach($stmt->fetchAll(\PDO::FETCH_ASSOC) as $row) { + $result[] = array( + 'id' => $row['id'], + 'uri' => $row['uri'], + 'lastmodified' => $row['lastmodified'], + 'etag' => '"' . $row['etag'] . '"', + 'calendarid' => $row['calendarid'], + 'size' => (int)$row['size'], + ); + } + + return $result; } @@ -336,44 +378,166 @@ class Sabre_CalDAV_Backend_PDO extends Sabre_CalDAV_Backend_Abstract { */ public function getCalendarObject($calendarId,$objectUri) { - $stmt = $this->pdo->prepare('SELECT * FROM '.$this->calendarObjectTableName.' WHERE calendarid = ? AND uri = ?'); + $stmt = $this->pdo->prepare('SELECT id, uri, lastmodified, etag, calendarid, size, calendardata FROM '.$this->calendarObjectTableName.' WHERE calendarid = ? AND uri = ?'); $stmt->execute(array($calendarId, $objectUri)); - return $stmt->fetch(); + $row = $stmt->fetch(\PDO::FETCH_ASSOC); + + if(!$row) return null; + + return array( + 'id' => $row['id'], + 'uri' => $row['uri'], + 'lastmodified' => $row['lastmodified'], + 'etag' => '"' . $row['etag'] . '"', + 'calendarid' => $row['calendarid'], + 'size' => (int)$row['size'], + 'calendardata' => $row['calendardata'], + ); } + /** * Creates a new calendar object. * - * @param string $calendarId + * It is possible return an etag from this function, which will be used in + * the response to this PUT request. Note that the ETag must be surrounded + * by double-quotes. + * + * However, you should only really return this ETag if you don't mangle the + * calendar-data. If the result of a subsequent GET to this object is not + * the exact same as this request body, you should omit the ETag. + * + * @param mixed $calendarId * @param string $objectUri * @param string $calendarData - * @return void + * @return string|null */ 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())); + $extraData = $this->getDenormalizedData($calendarData); + + $stmt = $this->pdo->prepare('INSERT INTO '.$this->calendarObjectTableName.' (calendarid, uri, calendardata, lastmodified, etag, size, componenttype, firstoccurence, lastoccurence) VALUES (?,?,?,?,?,?,?,?,?)'); + $stmt->execute(array( + $calendarId, + $objectUri, + $calendarData, + time(), + $extraData['etag'], + $extraData['size'], + $extraData['componentType'], + $extraData['firstOccurence'], + $extraData['lastOccurence'], + )); $stmt = $this->pdo->prepare('UPDATE '.$this->calendarTableName.' SET ctag = ctag + 1 WHERE id = ?'); $stmt->execute(array($calendarId)); + return '"' . $extraData['etag'] . '"'; + } /** * Updates an existing calendarobject, based on it's uri. * - * @param string $calendarId + * It is possible return an etag from this function, which will be used in + * the response to this PUT request. Note that the ETag must be surrounded + * by double-quotes. + * + * However, you should only really return this ETag if you don't mangle the + * calendar-data. If the result of a subsequent GET to this object is not + * the exact same as this request body, you should omit the ETag. + * + * @param mixed $calendarId * @param string $objectUri * @param string $calendarData - * @return void + * @return string|null */ 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)); + $extraData = $this->getDenormalizedData($calendarData); + + $stmt = $this->pdo->prepare('UPDATE '.$this->calendarObjectTableName.' SET calendardata = ?, lastmodified = ?, etag = ?, size = ?, componenttype = ?, firstoccurence = ?, lastoccurence = ? WHERE calendarid = ? AND uri = ?'); + $stmt->execute(array($calendarData,time(), $extraData['etag'], $extraData['size'], $extraData['componentType'], $extraData['firstOccurence'], $extraData['lastOccurence'] ,$calendarId,$objectUri)); $stmt = $this->pdo->prepare('UPDATE '.$this->calendarTableName.' SET ctag = ctag + 1 WHERE id = ?'); $stmt->execute(array($calendarId)); + return '"' . $extraData['etag'] . '"'; + + } + + /** + * Parses some information from calendar objects, used for optimized + * calendar-queries. + * + * Returns an array with the following keys: + * * etag + * * size + * * componentType + * * firstOccurence + * * lastOccurence + * + * @param string $calendarData + * @return array + */ + protected function getDenormalizedData($calendarData) { + + $vObject = VObject\Reader::read($calendarData); + $componentType = null; + $component = null; + $firstOccurence = null; + $lastOccurence = null; + foreach($vObject->getComponents() as $component) { + if ($component->name!=='VTIMEZONE') { + $componentType = $component->name; + break; + } + } + if (!$componentType) { + throw new Sabre_DAV_Exception_BadRequest('Calendar objects must have a VJOURNAL, VEVENT or VTODO component'); + } + if ($componentType === 'VEVENT') { + $firstOccurence = $component->DTSTART->getDateTime()->getTimeStamp(); + // Finding the last occurence is a bit harder + if (!isset($component->RRULE)) { + if (isset($component->DTEND)) { + $lastOccurence = $component->DTEND->getDateTime()->getTimeStamp(); + } elseif (isset($component->DURATION)) { + $endDate = clone $component->DTSTART->getDateTime(); + $endDate->add(VObject\DateTimeParser::parse($component->DURATION->value)); + $lastOccurence = $endDate->getTimeStamp(); + } elseif ($component->DTSTART->getDateType()===VObject\Property\DateTime::DATE) { + $endDate = clone $component->DTSTART->getDateTime(); + $endDate->modify('+1 day'); + $lastOccurence = $endDate->getTimeStamp(); + } else { + $lastOccurence = $firstOccurence; + } + } else { + $it = new VObject\RecurrenceIterator($vObject, (string)$component->UID); + $maxDate = new DateTime(self::MAX_DATE); + if ($it->isInfinite()) { + $lastOccurence = $maxDate->getTimeStamp(); + } else { + $end = $it->getDtEnd(); + while($it->valid() && $end < $maxDate) { + $end = $it->getDtEnd(); + $it->next(); + + } + $lastOccurence = $end->getTimeStamp(); + } + + } + } + + return array( + 'etag' => md5($calendarData), + 'size' => strlen($calendarData), + 'componentType' => $componentType, + 'firstOccurence' => $firstOccurence, + 'lastOccurence' => $lastOccurence, + ); + } /** @@ -392,5 +556,132 @@ class Sabre_CalDAV_Backend_PDO extends Sabre_CalDAV_Backend_Abstract { } + /** + * Performs a calendar-query on the contents of this calendar. + * + * The calendar-query is defined in RFC4791 : CalDAV. Using the + * calendar-query it is possible for a client to request a specific set of + * object, based on contents of iCalendar properties, date-ranges and + * iCalendar component types (VTODO, VEVENT). + * + * This method should just return a list of (relative) urls that match this + * query. + * + * The list of filters are specified as an array. The exact array is + * documented by Sabre_CalDAV_CalendarQueryParser. + * + * Note that it is extremely likely that getCalendarObject for every path + * returned from this method will be called almost immediately after. You + * may want to anticipate this to speed up these requests. + * + * This method provides a default implementation, which parses *all* the + * iCalendar objects in the specified calendar. + * + * This default may well be good enough for personal use, and calendars + * that aren't very large. But if you anticipate high usage, big calendars + * or high loads, you are strongly adviced to optimize certain paths. + * + * The best way to do so is override this method and to optimize + * specifically for 'common filters'. + * + * Requests that are extremely common are: + * * requests for just VEVENTS + * * requests for just VTODO + * * requests with a time-range-filter on a VEVENT. + * + * ..and combinations of these requests. It may not be worth it to try to + * handle every possible situation and just rely on the (relatively + * easy to use) CalendarQueryValidator to handle the rest. + * + * Note that especially time-range-filters may be difficult to parse. A + * time-range filter specified on a VEVENT must for instance also handle + * recurrence rules correctly. + * A good example of how to interprete all these filters can also simply + * be found in Sabre_CalDAV_CalendarQueryFilter. This class is as correct + * as possible, so it gives you a good idea on what type of stuff you need + * to think of. + * + * This specific implementation (for the PDO) backend optimizes filters on + * specific components, and VEVENT time-ranges. + * + * @param string $calendarId + * @param array $filters + * @return array + */ + public function calendarQuery($calendarId, array $filters) { + + $result = array(); + $validator = new Sabre_CalDAV_CalendarQueryValidator(); + + $componentType = null; + $requirePostFilter = true; + $timeRange = null; + + // if no filters were specified, we don't need to filter after a query + if (!$filters['prop-filters'] && !$filters['comp-filters']) { + $requirePostFilter = false; + } + + // Figuring out if there's a component filter + if (count($filters['comp-filters']) > 0 && !$filters['comp-filters'][0]['is-not-defined']) { + $componentType = $filters['comp-filters'][0]['name']; + + // Checking if we need post-filters + if (!$filters['prop-filters'] && !$filters['comp-filters'][0]['comp-filters'] && !$filters['comp-filters'][0]['time-range'] && !$filters['comp-filters'][0]['prop-filters']) { + $requirePostFilter = false; + } + // There was a time-range filter + if ($componentType == 'VEVENT' && isset($filters['comp-filters'][0]['time-range'])) { + $timeRange = $filters['comp-filters'][0]['time-range']; + + // If start time OR the end time is not specified, we can do a + // 100% accurate mysql query. + if (!$filters['prop-filters'] && !$filters['comp-filters'][0]['comp-filters'] && !$filters['comp-filters'][0]['prop-filters'] && (!$timeRange['start'] || !$timeRange['end'])) { + $requirePostFilter = false; + } + } + } + + if ($requirePostFilter) { + $query = "SELECT uri, calendardata FROM ".$this->calendarObjectTableName." WHERE calendarid = :calendarid"; + } else { + $query = "SELECT uri FROM ".$this->calendarObjectTableName." WHERE calendarid = :calendarid"; + } + + $values = array( + 'calendarid' => $calendarId, + ); + + if ($componentType) { + $query.=" AND componenttype = :componenttype"; + $values['componenttype'] = $componentType; + } + + if ($timeRange && $timeRange['start']) { + $query.=" AND lastoccurence > :startdate"; + $values['startdate'] = $timeRange['start']->getTimeStamp(); + } + if ($timeRange && $timeRange['end']) { + $query.=" AND firstoccurence < :enddate"; + $values['enddate'] = $timeRange['end']->getTimeStamp(); + } + + $stmt = $this->pdo->prepare($query); + $stmt->execute($values); + + $result = array(); + while($row = $stmt->fetch(\PDO::FETCH_ASSOC)) { + if ($requirePostFilter) { + if (!$this->validateFilterForObject($row, $filters)) { + continue; + } + } + $result[] = $row['uri']; + + } + + return $result; + + } } diff --git a/3rdparty/Sabre/CalDAV/Backend/SharingSupport.php b/3rdparty/Sabre/CalDAV/Backend/SharingSupport.php new file mode 100644 index 00000000000..f73f0ff3d8a --- /dev/null +++ b/3rdparty/Sabre/CalDAV/Backend/SharingSupport.php @@ -0,0 +1,238 @@ +<?php + +/** + * Adds support for sharing features to a CalDAV server. + * + * Note: This feature is experimental, and may change in between different + * SabreDAV versions. + * + * Early warning: Currently SabreDAV provides no implementation for this. This + * is, because in it's current state there is no elegant way to do this. + * The problem lies in the fact that a real CalDAV server with sharing support + * would first need email support (with invite notifications), and really also + * a browser-frontend that allows people to accept or reject these shares. + * + * In addition, the CalDAV backends are currently kept as independent as + * possible, and should not be aware of principals, email addresses or + * accounts. + * + * Adding an implementation for Sharing to standard-sabredav would contradict + * these goals, so for this reason this is currently not implemented, although + * it may very well in the future; but probably not before SabreDAV 2.0. + * + * The interface works however, so if you implement all this, and do it + * correctly sharing _will_ work. It's not particularly easy, and I _urge you_ + * to make yourself acquainted with the following document first: + * + * https://trac.calendarserver.org/browser/CalendarServer/trunk/doc/Extensions/caldav-sharing.txt + * + * An overview + * =========== + * + * Implementing this interface will allow a user to share his or her calendars + * to other users. Effectively, when a calendar is shared the calendar will + * show up in both the Sharer's and Sharee's calendar-home root. + * This interface adds a few methods that ensure that this happens, and there + * are also a number of new requirements in the base-class you must now follow. + * + * + * How it works + * ============ + * + * When a user shares a calendar, the addShare() method will be called with a + * list of sharees that are now added, and a list of sharees that have been + * removed. + * Removal is instant, but when a sharee is added the sharee first gets a + * chance to accept or reject the invitation for a share. + * + * After a share is accepted, the calendar will be returned from + * getUserCalendars for both the sharer, and the sharee. + * + * If the sharee deletes the calendar, only their share gets deleted. When the + * owner deletes a calendar, it will be removed for everybody. + * + * + * Notifications + * ============= + * + * During all these sharing operations, a lot of notifications are sent back + * and forward. + * + * Whenever the list of sharees for a calendar has been changed (they have been + * added, removed or modified) all sharees should get a notification for this + * change. + * This notification is always represented by: + * + * Sabre_CalDAV_Notifications_Notification_Invite + * + * In the case of an invite, the sharee may reply with an 'accept' or + * 'decline'. These are always represented by: + * + * Sabre_CalDAV_Notifications_Notification_Invite + * + * + * Calendar access by sharees + * ========================== + * + * As mentioned earlier, shared calendars must now also be returned for + * getCalendarsForUser for sharees. A few things change though. + * + * The following properties must be specified: + * + * 1. {http://calendarserver.org/ns/}shared-url + * + * This property MUST contain the url to the original calendar, that is.. the + * path to the calendar from the owner. + * + * 2. {http://sabredav.org/ns}owner-principal + * + * This is a url to to the principal who is sharing the calendar. + * + * 3. {http://sabredav.org/ns}read-only + * + * This should be either 0 or 1, depending on if the user has read-only or + * read-write access to the calendar. + * + * Only when this is done, the calendar will correctly be marked as a calendar + * that's shared to him, thus allowing clients to display the correct interface + * and ACL enforcement. + * + * If a sharee deletes their calendar, only their instance of the calendar + * should be deleted, the original should still exists. + * Pretty much any 'dead' WebDAV properties on these shared calendars should be + * specific to a user. This means that if the displayname is changed by a + * sharee, the original is not affected. This is also true for: + * * The description + * * The color + * * The order + * * And any other dead properties. + * + * Properties like a ctag should not be different for multiple instances of the + * calendar. + * + * Lastly, objects *within* calendars should also have user-specific data. The + * two things that are user-specific are: + * * VALARM objects + * * The TRANSP property + * + * This _also_ implies that if a VALARM is deleted by a sharee for some event, + * this has no effect on the original VALARM. + * + * Understandably, the this last requirement is one of the hardest. + * Realisticly, I can see people ignoring this part of the spec, but that could + * cause a different set of issues. + * + * + * Publishing + * ========== + * + * When a user publishes a url, the server should generate a 'publish url'. + * This is a read-only url, anybody can use to consume the calendar feed. + * + * Calendars are in one of two states: + * * published + * * unpublished + * + * If a calendar is published, the following property should be returned + * for each calendar in getCalendarsForPrincipal. + * + * {http://calendarserver.org/ns/}publish-url + * + * This element should contain a {DAV:}href element, which points to the + * public url that does not require authentication. Unlike every other href, + * this url must be absolute. + * + * Ideally, the following property is always returned + * + * {http://calendarserver.org/ns/}pre-publish-url + * + * This property should contain the url that the calendar _would_ have, if it + * were to be published. iCal uses this to display the url, before the user + * will actually publish it. + * + * + * Selectively disabling publish or share feature + * ============================================== + * + * If Sabre_CalDAV_Property_AllowedSharingModes is returned from + * getCalendarsByUser, this allows the server to specify wether either sharing, + * or publishing is supported. + * + * This allows a client to determine in advance which features are available, + * and update the interface appropriately. If this property is not returned by + * the backend, the SharingPlugin automatically injects it and assumes both + * features are available. + * + * @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_Backend_SharingSupport extends Sabre_CalDAV_Backend_NotificationSupport { + + /** + * Updates the list of shares. + * + * The first array is a list of people that are to be added to the + * calendar. + * + * Every element in the add array has the following properties: + * * href - A url. Usually a mailto: address + * * commonName - Usually a first and last name, or false + * * summary - A description of the share, can also be false + * * readOnly - A boolean value + * + * Every element in the remove array is just the address string. + * + * Note that if the calendar is currently marked as 'not shared' by and + * this method is called, the calendar should be 'upgraded' to a shared + * calendar. + * + * @param mixed $calendarId + * @param array $add + * @param array $remove + * @return void + */ + function updateShares($calendarId, array $add, array $remove); + + /** + * Returns the list of people whom this calendar is shared with. + * + * Every element in this array should have the following properties: + * * href - Often a mailto: address + * * commonName - Optional, for example a first + last name + * * status - See the Sabre_CalDAV_SharingPlugin::STATUS_ constants. + * * readOnly - boolean + * * summary - Optional, a description for the share + * + * @param mixed $calendarId + * @return array + */ + function getShares($calendarId); + + /** + * This method is called when a user replied to a request to share. + * + * If the user chose to accept the share, this method should return the + * newly created calendar url. + * + * @param string href The sharee who is replying (often a mailto: address) + * @param int status One of the SharingPlugin::STATUS_* constants + * @param string $calendarUri The url to the calendar thats being shared + * @param string $inReplyTo The unique id this message is a response to + * @param string $summary A description of the reply + * @return null|string + */ + function shareReply($href, $status, $calendarUri, $inReplyTo, $summary = null); + + /** + * Publishes a calendar + * + * @param mixed $calendarId + * @param bool $value + * @return void + */ + function setPublishStatus($calendarId, $value); + +} diff --git a/3rdparty/Sabre/CalDAV/Calendar.php b/3rdparty/Sabre/CalDAV/Calendar.php index 623df2dd1b8..63253febd5c 100755..100644 --- a/3rdparty/Sabre/CalDAV/Calendar.php +++ b/3rdparty/Sabre/CalDAV/Calendar.php @@ -9,7 +9,7 @@ * @package Sabre * @subpackage CalDAV * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. - * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @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 { @@ -24,7 +24,7 @@ class Sabre_CalDAV_Calendar implements Sabre_CalDAV_ICalendar, Sabre_DAV_IProper /** * CalDAV backend * - * @var Sabre_CalDAV_Backend_Abstract + * @var Sabre_CalDAV_Backend_BackendInterface */ protected $caldavBackend; @@ -39,16 +39,15 @@ class Sabre_CalDAV_Calendar implements Sabre_CalDAV_ICalendar, Sabre_DAV_IProper * Constructor * * @param Sabre_DAVACL_IPrincipalBackend $principalBackend - * @param Sabre_CalDAV_Backend_Abstract $caldavBackend + * @param Sabre_CalDAV_Backend_BackendInterface $caldavBackend * @param array $calendarInfo */ - public function __construct(Sabre_DAVACL_IPrincipalBackend $principalBackend, Sabre_CalDAV_Backend_Abstract $caldavBackend, $calendarInfo) { + public function __construct(Sabre_DAVACL_IPrincipalBackend $principalBackend, Sabre_CalDAV_Backend_BackendInterface $caldavBackend, $calendarInfo) { $this->caldavBackend = $caldavBackend; $this->principalBackend = $principalBackend; $this->calendarInfo = $calendarInfo; - } /** @@ -92,9 +91,6 @@ class Sabre_CalDAV_Calendar implements Sabre_CalDAV_ICalendar, Sabre_DAV_IProper 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; @@ -110,12 +106,21 @@ class Sabre_CalDAV_Calendar implements Sabre_CalDAV_ICalendar, Sabre_DAV_IProper * The contained calendar objects are for example Events or Todo's. * * @param string $name - * @return Sabre_DAV_ICalendarObject + * @return Sabre_CalDAV_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'); + + $obj['acl'] = $this->getACL(); + // Removing the irrelivant + foreach($obj['acl'] as $key=>$acl) { + if ($acl['privilege'] === '{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}read-free-busy') { + unset($obj['acl'][$key]); + } + } + return new Sabre_CalDAV_CalendarObject($this->caldavBackend,$this->calendarInfo,$obj); } @@ -130,6 +135,13 @@ class Sabre_CalDAV_Calendar implements Sabre_CalDAV_ICalendar, Sabre_DAV_IProper $objs = $this->caldavBackend->getCalendarObjects($this->calendarInfo['id']); $children = array(); foreach($objs as $obj) { + $obj['acl'] = $this->getACL(); + // Removing the irrelivant + foreach($obj['acl'] as $key=>$acl) { + if ($acl['privilege'] === '{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}read-free-busy') { + unset($obj['acl'][$key]); + } + } $children[] = new Sabre_CalDAV_CalendarObject($this->caldavBackend,$this->calendarInfo,$obj); } return $children; @@ -262,27 +274,27 @@ class Sabre_CalDAV_Calendar implements Sabre_CalDAV_ICalendar, Sabre_DAV_IProper return array( array( 'privilege' => '{DAV:}read', - 'principal' => $this->calendarInfo['principaluri'], + 'principal' => $this->getOwner(), 'protected' => true, ), array( 'privilege' => '{DAV:}write', - 'principal' => $this->calendarInfo['principaluri'], + 'principal' => $this->getOwner(), 'protected' => true, ), array( 'privilege' => '{DAV:}read', - 'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-write', + 'principal' => $this->getOwner() . '/calendar-proxy-write', 'protected' => true, ), array( 'privilege' => '{DAV:}write', - 'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-write', + 'principal' => $this->getOwner() . '/calendar-proxy-write', 'protected' => true, ), array( 'privilege' => '{DAV:}read', - 'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-read', + 'principal' => $this->getOwner() . '/calendar-proxy-read', 'protected' => true, ), array( @@ -340,4 +352,27 @@ class Sabre_CalDAV_Calendar implements Sabre_CalDAV_ICalendar, Sabre_DAV_IProper } + /** + * Performs a calendar-query on the contents of this calendar. + * + * The calendar-query is defined in RFC4791 : CalDAV. Using the + * calendar-query it is possible for a client to request a specific set of + * object, based on contents of iCalendar properties, date-ranges and + * iCalendar component types (VTODO, VEVENT). + * + * This method should just return a list of (relative) urls that match this + * query. + * + * The list of filters are specified as an array. The exact array is + * documented by Sabre_CalDAV_CalendarQueryParser. + * + * @param array $filters + * @return array + */ + public function calendarQuery(array $filters) { + + return $this->caldavBackend->calendarQuery($this->calendarInfo['id'], $filters); + + } + } diff --git a/3rdparty/Sabre/CalDAV/CalendarObject.php b/3rdparty/Sabre/CalDAV/CalendarObject.php index 72f0a578d16..40bd8588c70 100755..100644 --- a/3rdparty/Sabre/CalDAV/CalendarObject.php +++ b/3rdparty/Sabre/CalDAV/CalendarObject.php @@ -6,13 +6,13 @@ * @package Sabre * @subpackage CalDAV * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. - * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @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 + * Sabre_CalDAV_Backend_BackendInterface * * @var array */ @@ -35,11 +35,11 @@ class Sabre_CalDAV_CalendarObject extends Sabre_DAV_File implements Sabre_CalDAV /** * Constructor * - * @param Sabre_CalDAV_Backend_Abstract $caldavBackend + * @param Sabre_CalDAV_Backend_BackendInterface $caldavBackend * @param array $calendarInfo * @param array $objectData */ - public function __construct(Sabre_CalDAV_Backend_Abstract $caldavBackend,array $calendarInfo,array $objectData) { + public function __construct(Sabre_CalDAV_Backend_BackendInterface $caldavBackend,array $calendarInfo,array $objectData) { $this->caldavBackend = $caldavBackend; @@ -85,8 +85,8 @@ class Sabre_CalDAV_CalendarObject extends Sabre_DAV_File implements Sabre_CalDAV /** * Updates the ICalendar-formatted object * - * @param string $calendarData - * @return void + * @param string|resource $calendarData + * @return string */ public function put($calendarData) { @@ -119,7 +119,7 @@ class Sabre_CalDAV_CalendarObject extends Sabre_DAV_File implements Sabre_CalDAV */ public function getContentType() { - return 'text/calendar'; + return 'text/calendar; charset=utf-8'; } @@ -143,7 +143,7 @@ class Sabre_CalDAV_CalendarObject extends Sabre_DAV_File implements Sabre_CalDAV /** * Returns the last modification date as a unix timestamp * - * @return time + * @return int */ public function getLastModified() { @@ -206,6 +206,12 @@ class Sabre_CalDAV_CalendarObject extends Sabre_DAV_File implements Sabre_CalDAV */ public function getACL() { + // An alternative acl may be specified in the object data. + if (isset($this->objectData['acl'])) { + return $this->objectData['acl']; + } + + // The default ACL return array( array( 'privilege' => '{DAV:}read', diff --git a/3rdparty/Sabre/CalDAV/CalendarQueryParser.php b/3rdparty/Sabre/CalDAV/CalendarQueryParser.php index bd0d343382f..b95095f96fc 100755..100644 --- a/3rdparty/Sabre/CalDAV/CalendarQueryParser.php +++ b/3rdparty/Sabre/CalDAV/CalendarQueryParser.php @@ -1,5 +1,7 @@ <?php +use Sabre\VObject; + /** * Parses the calendar-query report request body. * @@ -68,7 +70,7 @@ class Sabre_CalDAV_CalendarQueryParser { $this->xpath = new DOMXPath($dom); $this->xpath->registerNameSpace('cal',Sabre_CalDAV_Plugin::NS_CALDAV); - $this->xpath->registerNameSpace('dav','urn:DAV'); + $this->xpath->registerNameSpace('dav','DAV:'); } @@ -241,12 +243,12 @@ class Sabre_CalDAV_CalendarQueryParser { $timeRangeNode = $timeRangeNodes->item(0); if ($start = $timeRangeNode->getAttribute('start')) { - $start = Sabre_VObject_DateTimeParser::parseDateTime($start); + $start = VObject\DateTimeParser::parseDateTime($start); } else { $start = null; } if ($end = $timeRangeNode->getAttribute('end')) { - $end = Sabre_VObject_DateTimeParser::parseDateTime($end); + $end = VObject\DateTimeParser::parseDateTime($end); } else { $end = null; } @@ -274,13 +276,13 @@ class Sabre_CalDAV_CalendarQueryParser { if(!$start) { throw new Sabre_DAV_Exception_BadRequest('The "start" attribute is required for the CALDAV:expand element'); } - $start = Sabre_VObject_DateTimeParser::parseDateTime($start); + $start = 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); + $end = 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.'); diff --git a/3rdparty/Sabre/CalDAV/CalendarQueryValidator.php b/3rdparty/Sabre/CalDAV/CalendarQueryValidator.php index 8f674840e87..53e86fc509f 100755..100644 --- a/3rdparty/Sabre/CalDAV/CalendarQueryValidator.php +++ b/3rdparty/Sabre/CalDAV/CalendarQueryValidator.php @@ -1,5 +1,7 @@ <?php +use Sabre\VObject; + /** * CalendarQuery Validator * @@ -22,11 +24,11 @@ class Sabre_CalDAV_CalendarQueryValidator { * * The list of filters must be formatted as parsed by Sabre_CalDAV_CalendarQueryParser * - * @param Sabre_VObject_Component $vObject + * @param VObject\Component $vObject * @param array $filters * @return bool */ - public function validate(Sabre_VObject_Component $vObject,array $filters) { + public function validate(VObject\Component $vObject,array $filters) { // The top level object is always a component filter. // We'll parse it manually, as it's pretty simple. @@ -48,11 +50,11 @@ class Sabre_CalDAV_CalendarQueryValidator { * component we're checking should be specified, not the component to check * itself. * - * @param Sabre_VObject_Component $parent + * @param VObject\Component $parent * @param array $filters * @return bool */ - protected function validateCompFilters(Sabre_VObject_Component $parent, array $filters) { + protected function validateCompFilters(VObject\Component $parent, array $filters) { foreach($filters as $filter) { @@ -117,11 +119,11 @@ class Sabre_CalDAV_CalendarQueryValidator { * property we're checking should be specified, not the property to check * itself. * - * @param Sabre_VObject_Component $parent + * @param VObject\Component $parent * @param array $filters * @return bool */ - protected function validatePropFilters(Sabre_VObject_Component $parent, array $filters) { + protected function validatePropFilters(VObject\Component $parent, array $filters) { foreach($filters as $filter) { @@ -187,11 +189,11 @@ class Sabre_CalDAV_CalendarQueryValidator { * parameter we're checking should be specified, not the parameter to check * itself. * - * @param Sabre_VObject_Property $parent + * @param VObject\Property $parent * @param array $filters * @return bool */ - protected function validateParamFilters(Sabre_VObject_Property $parent, array $filters) { + protected function validateParamFilters(VObject\Property $parent, array $filters) { foreach($filters as $filter) { @@ -243,11 +245,11 @@ class Sabre_CalDAV_CalendarQueryValidator { * 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 VObject\Node $parent * @param array $textMatch * @return bool */ - protected function validateTextMatch(Sabre_VObject_Node $parent, array $textMatch) { + protected function validateTextMatch(VObject\Node $parent, array $textMatch) { $value = (string)$parent; @@ -263,12 +265,12 @@ class Sabre_CalDAV_CalendarQueryValidator { * This is all based on the rules specified in rfc4791, which are quite * complex. * - * @param Sabre_VObject_Node $component + * @param VObject\Node $component * @param DateTime $start * @param DateTime $end * @return bool */ - protected function validateTimeRange(Sabre_VObject_Node $component, $start, $end) { + protected function validateTimeRange(VObject\Node $component, $start, $end) { if (is_null($start)) { $start = new DateTime('1900-01-01'); @@ -296,7 +298,7 @@ class Sabre_CalDAV_CalendarQueryValidator { if ($component->parent->name === 'VEVENT' && $component->parent->RRULE) { // Fire up the iterator! - $it = new Sabre_VObject_RecurrenceIterator($component->parent->parent, (string)$component->parent->UID); + $it = new VObject\RecurrenceIterator($component->parent->parent, (string)$component->parent->UID); while($it->valid()) { $expandedEvent = $it->getEventObject(); diff --git a/3rdparty/Sabre/CalDAV/CalendarRootNode.php b/3rdparty/Sabre/CalDAV/CalendarRootNode.php index 3907913cc78..eb62eea75a6 100755..100644 --- a/3rdparty/Sabre/CalDAV/CalendarRootNode.php +++ b/3rdparty/Sabre/CalDAV/CalendarRootNode.php @@ -1,14 +1,15 @@ <?php /** - * Users collection + * Calendars collection * - * This object is responsible for generating a collection of users. + * This object is responsible for generating a list of calendar-homes for each + * user. * * @package Sabre * @subpackage CalDAV * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. - * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @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 { @@ -16,7 +17,7 @@ class Sabre_CalDAV_CalendarRootNode extends Sabre_DAVACL_AbstractPrincipalCollec /** * CalDAV backend * - * @var Sabre_CalDAV_Backend_Abstract + * @var Sabre_CalDAV_Backend_BackendInterface */ protected $caldavBackend; @@ -32,10 +33,10 @@ class Sabre_CalDAV_CalendarRootNode extends Sabre_DAVACL_AbstractPrincipalCollec * * * @param Sabre_DAVACL_IPrincipalBackend $principalBackend - * @param Sabre_CalDAV_Backend_Abstract $caldavBackend + * @param Sabre_CalDAV_Backend_BackendInterface $caldavBackend * @param string $principalPrefix */ - public function __construct(Sabre_DAVACL_IPrincipalBackend $principalBackend,Sabre_CalDAV_Backend_Abstract $caldavBackend, $principalPrefix = 'principals') { + public function __construct(Sabre_DAVACL_IPrincipalBackend $principalBackend,Sabre_CalDAV_Backend_BackendInterface $caldavBackend, $principalPrefix = 'principals') { parent::__construct($principalBackend, $principalPrefix); $this->caldavBackend = $caldavBackend; diff --git a/3rdparty/Sabre/CalDAV/Exception/InvalidComponentType.php b/3rdparty/Sabre/CalDAV/Exception/InvalidComponentType.php new file mode 100644 index 00000000000..4ac617d22f0 --- /dev/null +++ b/3rdparty/Sabre/CalDAV/Exception/InvalidComponentType.php @@ -0,0 +1,32 @@ +<?php + +/** + * Sabre_CalDAV_Exception_InvalidComponentType + * + * @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_Exception_InvalidComponentType extends Sabre_DAV_Exception_Forbidden { + + /** + * Adds in extra information in the xml response. + * + * This method adds the {CALDAV:}supported-calendar-component as defined in rfc4791 + * + * @param Sabre_DAV_Server $server + * @param DOMElement $errorNode + * @return void + */ + public function serialize(Sabre_DAV_Server $server,DOMElement $errorNode) { + + $doc = $errorNode->ownerDocument; + + $np = $doc->createElementNS(Sabre_CalDAV_Plugin::NS_CALDAV,'cal:supported-calendar-component'); + $errorNode->appendChild($np); + + } + +}
\ No newline at end of file diff --git a/3rdparty/Sabre/CalDAV/ICSExportPlugin.php b/3rdparty/Sabre/CalDAV/ICSExportPlugin.php index ec42b406b2f..d3e4e7b7201 100755..100644 --- a/3rdparty/Sabre/CalDAV/ICSExportPlugin.php +++ b/3rdparty/Sabre/CalDAV/ICSExportPlugin.php @@ -1,5 +1,7 @@ <?php +use Sabre\VObject; + /** * ICS Exporter * @@ -82,7 +84,7 @@ class Sabre_CalDAV_ICSExportPlugin extends Sabre_DAV_ServerPlugin { */ public function generateICS(array $nodes) { - $calendar = new Sabre_VObject_Component('vcalendar'); + $calendar = new VObject\Component('vcalendar'); $calendar->version = '2.0'; if (Sabre_DAV_Server::$exposeVersion) { $calendar->prodid = '-//SabreDAV//SabreDAV ' . Sabre_DAV_Version::VERSION . '//EN'; @@ -103,7 +105,7 @@ class Sabre_CalDAV_ICSExportPlugin extends Sabre_DAV_ServerPlugin { } $nodeData = $node[200]['{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}calendar-data']; - $nodeComp = Sabre_VObject_Reader::read($nodeData); + $nodeComp = VObject\Reader::read($nodeData); foreach($nodeComp->children() as $child) { diff --git a/3rdparty/Sabre/CalDAV/ICalendar.php b/3rdparty/Sabre/CalDAV/ICalendar.php index 15d51ebcf79..40aa9f9579c 100755..100644 --- a/3rdparty/Sabre/CalDAV/ICalendar.php +++ b/3rdparty/Sabre/CalDAV/ICalendar.php @@ -8,11 +8,28 @@ * @package Sabre * @subpackage CalDAV * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. - * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @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 { - + /** + * Performs a calendar-query on the contents of this calendar. + * + * The calendar-query is defined in RFC4791 : CalDAV. Using the + * calendar-query it is possible for a client to request a specific set of + * object, based on contents of iCalendar properties, date-ranges and + * iCalendar component types (VTODO, VEVENT). + * + * This method should just return a list of (relative) urls that match this + * query. + * + * The list of filters are specified as an array. The exact array is + * documented by Sabre_CalDAV_CalendarQueryParser. + * + * @param array $filters + * @return array + */ + public function calendarQuery(array $filters); } diff --git a/3rdparty/Sabre/CalDAV/ICalendarObject.php b/3rdparty/Sabre/CalDAV/ICalendarObject.php index 280f982a310..280f982a310 100755..100644 --- a/3rdparty/Sabre/CalDAV/ICalendarObject.php +++ b/3rdparty/Sabre/CalDAV/ICalendarObject.php diff --git a/3rdparty/Sabre/CalDAV/IShareableCalendar.php b/3rdparty/Sabre/CalDAV/IShareableCalendar.php new file mode 100644 index 00000000000..5b55788c014 --- /dev/null +++ b/3rdparty/Sabre/CalDAV/IShareableCalendar.php @@ -0,0 +1,48 @@ +<?php + +/** + * This interface represents a Calendar that can be shared with other 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 + */ +interface Sabre_CalDAV_IShareableCalendar extends Sabre_CalDAV_ICalendar { + + /** + * Updates the list of shares. + * + * The first array is a list of people that are to be added to the + * calendar. + * + * Every element in the add array has the following properties: + * * href - A url. Usually a mailto: address + * * commonName - Usually a first and last name, or false + * * summary - A description of the share, can also be false + * * readOnly - A boolean value + * + * Every element in the remove array is just the address string. + * + * @param array $add + * @param array $remove + * @return void + */ + function updateShares(array $add, array $remove); + + /** + * Returns the list of people whom this calendar is shared with. + * + * Every element in this array should have the following properties: + * * href - Often a mailto: address + * * commonName - Optional, for example a first + last name + * * status - See the Sabre_CalDAV_SharingPlugin::STATUS_ constants. + * * readOnly - boolean + * * summary - Optional, a description for the share + * + * @return array + */ + function getShares(); + +} diff --git a/3rdparty/Sabre/CalDAV/ISharedCalendar.php b/3rdparty/Sabre/CalDAV/ISharedCalendar.php new file mode 100644 index 00000000000..eb4d31fead5 --- /dev/null +++ b/3rdparty/Sabre/CalDAV/ISharedCalendar.php @@ -0,0 +1,22 @@ +<?php + +/** + * This interface represents a Calendar that is shared by a different 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 + */ +interface Sabre_CalDAV_ISharedCalendar extends Sabre_CalDAV_ICalendar { + + /** + * This method should return the url of the owners' copy of the shared + * calendar. + * + * @return string + */ + function getSharedUrl(); + +} diff --git a/3rdparty/Sabre/CalDAV/Notifications/Collection.php b/3rdparty/Sabre/CalDAV/Notifications/Collection.php new file mode 100644 index 00000000000..83b6c9301dc --- /dev/null +++ b/3rdparty/Sabre/CalDAV/Notifications/Collection.php @@ -0,0 +1,169 @@ +<?php + +/** + * This node represents a list of notifications. + * + * It provides no additional functionality, but you must implement this + * interface to allow the Notifications plugin to mark the collection + * as a notifications collection. + * + * This collection should only return Sabre_CalDAV_Notifications_INode nodes as + * its children. + * + * @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_Notifications_Collection extends Sabre_DAV_Collection implements Sabre_CalDAV_Notifications_ICollection, Sabre_DAVACL_IACL { + + /** + * The notification backend + * + * @var Sabre_CalDAV_Backend_NotificationSupport + */ + protected $caldavBackend; + + /** + * Principal uri + * + * @var string + */ + protected $principalUri; + + /** + * Constructor + * + * @param Sabre_CalDAV_Backend_NotificationSupport $caldavBackend + * @param string $principalUri + */ + public function __construct(Sabre_CalDAV_Backend_NotificationSupport $caldavBackend, $principalUri) { + + $this->caldavBackend = $caldavBackend; + $this->principalUri = $principalUri; + + } + + /** + * Returns all notifications for a principal + * + * @return array + */ + public function getChildren() { + + $children = array(); + $notifications = $this->caldavBackend->getNotificationsForPrincipal($this->principalUri); + + foreach($notifications as $notification) { + + $children[] = new Sabre_CalDAV_Notifications_Node( + $this->caldavBackend, + $this->principalUri, + $notification + ); + } + + return $children; + + } + + /** + * Returns the name of this object + * + * @return string + */ + public function getName() { + + return 'notifications'; + + } + + /** + * 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( + 'principal' => $this->getOwner(), + 'privilege' => '{DAV:}read', + 'protected' => true, + ), + array( + 'principal' => $this->getOwner(), + 'privilege' => '{DAV:}write', + 'protected' => true, + ) + ); + + } + + /** + * Updates the ACL + * + * This method will receive a list of new ACE's as an array argument. + * + * @param array $acl + * @return void + */ + public function setACL(array $acl) { + + throw new Sabre_DAV_Exception_NotImplemented('Updating ACLs is not implemented here'); + + } + + /** + * Returns the list of supported privileges for this node. + * + * The returned data structure is a list of nested privileges. + * See Sabre_DAVACL_Plugin::getDefaultSupportedPrivilegeSet for a simple + * standard structure. + * + * If null is returned from this method, the default privilege set is used, + * which is fine for most common usecases. + * + * @return array|null + */ + public function getSupportedPrivilegeSet() { + + return null; + + } + +} diff --git a/3rdparty/Sabre/CalDAV/Notifications/ICollection.php b/3rdparty/Sabre/CalDAV/Notifications/ICollection.php new file mode 100644 index 00000000000..eb873af3f92 --- /dev/null +++ b/3rdparty/Sabre/CalDAV/Notifications/ICollection.php @@ -0,0 +1,22 @@ +<?php + +/** + * This node represents a list of notifications. + * + * It provides no additional functionality, but you must implement this + * interface to allow the Notifications plugin to mark the collection + * as a notifications collection. + * + * This collection should only return Sabre_CalDAV_Notifications_INode nodes as + * its children. + * + * @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_Notifications_ICollection extends Sabre_DAV_ICollection { + + +} diff --git a/3rdparty/Sabre/CalDAV/Notifications/INode.php b/3rdparty/Sabre/CalDAV/Notifications/INode.php new file mode 100644 index 00000000000..84721de5ab7 --- /dev/null +++ b/3rdparty/Sabre/CalDAV/Notifications/INode.php @@ -0,0 +1,38 @@ +<?php + +/** + * This node represents a single notification. + * + * The signature is mostly identical to that of Sabre_DAV_IFile, but the get() method + * MUST return an xml document that matches the requirements of the + * 'caldav-notifications.txt' spec. + * + * For a complete example, check out the Notification class, which contains + * some helper functions. + * + * @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_Notifications_INode { + + /** + * This method must return an xml element, using the + * Sabre_CalDAV_Notifications_INotificationType classes. + * + * @return Sabre_DAVNotification_INotificationType + */ + function getNotificationType(); + + /** + * Returns the etag for the notification. + * + * The etag must be surrounded by litteral double-quotes. + * + * @return string + */ + function getETag(); + +} diff --git a/3rdparty/Sabre/CalDAV/Notifications/INotificationType.php b/3rdparty/Sabre/CalDAV/Notifications/INotificationType.php new file mode 100644 index 00000000000..02a65a01fa3 --- /dev/null +++ b/3rdparty/Sabre/CalDAV/Notifications/INotificationType.php @@ -0,0 +1,43 @@ +<?php + +/** + * This interface reflects a single notification type. + * + * @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_Notifications_INotificationType extends Sabre_DAV_PropertyInterface { + + /** + * This method serializes the entire notification, as it is used in the + * response body. + * + * @param Sabre_DAV_Server $server + * @param DOMElement $node + * @return void + */ + function serializeBody(Sabre_DAV_Server $server, \DOMElement $node); + + /** + * Returns a unique id for this notification + * + * This is just the base url. This should generally be some kind of unique + * id. + * + * @return string + */ + function getId(); + + /** + * Returns the ETag for this notification. + * + * The ETag must be surrounded by literal double-quotes. + * + * @return string + */ + function getETag(); + +} diff --git a/3rdparty/Sabre/CalDAV/Notifications/Node.php b/3rdparty/Sabre/CalDAV/Notifications/Node.php new file mode 100644 index 00000000000..8021a75debf --- /dev/null +++ b/3rdparty/Sabre/CalDAV/Notifications/Node.php @@ -0,0 +1,188 @@ +<?php + +/** + * This node represents a single notification. + * + * The signature is mostly identical to that of Sabre_DAV_IFile, but the get() method + * MUST return an xml document that matches the requirements of the + * 'caldav-notifications.txt' spec. + + * @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_Notifications_Node extends Sabre_DAV_File implements Sabre_CalDAV_Notifications_INode, Sabre_DAVACL_IACL { + + /** + * The notification backend + * + * @var Sabre_CalDAV_Backend_NotificationSupport + */ + protected $caldavBackend; + + /** + * The actual notification + * + * @var Sabre_CalDAV_Notifications_INotificationType + */ + protected $notification; + + /** + * Owner principal of the notification + * + * @var string + */ + protected $principalUri; + + /** + * Constructor + * + * @param Sabre_CalDAV_Backend_NotificationSupport $caldavBackend + * @param string $principalUri + * @param Sabre_CalDAV_Notifications_INotificationType $notification + */ + public function __construct(Sabre_CalDAV_Backend_NotificationSupport $caldavBackend, $principalUri, Sabre_CalDAV_Notifications_INotificationType $notification) { + + $this->caldavBackend = $caldavBackend; + $this->principalUri = $principalUri; + $this->notification = $notification; + + } + + /** + * Returns the path name for this notification + * + * @return id + */ + public function getName() { + + return $this->notification->getId() . '.xml'; + + } + + /** + * Returns the etag for the notification. + * + * The etag must be surrounded by litteral double-quotes. + * + * @return string + */ + public function getETag() { + + return $this->notification->getETag(); + + } + + /** + * This method must return an xml element, using the + * Sabre_CalDAV_Notifications_INotificationType classes. + * + * @return Sabre_DAVNotification_INotificationType + */ + public function getNotificationType() { + + return $this->notification; + + } + + /** + * Deletes this notification + * + * @return void + */ + public function delete() { + + $this->caldavBackend->deleteNotification($this->getOwner(), $this->notification); + + } + + /** + * 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( + 'principal' => $this->getOwner(), + 'privilege' => '{DAV:}read', + 'protected' => true, + ), + array( + 'principal' => $this->getOwner(), + 'privilege' => '{DAV:}write', + 'protected' => true, + ) + ); + + } + + /** + * Updates the ACL + * + * This method will receive a list of new ACE's as an array argument. + * + * @param array $acl + * @return void + */ + public function setACL(array $acl) { + + throw new Sabre_DAV_Exception_NotImplemented('Updating ACLs is not implemented here'); + + } + + /** + * Returns the list of supported privileges for this node. + * + * The returned data structure is a list of nested privileges. + * See Sabre_DAVACL_Plugin::getDefaultSupportedPrivilegeSet for a simple + * standard structure. + * + * If null is returned from this method, the default privilege set is used, + * which is fine for most common usecases. + * + * @return array|null + */ + public function getSupportedPrivilegeSet() { + + return null; + + } + +} diff --git a/3rdparty/Sabre/CalDAV/Notifications/Notification/Invite.php b/3rdparty/Sabre/CalDAV/Notifications/Notification/Invite.php new file mode 100644 index 00000000000..a6b36203f34 --- /dev/null +++ b/3rdparty/Sabre/CalDAV/Notifications/Notification/Invite.php @@ -0,0 +1,276 @@ +<?php + +use Sabre_CalDAV_SharingPlugin as SharingPlugin; + +/** + * This class represents the cs:invite-notification notification element. + * + * @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_Notifications_Notification_Invite extends Sabre_DAV_Property implements Sabre_CalDAV_Notifications_INotificationType { + + /** + * A unique id for the message + * + * @var string + */ + protected $id; + + /** + * Timestamp of the notification + * + * @var DateTime + */ + protected $dtStamp; + + /** + * A url to the recipient of the notification. This can be an email + * address (mailto:), or a principal url. + * + * @var string + */ + protected $href; + + /** + * The type of message, see the SharingPlugin::STATUS_* constants. + * + * @var int + */ + protected $type; + + /** + * True if access to a calendar is read-only. + * + * @var bool + */ + protected $readOnly; + + /** + * A url to the shared calendar. + * + * @var string + */ + protected $hostUrl; + + /** + * Url to the sharer of the calendar + * + * @var string + */ + protected $organizer; + + /** + * The name of the sharer. + * + * @var string + */ + protected $commonName; + + /** + * A description of the share request + * + * @var string + */ + protected $summary; + + /** + * The Etag for the notification + * + * @var string + */ + protected $etag; + + /** + * The list of supported components + * + * @var Sabre_CalDAV_Property_SupportedCalendarComponentSet + */ + protected $supportedComponents; + + /** + * Creates the Invite notification. + * + * This constructor receives an array with the following elements: + * + * * id - A unique id + * * etag - The etag + * * dtStamp - A DateTime object with a timestamp for the notification. + * * type - The type of notification, see SharingPlugin::STATUS_* + * constants for details. + * * readOnly - This must be set to true, if this is an invite for + * read-only access to a calendar. + * * hostUrl - A url to the shared calendar. + * * organizer - Url to the sharer principal. + * * commonName - The real name of the sharer (optional). + * * summary - Description of the share, can be the same as the + * calendar, but may also be modified (optional). + * * supportedComponents - An instance of + * Sabre_CalDAV_Property_SupportedCalendarComponentSet. + * This allows the client to determine which components + * will be supported in the shared calendar. This is + * also optional. + * + * @param array $values All the options + */ + public function __construct(array $values) { + + $required = array( + 'id', + 'etag', + 'href', + 'dtStamp', + 'type', + 'readOnly', + 'hostUrl', + 'organizer', + ); + foreach($required as $item) { + if (!isset($values[$item])) { + throw new InvalidArgumentException($item . ' is a required constructor option'); + } + } + + foreach($values as $key=>$value) { + if (!property_exists($this, $key)) { + throw new InvalidArgumentException('Unknown option: ' . $key); + } + $this->$key = $value; + } + + } + + /** + * Serializes the notification as a single property. + * + * You should usually just encode the single top-level element of the + * notification. + * + * @param Sabre_DAV_Server $server + * @param DOMElement $node + * @return void + */ + public function serialize(Sabre_DAV_Server $server, \DOMElement $node) { + + $prop = $node->ownerDocument->createElement('cs:invite-notification'); + $node->appendChild($prop); + + } + + /** + * This method serializes the entire notification, as it is used in the + * response body. + * + * @param Sabre_DAV_Server $server + * @param DOMElement $node + * @return void + */ + public function serializeBody(Sabre_DAV_Server $server, \DOMElement $node) { + + $doc = $node->ownerDocument; + + $dt = $doc->createElement('cs:dtstamp'); + $this->dtStamp->setTimezone(new \DateTimezone('GMT')); + $dt->appendChild($doc->createTextNode($this->dtStamp->format('Ymd\\THis\\Z'))); + $node->appendChild($dt); + + $prop = $doc->createElement('cs:invite-notification'); + $node->appendChild($prop); + + $uid = $doc->createElement('cs:uid'); + $uid->appendChild( $doc->createTextNode($this->id) ); + $prop->appendChild($uid); + + $href = $doc->createElement('d:href'); + $href->appendChild( $doc->createTextNode( $this->href ) ); + $prop->appendChild($href); + + $nodeName = null; + switch($this->type) { + + case SharingPlugin::STATUS_ACCEPTED : + $nodeName = 'cs:invite-accepted'; + break; + case SharingPlugin::STATUS_DECLINED : + $nodeName = 'cs:invite-declined'; + break; + case SharingPlugin::STATUS_DELETED : + $nodeName = 'cs:invite-deleted'; + break; + case SharingPlugin::STATUS_NORESPONSE : + $nodeName = 'cs:invite-noresponse'; + break; + + } + $prop->appendChild( + $doc->createElement($nodeName) + ); + $hostHref = $doc->createElement('d:href', $server->getBaseUri() . $this->hostUrl); + $hostUrl = $doc->createElement('cs:hosturl'); + $hostUrl->appendChild($hostHref); + $prop->appendChild($hostUrl); + + $access = $doc->createElement('cs:access'); + if ($this->readOnly) { + $access->appendChild($doc->createElement('cs:read')); + } else { + $access->appendChild($doc->createElement('cs:read-write')); + } + $prop->appendChild($access); + + $organizerHref = $doc->createElement('d:href', $server->getBaseUri() . $this->organizer); + $organizerUrl = $doc->createElement('cs:organizer'); + if ($this->commonName) { + $commonName = $doc->createElement('cs:common-name'); + $commonName->appendChild($doc->createTextNode($this->commonName)); + $organizerUrl->appendChild($commonName); + } + $organizerUrl->appendChild($organizerHref); + $prop->appendChild($organizerUrl); + + if ($this->summary) { + $summary = $doc->createElement('cs:summary'); + $summary->appendChild($doc->createTextNode($this->summary)); + $prop->appendChild($summary); + } + if ($this->supportedComponents) { + + $xcomp = $doc->createElement('cal:supported-calendar-component-set'); + $this->supportedComponents->serialize($server, $xcomp); + $prop->appendChild($xcomp); + + } + + } + + /** + * Returns a unique id for this notification + * + * This is just the base url. This should generally be some kind of unique + * id. + * + * @return string + */ + public function getId() { + + return $this->id; + + } + + /** + * Returns the ETag for this notification. + * + * The ETag must be surrounded by literal double-quotes. + * + * @return string + */ + public function getETag() { + + return $this->etag; + + } + +} diff --git a/3rdparty/Sabre/CalDAV/Notifications/Notification/InviteReply.php b/3rdparty/Sabre/CalDAV/Notifications/Notification/InviteReply.php new file mode 100644 index 00000000000..e935aa5aa11 --- /dev/null +++ b/3rdparty/Sabre/CalDAV/Notifications/Notification/InviteReply.php @@ -0,0 +1,216 @@ +<?php + +use Sabre_CalDAV_SharingPlugin as SharingPlugin; + +/** + * This class represents the cs:invite-reply notification element. + * + * @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_Notifications_Notification_InviteReply extends Sabre_DAV_Property implements Sabre_CalDAV_Notifications_INotificationType { + + /** + * A unique id for the message + * + * @var string + */ + protected $id; + + /** + * Timestamp of the notification + * + * @var DateTime + */ + protected $dtStamp; + + /** + * The unique id of the notification this was a reply to. + * + * @var string + */ + protected $inReplyTo; + + /** + * A url to the recipient of the original (!) notification. + * + * @var string + */ + protected $href; + + /** + * The type of message, see the SharingPlugin::STATUS_ constants. + * + * @var int + */ + protected $type; + + /** + * A url to the shared calendar. + * + * @var string + */ + protected $hostUrl; + + /** + * A description of the share request + * + * @var string + */ + protected $summary; + + /** + * Notification Etag + * + * @var string + */ + protected $etag; + + /** + * Creates the Invite Reply Notification. + * + * This constructor receives an array with the following elements: + * + * * id - A unique id + * * etag - The etag + * * dtStamp - A DateTime object with a timestamp for the notification. + * * inReplyTo - This should refer to the 'id' of the notification + * this is a reply to. + * * type - The type of notification, see SharingPlugin::STATUS_* + * constants for details. + * * hostUrl - A url to the shared calendar. + * * summary - Description of the share, can be the same as the + * calendar, but may also be modified (optional). + */ + public function __construct(array $values) { + + $required = array( + 'id', + 'etag', + 'href', + 'dtStamp', + 'inReplyTo', + 'type', + 'hostUrl', + ); + foreach($required as $item) { + if (!isset($values[$item])) { + throw new InvalidArgumentException($item . ' is a required constructor option'); + } + } + + foreach($values as $key=>$value) { + if (!property_exists($this, $key)) { + throw new InvalidArgumentException('Unknown option: ' . $key); + } + $this->$key = $value; + } + + } + + /** + * Serializes the notification as a single property. + * + * You should usually just encode the single top-level element of the + * notification. + * + * @param Sabre_DAV_Server $server + * @param DOMElement $node + * @return void + */ + public function serialize(Sabre_DAV_Server $server, \DOMElement $node) { + + $prop = $node->ownerDocument->createElement('cs:invite-reply'); + $node->appendChild($prop); + + } + + /** + * This method serializes the entire notification, as it is used in the + * response body. + * + * @param Sabre_DAV_Server $server + * @param DOMElement $node + * @return void + */ + public function serializeBody(Sabre_DAV_Server $server, \DOMElement $node) { + + $doc = $node->ownerDocument; + + $dt = $doc->createElement('cs:dtstamp'); + $this->dtStamp->setTimezone(new \DateTimezone('GMT')); + $dt->appendChild($doc->createTextNode($this->dtStamp->format('Ymd\\THis\\Z'))); + $node->appendChild($dt); + + $prop = $doc->createElement('cs:invite-reply'); + $node->appendChild($prop); + + $uid = $doc->createElement('cs:uid'); + $uid->appendChild($doc->createTextNode($this->id)); + $prop->appendChild($uid); + + $inReplyTo = $doc->createElement('cs:in-reply-to'); + $inReplyTo->appendChild( $doc->createTextNode($this->inReplyTo) ); + $prop->appendChild($inReplyTo); + + $href = $doc->createElement('d:href'); + $href->appendChild( $doc->createTextNode($this->href) ); + $prop->appendChild($href); + + $nodeName = null; + switch($this->type) { + + case SharingPlugin::STATUS_ACCEPTED : + $nodeName = 'cs:invite-accepted'; + break; + case SharingPlugin::STATUS_DECLINED : + $nodeName = 'cs:invite-declined'; + break; + + } + $prop->appendChild( + $doc->createElement($nodeName) + ); + $hostHref = $doc->createElement('d:href', $server->getBaseUri() . $this->hostUrl); + $hostUrl = $doc->createElement('cs:hosturl'); + $hostUrl->appendChild($hostHref); + $prop->appendChild($hostUrl); + + if ($this->summary) { + $summary = $doc->createElement('cs:summary'); + $summary->appendChild($doc->createTextNode($this->summary)); + $prop->appendChild($summary); + } + + } + + /** + * Returns a unique id for this notification + * + * This is just the base url. This should generally be some kind of unique + * id. + * + * @return string + */ + public function getId() { + + return $this->id; + + } + + /** + * Returns the ETag for this notification. + * + * The ETag must be surrounded by literal double-quotes. + * + * @return string + */ + public function getETag() { + + return $this->etag; + + } +} diff --git a/3rdparty/Sabre/CalDAV/Notifications/Notification/SystemStatus.php b/3rdparty/Sabre/CalDAV/Notifications/Notification/SystemStatus.php new file mode 100644 index 00000000000..f09ed3525f5 --- /dev/null +++ b/3rdparty/Sabre/CalDAV/Notifications/Notification/SystemStatus.php @@ -0,0 +1,179 @@ +<?php + +/** + * SystemStatus notification + * + * This notification can be used to indicate to the user that the system is + * down. + * + * @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_Notifications_Notification_SystemStatus extends Sabre_DAV_Property implements Sabre_CalDAV_Notifications_INotificationType { + + const TYPE_LOW = 1; + const TYPE_MEDIUM = 2; + const TYPE_HIGH = 3; + + /** + * A unique id + * + * @var string + */ + protected $id; + + /** + * The type of alert. This should be one of the TYPE_ constants. + * + * @var int + */ + protected $type; + + /** + * A human-readable description of the problem. + * + * @var string + */ + protected $description; + + /** + * A url to a website with more information for the user. + * + * @var string + */ + protected $href; + + /** + * Notification Etag + * + * @var string + */ + protected $etag; + + /** + * Creates the notification. + * + * Some kind of unique id should be provided. This is used to generate a + * url. + * + * @param string $id + * @param string $etag + * @param int $type + * @param string $description + * @param string $href + */ + public function __construct($id, $etag, $type = self::TYPE_HIGH, $description = null, $href = null) { + + $this->id = $id; + $this->type = $type; + $this->description = $description; + $this->href = $href; + $this->etag = $etag; + + } + + /** + * Serializes the notification as a single property. + * + * You should usually just encode the single top-level element of the + * notification. + * + * @param Sabre_DAV_Server $server + * @param DOMElement $node + * @return void + */ + public function serialize(Sabre_DAV_Server $server, \DOMElement $node) { + + switch($this->type) { + case self::TYPE_LOW : + $type = 'low'; + break; + case self::TYPE_MEDIUM : + $type = 'medium'; + break; + default : + case self::TYPE_HIGH : + $type = 'high'; + break; + } + + $prop = $node->ownerDocument->createElement('cs:systemstatus'); + $prop->setAttribute('type', $type); + + $node->appendChild($prop); + + } + + /** + * This method serializes the entire notification, as it is used in the + * response body. + * + * @param Sabre_DAV_Server $server + * @param DOMElement $node + * @return void + */ + public function serializeBody(Sabre_DAV_Server $server, \DOMElement $node) { + + switch($this->type) { + case self::TYPE_LOW : + $type = 'low'; + break; + case self::TYPE_MEDIUM : + $type = 'medium'; + break; + default : + case self::TYPE_HIGH : + $type = 'high'; + break; + } + + $prop = $node->ownerDocument->createElement('cs:systemstatus'); + $prop->setAttribute('type', $type); + + if ($this->description) { + $text = $node->ownerDocument->createTextNode($this->description); + $desc = $node->ownerDocument->createElement('cs:description'); + $desc->appendChild($text); + $prop->appendChild($desc); + } + if ($this->href) { + $text = $node->ownerDocument->createTextNode($this->href); + $href = $node->ownerDocument->createElement('d:href'); + $href->appendChild($text); + $prop->appendChild($href); + } + + $node->appendChild($prop); + + } + + /** + * Returns a unique id for this notification + * + * This is just the base url. This should generally be some kind of unique + * id. + * + * @return string + */ + public function getId() { + + return $this->id; + + } + + /* + * Returns the ETag for this notification. + * + * The ETag must be surrounded by literal double-quotes. + * + * @return string + */ + public function getETag() { + + return $this->etag; + + } +} diff --git a/3rdparty/Sabre/CalDAV/Plugin.php b/3rdparty/Sabre/CalDAV/Plugin.php index c56ab384844..f3d11969c8b 100755..100644 --- a/3rdparty/Sabre/CalDAV/Plugin.php +++ b/3rdparty/Sabre/CalDAV/Plugin.php @@ -1,5 +1,7 @@ <?php +use Sabre\VObject; + /** * CalDAV plugin * @@ -25,16 +27,6 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin { 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 */ @@ -172,16 +164,19 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin { $server->subscribeEvent('onBrowserPostAction', array($this,'browserPostAction')); $server->subscribeEvent('beforeWriteContent', array($this, 'beforeWriteContent')); $server->subscribeEvent('beforeCreateFile', array($this, 'beforeCreateFile')); + $server->subscribeEvent('beforeMethod', array($this,'beforeMethod')); $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->propertyMap['{' . self::NS_CALDAV . '}schedule-calendar-transp'] = 'Sabre_CalDAV_Property_ScheduleCalendarTransp'; $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'; + $server->resourceTypeMapping['Sabre_CalDAV_Notifications_ICollection'] = '{' . self::NS_CALENDARSERVER . '}notification'; array_push($server->protectedProperties, @@ -205,7 +200,9 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin { // CalendarServer extensions '{' . self::NS_CALENDARSERVER . '}getctag', '{' . self::NS_CALENDARSERVER . '}calendar-proxy-read-for', - '{' . self::NS_CALENDARSERVER . '}calendar-proxy-write-for' + '{' . self::NS_CALENDARSERVER . '}calendar-proxy-write-for', + '{' . self::NS_CALENDARSERVER . '}notification-URL', + '{' . self::NS_CALENDARSERVER . '}notificationtype' ); } @@ -226,6 +223,13 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin { // unknownMethod event. return false; case 'POST' : + + // Checking if this is a text/calendar content type + $contentType = $this->server->httpRequest->getHeader('Content-Type'); + if (strpos($contentType, 'text/calendar')!==0) { + return; + } + // Checking if we're talking to an outbox try { $node = $this->server->tree->getNodeForPath($uri); @@ -235,7 +239,7 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin { if (!$node instanceof Sabre_CalDAV_Schedule_IOutbox) return; - $this->outboxRequest($node); + $this->outboxRequest($node, $uri); return false; } @@ -348,7 +352,7 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin { if (in_array($calProp,$requestedProperties)) { $addresses = $node->getAlternateUriSet(); - $addresses[] = $this->server->getBaseUri() . $node->getPrincipalUrl(); + $addresses[] = $this->server->getBaseUri() . $node->getPrincipalUrl() . '/'; unset($requestedProperties[$calProp]); $returnedProperties[200][$calProp] = new Sabre_DAV_Property_HrefList($addresses, false); @@ -390,8 +394,31 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin { } + // notification-URL property + $notificationUrl = '{' . self::NS_CALENDARSERVER . '}notification-URL'; + if (($index = array_search($notificationUrl, $requestedProperties)) !== false) { + $principalId = $node->getName(); + $calendarHomePath = 'calendars/' . $principalId . '/notifications/'; + unset($requestedProperties[$index]); + $returnedProperties[200][$notificationUrl] = new Sabre_DAV_Property_Href($calendarHomePath); + } + } // instanceof IPrincipal + if ($node instanceof Sabre_CalDAV_Notifications_INode) { + + $propertyName = '{' . self::NS_CALENDARSERVER . '}notificationtype'; + if (($index = array_search($propertyName, $requestedProperties)) !== false) { + + $returnedProperties[200][$propertyName] = + $node->getNotificationType(); + + unset($requestedProperties[$index]); + + } + + } // instanceof Notifications_INode + if ($node instanceof Sabre_CalDAV_ICalendarObject) { // The calendar-data property is not supposed to be a 'real' @@ -424,11 +451,11 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin { public function calendarMultiGetReport($dom) { $properties = array_keys(Sabre_DAV_XMLUtil::parseProperties($dom->firstChild)); - $hrefElems = $dom->getElementsByTagNameNS('urn:DAV','href'); + $hrefElems = $dom->getElementsByTagNameNS('DAV:','href'); $xpath = new DOMXPath($dom); $xpath->registerNameSpace('cal',Sabre_CalDAV_Plugin::NS_CALDAV); - $xpath->registerNameSpace('dav','urn:DAV'); + $xpath->registerNameSpace('dav','DAV:'); $expand = $xpath->query('/cal:calendar-multiget/dav:prop/cal:calendar-data/cal:expand'); if ($expand->length>0) { @@ -438,8 +465,8 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin { 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); + $start = VObject\DateTimeParser::parseDateTime($start); + $end = 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.'); @@ -458,7 +485,7 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin { 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 = VObject\Reader::read($objProps[200]['{' . self::NS_CALDAV . '}calendar-data']); $vObject->expand($start, $end); $objProps[200]['{' . self::NS_CALDAV . '}calendar-data'] = $vObject->serialize(); } @@ -467,9 +494,12 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin { } + $prefer = $this->server->getHTTPPRefer(); + $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->server->httpResponse->setHeader('Vary','Brief,Prefer'); + $this->server->httpResponse->sendBody($this->server->generateMultiStatus($propertyList, $prefer['return-minimal'])); } @@ -487,54 +517,95 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin { $parser = new Sabre_CalDAV_CalendarQueryParser($dom); $parser->parse(); - $requestedCalendarData = true; - $requestedProperties = $parser->requestedProperties; + $node = $this->server->tree->getNodeForPath($this->server->getRequestUri()); + $depth = $this->server->getHTTPDepth(0); - if (!in_array('{urn:ietf:params:xml:ns:caldav}calendar-data', $requestedProperties)) { + // The default result is an empty array + $result = array(); - // We always retrieve calendar-data, as we need it for filtering. - $requestedProperties[] = '{urn:ietf:params:xml:ns:caldav}calendar-data'; + // The calendarobject was requested directly. In this case we handle + // this locally. + if ($depth == 0 && $node instanceof Sabre_CalDAV_ICalendarObject) { - // If calendar-data wasn't explicitly requested, we need to remove - // it after processing. - $requestedCalendarData = false; - } + $requestedCalendarData = true; + $requestedProperties = $parser->requestedProperties; - // These are the list of nodes that potentially match the requirement - $candidateNodes = $this->server->getPropertiesForPath( - $this->server->getRequestUri(), - $requestedProperties, - $this->server->getHTTPDepth(0) - ); + if (!in_array('{urn:ietf:params:xml:ns:caldav}calendar-data', $requestedProperties)) { - $verifiedNodes = array(); + // We always retrieve calendar-data, as we need it for filtering. + $requestedProperties[] = '{urn:ietf:params:xml:ns:caldav}calendar-data'; - $validator = new Sabre_CalDAV_CalendarQueryValidator(); + // If calendar-data wasn't explicitly requested, we need to remove + // it after processing. + $requestedCalendarData = false; + } - foreach($candidateNodes as $node) { + $properties = $this->server->getPropertiesForPath( + $this->server->getRequestUri(), + $requestedProperties, + 0 + ); - // 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; + // This array should have only 1 element, the first calendar + // object. + $properties = current($properties); - $vObject = Sabre_VObject_Reader::read($node[200]['{urn:ietf:params:xml:ns:caldav}calendar-data']); - if ($validator->validate($vObject,$parser->filters)) { + // If there wasn't any calendar-data returned somehow, we ignore + // this. + if (isset($properties[200]['{urn:ietf:params:xml:ns:caldav}calendar-data'])) { + + $validator = new Sabre_CalDAV_CalendarQueryValidator(); + $vObject = VObject\Reader::read($properties[200]['{urn:ietf:params:xml:ns:caldav}calendar-data']); + if ($validator->validate($vObject,$parser->filters)) { + + // If the client didn't require the calendar-data property, + // we won't give it back. + if (!$requestedCalendarData) { + unset($properties[200]['{urn:ietf:params:xml:ns:caldav}calendar-data']); + } else { + if ($parser->expand) { + $vObject->expand($parser->expand['start'], $parser->expand['end']); + $properties[200]['{' . self::NS_CALDAV . '}calendar-data'] = $vObject->serialize(); + } + } + + $result = array($properties); - if (!$requestedCalendarData) { - unset($node[200]['{urn:ietf:params:xml:ns:caldav}calendar-data']); } + + } + + } + // If we're dealing with a calendar, the calendar itself is responsible + // for the calendar-query. + if ($node instanceof Sabre_CalDAV_ICalendar && $depth = 1) { + + $nodePaths = $node->calendarQuery($parser->filters); + + foreach($nodePaths as $path) { + + list($properties) = + $this->server->getPropertiesForPath($this->server->getRequestUri() . '/' . $path, $parser->requestedProperties); + if ($parser->expand) { + // We need to do some post-processing + $vObject = VObject\Reader::read($properties[200]['{urn:ietf:params:xml:ns:caldav}calendar-data']); $vObject->expand($parser->expand['start'], $parser->expand['end']); - $node[200]['{' . self::NS_CALDAV . '}calendar-data'] = $vObject->serialize(); + $properties[200]['{' . self::NS_CALDAV . '}calendar-data'] = $vObject->serialize(); } - $verifiedNodes[] = $node; + + $result[] = $properties; + } } + $prefer = $this->server->getHTTPPRefer(); + $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->server->httpResponse->setHeader('Vary','Brief,Prefer'); + $this->server->httpResponse->sendBody($this->server->generateMultiStatus($result, $prefer['return-minimal'])); } @@ -561,10 +632,10 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin { } if ($start) { - $start = Sabre_VObject_DateTimeParser::parseDateTime($start); + $start = VObject\DateTimeParser::parseDateTime($start); } if ($end) { - $end = Sabre_VObject_DateTimeParser::parseDateTime($end); + $end = VObject\DateTimeParser::parseDateTime($end); } if (!$start && !$end) { @@ -583,15 +654,33 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin { 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); - } + // Doing a calendar-query first, to make sure we get the most + // performance. + $urls = $calendar->calendarQuery(array( + 'name' => 'VCALENDAR', + 'comp-filters' => array( + array( + 'name' => 'VEVENT', + 'comp-filters' => array(), + 'prop-filters' => array(), + 'is-not-defined' => false, + 'time-range' => array( + 'start' => $start, + 'end' => $end, + ), + ), + ), + 'prop-filters' => array(), + 'is-not-defined' => false, + 'time-range' => null, + )); + + $objects = array_map(function($url) use ($calendar) { + $obj = $calendar->getChild($url)->get(); return $obj; - }, $calendar->getChildren()); + }, $urls); - $generator = new Sabre_VObject_FreeBusyGenerator(); + $generator = new VObject\FreeBusyGenerator(); $generator->setObjects($objects); $generator->setTimeRange($start, $end); $result = $generator->getResult(); @@ -620,7 +709,7 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin { if (!$node instanceof Sabre_CalDAV_ICalendarObject) return; - $this->validateICalendar($data); + $this->validateICalendar($data, $path); } @@ -640,7 +729,52 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin { if (!$parentNode instanceof Sabre_CalDAV_Calendar) return; - $this->validateICalendar($data); + $this->validateICalendar($data, $path); + + } + + /** + * This event is triggered before any HTTP request is handled. + * + * We use this to intercept GET calls to notification nodes, and return the + * proper response. + * + * @param string $method + * @param string $path + * @return void + */ + public function beforeMethod($method, $path) { + + if ($method!=='GET') return; + + try { + $node = $this->server->tree->getNodeForPath($path); + } catch (Sabre_DAV_Exception_NotFound $e) { + return; + } + + if (!$node instanceof Sabre_CalDAV_Notifications_INode) + return; + + if (!$this->server->checkPreconditions(true)) return false; + + $dom = new DOMDocument('1.0', 'UTF-8'); + $dom->formatOutput = true; + + $root = $dom->createElement('cs:notification'); + foreach($this->server->xmlNamespaces as $namespace => $prefix) { + $root->setAttribute('xmlns:' . $prefix, $namespace); + } + + $dom->appendChild($root); + $node->getNotificationType()->serializeBody($this->server, $root); + + $this->server->httpResponse->setHeader('Content-Type','application/xml'); + $this->server->httpResponse->setHeader('ETag',$node->getETag()); + $this->server->httpResponse->sendStatus(200); + $this->server->httpResponse->sendBody($dom->saveXML()); + + return false; } @@ -650,9 +784,10 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin { * An exception is thrown if it's not. * * @param resource|string $data + * @param string $path * @return void */ - protected function validateICalendar(&$data) { + protected function validateICalendar(&$data, $path) { // If it's a stream, we convert it to a string first. if (is_resource($data)) { @@ -664,9 +799,9 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin { try { - $vobj = Sabre_VObject_Reader::read($data); + $vobj = VObject\Reader::read($data); - } catch (Sabre_VObject_ParseException $e) { + } catch (VObject\ParseException $e) { throw new Sabre_DAV_Exception_UnsupportedMediaType('This resource only supports valid iCalendar 2.0 data. Parse error: ' . $e->getMessage()); @@ -676,6 +811,11 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin { throw new Sabre_DAV_Exception_UnsupportedMediaType('This collection can only support iCalendar objects.'); } + // Get the Supported Components for the target calendar + list($parentPath,$object) = Sabre_Dav_URLUtil::splitPath($path); + $calendarProperties = $this->server->getProperties($parentPath,array('{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set')); + $supportedComponents = $calendarProperties['{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set']->getValue(); + $foundType = null; $foundUID = null; foreach($vobj->getComponents() as $component) { @@ -687,6 +827,9 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin { case 'VJOURNAL' : if (is_null($foundType)) { $foundType = $component->name; + if (!in_array($foundType, $supportedComponents)) { + throw new Sabre_CalDAV_Exception_InvalidComponentType('This calendar only supports ' . implode(', ', $supportedComponents) . '. We found a ' . $foundType); + } if (!isset($component->UID)) { throw new Sabre_DAV_Exception_BadRequest('Every ' . $component->name . ' component must have an UID'); } @@ -711,12 +854,81 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin { } /** - * This method handles POST requests to the schedule-outbox + * This method handles POST requests to the schedule-outbox. + * + * Currently, two types of requests are support: + * * FREEBUSY requests from RFC 6638 + * * Simple iTIP messages from draft-desruisseaux-caldav-sched-04 + * + * The latter is from an expired early draft of the CalDAV scheduling + * extensions, but iCal depends on a feature from that spec, so we + * implement it. * * @param Sabre_CalDAV_Schedule_IOutbox $outboxNode + * @param string $outboxUri + * @return void + */ + public function outboxRequest(Sabre_CalDAV_Schedule_IOutbox $outboxNode, $outboxUri) { + + // Parsing the request body + try { + $vObject = VObject\Reader::read($this->server->httpRequest->getBody(true)); + } catch (VObject\ParseException $e) { + throw new Sabre_DAV_Exception_BadRequest('The request body must be a valid iCalendar object. Parse error: ' . $e->getMessage()); + } + + // The incoming iCalendar object must have a METHOD property, and a + // component. The combination of both determines what type of request + // this is. + $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'); + } + + // So we support two types of requests: + // + // REQUEST with a VFREEBUSY component + // REQUEST, REPLY, ADD, CANCEL on VEVENT components + + $acl = $this->server->getPlugin('acl'); + + if ($componentType === 'VFREEBUSY' && $method === 'REQUEST') { + + $acl && $acl->checkPrivileges($outboxUri,'{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}schedule-query-freebusy'); + $this->handleFreeBusyRequest($outboxNode, $vObject); + + } elseif ($componentType === 'VEVENT' && in_array($method, array('REQUEST','REPLY','ADD','CANCEL'))) { + + $acl && $acl->checkPrivileges($outboxUri,'{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}schedule-post-vevent'); + $this->handleEventNotification($outboxNode, $vObject); + + } else { + + throw new Sabre_DAV_Exception_NotImplemented('SabreDAV supports only VFREEBUSY (REQUEST) and VEVENT (REQUEST, REPLY, ADD, CANCEL)'); + + } + + } + + /** + * This method handles the REQUEST, REPLY, ADD and CANCEL methods for + * VEVENT iTip messages. + * * @return void */ - public function outboxRequest(Sabre_CalDAV_Schedule_IOutbox $outboxNode) { + protected function handleEventNotification(Sabre_CalDAV_Schedule_IOutbox $outboxNode, VObject\Component $vObject) { $originator = $this->server->httpRequest->getHeader('Originator'); $recipients = $this->server->httpRequest->getHeader('Recipient'); @@ -760,38 +972,10 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin { throw new Sabre_DAV_Exception_Forbidden('The addresses specified in the Originator header did not match any addresses in the owners calendar-user-address-set header'); } - try { - $vObject = Sabre_VObject_Reader::read($this->server->httpRequest->getBody(true)); - } catch (Sabre_VObject_ParseException $e) { - throw new Sabre_DAV_Exception_BadRequest('The request body must be a valid iCalendar object. Parse error: ' . $e->getMessage()); - } - - // Checking for the object type - $componentType = null; - foreach($vObject->getComponents() as $component) { - if ($component->name !== 'VTIMEZONE') { - $componentType = $component->name; - break; - } - } - if (is_null($componentType)) { - throw new Sabre_DAV_Exception_BadRequest('We expected at least one VTODO, VJOURNAL, VFREEBUSY or VEVENT component'); - } - - // Validating the METHOD - $method = strtoupper((string)$vObject->METHOD); - if (!$method) { - throw new Sabre_DAV_Exception_BadRequest('A METHOD property must be specified in iTIP messages'); - } - - if (in_array($method, array('REQUEST','REPLY','ADD','CANCEL')) && $componentType==='VEVENT') { - $result = $this->iMIPMessage($originator, $recipients, $vObject); - $this->server->httpResponse->sendStatus(200); - $this->server->httpResponse->setHeader('Content-Type','application/xml'); - $this->server->httpResponse->sendBody($this->generateScheduleResponse($result)); - } else { - throw new Sabre_DAV_Exception_NotImplemented('This iTIP method is currently not implemented'); - } + $result = $this->iMIPMessage($originator, $recipients, $vObject, $principal); + $this->server->httpResponse->sendStatus(200); + $this->server->httpResponse->setHeader('Content-Type','application/xml'); + $this->server->httpResponse->sendBody($this->generateScheduleResponse($result)); } @@ -813,15 +997,15 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin { * * @param string $originator * @param array $recipients - * @param Sabre_VObject_Component $vObject + * @param Sabre\VObject\Component $vObject * @return array */ - protected function iMIPMessage($originator, array $recipients, Sabre_VObject_Component $vObject) { + protected function iMIPMessage($originator, array $recipients, VObject\Component $vObject, $principal) { if (!$this->imipHandler) { $resultStatus = '5.2;This server does not support this operation'; } else { - $this->imipHandler->sendMessage($originator, $recipients, $vObject); + $this->imipHandler->sendMessage($originator, $recipients, $vObject, $principal); $resultStatus = '2.0;Success'; } @@ -832,7 +1016,6 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin { return $result; - } /** @@ -878,6 +1061,204 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin { } /** + * This method is responsible for parsing a free-busy query request and + * returning it's result. + * + * @param Sabre_CalDAV_Schedule_IOutbox $outbox + * @param string $request + * @return string + */ + protected function handleFreeBusyRequest(Sabre_CalDAV_Schedule_IOutbox $outbox, VObject\Component $vObject) { + + $vFreeBusy = $vObject->VFREEBUSY; + $organizer = $vFreeBusy->organizer; + + $organizer = (string)$organizer; + + // Validating if the organizer matches the owner of the inbox. + $owner = $outbox->getOwner(); + + $caldavNS = '{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}'; + + $uas = $caldavNS . 'calendar-user-address-set'; + $props = $this->server->getProperties($owner,array($uas)); + + if (empty($props[$uas]) || !in_array($organizer, $props[$uas]->getHrefs())) { + throw new Sabre_DAV_Exception_Forbidden('The organizer in the request did not match any of the addresses for the owner of this inbox'); + } + + if (!isset($vFreeBusy->ATTENDEE)) { + throw new Sabre_DAV_Exception_BadRequest('You must at least specify 1 attendee'); + } + + $attendees = array(); + foreach($vFreeBusy->ATTENDEE as $attendee) { + $attendees[]= (string)$attendee; + } + + + if (!isset($vFreeBusy->DTSTART) || !isset($vFreeBusy->DTEND)) { + throw new Sabre_DAV_Exception_BadRequest('DTSTART and DTEND must both be specified'); + } + + $startRange = $vFreeBusy->DTSTART->getDateTime(); + $endRange = $vFreeBusy->DTEND->getDateTime(); + + $results = array(); + foreach($attendees as $attendee) { + $results[] = $this->getFreeBusyForEmail($attendee, $startRange, $endRange, $vObject); + } + + $dom = new DOMDocument('1.0','utf-8'); + $dom->formatOutput = true; + $scheduleResponse = $dom->createElement('cal:schedule-response'); + foreach($this->server->xmlNamespaces as $namespace=>$prefix) { + + $scheduleResponse->setAttribute('xmlns:' . $prefix,$namespace); + + } + $dom->appendChild($scheduleResponse); + + foreach($results as $result) { + $response = $dom->createElement('cal:response'); + + $recipient = $dom->createElement('cal:recipient'); + $recipientHref = $dom->createElement('d:href'); + + $recipientHref->appendChild($dom->createTextNode($result['href'])); + $recipient->appendChild($recipientHref); + $response->appendChild($recipient); + + $reqStatus = $dom->createElement('cal:request-status'); + $reqStatus->appendChild($dom->createTextNode($result['request-status'])); + $response->appendChild($reqStatus); + + if (isset($result['calendar-data'])) { + + $calendardata = $dom->createElement('cal:calendar-data'); + $calendardata->appendChild($dom->createTextNode(str_replace("\r\n","\n",$result['calendar-data']->serialize()))); + $response->appendChild($calendardata); + + } + $scheduleResponse->appendChild($response); + } + + $this->server->httpResponse->sendStatus(200); + $this->server->httpResponse->setHeader('Content-Type','application/xml'); + $this->server->httpResponse->sendBody($dom->saveXML()); + + } + + /** + * Returns free-busy information for a specific address. The returned + * data is an array containing the following properties: + * + * calendar-data : A VFREEBUSY VObject + * request-status : an iTip status code. + * href: The principal's email address, as requested + * + * The following request status codes may be returned: + * * 2.0;description + * * 3.7;description + * + * @param string $email address + * @param DateTime $start + * @param DateTime $end + * @param Sabre_VObject_Component $request + * @return Sabre_VObject_Component + */ + protected function getFreeBusyForEmail($email, DateTime $start, DateTime $end, VObject\Component $request) { + + $caldavNS = '{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}'; + + $aclPlugin = $this->server->getPlugin('acl'); + if (substr($email,0,7)==='mailto:') $email = substr($email,7); + + $result = $aclPlugin->principalSearch( + array('{http://sabredav.org/ns}email-address' => $email), + array( + '{DAV:}principal-URL', $caldavNS . 'calendar-home-set', + '{http://sabredav.org/ns}email-address', + ) + ); + + if (!count($result)) { + return array( + 'request-status' => '3.7;Could not find principal', + 'href' => 'mailto:' . $email, + ); + } + + if (!isset($result[0][200][$caldavNS . 'calendar-home-set'])) { + return array( + 'request-status' => '3.7;No calendar-home-set property found', + 'href' => 'mailto:' . $email, + ); + } + $homeSet = $result[0][200][$caldavNS . 'calendar-home-set']->getHref(); + + // Grabbing the calendar list + $objects = array(); + foreach($this->server->tree->getNodeForPath($homeSet)->getChildren() as $node) { + if (!$node instanceof Sabre_CalDAV_ICalendar) { + continue; + } + $aclPlugin->checkPrivileges($homeSet . $node->getName() ,$caldavNS . 'read-free-busy'); + + // Getting the list of object uris within the time-range + $urls = $node->calendarQuery(array( + 'name' => 'VCALENDAR', + 'comp-filters' => array( + array( + 'name' => 'VEVENT', + 'comp-filters' => array(), + 'prop-filters' => array(), + 'is-not-defined' => false, + 'time-range' => array( + 'start' => $start, + 'end' => $end, + ), + ), + ), + 'prop-filters' => array(), + 'is-not-defined' => false, + 'time-range' => null, + )); + + $calObjects = array_map(function($url) use ($node) { + $obj = $node->getChild($url)->get(); + return $obj; + }, $urls); + + $objects = array_merge($objects,$calObjects); + + } + + $vcalendar = VObject\Component::create('VCALENDAR'); + $vcalendar->VERSION = '2.0'; + $vcalendar->METHOD = 'REPLY'; + $vcalendar->CALSCALE = 'GREGORIAN'; + $vcalendar->PRODID = '-//SabreDAV//SabreDAV ' . Sabre_DAV_Version::VERSION . '//EN'; + + $generator = new VObject\FreeBusyGenerator(); + $generator->setObjects($objects); + $generator->setTimeRange($start, $end); + $generator->setBaseObject($vcalendar); + + $result = $generator->getResult(); + + $vcalendar->VFREEBUSY->ATTENDEE = 'mailto:' . $email; + $vcalendar->VFREEBUSY->UID = (string)$request->VFREEBUSY->UID; + $vcalendar->VFREEBUSY->ORGANIZER = clone $request->VFREEBUSY->ORGANIZER; + + return array( + 'calendar-data' => $result, + 'request-status' => '2.0;Success', + 'href' => 'mailto:' . $email, + ); + } + + /** * 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. diff --git a/3rdparty/Sabre/CalDAV/Principal/Collection.php b/3rdparty/Sabre/CalDAV/Principal/Collection.php index abbefa5567a..abbefa5567a 100755..100644 --- a/3rdparty/Sabre/CalDAV/Principal/Collection.php +++ b/3rdparty/Sabre/CalDAV/Principal/Collection.php diff --git a/3rdparty/Sabre/CalDAV/Principal/ProxyRead.php b/3rdparty/Sabre/CalDAV/Principal/ProxyRead.php index 4b3f035634a..4b3f035634a 100755..100644 --- a/3rdparty/Sabre/CalDAV/Principal/ProxyRead.php +++ b/3rdparty/Sabre/CalDAV/Principal/ProxyRead.php diff --git a/3rdparty/Sabre/CalDAV/Principal/ProxyWrite.php b/3rdparty/Sabre/CalDAV/Principal/ProxyWrite.php index dd0c2e86edd..dd0c2e86edd 100755..100644 --- a/3rdparty/Sabre/CalDAV/Principal/ProxyWrite.php +++ b/3rdparty/Sabre/CalDAV/Principal/ProxyWrite.php diff --git a/3rdparty/Sabre/CalDAV/Principal/User.php b/3rdparty/Sabre/CalDAV/Principal/User.php index 8453b877a73..8453b877a73 100755..100644 --- a/3rdparty/Sabre/CalDAV/Principal/User.php +++ b/3rdparty/Sabre/CalDAV/Principal/User.php diff --git a/3rdparty/Sabre/CalDAV/Property/AllowedSharingModes.php b/3rdparty/Sabre/CalDAV/Property/AllowedSharingModes.php new file mode 100644 index 00000000000..efe751732c5 --- /dev/null +++ b/3rdparty/Sabre/CalDAV/Property/AllowedSharingModes.php @@ -0,0 +1,72 @@ +<?php + +/** + * AllowedSharingModes + * + * This property encodes the 'allowed-sharing-modes' property, as defined by + * the 'caldav-sharing-02' spec, in the http://calendarserver.org/ns/ + * namespace. + * + * 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 + * @see https://trac.calendarserver.org/browser/CalendarServer/trunk/doc/Extensions/caldav-sharing-02.txt + * @copyright Copyright (C) 2007-2012 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_AllowedSharingModes extends Sabre_DAV_Property { + + /** + * Whether or not a calendar can be shared with another user + * + * @var bool + */ + protected $canBeShared; + + /** + * Whether or not the calendar can be placed on a public url. + * + * @var bool + */ + protected $canBePublished; + + /** + * Constructor + * + * @param bool $canBeShared + * @param bool $canBePublished + * @return void + */ + public function __construct($canBeShared, $canBePublished) { + + $this->canBeShared = $canBeShared; + $this->canBePublished = $canBePublished; + + } + + /** + * 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; + if ($this->canBeShared) { + $xcomp = $doc->createElement('cs:can-be-shared'); + $node->appendChild($xcomp); + } + if ($this->canBePublished) { + $xcomp = $doc->createElement('cs:can-be-published'); + $node->appendChild($xcomp); + } + + } + +} diff --git a/3rdparty/Sabre/CalDAV/Property/Invite.php b/3rdparty/Sabre/CalDAV/Property/Invite.php new file mode 100644 index 00000000000..4ed94877df2 --- /dev/null +++ b/3rdparty/Sabre/CalDAV/Property/Invite.php @@ -0,0 +1,173 @@ +<?php + +use Sabre_CalDAV_SharingPlugin as SharingPlugin; + +/** + * Invite property + * + * This property encodes the 'invite' property, as defined by + * the 'caldav-sharing-02' spec, in the http://calendarserver.org/ns/ + * namespace. + * + * @package Sabre + * @subpackage CalDAV + * @see https://trac.calendarserver.org/browser/CalendarServer/trunk/doc/Extensions/caldav-sharing-02.txt + * @copyright Copyright (C) 2007-2012 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_Invite extends Sabre_DAV_Property { + + /** + * The list of users a calendar has been shared to. + * + * @var array + */ + protected $users; + + /** + * Creates the property. + * + * Users is an array. Each element of the array has the following + * properties: + * + * * href - Often a mailto: address + * * commonName - Optional, for example a first and lastname for a user. + * * status - One of the SharingPlugin::STATUS_* constants. + * * readOnly - true or false + * * summary - Optional, description of the share + * + * @param array $users + */ + public function __construct(array $users) { + + $this->users = $users; + + } + + /** + * Returns the list of users, as it was passed to the constructor. + * + * @return array + */ + public function getValue() { + + return $this->users; + + } + + /** + * 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->users as $user) { + + $xuser = $doc->createElement('cs:user'); + + $href = $doc->createElement('d:href'); + $href->appendChild($doc->createTextNode($user['href'])); + $xuser->appendChild($href); + + if (isset($user['commonName']) && $user['commonName']) { + $commonName = $doc->createElement('cs:common-name'); + $commonName->appendChild($doc->createTextNode($user['commonName'])); + $xuser->appendChild($commonName); + } + + switch($user['status']) { + + case SharingPlugin::STATUS_ACCEPTED : + $status = $doc->createElement('cs:invite-accepted'); + $xuser->appendChild($status); + break; + case SharingPlugin::STATUS_DECLINED : + $status = $doc->createElement('cs:invite-declined'); + $xuser->appendChild($status); + break; + case SharingPlugin::STATUS_NORESPONSE : + $status = $doc->createElement('cs:invite-noresponse'); + $xuser->appendChild($status); + break; + case SharingPlugin::STATUS_INVALID : + $status = $doc->createElement('cs:invite-invalid'); + $xuser->appendChild($status); + break; + + } + + $xaccess = $doc->createElement('cs:access'); + + if ($user['readOnly']) { + $xaccess->appendChild( + $doc->createElement('cs:read') + ); + } else { + $xaccess->appendChild( + $doc->createElement('cs:read-write') + ); + } + $xuser->appendChild($xaccess); + + if (isset($user['summary']) && $user['summary']) { + $summary = $doc->createElement('cs:summary'); + $summary->appendChild($doc->createTextNode($user['summary'])); + $xuser->appendChild($summary); + } + + $node->appendChild($xuser); + + } + + } + + /** + * Unserializes the property. + * + * This static method should return a an instance of this object. + * + * @param DOMElement $prop + * @return Sabre_DAV_IProperty + */ + static function unserialize(DOMElement $prop) { + + $xpath = new \DOMXPath($prop->ownerDocument); + $xpath->registerNamespace('cs', Sabre_CalDAV_Plugin::NS_CALENDARSERVER); + $xpath->registerNamespace('d', 'DAV:'); + + $users = array(); + + foreach($xpath->query('cs:user', $prop) as $user) { + + $status = null; + if ($xpath->evaluate('boolean(cs:invite-accepted)', $user)) { + $status = SharingPlugin::STATUS_ACCEPTED; + } elseif ($xpath->evaluate('boolean(cs:invite-declined)', $user)) { + $status = SharingPlugin::STATUS_DECLINED; + } elseif ($xpath->evaluate('boolean(cs:invite-noresponse)', $user)) { + $status = SharingPlugin::STATUS_NORESPONSE; + } elseif ($xpath->evaluate('boolean(cs:invite-invalid)', $user)) { + $status = SharingPlugin::STATUS_INVALID; + } else { + throw new Sabre_DAV_Exception('Every cs:user property must have one of cs:invite-accepted, cs:invite-declined, cs:invite-noresponse or cs:invite-invalid'); + } + $users[] = array( + 'href' => $xpath->evaluate('string(d:href)', $user), + 'commonName' => $xpath->evaluate('string(cs:common-name)', $user), + 'readOnly' => $xpath->evaluate('boolean(cs:access/cs:read)', $user), + 'summary' => $xpath->evaluate('string(cs:summary)', $user), + 'status' => $status, + ); + + } + + return new self($users); + + } + +} diff --git a/3rdparty/Sabre/CalDAV/Property/ScheduleCalendarTransp.php b/3rdparty/Sabre/CalDAV/Property/ScheduleCalendarTransp.php new file mode 100644 index 00000000000..76c1dbaec21 --- /dev/null +++ b/3rdparty/Sabre/CalDAV/Property/ScheduleCalendarTransp.php @@ -0,0 +1,99 @@ +<?php + +/** + * schedule-calendar-transp property. + * + * This property is a representation of the schedule-calendar-transp property. + * This property is defined in RFC6638 (caldav scheduling). + * + * Its values are either 'transparent' or 'opaque'. If it's transparent, it + * means that this calendar will not be taken into consideration when a + * different user queries for free-busy information. If it's 'opaque', it will. + * + * @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_ScheduleCalendarTransp extends Sabre_DAV_Property { + + const TRANSPARENT = 'transparent'; + const OPAQUE = 'opaque'; + + protected $value; + + /** + * Creates the property + * + * @param string $value + */ + public function __construct($value) { + + if ($value !== self::TRANSPARENT && $value !== self::OPAQUE) { + throw new \InvalidArgumentException('The value must either be specified as "transparent" or "opaque"'); + } + $this->value = $value; + + } + + /** + * Returns the current value + * + * @return string + */ + public function getValue() { + + return $this->value; + + } + + /** + * 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; + switch($this->value) { + case self::TRANSPARENT : + $xval = $doc->createElement('cal:transparent'); + break; + case self::OPAQUE : + $xval = $doc->createElement('cal:opaque'); + break; + } + + $node->appendChild($xval); + + } + + /** + * Unserializes the DOMElement back into a Property class. + * + * @param DOMElement $node + * @return Sabre_CalDAV_Property_ScheduleCalendarTransp + */ + static function unserialize(DOMElement $node) { + + $value = null; + foreach($node->childNodes as $childNode) { + switch(Sabre_DAV_XMLUtil::toClarkNotation($childNode)) { + case '{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}opaque' : + $value = self::OPAQUE; + break; + case '{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}transparent' : + $value = self::TRANSPARENT; + break; + } + } + if (is_null($value)) + return null; + + return new self($value); + + } +} diff --git a/3rdparty/Sabre/CalDAV/Property/SupportedCalendarComponentSet.php b/3rdparty/Sabre/CalDAV/Property/SupportedCalendarComponentSet.php index 2ea078d7dac..2ea078d7dac 100755..100644 --- a/3rdparty/Sabre/CalDAV/Property/SupportedCalendarComponentSet.php +++ b/3rdparty/Sabre/CalDAV/Property/SupportedCalendarComponentSet.php diff --git a/3rdparty/Sabre/CalDAV/Property/SupportedCalendarData.php b/3rdparty/Sabre/CalDAV/Property/SupportedCalendarData.php index 1d848dd5cf6..1d848dd5cf6 100755..100644 --- a/3rdparty/Sabre/CalDAV/Property/SupportedCalendarData.php +++ b/3rdparty/Sabre/CalDAV/Property/SupportedCalendarData.php diff --git a/3rdparty/Sabre/CalDAV/Property/SupportedCollationSet.php b/3rdparty/Sabre/CalDAV/Property/SupportedCollationSet.php index 24e84d4c17d..24e84d4c17d 100755..100644 --- a/3rdparty/Sabre/CalDAV/Property/SupportedCollationSet.php +++ b/3rdparty/Sabre/CalDAV/Property/SupportedCollationSet.php diff --git a/3rdparty/Sabre/CalDAV/Schedule/IMip.php b/3rdparty/Sabre/CalDAV/Schedule/IMip.php index 37e75fcc4a7..92c3c097c15 100755..100644 --- a/3rdparty/Sabre/CalDAV/Schedule/IMip.php +++ b/3rdparty/Sabre/CalDAV/Schedule/IMip.php @@ -1,5 +1,7 @@ <?php +use Sabre\VObject; + /** * iMIP handler. * @@ -42,12 +44,13 @@ class Sabre_CalDAV_Schedule_IMip { /** * Sends one or more iTip messages through email. * - * @param string $originator - * @param array $recipients - * @param Sabre_VObject_Component $vObject + * @param string $originator Originator Email + * @param array $recipients Array of email addresses + * @param Sabre\VObject\Component $vObject + * @param string $principal Principal Url of the originator * @return void */ - public function sendMessage($originator, array $recipients, Sabre_VObject_Component $vObject) { + public function sendMessage($originator, array $recipients, VObject\Component $vObject, $principal) { foreach($recipients as $recipient) { @@ -83,6 +86,9 @@ class Sabre_CalDAV_Schedule_IMip { } + // @codeCoverageIgnoreStart + // This is deemed untestable in a reasonable manner + /** * This function is reponsible for sending the actual email. * @@ -94,11 +100,11 @@ class Sabre_CalDAV_Schedule_IMip { */ protected function mail($to, $subject, $body, array $headers) { + mail($to, $subject, $body, implode("\r\n", $headers)); } + // @codeCoverageIgnoreEnd } - -?> diff --git a/3rdparty/Sabre/CalDAV/Schedule/IOutbox.php b/3rdparty/Sabre/CalDAV/Schedule/IOutbox.php index 46d77514bc0..46d77514bc0 100755..100644 --- a/3rdparty/Sabre/CalDAV/Schedule/IOutbox.php +++ b/3rdparty/Sabre/CalDAV/Schedule/IOutbox.php diff --git a/3rdparty/Sabre/CalDAV/Schedule/Outbox.php b/3rdparty/Sabre/CalDAV/Schedule/Outbox.php index 014c37230d1..462aa527e23 100755..100644 --- a/3rdparty/Sabre/CalDAV/Schedule/Outbox.php +++ b/3rdparty/Sabre/CalDAV/Schedule/Outbox.php @@ -13,7 +13,7 @@ * @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 { +class Sabre_CalDAV_Schedule_Outbox extends Sabre_DAV_Collection implements Sabre_CalDAV_Schedule_IOutbox { /** * The principal Uri @@ -104,6 +104,11 @@ class Sabre_CalDAV_Schedule_Outbox extends Sabre_DAV_Directory implements Sabre_ 'protected' => true, ), array( + 'privilege' => '{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}schedule-post-vevent', + 'principal' => $this->getOwner(), + 'protected' => true, + ), + array( 'privilege' => '{DAV:}read', 'principal' => $this->getOwner(), 'protected' => true, @@ -144,6 +149,9 @@ class Sabre_CalDAV_Schedule_Outbox extends Sabre_DAV_Directory implements Sabre_ $default['aggregates'][] = array( 'privilege' => '{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}schedule-query-freebusy', ); + $default['aggregates'][] = array( + 'privilege' => '{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}schedule-post-vevent', + ); return $default; diff --git a/3rdparty/Sabre/CalDAV/Server.php b/3rdparty/Sabre/CalDAV/Server.php deleted file mode 100755 index 325e3d80a7f..00000000000 --- a/3rdparty/Sabre/CalDAV/Server.php +++ /dev/null @@ -1,68 +0,0 @@ -<?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/ShareableCalendar.php b/3rdparty/Sabre/CalDAV/ShareableCalendar.php new file mode 100644 index 00000000000..0e44885c621 --- /dev/null +++ b/3rdparty/Sabre/CalDAV/ShareableCalendar.php @@ -0,0 +1,72 @@ +<?php + +/** + * This object represents a CalDAV calendar that can be shared with other + * 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_ShareableCalendar extends Sabre_CalDAV_Calendar implements Sabre_CalDAV_IShareableCalendar { + + /** + * Updates the list of shares. + * + * The first array is a list of people that are to be added to the + * calendar. + * + * Every element in the add array has the following properties: + * * href - A url. Usually a mailto: address + * * commonName - Usually a first and last name, or false + * * summary - A description of the share, can also be false + * * readOnly - A boolean value + * + * Every element in the remove array is just the address string. + * + * @param array $add + * @param array $remove + * @return void + */ + public function updateShares(array $add, array $remove) { + + $this->caldavBackend->updateShares($this->calendarInfo['id'], $add, $remove); + + } + + /** + * Returns the list of people whom this calendar is shared with. + * + * Every element in this array should have the following properties: + * * href - Often a mailto: address + * * commonName - Optional, for example a first + last name + * * status - See the Sabre_CalDAV_SharingPlugin::STATUS_ constants. + * * readOnly - boolean + * * summary - Optional, a description for the share + * + * @return array + */ + public function getShares() { + + return $this->caldavBackend->getShares($this->calendarInfo['id']); + + } + + /** + * Marks this calendar as published. + * + * Publishing a calendar should automatically create a read-only, public, + * subscribable calendar. + * + * @param bool $value + * @return void + */ + public function setPublishStatus($value) { + + $this->caldavBackend->setPublishStatus($this->calendarInfo['id'], $value); + + } + +} diff --git a/3rdparty/Sabre/CalDAV/SharedCalendar.php b/3rdparty/Sabre/CalDAV/SharedCalendar.php new file mode 100644 index 00000000000..9000697d3ec --- /dev/null +++ b/3rdparty/Sabre/CalDAV/SharedCalendar.php @@ -0,0 +1,98 @@ +<?php + +/** + * This object represents a CalDAV calendar that is shared by a different 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_SharedCalendar extends Sabre_CalDAV_Calendar implements Sabre_CalDAV_ISharedCalendar { + + /** + * Constructor + * + * @param Sabre_DAVACL_IPrincipalBackend $principalBackend + * @param Sabre_CalDAV_Backend_BackendInterface $caldavBackend + * @param array $calendarInfo + */ + public function __construct(Sabre_DAVACL_IPrincipalBackend $principalBackend, Sabre_CalDAV_Backend_BackendInterface $caldavBackend, $calendarInfo) { + + $required = array( + '{http://calendarserver.org/ns/}shared-url', + '{http://sabredav.org/ns}owner-principal', + '{http://sabredav.org/ns}read-only', + ); + foreach($required as $r) { + if (!isset($calendarInfo[$r])) { + throw new InvalidArgumentException('The ' . $r . ' property must be specified for SharedCalendar(s)'); + } + } + + parent::__construct($principalBackend, $caldavBackend, $calendarInfo); + + } + + /** + * This method should return the url of the owners' copy of the shared + * calendar. + * + * @return string + */ + public function getSharedUrl() { + + return $this->calendarInfo['{http://calendarserver.org/ns/}shared-url']; + + } + + /** + * 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['{http://sabredav.org/ns}owner-principal']; + + } + + /** + * 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() { + + // The top-level ACL only contains access information for the true + // owner of the calendar, so we need to add the information for the + // sharee. + $acl = parent::getACL(); + $acl[] = array( + 'privilege' => '{DAV:}read', + 'principal' => $this->calendarInfo['principaluri'], + 'protected' => true, + ); + if (!$this->calendarInfo['{http://sabredav.org/ns}read-only']) { + $acl[] = array( + 'privilege' => '{DAV:}write', + 'principal' => $this->calendarInfo['principaluri'], + 'protected' => true, + ); + } + return $acl; + + } + + +} diff --git a/3rdparty/Sabre/CalDAV/SharingPlugin.php b/3rdparty/Sabre/CalDAV/SharingPlugin.php new file mode 100644 index 00000000000..31df8057b24 --- /dev/null +++ b/3rdparty/Sabre/CalDAV/SharingPlugin.php @@ -0,0 +1,475 @@ +<?php + +/** + * This plugin implements support for caldav sharing. + * + * This spec is defined at: + * http://svn.calendarserver.org/repository/calendarserver/CalendarServer/trunk/doc/Extensions/caldav-sharing.txt + * + * See: + * Sabre_CalDAV_Backend_SharingSupport for all the documentation. + * + * Note: This feature is experimental, and may change in between different + * SabreDAV versions. + * + * @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_SharingPlugin extends Sabre_DAV_ServerPlugin { + + /** + * These are the various status constants used by sharing-messages. + */ + const STATUS_ACCEPTED = 1; + const STATUS_DECLINED = 2; + const STATUS_DELETED = 3; + const STATUS_NORESPONSE = 4; + const STATUS_INVALID = 5; + + /** + * Reference to SabreDAV server object. + * + * @var Sabre_DAV_Server + */ + protected $server; + + /** + * This method should return a list of server-features. + * + * This is for example 'versioning' and is added to the DAV: header + * in an OPTIONS response. + * + * @return array + */ + public function getFeatures() { + + return array('calendarserver-sharing'); + + } + + /** + * 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-sharing'; + + } + + /** + * This initializes the plugin. + * + * This function is called by Sabre_DAV_Server, after + * addPlugin is called. + * + * This method should set up the required event subscriptions. + * + * @param Sabre_DAV_Server $server + * @return void + */ + public function initialize(Sabre_DAV_Server $server) { + + $this->server = $server; + //$server->resourceTypeMapping['Sabre_CalDAV_IShareableCalendar'] = '{' . Sabre_CalDAV_Plugin::NS_CALENDARSERVER . '}shared-owner'; + $server->resourceTypeMapping['Sabre_CalDAV_ISharedCalendar'] = '{' . Sabre_CalDAV_Plugin::NS_CALENDARSERVER . '}shared'; + + array_push( + $this->server->protectedProperties, + '{' . Sabre_CalDAV_Plugin::NS_CALENDARSERVER . '}invite', + '{' . Sabre_CalDAV_Plugin::NS_CALENDARSERVER . '}allowed-sharing-modes', + '{' . Sabre_CalDAV_Plugin::NS_CALENDARSERVER . '}shared-url' + ); + + $this->server->subscribeEvent('beforeGetProperties', array($this, 'beforeGetProperties')); + $this->server->subscribeEvent('afterGetProperties', array($this, 'afterGetProperties')); + $this->server->subscribeEvent('updateProperties', array($this, 'updateProperties')); + $this->server->subscribeEvent('unknownMethod', array($this,'unknownMethod')); + + } + + /** + * This event is triggered when properties are requested for a certain + * node. + * + * This allows us to inject any properties early. + * + * @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_CalDAV_IShareableCalendar) { + if (($index = array_search('{' . Sabre_CalDAV_Plugin::NS_CALENDARSERVER . '}invite', $requestedProperties))!==false) { + + unset($requestedProperties[$index]); + $returnedProperties[200]['{' . Sabre_CalDAV_Plugin::NS_CALENDARSERVER . '}invite'] = + new Sabre_CalDAV_Property_Invite( + $node->getShares() + ); + + } + + } + if ($node instanceof Sabre_CalDAV_ISharedCalendar) { + if (($index = array_search('{' . Sabre_CalDAV_Plugin::NS_CALENDARSERVER . '}shared-url', $requestedProperties))!==false) { + + unset($requestedProperties[$index]); + $returnedProperties[200]['{' . Sabre_CalDAV_Plugin::NS_CALENDARSERVER . '}shared-url'] = + new Sabre_DAV_Property_Href( + $node->getSharedUrl() + ); + + } + + } + + } + + /** + * This method is triggered *after* all properties have been retrieved. + * This allows us to inject the correct resourcetype for calendars that + * have been shared. + * + * @param string $path + * @param array $properties + * @param Sabre_DAV_INode $node + * @return void + */ + public function afterGetProperties($path, &$properties, Sabre_DAV_INode $node) { + + if ($node instanceof Sabre_CalDAV_IShareableCalendar) { + if (isset($properties[200]['{DAV:}resourcetype'])) { + if (count($node->getShares())>0) { + $properties[200]['{DAV:}resourcetype']->add( + '{' . Sabre_CalDAV_Plugin::NS_CALENDARSERVER . '}shared-owner' + ); + } + } + $propName = '{' . Sabre_CalDAV_Plugin::NS_CALENDARSERVER . '}allowed-sharing-modes'; + if (array_key_exists($propName, $properties[404])) { + unset($properties[404][$propName]); + $properties[200][$propName] = new Sabre_CalDAV_Property_AllowedSharingModes(true,false); + } + + } + + } + + /** + * This method is trigged when a user attempts to update a node's + * properties. + * + * A previous draft of the sharing spec stated that it was possible to use + * PROPPATCH to remove 'shared-owner' from the resourcetype, thus unsharing + * the calendar. + * + * Even though this is no longer in the current spec, we keep this around + * because OS X 10.7 may still make use of this feature. + * + * @param array $mutations + * @param array $result + * @param Sabre_DAV_INode $node + * @return void + */ + public function updateProperties(array &$mutations, array &$result, Sabre_DAV_INode $node) { + + if (!$node instanceof Sabre_CalDAV_IShareableCalendar) + return; + + if (!isset($mutations['{DAV:}resourcetype'])) { + return; + } + + // Only doing something if shared-owner is indeed not in the list. + if($mutations['{DAV:}resourcetype']->is('{' . Sabre_CalDAV_Plugin::NS_CALENDARSERVER . '}shared-owner')) return; + + $shares = $node->getShares(); + $remove = array(); + foreach($shares as $share) { + $remove[] = $share['href']; + } + $node->updateShares(array(), $remove); + + // We're marking this update as 200 OK + $result[200]['{DAV:}resourcetype'] = null; + + // Removing it from the mutations list + unset($mutations['{DAV:}resourcetype']); + + } + + /** + * This event is triggered when the server didn't know how to handle a + * certain request. + * + * We intercept this to handle POST requests on calendars. + * + * @param string $method + * @param string $uri + * @return null|bool + */ + public function unknownMethod($method, $uri) { + + if ($method!=='POST') { + return; + } + + // Only handling xml + $contentType = $this->server->httpRequest->getHeader('Content-Type'); + if (strpos($contentType,'application/xml')===false && strpos($contentType,'text/xml')===false) + return; + + // Making sure the node exists + try { + $node = $this->server->tree->getNodeForPath($uri); + } catch (Sabre_DAV_Exception_NotFound $e) { + return; + } + + + $dom = Sabre_DAV_XMLUtil::loadDOMDocument($this->server->httpRequest->getBody(true)); + + $documentType = Sabre_DAV_XMLUtil::toClarkNotation($dom->firstChild); + + switch($documentType) { + + // Dealing with the 'share' document, which modified invitees on a + // calendar. + case '{' . Sabre_CalDAV_Plugin::NS_CALENDARSERVER . '}share' : + + // We can only deal with IShareableCalendar objects + if (!$node instanceof Sabre_CalDAV_IShareableCalendar) { + return; + } + + // Getting ACL info + $acl = $this->server->getPlugin('acl'); + + // If there's no ACL support, we allow everything + if ($acl) { + $acl->checkPrivileges($uri, '{DAV:}write'); + } + + $mutations = $this->parseShareRequest($dom); + + $node->updateShares($mutations[0], $mutations[1]); + + $this->server->httpResponse->sendStatus(200); + // Adding this because sending a response body may cause issues, + // and I wanted some type of indicator the response was handled. + $this->server->httpResponse->setHeader('X-Sabre-Status', 'everything-went-well'); + + // Breaking the event chain + return false; + + // The invite-reply document is sent when the user replies to an + // invitation of a calendar share. + case '{'. Sabre_CalDAV_Plugin::NS_CALENDARSERVER.'}invite-reply' : + + // This only works on the calendar-home-root node. + if (!$node instanceof Sabre_CalDAV_UserCalendars) { + return; + } + + // Getting ACL info + $acl = $this->server->getPlugin('acl'); + + // If there's no ACL support, we allow everything + if ($acl) { + $acl->checkPrivileges($uri, '{DAV:}write'); + } + + $message = $this->parseInviteReplyRequest($dom); + + $url = $node->shareReply( + $message['href'], + $message['status'], + $message['calendarUri'], + $message['inReplyTo'], + $message['summary'] + ); + + $this->server->httpResponse->sendStatus(200); + // Adding this because sending a response body may cause issues, + // and I wanted some type of indicator the response was handled. + $this->server->httpResponse->setHeader('X-Sabre-Status', 'everything-went-well'); + + if ($url) { + $dom = new DOMDocument('1.0', 'UTF-8'); + $dom->formatOutput = true; + + $root = $dom->createElement('cs:shared-as'); + foreach($this->server->xmlNamespaces as $namespace => $prefix) { + $root->setAttribute('xmlns:' . $prefix, $namespace); + } + + $dom->appendChild($root); + $href = new Sabre_DAV_Property_Href($url); + + $href->serialize($this->server, $root); + $this->server->httpResponse->setHeader('Content-Type','application/xml'); + $this->server->httpResponse->sendBody($dom->saveXML()); + + } + + // Breaking the event chain + return false; + + case '{' . Sabre_CalDAV_Plugin::NS_CALENDARSERVER . '}publish-calendar' : + + // We can only deal with IShareableCalendar objects + if (!$node instanceof Sabre_CalDAV_IShareableCalendar) { + return; + } + + // Getting ACL info + $acl = $this->server->getPlugin('acl'); + + // If there's no ACL support, we allow everything + if ($acl) { + $acl->checkPrivileges($uri, '{DAV:}write'); + } + + $node->setPublishStatus(true); + + // iCloud sends back the 202, so we will too. + $this->server->httpResponse->sendStatus(202); + + // Adding this because sending a response body may cause issues, + // and I wanted some type of indicator the response was handled. + $this->server->httpResponse->setHeader('X-Sabre-Status', 'everything-went-well'); + + // Breaking the event chain + return false; + + case '{' . Sabre_CalDAV_Plugin::NS_CALENDARSERVER . '}unpublish-calendar' : + + // We can only deal with IShareableCalendar objects + if (!$node instanceof Sabre_CalDAV_IShareableCalendar) { + return; + } + + // Getting ACL info + $acl = $this->server->getPlugin('acl'); + + // If there's no ACL support, we allow everything + if ($acl) { + $acl->checkPrivileges($uri, '{DAV:}write'); + } + + $node->setPublishStatus(false); + + $this->server->httpResponse->sendStatus(200); + + // Adding this because sending a response body may cause issues, + // and I wanted some type of indicator the response was handled. + $this->server->httpResponse->setHeader('X-Sabre-Status', 'everything-went-well'); + + // Breaking the event chain + return false; + + } + + + } + + /** + * Parses the 'share' POST request. + * + * This method returns an array, containing two arrays. + * The first array is a list of new sharees. Every element is a struct + * containing a: + * * href element. (usually a mailto: address) + * * commonName element (often a first and lastname, but can also be + * false) + * * readOnly (true or false) + * * summary (A description of the share, can also be false) + * + * The second array is a list of sharees that are to be removed. This is + * just a simple array with 'hrefs'. + * + * @param DOMDocument $dom + * @return array + */ + protected function parseShareRequest(DOMDocument $dom) { + + $xpath = new \DOMXPath($dom); + $xpath->registerNamespace('cs', Sabre_CalDAV_Plugin::NS_CALENDARSERVER); + $xpath->registerNamespace('d', 'DAV:'); + + + $set = array(); + $elems = $xpath->query('cs:set'); + + for($i=0; $i < $elems->length; $i++) { + + $xset = $elems->item($i); + $set[] = array( + 'href' => $xpath->evaluate('string(d:href)', $xset), + 'commonName' => $xpath->evaluate('string(cs:common-name)', $xset), + 'summary' => $xpath->evaluate('string(cs:summary)', $xset), + 'readOnly' => $xpath->evaluate('boolean(cs:read)', $xset)!==false + ); + + } + + $remove = array(); + $elems = $xpath->query('cs:remove'); + + for($i=0; $i < $elems->length; $i++) { + + $xremove = $elems->item($i); + $remove[] = $xpath->evaluate('string(d:href)', $xremove); + + } + + return array($set, $remove); + + } + + /** + * Parses the 'invite-reply' POST request. + * + * This method returns an array, containing the following properties: + * * href - The sharee who is replying + * * status - One of the self::STATUS_* constants + * * calendarUri - The url of the shared calendar + * * inReplyTo - The unique id of the share invitation. + * * summary - Optional description of the reply. + * + * @param DOMDocument $dom + * @return array + */ + protected function parseInviteReplyRequest(DOMDocument $dom) { + + $xpath = new \DOMXPath($dom); + $xpath->registerNamespace('cs', Sabre_CalDAV_Plugin::NS_CALENDARSERVER); + $xpath->registerNamespace('d', 'DAV:'); + + $hostHref = $xpath->evaluate('string(cs:hosturl/d:href)'); + if (!$hostHref) { + throw new Sabre_DAV_Exception_BadRequest('The {' . Sabre_CalDAV_Plugin::NS_CALENDARSERVER . '}hosturl/{DAV:}href element is required'); + } + + return array( + 'href' => $xpath->evaluate('string(d:href)'), + 'calendarUri' => $this->server->calculateUri($hostHref), + 'inReplyTo' => $xpath->evaluate('string(cs:in-reply-to)'), + 'summary' => $xpath->evaluate('string(cs:summary)'), + 'status' => $xpath->evaluate('boolean(cs:invite-accepted)')?self::STATUS_ACCEPTED:self::STATUS_DECLINED + ); + + } + +} diff --git a/3rdparty/Sabre/CalDAV/UserCalendars.php b/3rdparty/Sabre/CalDAV/UserCalendars.php index b8d3f0573fa..3194e6677ab 100755..100644 --- a/3rdparty/Sabre/CalDAV/UserCalendars.php +++ b/3rdparty/Sabre/CalDAV/UserCalendars.php @@ -6,7 +6,7 @@ * @package Sabre * @subpackage CalDAV * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. - * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @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 { @@ -21,7 +21,7 @@ class Sabre_CalDAV_UserCalendars implements Sabre_DAV_IExtendedCollection, Sabre /** * CalDAV backend * - * @var Sabre_CalDAV_Backend_Abstract + * @var Sabre_CalDAV_Backend_BackendInterface */ protected $caldavBackend; @@ -36,10 +36,10 @@ class Sabre_CalDAV_UserCalendars implements Sabre_DAV_IExtendedCollection, Sabre * Constructor * * @param Sabre_DAVACL_IPrincipalBackend $principalBackend - * @param Sabre_CalDAV_Backend_Abstract $caldavBackend + * @param Sabre_CalDAV_Backend_BackendInterface $caldavBackend * @param mixed $userUri */ - public function __construct(Sabre_DAVACL_IPrincipalBackend $principalBackend, Sabre_CalDAV_Backend_Abstract $caldavBackend, $userUri) { + public function __construct(Sabre_DAVACL_IPrincipalBackend $principalBackend, Sabre_CalDAV_Backend_BackendInterface $caldavBackend, $userUri) { $this->principalBackend = $principalBackend; $this->caldavBackend = $caldavBackend; @@ -168,9 +168,22 @@ class Sabre_CalDAV_UserCalendars implements Sabre_DAV_IExtendedCollection, Sabre $calendars = $this->caldavBackend->getCalendarsForUser($this->principalInfo['uri']); $objs = array(); foreach($calendars as $calendar) { - $objs[] = new Sabre_CalDAV_Calendar($this->principalBackend, $this->caldavBackend, $calendar); + if ($this->caldavBackend instanceof Sabre_CalDAV_Backend_SharingSupport) { + if (isset($calendar['{http://calendarserver.org/ns/}shared-url'])) { + $objs[] = new Sabre_CalDAV_SharedCalendar($this->principalBackend, $this->caldavBackend, $calendar); + } else { + $objs[] = new Sabre_CalDAV_ShareableCalendar($this->principalBackend, $this->caldavBackend, $calendar); + } + } else { + $objs[] = new Sabre_CalDAV_Calendar($this->principalBackend, $this->caldavBackend, $calendar); + } } $objs[] = new Sabre_CalDAV_Schedule_Outbox($this->principalInfo['uri']); + + // We're adding a notifications node, if it's supported by the backend. + if ($this->caldavBackend instanceof Sabre_CalDAV_Backend_NotificationSupport) { + $objs[] = new Sabre_CalDAV_Notifications_Collection($this->caldavBackend, $this->principalInfo['uri']); + } return $objs; } @@ -185,8 +198,22 @@ class Sabre_CalDAV_UserCalendars implements Sabre_DAV_IExtendedCollection, Sabre */ 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'); + $isCalendar = false; + foreach($resourceType as $rt) { + switch ($rt) { + case '{DAV:}collection' : + case '{http://calendarserver.org/ns/}shared-owner' : + // ignore + break; + case '{urn:ietf:params:xml:ns:caldav}calendar' : + $isCalendar = true; + break; + default : + throw new Sabre_DAV_Exception_InvalidResourceType('Unknown resourceType: ' . $rt); + } + } + if (!$isCalendar) { + throw new Sabre_DAV_Exception_InvalidResourceType('You can only create calendars in this collection'); } $this->caldavBackend->createCalendar($this->principalInfo['uri'], $name, $properties); @@ -295,4 +322,27 @@ class Sabre_CalDAV_UserCalendars implements Sabre_DAV_IExtendedCollection, Sabre } + /** + * This method is called when a user replied to a request to share. + * + * This method should return the url of the newly created calendar if the + * share was accepted. + * + * @param string href The sharee who is replying (often a mailto: address) + * @param int status One of the SharingPlugin::STATUS_* constants + * @param string $calendarUri The url to the calendar thats being shared + * @param string $inReplyTo The unique id this message is a response to + * @param string $summary A description of the reply + * @return null|string + */ + public function shareReply($href, $status, $calendarUri, $inReplyTo, $summary = null) { + + if (!$this->caldavBackend instanceof Sabre_CalDAV_Backend_SharingSupport) { + throw new Sabre_DAV_Exception_NotImplemented('Sharing support is not implemented by this backend.'); + } + + return $this->caldavBackend->shareReply($href, $status, $calendarUri, $inReplyTo, $summary); + + } + } diff --git a/3rdparty/Sabre/CalDAV/Version.php b/3rdparty/Sabre/CalDAV/Version.php index ace9901c089..0ad14fa0869 100755..100644 --- a/3rdparty/Sabre/CalDAV/Version.php +++ b/3rdparty/Sabre/CalDAV/Version.php @@ -14,7 +14,7 @@ class Sabre_CalDAV_Version { /** * Full version number */ - const VERSION = '1.6.4'; + const VERSION = '1.7.0'; /** * Stability : alpha, beta, stable diff --git a/3rdparty/Sabre/CalDAV/includes.php b/3rdparty/Sabre/CalDAV/includes.php index 1ecb870a0e1..8b38e398f65 100755..100644 --- a/3rdparty/Sabre/CalDAV/includes.php +++ b/3rdparty/Sabre/CalDAV/includes.php @@ -16,28 +16,47 @@ */ // Begin includes -include __DIR__ . '/Backend/Abstract.php'; -include __DIR__ . '/Backend/PDO.php'; +include __DIR__ . '/Backend/BackendInterface.php'; +include __DIR__ . '/Backend/NotificationSupport.php'; +include __DIR__ . '/Backend/SharingSupport.php'; include __DIR__ . '/CalendarQueryParser.php'; include __DIR__ . '/CalendarQueryValidator.php'; include __DIR__ . '/CalendarRootNode.php'; +include __DIR__ . '/Exception/InvalidComponentType.php'; include __DIR__ . '/ICalendar.php'; include __DIR__ . '/ICalendarObject.php'; include __DIR__ . '/ICSExportPlugin.php'; +include __DIR__ . '/IShareableCalendar.php'; +include __DIR__ . '/ISharedCalendar.php'; +include __DIR__ . '/Notifications/ICollection.php'; +include __DIR__ . '/Notifications/INode.php'; +include __DIR__ . '/Notifications/INotificationType.php'; +include __DIR__ . '/Notifications/Node.php'; +include __DIR__ . '/Notifications/Notification/Invite.php'; +include __DIR__ . '/Notifications/Notification/InviteReply.php'; +include __DIR__ . '/Notifications/Notification/SystemStatus.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/AllowedSharingModes.php'; +include __DIR__ . '/Property/Invite.php'; +include __DIR__ . '/Property/ScheduleCalendarTransp.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__ . '/SharingPlugin.php'; include __DIR__ . '/UserCalendars.php'; include __DIR__ . '/Version.php'; +include __DIR__ . '/Backend/Abstract.php'; +include __DIR__ . '/Backend/PDO.php'; include __DIR__ . '/Calendar.php'; include __DIR__ . '/CalendarObject.php'; +include __DIR__ . '/Notifications/Collection.php'; +include __DIR__ . '/ShareableCalendar.php'; +include __DIR__ . '/SharedCalendar.php'; // End includes diff --git a/3rdparty/Sabre/CardDAV/AddressBook.php b/3rdparty/Sabre/CardDAV/AddressBook.php index 12297175a85..8d545114d97 100755..100644 --- a/3rdparty/Sabre/CardDAV/AddressBook.php +++ b/3rdparty/Sabre/CardDAV/AddressBook.php @@ -55,7 +55,7 @@ class Sabre_CardDAV_AddressBook extends Sabre_DAV_Collection implements Sabre_Ca * Returns a card * * @param string $name - * @return Sabre_DAV_Card + * @return Sabre_CardDAV_ICard */ public function getChild($name) { @@ -104,7 +104,7 @@ class Sabre_CardDAV_AddressBook extends Sabre_DAV_Collection implements Sabre_Ca * * @param string $name * @param resource $vcardData - * @return void|null + * @return string|null */ public function createFile($name,$vcardData = null) { diff --git a/3rdparty/Sabre/CardDAV/AddressBookQueryParser.php b/3rdparty/Sabre/CardDAV/AddressBookQueryParser.php index 46bb8ff18dd..46bb8ff18dd 100755..100644 --- a/3rdparty/Sabre/CardDAV/AddressBookQueryParser.php +++ b/3rdparty/Sabre/CardDAV/AddressBookQueryParser.php diff --git a/3rdparty/Sabre/CardDAV/AddressBookRoot.php b/3rdparty/Sabre/CardDAV/AddressBookRoot.php index 9d37b15f08e..9d37b15f08e 100755..100644 --- a/3rdparty/Sabre/CardDAV/AddressBookRoot.php +++ b/3rdparty/Sabre/CardDAV/AddressBookRoot.php diff --git a/3rdparty/Sabre/CardDAV/Backend/Abstract.php b/3rdparty/Sabre/CardDAV/Backend/Abstract.php index e4806b7161f..e4806b7161f 100755..100644 --- a/3rdparty/Sabre/CardDAV/Backend/Abstract.php +++ b/3rdparty/Sabre/CardDAV/Backend/Abstract.php diff --git a/3rdparty/Sabre/CardDAV/Backend/PDO.php b/3rdparty/Sabre/CardDAV/Backend/PDO.php index 413a77f3bcc..413a77f3bcc 100755..100644 --- a/3rdparty/Sabre/CardDAV/Backend/PDO.php +++ b/3rdparty/Sabre/CardDAV/Backend/PDO.php diff --git a/3rdparty/Sabre/CardDAV/Card.php b/3rdparty/Sabre/CardDAV/Card.php index d7c66333837..0e35d321eb8 100755..100644 --- a/3rdparty/Sabre/CardDAV/Card.php +++ b/3rdparty/Sabre/CardDAV/Card.php @@ -6,7 +6,7 @@ * @package Sabre * @subpackage CardDAV * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. - * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ class Sabre_CardDAV_Card extends Sabre_DAV_File implements Sabre_CardDAV_ICard, Sabre_DAVACL_IACL { @@ -78,7 +78,7 @@ class Sabre_CardDAV_Card extends Sabre_DAV_File implements Sabre_CardDAV_ICard, * Updates the VCard-formatted object * * @param string $cardData - * @return void + * @return string|null */ public function put($cardData) { @@ -114,7 +114,7 @@ class Sabre_CardDAV_Card extends Sabre_DAV_File implements Sabre_CardDAV_ICard, */ public function getContentType() { - return 'text/x-vcard'; + return 'text/x-vcard; charset=utf-8'; } @@ -128,7 +128,13 @@ class Sabre_CardDAV_Card extends Sabre_DAV_File implements Sabre_CardDAV_ICard, if (isset($this->cardData['etag'])) { return $this->cardData['etag']; } else { - return '"' . md5($this->get()) . '"'; + $data = $this->get(); + if (is_string($data)) { + return '"' . md5($data) . '"'; + } else { + // We refuse to calculate the md5 if it's a stream. + return null; + } } } @@ -136,7 +142,7 @@ class Sabre_CardDAV_Card extends Sabre_DAV_File implements Sabre_CardDAV_ICard, /** * Returns the last modification date as a unix timestamp * - * @return time + * @return int */ public function getLastModified() { diff --git a/3rdparty/Sabre/CardDAV/IAddressBook.php b/3rdparty/Sabre/CardDAV/IAddressBook.php index 2bc275bcf74..2bc275bcf74 100755..100644 --- a/3rdparty/Sabre/CardDAV/IAddressBook.php +++ b/3rdparty/Sabre/CardDAV/IAddressBook.php diff --git a/3rdparty/Sabre/CardDAV/ICard.php b/3rdparty/Sabre/CardDAV/ICard.php index a17299316c1..a17299316c1 100755..100644 --- a/3rdparty/Sabre/CardDAV/ICard.php +++ b/3rdparty/Sabre/CardDAV/ICard.php diff --git a/3rdparty/Sabre/CardDAV/IDirectory.php b/3rdparty/Sabre/CardDAV/IDirectory.php index 22d4afeb24a..22d4afeb24a 100755..100644 --- a/3rdparty/Sabre/CardDAV/IDirectory.php +++ b/3rdparty/Sabre/CardDAV/IDirectory.php diff --git a/3rdparty/Sabre/CardDAV/Plugin.php b/3rdparty/Sabre/CardDAV/Plugin.php index 96def6dd96b..12bccaec4fb 100755..100644 --- a/3rdparty/Sabre/CardDAV/Plugin.php +++ b/3rdparty/Sabre/CardDAV/Plugin.php @@ -1,5 +1,7 @@ <?php +use Sabre\VObject; + /** * CardDAV plugin * @@ -48,6 +50,7 @@ class Sabre_CardDAV_Plugin extends Sabre_DAV_ServerPlugin { /* Events */ $server->subscribeEvent('beforeGetProperties', array($this, 'beforeGetProperties')); + $server->subscribeEvent('afterGetProperties', array($this, 'afterGetProperties')); $server->subscribeEvent('updateProperties', array($this, 'updateProperties')); $server->subscribeEvent('report', array($this,'report')); $server->subscribeEvent('onHTMLActionsPanel', array($this,'htmlActionsPanel')); @@ -153,10 +156,6 @@ class Sabre_CardDAV_Plugin extends Sabre_DAV_ServerPlugin { if (is_resource($val)) $val = stream_get_contents($val); - // Taking out \r to not screw up the xml output - //$returnedProperties[200][$addressDataProp] = str_replace("\r","", $val); - // The stripping of \r breaks the Mail App in OSX Mountain Lion - // this is fixed in master, but not backported. /Tanghus $returnedProperties[200][$addressDataProp] = $val; } @@ -190,7 +189,7 @@ class Sabre_CardDAV_Plugin extends Sabre_DAV_ServerPlugin { * @param array $mutations * @param array $result * @param Sabre_DAV_INode $node - * @return void + * @return bool */ public function updateProperties(&$mutations, &$result, $node) { @@ -272,7 +271,7 @@ class Sabre_CardDAV_Plugin extends Sabre_DAV_ServerPlugin { $properties = array_keys(Sabre_DAV_XMLUtil::parseProperties($dom->firstChild)); - $hrefElems = $dom->getElementsByTagNameNS('urn:DAV','href'); + $hrefElems = $dom->getElementsByTagNameNS('DAV:','href'); $propertyList = array(); foreach($hrefElems as $elem) { @@ -282,9 +281,12 @@ class Sabre_CardDAV_Plugin extends Sabre_DAV_ServerPlugin { } + $prefer = $this->server->getHTTPPRefer(); + $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->server->httpResponse->setHeader('Vary','Brief,Prefer'); + $this->server->httpResponse->sendBody($this->server->generateMultiStatus($propertyList, $prefer['return-minimal'])); } @@ -348,9 +350,9 @@ class Sabre_CardDAV_Plugin extends Sabre_DAV_ServerPlugin { try { - $vobj = Sabre_VObject_Reader::read($data); + $vobj = VObject\Reader::read($data); - } catch (Sabre_VObject_ParseException $e) { + } catch (VObject\ParseException $e) { throw new Sabre_DAV_Exception_UnsupportedMediaType('This resource only supports valid vcard data. Parse error: ' . $e->getMessage()); @@ -360,6 +362,10 @@ class Sabre_CardDAV_Plugin extends Sabre_DAV_ServerPlugin { throw new Sabre_DAV_Exception_UnsupportedMediaType('This collection can only support vcard objects.'); } + if (!isset($vobj->UID)) { + throw new Sabre_DAV_Exception_BadRequest('Every vcard must have an UID.'); + } + } @@ -424,9 +430,12 @@ class Sabre_CardDAV_Plugin extends Sabre_DAV_ServerPlugin { } + $prefer = $this->server->getHTTPPRefer(); + $this->server->httpResponse->sendStatus(207); $this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8'); - $this->server->httpResponse->sendBody($this->server->generateMultiStatus($result)); + $this->server->httpResponse->setHeader('Vary','Brief,Prefer'); + $this->server->httpResponse->sendBody($this->server->generateMultiStatus($result, $prefer['return-minimal'])); } @@ -440,7 +449,7 @@ class Sabre_CardDAV_Plugin extends Sabre_DAV_ServerPlugin { */ public function validateFilters($vcardData, array $filters, $test) { - $vcard = Sabre_VObject_Reader::read($vcardData); + $vcard = VObject\Reader::read($vcardData); if (!$filters) return true; @@ -616,6 +625,30 @@ class Sabre_CardDAV_Plugin extends Sabre_DAV_ServerPlugin { } /** + * This event is triggered after webdav-properties have been retrieved. + * + * @return bool + */ + public function afterGetProperties($uri, &$properties) { + + // If the request was made using the SOGO connector, we must rewrite + // the content-type property. By default SabreDAV will send back + // text/x-vcard; charset=utf-8, but for SOGO we must strip that last + // part. + if (!isset($properties[200]['{DAV:}getcontenttype'])) + return; + + if (strpos($this->server->httpRequest->getHeader('User-Agent'),'Thunderbird')===false) { + return; + } + + if (strpos($properties[200]['{DAV:}getcontenttype'],'text/x-vcard')===0) { + $properties[200]['{DAV:}getcontenttype'] = 'text/x-vcard'; + } + + } + + /** * 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. diff --git a/3rdparty/Sabre/CardDAV/Property/SupportedAddressData.php b/3rdparty/Sabre/CardDAV/Property/SupportedAddressData.php index 36d9306e7aa..36d9306e7aa 100755..100644 --- a/3rdparty/Sabre/CardDAV/Property/SupportedAddressData.php +++ b/3rdparty/Sabre/CardDAV/Property/SupportedAddressData.php diff --git a/3rdparty/Sabre/CardDAV/UserAddressBooks.php b/3rdparty/Sabre/CardDAV/UserAddressBooks.php index 3f11fb11238..3f11fb11238 100755..100644 --- a/3rdparty/Sabre/CardDAV/UserAddressBooks.php +++ b/3rdparty/Sabre/CardDAV/UserAddressBooks.php diff --git a/3rdparty/Sabre/CardDAV/VCFExportPlugin.php b/3rdparty/Sabre/CardDAV/VCFExportPlugin.php new file mode 100644 index 00000000000..8850fef8afb --- /dev/null +++ b/3rdparty/Sabre/CardDAV/VCFExportPlugin.php @@ -0,0 +1,107 @@ +<?php + +use Sabre\VObject; + +/** + * VCF Exporter + * + * This plugin adds the ability to export entire address books as .vcf files. + * This is useful for clients that don't support CardDAV yet. They often do + * support vcf files. + * + * @package Sabre + * @subpackage CardDAV + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. + * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @author Thomas Tanghus (http://tanghus.net/) + * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License + */ +class Sabre_CardDAV_VCFExportPlugin 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_CardDAV_IAddressBook)) return; + + // Checking ACL, if available. + if ($aclPlugin = $this->server->getPlugin('acl')) { + $aclPlugin->checkPrivileges($uri, '{DAV:}read'); + } + + $this->server->httpResponse->setHeader('Content-Type','text/directory'); + $this->server->httpResponse->sendStatus(200); + + $nodes = $this->server->getPropertiesForPath($uri, array( + '{' . Sabre_CardDAV_Plugin::NS_CARDDAV . '}address-data', + ),1); + + $this->server->httpResponse->sendBody($this->generateVCF($nodes)); + + // Returning false to break the event chain + return false; + + } + + /** + * Merges all vcard objects, and builds one big vcf export + * + * @param array $nodes + * @return string + */ + public function generateVCF(array $nodes) { + + $output = ""; + + foreach($nodes as $node) { + + if (!isset($node[200]['{' . Sabre_CardDAV_Plugin::NS_CARDDAV . '}address-data'])) { + continue; + } + $nodeData = $node[200]['{' . Sabre_CardDAV_Plugin::NS_CARDDAV . '}address-data']; + + // Parsing this node so VObject can clean up the output. + $output .= + VObject\Reader::read($nodeData)->serialize(); + + } + + return $output; + + } + +} diff --git a/3rdparty/Sabre/CardDAV/Version.php b/3rdparty/Sabre/CardDAV/Version.php index d0623f0d3e8..6b70a2df9b5 100755..100644 --- a/3rdparty/Sabre/CardDAV/Version.php +++ b/3rdparty/Sabre/CardDAV/Version.php @@ -16,7 +16,7 @@ class Sabre_CardDAV_Version { /** * Full version number */ - const VERSION = '1.6.3'; + const VERSION = '1.7.0'; /** * Stability : alpha, beta, stable diff --git a/3rdparty/Sabre/CardDAV/includes.php b/3rdparty/Sabre/CardDAV/includes.php index c3b8c04b077..08b4f76bd01 100755..100644 --- a/3rdparty/Sabre/CardDAV/includes.php +++ b/3rdparty/Sabre/CardDAV/includes.php @@ -26,6 +26,7 @@ include __DIR__ . '/IDirectory.php'; include __DIR__ . '/Plugin.php'; include __DIR__ . '/Property/SupportedAddressData.php'; include __DIR__ . '/UserAddressBooks.php'; +include __DIR__ . '/VCFExportPlugin.php'; include __DIR__ . '/Version.php'; include __DIR__ . '/AddressBook.php'; include __DIR__ . '/Card.php'; diff --git a/3rdparty/Sabre/DAV/Auth/Backend/AbstractBasic.php b/3rdparty/Sabre/DAV/Auth/Backend/AbstractBasic.php index 1e89b84f9a1..1e89b84f9a1 100755..100644 --- a/3rdparty/Sabre/DAV/Auth/Backend/AbstractBasic.php +++ b/3rdparty/Sabre/DAV/Auth/Backend/AbstractBasic.php diff --git a/3rdparty/Sabre/DAV/Auth/Backend/AbstractDigest.php b/3rdparty/Sabre/DAV/Auth/Backend/AbstractDigest.php index 9833928b976..9833928b976 100755..100644 --- a/3rdparty/Sabre/DAV/Auth/Backend/AbstractDigest.php +++ b/3rdparty/Sabre/DAV/Auth/Backend/AbstractDigest.php diff --git a/3rdparty/Sabre/DAV/Auth/Backend/Apache.php b/3rdparty/Sabre/DAV/Auth/Backend/Apache.php index d4294ea4d86..d4294ea4d86 100755..100644 --- a/3rdparty/Sabre/DAV/Auth/Backend/Apache.php +++ b/3rdparty/Sabre/DAV/Auth/Backend/Apache.php diff --git a/3rdparty/Sabre/DAV/Auth/Backend/File.php b/3rdparty/Sabre/DAV/Auth/Backend/File.php index de308d64a67..de308d64a67 100755..100644 --- a/3rdparty/Sabre/DAV/Auth/Backend/File.php +++ b/3rdparty/Sabre/DAV/Auth/Backend/File.php diff --git a/3rdparty/Sabre/DAV/Auth/Backend/PDO.php b/3rdparty/Sabre/DAV/Auth/Backend/PDO.php index eac18a23fbb..eac18a23fbb 100755..100644 --- a/3rdparty/Sabre/DAV/Auth/Backend/PDO.php +++ b/3rdparty/Sabre/DAV/Auth/Backend/PDO.php diff --git a/3rdparty/Sabre/DAV/Auth/IBackend.php b/3rdparty/Sabre/DAV/Auth/IBackend.php index 5be5d1bc93d..5be5d1bc93d 100755..100644 --- a/3rdparty/Sabre/DAV/Auth/IBackend.php +++ b/3rdparty/Sabre/DAV/Auth/IBackend.php diff --git a/3rdparty/Sabre/DAV/Auth/Plugin.php b/3rdparty/Sabre/DAV/Auth/Plugin.php index 55a4e391674..55a4e391674 100755..100644 --- a/3rdparty/Sabre/DAV/Auth/Plugin.php +++ b/3rdparty/Sabre/DAV/Auth/Plugin.php diff --git a/3rdparty/Sabre/DAV/Browser/GuessContentType.php b/3rdparty/Sabre/DAV/Browser/GuessContentType.php index b6c00d461cb..b6c00d461cb 100755..100644 --- a/3rdparty/Sabre/DAV/Browser/GuessContentType.php +++ b/3rdparty/Sabre/DAV/Browser/GuessContentType.php diff --git a/3rdparty/Sabre/DAV/Browser/MapGetToPropFind.php b/3rdparty/Sabre/DAV/Browser/MapGetToPropFind.php index 15884887641..15884887641 100755..100644 --- a/3rdparty/Sabre/DAV/Browser/MapGetToPropFind.php +++ b/3rdparty/Sabre/DAV/Browser/MapGetToPropFind.php diff --git a/3rdparty/Sabre/DAV/Browser/Plugin.php b/3rdparty/Sabre/DAV/Browser/Plugin.php index 09bbdd2ae02..b6440ab6340 100755..100644 --- a/3rdparty/Sabre/DAV/Browser/Plugin.php +++ b/3rdparty/Sabre/DAV/Browser/Plugin.php @@ -338,7 +338,7 @@ class Sabre_DAV_Browser_Plugin extends Sabre_DAV_ServerPlugin { $icon = ''; if ($this->enableAssets) { - $node = $parent->getChild($name); + $node = $this->server->tree->getNodeForPath(($path?$path.'/':'') . $name); foreach(array_reverse($this->iconMap) as $class=>$iconName) { if ($node instanceof $class) { diff --git a/3rdparty/Sabre/DAV/Browser/assets/favicon.ico b/3rdparty/Sabre/DAV/Browser/assets/favicon.ico Binary files differindex 2b2c10a22cc..2b2c10a22cc 100755..100644 --- a/3rdparty/Sabre/DAV/Browser/assets/favicon.ico +++ b/3rdparty/Sabre/DAV/Browser/assets/favicon.ico diff --git a/3rdparty/Sabre/DAV/Browser/assets/icons/addressbook.png b/3rdparty/Sabre/DAV/Browser/assets/icons/addressbook.png Binary files differindex c9acc84172d..c9acc84172d 100755..100644 --- a/3rdparty/Sabre/DAV/Browser/assets/icons/addressbook.png +++ b/3rdparty/Sabre/DAV/Browser/assets/icons/addressbook.png diff --git a/3rdparty/Sabre/DAV/Browser/assets/icons/calendar.png b/3rdparty/Sabre/DAV/Browser/assets/icons/calendar.png Binary files differindex 3ecd6a800a0..3ecd6a800a0 100755..100644 --- a/3rdparty/Sabre/DAV/Browser/assets/icons/calendar.png +++ b/3rdparty/Sabre/DAV/Browser/assets/icons/calendar.png diff --git a/3rdparty/Sabre/DAV/Browser/assets/icons/card.png b/3rdparty/Sabre/DAV/Browser/assets/icons/card.png Binary files differindex 2ce954866d8..2ce954866d8 100755..100644 --- a/3rdparty/Sabre/DAV/Browser/assets/icons/card.png +++ b/3rdparty/Sabre/DAV/Browser/assets/icons/card.png diff --git a/3rdparty/Sabre/DAV/Browser/assets/icons/collection.png b/3rdparty/Sabre/DAV/Browser/assets/icons/collection.png Binary files differindex 156fa64fd50..156fa64fd50 100755..100644 --- a/3rdparty/Sabre/DAV/Browser/assets/icons/collection.png +++ b/3rdparty/Sabre/DAV/Browser/assets/icons/collection.png diff --git a/3rdparty/Sabre/DAV/Browser/assets/icons/file.png b/3rdparty/Sabre/DAV/Browser/assets/icons/file.png Binary files differindex 3b98551cec3..3b98551cec3 100755..100644 --- a/3rdparty/Sabre/DAV/Browser/assets/icons/file.png +++ b/3rdparty/Sabre/DAV/Browser/assets/icons/file.png diff --git a/3rdparty/Sabre/DAV/Browser/assets/icons/parent.png b/3rdparty/Sabre/DAV/Browser/assets/icons/parent.png Binary files differindex 156fa64fd50..156fa64fd50 100755..100644 --- a/3rdparty/Sabre/DAV/Browser/assets/icons/parent.png +++ b/3rdparty/Sabre/DAV/Browser/assets/icons/parent.png diff --git a/3rdparty/Sabre/DAV/Browser/assets/icons/principal.png b/3rdparty/Sabre/DAV/Browser/assets/icons/principal.png Binary files differindex f8988f828e6..f8988f828e6 100755..100644 --- a/3rdparty/Sabre/DAV/Browser/assets/icons/principal.png +++ b/3rdparty/Sabre/DAV/Browser/assets/icons/principal.png diff --git a/3rdparty/Sabre/DAV/Client.php b/3rdparty/Sabre/DAV/Client.php index 9a428765e90..9d9f4c96b02 100755..100644 --- a/3rdparty/Sabre/DAV/Client.php +++ b/3rdparty/Sabre/DAV/Client.php @@ -16,12 +16,25 @@ */ class Sabre_DAV_Client { + /** + * The propertyMap is a key-value array. + * + * If you use the propertyMap, any {DAV:}multistatus responses with the + * proeprties listed in this array, will automatically be mapped to a + * respective class. + * + * The {DAV:}resourcetype property is automatically added. This maps to + * Sabre_DAV_Property_ResourceType + * + * @var array + */ public $propertyMap = array(); protected $baseUri; protected $userName; protected $password; protected $proxy; + protected $trustedCertificates; /** * Basic authentication @@ -88,6 +101,18 @@ class Sabre_DAV_Client { } /** + * Add trusted root certificates to the webdav client. + * + * The parameter certificates should be a absulute path to a file + * which contains all trusted certificates + * + * @param string $certificates + */ + public function addTrustedCertificates($certificates) { + $this->trustedCertificates = $certificates; + } + + /** * Does a PROPFIND request * * The list of requested properties must be specified as an array, in clark @@ -143,13 +168,13 @@ class Sabre_DAV_Client { if ($depth===0) { reset($result); $result = current($result); - return $result[200]; + return isset($result[200])?$result[200]:array(); } $newResult = array(); foreach($result as $href => $statusList) { - $newResult[$href] = $statusList[200]; + $newResult[$href] = isset($statusList[200])?$statusList[200]:array(); } @@ -279,6 +304,10 @@ class Sabre_DAV_Client { CURLOPT_MAXREDIRS => 5, ); + if($this->trustedCertificates) { + $curlSettings[CURLOPT_CAINFO] = $this->trustedCertificates; + } + switch ($method) { case 'HEAD' : @@ -363,10 +392,30 @@ class Sabre_DAV_Client { if ($response['statusCode']>=400) { switch ($response['statusCode']) { + case 400 : + throw new Sabre_DAV_Exception_BadRequest('Bad request'); + case 401 : + throw new Sabre_DAV_Exception_NotAuthenticated('Not authenticated'); + case 402 : + throw new Sabre_DAV_Exception_PaymentRequired('Payment required'); + case 403 : + throw new Sabre_DAV_Exception_Forbidden('Forbidden'); case 404: - throw new Sabre_DAV_Exception_NotFound('Resource ' . $url . ' not found.'); - break; - + throw new Sabre_DAV_Exception_NotFound('Resource not found.'); + case 405 : + throw new Sabre_DAV_Exception_MethodNotAllowed('Method not allowed'); + case 409 : + throw new Sabre_DAV_Exception_Conflict('Conflict'); + case 412 : + throw new Sabre_DAV_Exception_PreconditionFailed('Precondition failed'); + case 416 : + throw new Sabre_DAV_Exception_RequestedRangeNotSatisfiable('Requested Range Not Satisfiable'); + case 500 : + throw new Sabre_DAV_Exception('Internal server error'); + case 501 : + throw new Sabre_DAV_Exception_NotImplemented('Not Implemented'); + case 507 : + throw new Sabre_DAV_Exception_InsufficientStorage('Insufficient storage'); default: throw new Sabre_DAV_Exception('HTTP error response. (errorcode ' . $response['statusCode'] . ')'); } @@ -386,6 +435,7 @@ class Sabre_DAV_Client { * @param array $settings * @return array */ + // @codeCoverageIgnoreStart protected function curlRequest($url, $settings) { $curl = curl_init($url); @@ -399,6 +449,7 @@ class Sabre_DAV_Client { ); } + // @codeCoverageIgnoreEnd /** * Returns the full url based on the given url (which may be relative). All @@ -453,19 +504,17 @@ class Sabre_DAV_Client { */ public function parseMultiStatus($body) { - $body = Sabre_DAV_XMLUtil::convertDAVNamespace($body); - $responseXML = simplexml_load_string($body, null, LIBXML_NOBLANKS | LIBXML_NOCDATA); if ($responseXML===false) { throw new InvalidArgumentException('The passed data is not valid XML'); } - $responseXML->registerXPathNamespace('d', 'urn:DAV'); + $responseXML->registerXPathNamespace('d', 'DAV:'); $propResult = array(); foreach($responseXML->xpath('d:response') as $response) { - $response->registerXPathNamespace('d', 'urn:DAV'); + $response->registerXPathNamespace('d', 'DAV:'); $href = $response->xpath('d:href'); $href = (string)$href[0]; @@ -473,7 +522,7 @@ class Sabre_DAV_Client { foreach($response->xpath('d:propstat') as $propStat) { - $propStat->registerXPathNamespace('d', 'urn:DAV'); + $propStat->registerXPathNamespace('d', 'DAV:'); $status = $propStat->xpath('d:status'); list($httpVersion, $statusCode, $message) = explode(' ', (string)$status[0],3); diff --git a/3rdparty/Sabre/DAV/Collection.php b/3rdparty/Sabre/DAV/Collection.php index 776c22531b2..c7648a8a52d 100755..100644 --- a/3rdparty/Sabre/DAV/Collection.php +++ b/3rdparty/Sabre/DAV/Collection.php @@ -17,9 +17,13 @@ abstract class Sabre_DAV_Collection extends Sabre_DAV_Node implements Sabre_DAV_ /** * Returns a child object, by its name. * - * This method makes use of the getChildren method to grab all the child nodes, and compares the name. + * This method makes use of the getChildren method to grab all the child + * nodes, and compares the name. * Generally its wise to override this, as this can usually be optimized * + * This method must throw Sabre_DAV_Exception_NotFound if the node does not + * exist. + * * @param string $name * @throws Sabre_DAV_Exception_NotFound * @return Sabre_DAV_INode diff --git a/3rdparty/Sabre/DAV/Directory.php b/3rdparty/Sabre/DAV/Directory.php deleted file mode 100755 index 6db8febc02e..00000000000 --- a/3rdparty/Sabre/DAV/Directory.php +++ /dev/null @@ -1,17 +0,0 @@ -<?php - -/** - * Directory class - * - * This class is now deprecated in favor of the Sabre_DAV_Collection class. - * - * @package Sabre - * @subpackage DAV - * @deprecated Use Sabre_DAV_Collection instead - * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. - * @author Evert Pot (http://www.rooftopsolutions.nl/) - * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License - */ -abstract class Sabre_DAV_Directory extends Sabre_DAV_Collection { -} - diff --git a/3rdparty/Sabre/DAV/Exception.php b/3rdparty/Sabre/DAV/Exception.php index a2cd6cf5820..a2cd6cf5820 100755..100644 --- a/3rdparty/Sabre/DAV/Exception.php +++ b/3rdparty/Sabre/DAV/Exception.php diff --git a/3rdparty/Sabre/DAV/Exception/BadRequest.php b/3rdparty/Sabre/DAV/Exception/BadRequest.php index b198648a754..b198648a754 100755..100644 --- a/3rdparty/Sabre/DAV/Exception/BadRequest.php +++ b/3rdparty/Sabre/DAV/Exception/BadRequest.php diff --git a/3rdparty/Sabre/DAV/Exception/Conflict.php b/3rdparty/Sabre/DAV/Exception/Conflict.php index 6b0bd1fad73..6b0bd1fad73 100755..100644 --- a/3rdparty/Sabre/DAV/Exception/Conflict.php +++ b/3rdparty/Sabre/DAV/Exception/Conflict.php diff --git a/3rdparty/Sabre/DAV/Exception/ConflictingLock.php b/3rdparty/Sabre/DAV/Exception/ConflictingLock.php index 6121868e69e..6121868e69e 100755..100644 --- a/3rdparty/Sabre/DAV/Exception/ConflictingLock.php +++ b/3rdparty/Sabre/DAV/Exception/ConflictingLock.php diff --git a/3rdparty/Sabre/DAV/Exception/FileNotFound.php b/3rdparty/Sabre/DAV/Exception/FileNotFound.php index d76e400c93b..d76e400c93b 100755..100644 --- a/3rdparty/Sabre/DAV/Exception/FileNotFound.php +++ b/3rdparty/Sabre/DAV/Exception/FileNotFound.php diff --git a/3rdparty/Sabre/DAV/Exception/Forbidden.php b/3rdparty/Sabre/DAV/Exception/Forbidden.php index 20b1056e31b..20b1056e31b 100755..100644 --- a/3rdparty/Sabre/DAV/Exception/Forbidden.php +++ b/3rdparty/Sabre/DAV/Exception/Forbidden.php diff --git a/3rdparty/Sabre/DAV/Exception/InsufficientStorage.php b/3rdparty/Sabre/DAV/Exception/InsufficientStorage.php index 1a15089b0a3..1a15089b0a3 100755..100644 --- a/3rdparty/Sabre/DAV/Exception/InsufficientStorage.php +++ b/3rdparty/Sabre/DAV/Exception/InsufficientStorage.php diff --git a/3rdparty/Sabre/DAV/Exception/InvalidResourceType.php b/3rdparty/Sabre/DAV/Exception/InvalidResourceType.php index 2230f1d0811..2230f1d0811 100755..100644 --- a/3rdparty/Sabre/DAV/Exception/InvalidResourceType.php +++ b/3rdparty/Sabre/DAV/Exception/InvalidResourceType.php diff --git a/3rdparty/Sabre/DAV/Exception/LockTokenMatchesRequestUri.php b/3rdparty/Sabre/DAV/Exception/LockTokenMatchesRequestUri.php index 80ab7aff65a..80ab7aff65a 100755..100644 --- a/3rdparty/Sabre/DAV/Exception/LockTokenMatchesRequestUri.php +++ b/3rdparty/Sabre/DAV/Exception/LockTokenMatchesRequestUri.php diff --git a/3rdparty/Sabre/DAV/Exception/Locked.php b/3rdparty/Sabre/DAV/Exception/Locked.php index 976365ac1f8..976365ac1f8 100755..100644 --- a/3rdparty/Sabre/DAV/Exception/Locked.php +++ b/3rdparty/Sabre/DAV/Exception/Locked.php diff --git a/3rdparty/Sabre/DAV/Exception/MethodNotAllowed.php b/3rdparty/Sabre/DAV/Exception/MethodNotAllowed.php index 31875751505..31875751505 100755..100644 --- a/3rdparty/Sabre/DAV/Exception/MethodNotAllowed.php +++ b/3rdparty/Sabre/DAV/Exception/MethodNotAllowed.php diff --git a/3rdparty/Sabre/DAV/Exception/NotAuthenticated.php b/3rdparty/Sabre/DAV/Exception/NotAuthenticated.php index 87ca624429f..87ca624429f 100755..100644 --- a/3rdparty/Sabre/DAV/Exception/NotAuthenticated.php +++ b/3rdparty/Sabre/DAV/Exception/NotAuthenticated.php diff --git a/3rdparty/Sabre/DAV/Exception/NotFound.php b/3rdparty/Sabre/DAV/Exception/NotFound.php index 2b9da560d23..2b9da560d23 100755..100644 --- a/3rdparty/Sabre/DAV/Exception/NotFound.php +++ b/3rdparty/Sabre/DAV/Exception/NotFound.php diff --git a/3rdparty/Sabre/DAV/Exception/NotImplemented.php b/3rdparty/Sabre/DAV/Exception/NotImplemented.php index d017a19f559..d017a19f559 100755..100644 --- a/3rdparty/Sabre/DAV/Exception/NotImplemented.php +++ b/3rdparty/Sabre/DAV/Exception/NotImplemented.php diff --git a/3rdparty/Sabre/DAV/Exception/PaymentRequired.php b/3rdparty/Sabre/DAV/Exception/PaymentRequired.php index 4982f45a4b5..4982f45a4b5 100755..100644 --- a/3rdparty/Sabre/DAV/Exception/PaymentRequired.php +++ b/3rdparty/Sabre/DAV/Exception/PaymentRequired.php diff --git a/3rdparty/Sabre/DAV/Exception/PreconditionFailed.php b/3rdparty/Sabre/DAV/Exception/PreconditionFailed.php index 213e9c52317..213e9c52317 100755..100644 --- a/3rdparty/Sabre/DAV/Exception/PreconditionFailed.php +++ b/3rdparty/Sabre/DAV/Exception/PreconditionFailed.php diff --git a/3rdparty/Sabre/DAV/Exception/ReportNotImplemented.php b/3rdparty/Sabre/DAV/Exception/ReportNotSupported.php index e86800f3038..a2906865f8e 100755..100644 --- a/3rdparty/Sabre/DAV/Exception/ReportNotImplemented.php +++ b/3rdparty/Sabre/DAV/Exception/ReportNotSupported.php @@ -1,7 +1,7 @@ <?php /** - * ReportNotImplemented + * ReportNotSupported * * This exception is thrown when the client requested an unknown report through the REPORT method * @@ -11,7 +11,7 @@ * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ -class Sabre_DAV_Exception_ReportNotImplemented extends Sabre_DAV_Exception_NotImplemented { +class Sabre_DAV_Exception_ReportNotSupported extends Sabre_DAV_Exception_Forbidden { /** * This method allows the exception to include additional information into the WebDAV error response diff --git a/3rdparty/Sabre/DAV/Exception/RequestedRangeNotSatisfiable.php b/3rdparty/Sabre/DAV/Exception/RequestedRangeNotSatisfiable.php index 29ee3654a7e..29ee3654a7e 100755..100644 --- a/3rdparty/Sabre/DAV/Exception/RequestedRangeNotSatisfiable.php +++ b/3rdparty/Sabre/DAV/Exception/RequestedRangeNotSatisfiable.php diff --git a/3rdparty/Sabre/DAV/Exception/UnsupportedMediaType.php b/3rdparty/Sabre/DAV/Exception/UnsupportedMediaType.php index 9a4693b21a8..9a4693b21a8 100755..100644 --- a/3rdparty/Sabre/DAV/Exception/UnsupportedMediaType.php +++ b/3rdparty/Sabre/DAV/Exception/UnsupportedMediaType.php diff --git a/3rdparty/Sabre/DAV/FS/Directory.php b/3rdparty/Sabre/DAV/FS/Directory.php index 3af2d755583..72fec744d0f 100755..100644 --- a/3rdparty/Sabre/DAV/FS/Directory.php +++ b/3rdparty/Sabre/DAV/FS/Directory.php @@ -17,7 +17,7 @@ class Sabre_DAV_FS_Directory extends Sabre_DAV_FS_Node implements Sabre_DAV_ICol * Data will either be supplied as a stream resource, or in certain cases * as a string. Keep in mind that you may have to support either. * - * After succesful creation of the file, you may choose to return the ETag + * After successful creation of the file, you may choose to return the ETag * of the new file here. * * The returned ETag must be surrounded by double-quotes (The quotes should @@ -58,6 +58,9 @@ class Sabre_DAV_FS_Directory extends Sabre_DAV_FS_Node implements Sabre_DAV_ICol /** * Returns a specific child node, referenced by its name * + * This method must throw Sabre_DAV_Exception_NotFound if the node does not + * exist. + * * @param string $name * @throws Sabre_DAV_Exception_NotFound * @return Sabre_DAV_INode diff --git a/3rdparty/Sabre/DAV/FS/File.php b/3rdparty/Sabre/DAV/FS/File.php index 6a8039fe303..6a8039fe303 100755..100644 --- a/3rdparty/Sabre/DAV/FS/File.php +++ b/3rdparty/Sabre/DAV/FS/File.php diff --git a/3rdparty/Sabre/DAV/FS/Node.php b/3rdparty/Sabre/DAV/FS/Node.php index 1283e9d0fdc..1283e9d0fdc 100755..100644 --- a/3rdparty/Sabre/DAV/FS/Node.php +++ b/3rdparty/Sabre/DAV/FS/Node.php diff --git a/3rdparty/Sabre/DAV/FSExt/Directory.php b/3rdparty/Sabre/DAV/FSExt/Directory.php index 540057183b3..70dfdc2c3b5 100755..100644 --- a/3rdparty/Sabre/DAV/FSExt/Directory.php +++ b/3rdparty/Sabre/DAV/FSExt/Directory.php @@ -17,7 +17,7 @@ class Sabre_DAV_FSExt_Directory extends Sabre_DAV_FSExt_Node implements Sabre_DA * Data will either be supplied as a stream resource, or in certain cases * as a string. Keep in mind that you may have to support either. * - * After succesful creation of the file, you may choose to return the ETag + * After successful creation of the file, you may choose to return the ETag * of the new file here. * * The returned ETag must be surrounded by double-quotes (The quotes should @@ -64,6 +64,9 @@ class Sabre_DAV_FSExt_Directory extends Sabre_DAV_FSExt_Node implements Sabre_DA /** * Returns a specific child node, referenced by its name * + * This method must throw Sabre_DAV_Exception_NotFound if the node does not + * exist. + * * @param string $name * @throws Sabre_DAV_Exception_NotFound * @return Sabre_DAV_INode diff --git a/3rdparty/Sabre/DAV/FSExt/File.php b/3rdparty/Sabre/DAV/FSExt/File.php index b93ce5aee21..590fb808e61 100755..100644 --- a/3rdparty/Sabre/DAV/FSExt/File.php +++ b/3rdparty/Sabre/DAV/FSExt/File.php @@ -9,15 +9,15 @@ * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ -class Sabre_DAV_FSExt_File extends Sabre_DAV_FSExt_Node implements Sabre_DAV_IFile { +class Sabre_DAV_FSExt_File extends Sabre_DAV_FSExt_Node implements Sabre_DAV_PartialUpdate_IFile { /** * Updates the data * * data is a readable stream resource. * - * @param resource $data - * @return void + * @param resource|string $data + * @return string */ public function put($data) { @@ -27,9 +27,33 @@ class Sabre_DAV_FSExt_File extends Sabre_DAV_FSExt_Node implements Sabre_DAV_IFi } /** + * Updates the data at a given offset + * + * The data argument is a readable stream resource. + * The offset argument is a 0-based offset where the data should be + * written. + * + * param resource|string $data + * @return void + */ + public function putRange($data, $offset) { + + $f = fopen($this->path, 'c'); + fseek($f,$offset-1); + if (is_string($data)) { + fwrite($f, $data); + } else { + stream_copy_to_stream($data,$f); + } + fclose($f); + return '"' . md5_file($this->path) . '"'; + + } + + /** * Returns the data * - * @return string + * @return resource */ public function get() { diff --git a/3rdparty/Sabre/DAV/FSExt/Node.php b/3rdparty/Sabre/DAV/FSExt/Node.php index 68ca06beb7e..68ca06beb7e 100755..100644 --- a/3rdparty/Sabre/DAV/FSExt/Node.php +++ b/3rdparty/Sabre/DAV/FSExt/Node.php diff --git a/3rdparty/Sabre/DAV/File.php b/3rdparty/Sabre/DAV/File.php index 3126bd8d364..3126bd8d364 100755..100644 --- a/3rdparty/Sabre/DAV/File.php +++ b/3rdparty/Sabre/DAV/File.php diff --git a/3rdparty/Sabre/DAV/ICollection.php b/3rdparty/Sabre/DAV/ICollection.php index 4626038a66e..94431a0c496 100755..100644 --- a/3rdparty/Sabre/DAV/ICollection.php +++ b/3rdparty/Sabre/DAV/ICollection.php @@ -19,7 +19,7 @@ interface Sabre_DAV_ICollection extends Sabre_DAV_INode { * Data will either be supplied as a stream resource, or in certain cases * as a string. Keep in mind that you may have to support either. * - * After succesful creation of the file, you may choose to return the ETag + * After successful creation of the file, you may choose to return the ETag * of the new file here. * * The returned ETag must be surrounded by double-quotes (The quotes should @@ -50,6 +50,9 @@ interface Sabre_DAV_ICollection extends Sabre_DAV_INode { /** * Returns a specific child node, referenced by its name * + * This method must throw Sabre_DAV_Exception_NotFound if the node does not + * exist. + * * @param string $name * @return Sabre_DAV_INode */ diff --git a/3rdparty/Sabre/DAV/IExtendedCollection.php b/3rdparty/Sabre/DAV/IExtendedCollection.php index 6ec345f9a62..6ec345f9a62 100755..100644 --- a/3rdparty/Sabre/DAV/IExtendedCollection.php +++ b/3rdparty/Sabre/DAV/IExtendedCollection.php diff --git a/3rdparty/Sabre/DAV/IFile.php b/3rdparty/Sabre/DAV/IFile.php index 478f822ae71..1eca8986a5c 100755..100644 --- a/3rdparty/Sabre/DAV/IFile.php +++ b/3rdparty/Sabre/DAV/IFile.php @@ -51,7 +51,7 @@ interface Sabre_DAV_IFile extends Sabre_DAV_INode { * * If null is returned, we'll assume application/octet-stream * - * @return void + * @return string|null */ function getContentType(); diff --git a/3rdparty/Sabre/DAV/INode.php b/3rdparty/Sabre/DAV/INode.php index c57d3923105..c57d3923105 100755..100644 --- a/3rdparty/Sabre/DAV/INode.php +++ b/3rdparty/Sabre/DAV/INode.php diff --git a/3rdparty/Sabre/DAV/IProperties.php b/3rdparty/Sabre/DAV/IProperties.php index 38eaab16dad..38eaab16dad 100755..100644 --- a/3rdparty/Sabre/DAV/IProperties.php +++ b/3rdparty/Sabre/DAV/IProperties.php diff --git a/3rdparty/Sabre/DAV/IQuota.php b/3rdparty/Sabre/DAV/IQuota.php index 3fe4c4eced4..3fe4c4eced4 100755..100644 --- a/3rdparty/Sabre/DAV/IQuota.php +++ b/3rdparty/Sabre/DAV/IQuota.php diff --git a/3rdparty/Sabre/DAV/Locks/Backend/Abstract.php b/3rdparty/Sabre/DAV/Locks/Backend/Abstract.php index 127e643a2b9..127e643a2b9 100755..100644 --- a/3rdparty/Sabre/DAV/Locks/Backend/Abstract.php +++ b/3rdparty/Sabre/DAV/Locks/Backend/Abstract.php diff --git a/3rdparty/Sabre/DAV/Locks/Backend/FS.php b/3rdparty/Sabre/DAV/Locks/Backend/FS.php index 02cab87fc82..02cab87fc82 100755..100644 --- a/3rdparty/Sabre/DAV/Locks/Backend/FS.php +++ b/3rdparty/Sabre/DAV/Locks/Backend/FS.php diff --git a/3rdparty/Sabre/DAV/Locks/Backend/File.php b/3rdparty/Sabre/DAV/Locks/Backend/File.php index c33f963514b..c33f963514b 100755..100644 --- a/3rdparty/Sabre/DAV/Locks/Backend/File.php +++ b/3rdparty/Sabre/DAV/Locks/Backend/File.php diff --git a/3rdparty/Sabre/DAV/Locks/Backend/PDO.php b/3rdparty/Sabre/DAV/Locks/Backend/PDO.php index acce80638ec..acce80638ec 100755..100644 --- a/3rdparty/Sabre/DAV/Locks/Backend/PDO.php +++ b/3rdparty/Sabre/DAV/Locks/Backend/PDO.php diff --git a/3rdparty/Sabre/DAV/Locks/LockInfo.php b/3rdparty/Sabre/DAV/Locks/LockInfo.php index 9df014a4281..9df014a4281 100755..100644 --- a/3rdparty/Sabre/DAV/Locks/LockInfo.php +++ b/3rdparty/Sabre/DAV/Locks/LockInfo.php diff --git a/3rdparty/Sabre/DAV/Locks/Plugin.php b/3rdparty/Sabre/DAV/Locks/Plugin.php index 035b3a63863..957ac506a9c 100755..100644 --- a/3rdparty/Sabre/DAV/Locks/Plugin.php +++ b/3rdparty/Sabre/DAV/Locks/Plugin.php @@ -152,6 +152,7 @@ class Sabre_DAV_Locks_Plugin extends Sabre_DAV_ServerPlugin { case 'MKCOL' : case 'PROPPATCH' : case 'PUT' : + case 'PATCH' : $lastLock = null; if (!$this->validateLock($uri,$lastLock)) throw new Sabre_DAV_Exception_Locked($lastLock); diff --git a/3rdparty/Sabre/DAV/Mount/Plugin.php b/3rdparty/Sabre/DAV/Mount/Plugin.php index b37a90ae993..b37a90ae993 100755..100644 --- a/3rdparty/Sabre/DAV/Mount/Plugin.php +++ b/3rdparty/Sabre/DAV/Mount/Plugin.php diff --git a/3rdparty/Sabre/DAV/Node.php b/3rdparty/Sabre/DAV/Node.php index 070b7176afd..3b95dfec2fa 100755..100644 --- a/3rdparty/Sabre/DAV/Node.php +++ b/3rdparty/Sabre/DAV/Node.php @@ -27,7 +27,7 @@ abstract class Sabre_DAV_Node implements Sabre_DAV_INode { } /** - * Deleted the current node + * Deletes the current node * * @throws Sabre_DAV_Exception_Forbidden * @return void diff --git a/3rdparty/Sabre/DAV/ObjectTree.php b/3rdparty/Sabre/DAV/ObjectTree.php index bce51463900..3b7f222d64b 100755..100644 --- a/3rdparty/Sabre/DAV/ObjectTree.php +++ b/3rdparty/Sabre/DAV/ObjectTree.php @@ -51,24 +51,30 @@ class Sabre_DAV_ObjectTree extends Sabre_DAV_Tree { $path = trim($path,'/'); if (isset($this->cache[$path])) return $this->cache[$path]; - //if (!$path || $path=='.') return $this->rootNode; - $currentNode = $this->rootNode; + // Is it the root node? + if (!strlen($path)) { + return $this->rootNode; + } - // We're splitting up the path variable into folder/subfolder components and traverse to the correct node.. - foreach(explode('/',$path) as $pathPart) { + // Attempting to fetch its parent + list($parentName, $baseName) = Sabre_DAV_URLUtil::splitPath($path); - // If this part of the path is just a dot, it actually means we can skip it - if ($pathPart=='.' || $pathPart=='') continue; + // If there was no parent, we must simply ask it from the root node. + if ($parentName==="") { + $node = $this->rootNode->getChild($baseName); + } else { + // Otherwise, we recursively grab the parent and ask him/her. + $parent = $this->getNodeForPath($parentName); - if (!($currentNode instanceof Sabre_DAV_ICollection)) + if (!($parent instanceof Sabre_DAV_ICollection)) throw new Sabre_DAV_Exception_NotFound('Could not find node at path: ' . $path); - $currentNode = $currentNode->getChild($pathPart); + $node = $parent->getChild($baseName); } - $this->cache[$path] = $currentNode; - return $currentNode; + $this->cache[$path] = $node; + return $node; } diff --git a/3rdparty/Sabre/DAV/PartialUpdate/IFile.php b/3rdparty/Sabre/DAV/PartialUpdate/IFile.php new file mode 100644 index 00000000000..cf5ad55c6de --- /dev/null +++ b/3rdparty/Sabre/DAV/PartialUpdate/IFile.php @@ -0,0 +1,38 @@ +<?php + +/** + * This interface provides a way to modify only part of a target resource + * It may be used to update a file chunk, upload big a file into smaller + * chunks or resume an upload + * + * @package Sabre + * @subpackage DAV + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. + * @author Jean-Tiare LE BIGOT (http://www.jtlebi.fr/) + * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License + */ +interface Sabre_DAV_PartialUpdate_IFile extends Sabre_DAV_IFile { + + /** + * Updates the data at a given offset + * + * The data argument is a readable stream resource. + * The offset argument is an integer describing the offset. Contrary to + * what's sent in the request, the offset here is a 0-based index. + * + * After a successful put operation, you may choose to return an ETag. The + * etag must always be surrounded by double-quotes. These quotes must + * appear in the actual string you're returning. + * + * Clients may use the ETag from a PUT request to later on make sure that + * when they update the file, the contents haven't changed in the mean + * time. + * + * @param resource $data + * @param integer $offset + * @return string|null + */ + function putRange($data, $offset); + +} + diff --git a/3rdparty/Sabre/DAV/PartialUpdate/Plugin.php b/3rdparty/Sabre/DAV/PartialUpdate/Plugin.php new file mode 100644 index 00000000000..170acbc9785 --- /dev/null +++ b/3rdparty/Sabre/DAV/PartialUpdate/Plugin.php @@ -0,0 +1,209 @@ +<?php +/** + * Partial update plugin (Patch method) + * + * This plugin provides a way to modify only part of a target resource + * It may bu used to update a file chunk, upload big a file into smaller + * chunks or resume an upload. + * + * $patchPlugin = new Sabre_DAV_Patch_Plugin(); + * $server->addPlugin($patchPlugin); + * + * @package Sabre + * @subpackage DAV + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. + * @author Jean-Tiare LE BIGOT (http://www.jtlebi.fr/) + * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License + */ +class Sabre_DAV_PartialUpdate_Plugin extends Sabre_DAV_ServerPlugin { + + /** + * Reference to server + * + * @var Sabre_DAV_Server + */ + protected $server; + + /** + * Initializes the plugin + * + * This method is automatically called by the Server class after addPlugin. + * + * @param Sabre_DAV_Server $server + * @return void + */ + public function initialize(Sabre_DAV_Server $server) { + + $this->server = $server; + $server->subscribeEvent('unknownMethod',array($this,'unknownMethod')); + + } + + /** + * 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 'partialupdate'; + + } + + /** + * This method is called by the Server if the user used an HTTP method + * the server didn't recognize. + * + * This plugin intercepts the PATCH methods. + * + * @param string $method + * @param string $uri + * @return bool|null + */ + public function unknownMethod($method, $uri) { + + switch($method) { + + case 'PATCH': + return $this->httpPatch($uri); + + } + + } + + /** + * 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. + * + * We claim to support PATCH method (partial update) if and only if + * - the node exist + * - the node implements our partial update interface + * + * @param string $uri + * @return array + */ + public function getHTTPMethods($uri) { + + $tree = $this->server->tree; + + if ($tree->nodeExists($uri) && + $tree->getNodeForPath($uri) instanceof Sabre_DAV_PartialUpdate_IFile) { + return array('PATCH'); + } + + return array(); + + } + + /** + * Returns a list of features for the HTTP OPTIONS Dav: header. + * + * @return array + */ + public function getFeatures() { + + return array('sabredav-partialupdate'); + + } + + /** + * Patch an uri + * + * The WebDAV patch request can be used to modify only a part of an + * existing resource. If the resource does not exist yet and the first + * offset is not 0, the request fails + * + * @param string $uri + * @return void + */ + protected function httpPatch($uri) { + + // Get the node. Will throw a 404 if not found + $node = $this->server->tree->getNodeForPath($uri); + if (!($node instanceof Sabre_DAV_PartialUpdate_IFile)) { + throw new Sabre_DAV_Exception_MethodNotAllowed('The target resource does not support the PATCH method.'); + } + + $range = $this->getHTTPUpdateRange(); + + if (!$range) { + throw new Sabre_DAV_Exception_BadRequest('No valid "X-Update-Range" found in the headers'); + } + + $contentType = strtolower( + $this->server->httpRequest->getHeader('Content-Type') + ); + + if ($contentType != 'application/x-sabredav-partialupdate') { + throw new Sabre_DAV_Exception_UnsupportedMediaType('Unknown Content-Type header "' . $contentType . '"'); + } + + $len = $this->server->httpRequest->getHeader('Content-Length'); + + // Load the begin and end data + $start = ($range[0])?$range[0]:0; + $end = ($range[1])?$range[1]:$len-1; + + // Check consistency + if($end < $start) + throw new Sabre_DAV_Exception_RequestedRangeNotSatisfiable('The end offset (' . $range[1] . ') is lower than the start offset (' . $range[0] . ')'); + if($end - $start + 1 != $len) + throw new Sabre_DAV_Exception_RequestedRangeNotSatisfiable('Actual data length (' . $len . ') is not consistent with begin (' . $range[0] . ') and end (' . $range[1] . ') offsets'); + + // Checking If-None-Match and related headers. + if (!$this->server->checkPreconditions()) return; + + if (!$this->server->broadcastEvent('beforeWriteContent',array($uri, $node, null))) + return; + + $body = $this->server->httpRequest->getBody(); + $etag = $node->putRange($body, $start-1); + + $this->server->broadcastEvent('afterWriteContent',array($uri, $node)); + + $this->server->httpResponse->setHeader('Content-Length','0'); + if ($etag) $this->server->httpResponse->setHeader('ETag',$etag); + $this->server->httpResponse->sendStatus(204); + + return false; + + } + + /** + * Returns the HTTP custom range update header + * + * This method returns null if there is no well-formed HTTP range request + * header or array($start, $end). + * + * The first number is the offset of the first byte in the range. + * The second number is the offset of the last byte in the range. + * + * If the second offset is null, it should be treated as the offset of the last byte of the entity + * If the first offset is null, the second offset should be used to retrieve the last x bytes of the entity + * + * @return array|null + */ + public function getHTTPUpdateRange() { + + $range = $this->server->httpRequest->getHeader('X-Update-Range'); + if (is_null($range)) return null; + + // Matching "Range: bytes=1234-5678: both numbers are optional + + if (!preg_match('/^bytes=([0-9]*)-([0-9]*)$/i',$range,$matches)) return null; + + if ($matches[1]==='' && $matches[2]==='') return null; + + return array( + $matches[1]!==''?$matches[1]:null, + $matches[2]!==''?$matches[2]:null, + ); + + } +} diff --git a/3rdparty/Sabre/DAV/Property.php b/3rdparty/Sabre/DAV/Property.php index 1cfada3236c..6487bf44bc4 100755..100644 --- a/3rdparty/Sabre/DAV/Property.php +++ b/3rdparty/Sabre/DAV/Property.php @@ -11,10 +11,16 @@ * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ -abstract class Sabre_DAV_Property { - - abstract function serialize(Sabre_DAV_Server $server, DOMElement $prop); +abstract class Sabre_DAV_Property implements Sabre_DAV_PropertyInterface { + /** + * Unserializes the property. + * + * This static method should return a an instance of this object. + * + * @param DOMElement $prop + * @return Sabre_DAV_IProperty + */ static function unserialize(DOMElement $prop) { throw new Sabre_DAV_Exception('Unserialize has not been implemented for this class'); diff --git a/3rdparty/Sabre/DAV/Property/GetLastModified.php b/3rdparty/Sabre/DAV/Property/GetLastModified.php index bd63f573140..bd63f573140 100755..100644 --- a/3rdparty/Sabre/DAV/Property/GetLastModified.php +++ b/3rdparty/Sabre/DAV/Property/GetLastModified.php diff --git a/3rdparty/Sabre/DAV/Property/Href.php b/3rdparty/Sabre/DAV/Property/Href.php index dac564f24d7..cd1d867f71b 100755..100644 --- a/3rdparty/Sabre/DAV/Property/Href.php +++ b/3rdparty/Sabre/DAV/Property/Href.php @@ -82,7 +82,7 @@ class Sabre_DAV_Property_Href extends Sabre_DAV_Property implements Sabre_DAV_Pr */ static function unserialize(DOMElement $dom) { - if (Sabre_DAV_XMLUtil::toClarkNotation($dom->firstChild)==='{DAV:}href') { + if ($dom->firstChild && Sabre_DAV_XMLUtil::toClarkNotation($dom->firstChild)==='{DAV:}href') { return new self($dom->firstChild->textContent,false); } diff --git a/3rdparty/Sabre/DAV/Property/HrefList.php b/3rdparty/Sabre/DAV/Property/HrefList.php index 7a52272e885..282c452ca0b 100755..100644 --- a/3rdparty/Sabre/DAV/Property/HrefList.php +++ b/3rdparty/Sabre/DAV/Property/HrefList.php @@ -79,7 +79,7 @@ class Sabre_DAV_Property_HrefList extends Sabre_DAV_Property { * It will only decode {DAV:}href values. * * @param DOMElement $dom - * @return Sabre_DAV_Property_Href + * @return Sabre_DAV_Property_HrefList */ static function unserialize(DOMElement $dom) { diff --git a/3rdparty/Sabre/DAV/Property/IHref.php b/3rdparty/Sabre/DAV/Property/IHref.php index 5c0409064cb..5c0409064cb 100755..100644 --- a/3rdparty/Sabre/DAV/Property/IHref.php +++ b/3rdparty/Sabre/DAV/Property/IHref.php diff --git a/3rdparty/Sabre/DAV/Property/LockDiscovery.php b/3rdparty/Sabre/DAV/Property/LockDiscovery.php index 2ded5649a44..2ded5649a44 100755..100644 --- a/3rdparty/Sabre/DAV/Property/LockDiscovery.php +++ b/3rdparty/Sabre/DAV/Property/LockDiscovery.php diff --git a/3rdparty/Sabre/DAV/Property/ResourceType.php b/3rdparty/Sabre/DAV/Property/ResourceType.php index f6269611e54..f6269611e54 100755..100644 --- a/3rdparty/Sabre/DAV/Property/ResourceType.php +++ b/3rdparty/Sabre/DAV/Property/ResourceType.php diff --git a/3rdparty/Sabre/DAV/Property/Response.php b/3rdparty/Sabre/DAV/Property/Response.php index 88afbcfb26d..9f21163d12e 100755..100644 --- a/3rdparty/Sabre/DAV/Property/Response.php +++ b/3rdparty/Sabre/DAV/Property/Response.php @@ -138,7 +138,7 @@ class Sabre_DAV_Property_Response extends Sabre_DAV_Property implements Sabre_DA if (is_scalar($propertyValue)) { $text = $document->createTextNode($propertyValue); $currentProperty->appendChild($text); - } elseif ($propertyValue instanceof Sabre_DAV_Property) { + } elseif ($propertyValue instanceof Sabre_DAV_PropertyInterface) { $propertyValue->serialize($server,$currentProperty); } elseif (!is_null($propertyValue)) { throw new Sabre_DAV_Exception('Unknown property value type: ' . gettype($propertyValue) . ' for property: ' . $propertyName); diff --git a/3rdparty/Sabre/DAV/Property/ResponseList.php b/3rdparty/Sabre/DAV/Property/ResponseList.php index cae923afbf9..cae923afbf9 100755..100644 --- a/3rdparty/Sabre/DAV/Property/ResponseList.php +++ b/3rdparty/Sabre/DAV/Property/ResponseList.php diff --git a/3rdparty/Sabre/DAV/Property/SupportedLock.php b/3rdparty/Sabre/DAV/Property/SupportedLock.php index 4e3aaf23a1a..4e3aaf23a1a 100755..100644 --- a/3rdparty/Sabre/DAV/Property/SupportedLock.php +++ b/3rdparty/Sabre/DAV/Property/SupportedLock.php diff --git a/3rdparty/Sabre/DAV/Property/SupportedReportSet.php b/3rdparty/Sabre/DAV/Property/SupportedReportSet.php index e62699f3b5a..e62699f3b5a 100755..100644 --- a/3rdparty/Sabre/DAV/Property/SupportedReportSet.php +++ b/3rdparty/Sabre/DAV/Property/SupportedReportSet.php diff --git a/3rdparty/Sabre/DAV/PropertyInterface.php b/3rdparty/Sabre/DAV/PropertyInterface.php new file mode 100644 index 00000000000..515072cbd35 --- /dev/null +++ b/3rdparty/Sabre/DAV/PropertyInterface.php @@ -0,0 +1,21 @@ +<?php + +/** + * PropertyInterface + * + * Implement this interface to create new complex properties + * + * @package Sabre + * @subpackage DAV + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. + * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License + */ +interface Sabre_DAV_PropertyInterface { + + public function serialize(Sabre_DAV_Server $server, DOMElement $prop); + + static function unserialize(DOMElement $prop); + +} + diff --git a/3rdparty/Sabre/DAV/Server.php b/3rdparty/Sabre/DAV/Server.php index 0dfac8b0c71..cf6f87e68ab 100755..100644 --- a/3rdparty/Sabre/DAV/Server.php +++ b/3rdparty/Sabre/DAV/Server.php @@ -12,7 +12,7 @@ class Sabre_DAV_Server { /** - * Inifinity is used for some request supporting the HTTP Depth header and indicates that the operation should traverse the entire tree + * Infinity is used for some request supporting the HTTP Depth header and indicates that the operation should traverse the entire tree */ const DEPTH_INFINITY = -1; @@ -102,7 +102,6 @@ class Sabre_DAV_Server { '{DAV:}getetag', '{DAV:}getlastmodified', '{DAV:}lockdiscovery', - '{DAV:}resourcetype', '{DAV:}supportedlock', // RFC4331 @@ -162,7 +161,7 @@ class Sabre_DAV_Server { * If an array is passed, we automatically create a root node, and use * the nodes in the array as top-level children. * - * @param Sabre_DAV_Tree|Sabre_DAV_INode|null $treeOrNode The tree object + * @param Sabre_DAV_Tree|Sabre_DAV_INode|array|null $treeOrNode The tree object */ public function __construct($treeOrNode = null) { @@ -207,6 +206,10 @@ class Sabre_DAV_Server { } catch (Exception $e) { + try { + $this->broadcastEvent('exception', array($e)); + } catch (Exception $ignore) { + } $DOM = new DOMDocument('1.0','utf-8'); $DOM->formatOutput = true; @@ -214,17 +217,23 @@ class Sabre_DAV_Server { $error->setAttribute('xmlns:s',self::NS_SABREDAV); $DOM->appendChild($error); - $error->appendChild($DOM->createElement('s:exception',get_class($e))); - $error->appendChild($DOM->createElement('s:message',$e->getMessage())); + $h = function($v) { + + return htmlspecialchars($v, ENT_NOQUOTES, 'UTF-8'); + + }; + + $error->appendChild($DOM->createElement('s:exception',$h(get_class($e)))); + $error->appendChild($DOM->createElement('s:message',$h($e->getMessage()))); if ($this->debugExceptions) { - $error->appendChild($DOM->createElement('s:file',$e->getFile())); - $error->appendChild($DOM->createElement('s:line',$e->getLine())); - $error->appendChild($DOM->createElement('s:code',$e->getCode())); - $error->appendChild($DOM->createElement('s:stacktrace',$e->getTraceAsString())); + $error->appendChild($DOM->createElement('s:file',$h($e->getFile()))); + $error->appendChild($DOM->createElement('s:line',$h($e->getLine()))); + $error->appendChild($DOM->createElement('s:code',$h($e->getCode()))); + $error->appendChild($DOM->createElement('s:stacktrace',$h($e->getTraceAsString()))); } if (self::$exposeVersion) { - $error->appendChild($DOM->createElement('s:sabredav-version',Sabre_DAV_Version::VERSION)); + $error->appendChild($DOM->createElement('s:sabredav-version',$h(Sabre_DAV_Version::VERSION))); } if($e instanceof Sabre_DAV_Exception) { @@ -508,7 +517,7 @@ class Sabre_DAV_Server { if (!$this->checkPreconditions(true)) return false; - if (!($node instanceof Sabre_DAV_IFile)) throw new Sabre_DAV_Exception_NotImplemented('GET is only implemented on File objects'); + if (!$node instanceof Sabre_DAV_IFile) throw new Sabre_DAV_Exception_NotImplemented('GET is only implemented on File objects'); $body = $node->get(); // Converting string into stream, if needed. @@ -696,6 +705,7 @@ class Sabre_DAV_Server { // This is a multi-status response $this->httpResponse->sendStatus(207); $this->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8'); + $this->httpResponse->setHeader('Vary','Brief,Prefer'); // Normally this header is only needed for OPTIONS responses, however.. // iCal seems to also depend on these being set for PROPFIND. Since @@ -704,7 +714,10 @@ class Sabre_DAV_Server { foreach($this->plugins as $plugin) $features = array_merge($features,$plugin->getFeatures()); $this->httpResponse->setHeader('DAV',implode(', ',$features)); - $data = $this->generateMultiStatus($newProperties); + $prefer = $this->getHTTPPrefer(); + $minimal = $prefer['return-minimal']; + + $data = $this->generateMultiStatus($newProperties, $minimal); $this->httpResponse->sendBody($data); } @@ -724,6 +737,30 @@ class Sabre_DAV_Server { $result = $this->updateProperties($uri, $newProperties); + $prefer = $this->getHTTPPrefer(); + $this->httpResponse->setHeader('Vary','Brief,Prefer'); + + if ($prefer['return-minimal']) { + + // If return-minimal is specified, we only have to check if the + // request was succesful, and don't need to return the + // multi-status. + $ok = true; + foreach($result as $code=>$prop) { + if ((int)$code > 299) { + $ok = false; + } + } + + if ($ok) { + + $this->httpResponse->sendStatus(204); + return; + + } + + } + $this->httpResponse->sendStatus(207); $this->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8'); @@ -927,7 +964,7 @@ class Sabre_DAV_Server { * This method moves one uri to a different uri. A lot of the actual request processing is done in getCopyMoveInfo * * @param string $uri - * @return void + * @return bool */ protected function httpMove($uri) { @@ -1009,7 +1046,7 @@ class Sabre_DAV_Server { if ($this->broadcastEvent('report',array($reportName,$dom, $uri))) { // If broadcastEvent returned true, it means the report was not supported - throw new Sabre_DAV_Exception_ReportNotImplemented(); + throw new Sabre_DAV_Exception_ReportNotSupported(); } @@ -1158,6 +1195,85 @@ class Sabre_DAV_Server { } + /** + * Returns the HTTP Prefer header information. + * + * The prefer header is defined in: + * http://tools.ietf.org/html/draft-snell-http-prefer-14 + * + * This method will return an array with options. + * + * Currently, the following options may be returned: + * array( + * 'return-asynch' => true, + * 'return-minimal' => true, + * 'return-representation' => true, + * 'wait' => 30, + * 'strict' => true, + * 'lenient' => true, + * ) + * + * This method also supports the Brief header, and will also return + * 'return-minimal' if the brief header was set to 't'. + * + * For the boolean options, false will be returned if the headers are not + * specified. For the integer options it will be 'null'. + * + * @return array + */ + public function getHTTPPrefer() { + + $result = array( + 'return-asynch' => false, + 'return-minimal' => false, + 'return-representation' => false, + 'wait' => null, + 'strict' => false, + 'lenient' => false, + ); + + if ($prefer = $this->httpRequest->getHeader('Prefer')) { + + $parameters = array_map('trim', + explode(',', $prefer) + ); + + foreach($parameters as $parameter) { + + // Right now our regex only supports the tokens actually + // specified in the draft. We may need to expand this if new + // tokens get registered. + if(!preg_match('/^(?P<token>[a-z0-9-]+)(?:=(?P<value>[0-9]+))?$/', $parameter, $matches)) { + continue; + } + + switch($matches['token']) { + + case 'return-asynch' : + case 'return-minimal' : + case 'return-representation' : + case 'strict' : + case 'lenient' : + $result[$matches['token']] = true; + break; + case 'wait' : + $result[$matches['token']] = $matches['value']; + break; + + } + + } + + } + + if ($this->httpRequest->getHeader('Brief')=='t') { + $result['return-minimal'] = true; + } + + return $result; + + } + /** * Returns information about Copy and Move requests @@ -1433,15 +1549,18 @@ class Sabre_DAV_Server { } - $this->broadcastEvent('afterGetProperties',array(trim($myPath,'/'),&$newProperties)); + $this->broadcastEvent('afterGetProperties',array(trim($myPath,'/'),&$newProperties, $node)); $newProperties['href'] = trim($myPath,'/'); // Its is a WebDAV recommendation to add a trailing slash to collectionnames. - // Apple's iCal also requires a trailing slash for principals (rfc 3744). - // Therefore we add a trailing / for any non-file. This might need adjustments - // if we find there are other edge cases. - if ($myPath!='' && isset($newProperties[200]['{DAV:}resourcetype']) && count($newProperties[200]['{DAV:}resourcetype']->getValue())>0) $newProperties['href'] .='/'; + // Apple's iCal also requires a trailing slash for principals (rfc 3744), though this is non-standard. + if ($myPath!='' && isset($newProperties[200]['{DAV:}resourcetype'])) { + $rt = $newProperties[200]['{DAV:}resourcetype']; + if ($rt->is('{DAV:}collection') || $rt->is('{DAV:}principal')) { + $newProperties['href'] .='/'; + } + } // If the resourcetype property was manually added to the requested property list, // we will remove it again. @@ -1476,11 +1595,14 @@ class Sabre_DAV_Server { if (!$this->broadcastEvent('beforeBind',array($uri))) return false; $parent = $this->tree->getNodeForPath($dir); + if (!$parent instanceof Sabre_DAV_ICollection) { + throw new Sabre_DAV_Exception_Conflict('Files can only be created as children of collections'); + } if (!$this->broadcastEvent('beforeCreateFile',array($uri, &$data, $parent))) return false; $etag = $parent->createFile($name,$data); - $this->tree->markDirty($dir); + $this->tree->markDirty($dir . '/' . $name); $this->broadcastEvent('afterBind',array($uri)); $this->broadcastEvent('afterCreateFile',array($uri, $parent)); @@ -1901,12 +2023,15 @@ class Sabre_DAV_Server { /** - * Generates a WebDAV propfind response body based on a list of nodes + * Generates a WebDAV propfind response body based on a list of nodes. + * + * If 'strip404s' is set to true, all 404 responses will be removed. * * @param array $fileProperties The list with nodes + * @param bool strip404s * @return string */ - public function generateMultiStatus(array $fileProperties) { + public function generateMultiStatus(array $fileProperties, $strip404s = false) { $dom = new DOMDocument('1.0','utf-8'); //$dom->formatOutput = true; @@ -1925,6 +2050,10 @@ class Sabre_DAV_Server { $href = $entry['href']; unset($entry['href']); + if ($strip404s && isset($entry[404])) { + unset($entry[404]); + } + $response = new Sabre_DAV_Property_Response($href,$entry); $response->serialize($this,$multiStatus); @@ -1995,7 +2124,7 @@ class Sabre_DAV_Server { if (!$body) return array(); $dom = Sabre_DAV_XMLUtil::loadDOMDocument($body); - $elem = $dom->getElementsByTagNameNS('urn:DAV','propfind')->item(0); + $elem = $dom->getElementsByTagNameNS('DAV:','propfind')->item(0); return array_keys(Sabre_DAV_XMLUtil::parseProperties($elem)); } diff --git a/3rdparty/Sabre/DAV/ServerPlugin.php b/3rdparty/Sabre/DAV/ServerPlugin.php index 131863d13fb..120569ffccd 100755..100644 --- a/3rdparty/Sabre/DAV/ServerPlugin.php +++ b/3rdparty/Sabre/DAV/ServerPlugin.php @@ -19,7 +19,7 @@ abstract class Sabre_DAV_ServerPlugin { * This function is called by Sabre_DAV_Server, after * addPlugin is called. * - * This method should set up the requires event subscriptions. + * This method should set up the required event subscriptions. * * @param Sabre_DAV_Server $server * @return void diff --git a/3rdparty/Sabre/DAV/SimpleCollection.php b/3rdparty/Sabre/DAV/SimpleCollection.php index 4acf971caa5..79e2eaaacd4 100755..100644 --- a/3rdparty/Sabre/DAV/SimpleCollection.php +++ b/3rdparty/Sabre/DAV/SimpleCollection.php @@ -31,7 +31,7 @@ class Sabre_DAV_SimpleCollection extends Sabre_DAV_Collection { /** * Creates this node * - * The name of the node must be passed, child nodes can also be bassed. + * The name of the node must be passed, child nodes can also be passed. * This nodes must be instances of Sabre_DAV_INode * * @param string $name @@ -78,6 +78,9 @@ class Sabre_DAV_SimpleCollection extends Sabre_DAV_Collection { * This method makes use of the getChildren method to grab all the child nodes, and compares the name. * Generally its wise to override this, as this can usually be optimized * + * This method must throw Sabre_DAV_Exception_NotFound if the node does not + * exist. + * * @param string $name * @throws Sabre_DAV_Exception_NotFound * @return Sabre_DAV_INode diff --git a/3rdparty/Sabre/DAV/SimpleDirectory.php b/3rdparty/Sabre/DAV/SimpleDirectory.php deleted file mode 100755 index 621222ebc53..00000000000 --- a/3rdparty/Sabre/DAV/SimpleDirectory.php +++ /dev/null @@ -1,21 +0,0 @@ -<?php - -/** - * SimpleDirectory - * - * The SimpleDirectory is used to quickly setup static directory structures. - * Just create the object with a proper name, and add children to use it. - * - * This class is now deprecated, use Sabre_DAV_SimpleCollection instead. - * - * @package Sabre - * @subpackage DAV - * @deprecated Use Sabre_DAV_SimpleCollection instead. - * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. - * @author Evert Pot (http://www.rooftopsolutions.nl/) - * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License - */ -class Sabre_DAV_SimpleDirectory extends Sabre_DAV_SimpleCollection { - -} - diff --git a/3rdparty/Sabre/DAV/SimpleFile.php b/3rdparty/Sabre/DAV/SimpleFile.php index 58330d6861d..58330d6861d 100755..100644 --- a/3rdparty/Sabre/DAV/SimpleFile.php +++ b/3rdparty/Sabre/DAV/SimpleFile.php diff --git a/3rdparty/Sabre/DAV/StringUtil.php b/3rdparty/Sabre/DAV/StringUtil.php index b126a94c825..b126a94c825 100755..100644 --- a/3rdparty/Sabre/DAV/StringUtil.php +++ b/3rdparty/Sabre/DAV/StringUtil.php diff --git a/3rdparty/Sabre/DAV/TemporaryFileFilterPlugin.php b/3rdparty/Sabre/DAV/TemporaryFileFilterPlugin.php index 36096e67775..36096e67775 100755..100644 --- a/3rdparty/Sabre/DAV/TemporaryFileFilterPlugin.php +++ b/3rdparty/Sabre/DAV/TemporaryFileFilterPlugin.php diff --git a/3rdparty/Sabre/DAV/Tree.php b/3rdparty/Sabre/DAV/Tree.php index 50216394155..50216394155 100755..100644 --- a/3rdparty/Sabre/DAV/Tree.php +++ b/3rdparty/Sabre/DAV/Tree.php diff --git a/3rdparty/Sabre/DAV/Tree/Filesystem.php b/3rdparty/Sabre/DAV/Tree/Filesystem.php index 40580ae366f..40580ae366f 100755..100644 --- a/3rdparty/Sabre/DAV/Tree/Filesystem.php +++ b/3rdparty/Sabre/DAV/Tree/Filesystem.php diff --git a/3rdparty/Sabre/DAV/URLUtil.php b/3rdparty/Sabre/DAV/URLUtil.php index 794665a44f6..794665a44f6 100755..100644 --- a/3rdparty/Sabre/DAV/URLUtil.php +++ b/3rdparty/Sabre/DAV/URLUtil.php diff --git a/3rdparty/Sabre/DAV/UUIDUtil.php b/3rdparty/Sabre/DAV/UUIDUtil.php index f0eebe598e5..f0eebe598e5 100755..100644 --- a/3rdparty/Sabre/DAV/UUIDUtil.php +++ b/3rdparty/Sabre/DAV/UUIDUtil.php diff --git a/3rdparty/Sabre/DAV/Version.php b/3rdparty/Sabre/DAV/Version.php index 274646240ab..55611cf2148 100755..100644 --- a/3rdparty/Sabre/DAV/Version.php +++ b/3rdparty/Sabre/DAV/Version.php @@ -14,7 +14,7 @@ class Sabre_DAV_Version { /** * Full version number */ - const VERSION = '1.6.4'; + const VERSION = '1.7.1'; /** * Stability : alpha, beta, stable diff --git a/3rdparty/Sabre/DAV/XMLUtil.php b/3rdparty/Sabre/DAV/XMLUtil.php index 60eff3b159a..712fa3fc014 100755..100644 --- a/3rdparty/Sabre/DAV/XMLUtil.php +++ b/3rdparty/Sabre/DAV/XMLUtil.php @@ -20,9 +20,6 @@ class Sabre_DAV_XMLUtil { * {http://www.example.org}myelem * * This format is used throughout the SabreDAV sourcecode. - * Elements encoded with the urn:DAV namespace will - * be returned as if they were in the DAV: namespace. This is to avoid - * compatibility problems. * * This function will return null if a nodetype other than an Element is passed. * @@ -33,8 +30,7 @@ class Sabre_DAV_XMLUtil { if ($dom->nodeType !== XML_ELEMENT_NODE) return null; - // Mapping back to the real namespace, in case it was dav - if ($dom->namespaceURI=='urn:DAV') $ns = 'DAV:'; else $ns = $dom->namespaceURI; + $ns = $dom->namespaceURI; // Mapping to clark notation return '{' . $ns . '}' . $dom->localName; @@ -65,28 +61,10 @@ class Sabre_DAV_XMLUtil { } /** - * This method takes an XML document (as string) and converts all instances of the - * DAV: namespace to urn:DAV - * - * This is unfortunately needed, because the DAV: namespace violates the xml namespaces - * spec, and causes the DOM to throw errors - * - * @param string $xmlDocument - * @return array|string|null - */ - static function convertDAVNamespace($xmlDocument) { - - // This is used to map the DAV: namespace to urn:DAV. This is needed, because the DAV: - // namespace is actually a violation of the XML namespaces specification, and will cause errors - return preg_replace("/xmlns(:[A-Za-z0-9_]*)?=(\"|\')DAV:(\\2)/","xmlns\\1=\\2urn:DAV\\2",$xmlDocument); - - } - - /** * This method provides a generic way to load a DOMDocument for WebDAV use. * * This method throws a Sabre_DAV_Exception_BadRequest exception for any xml errors. - * It does not preserve whitespace, and it converts the DAV: namespace to urn:DAV. + * It does not preserve whitespace. * * @param string $xml * @throws Sabre_DAV_Exception_BadRequest @@ -118,10 +96,11 @@ class Sabre_DAV_XMLUtil { libxml_clear_errors(); $dom = new DOMDocument(); - $dom->loadXML(self::convertDAVNamespace($xml),LIBXML_NOWARNING | LIBXML_NOERROR); // We don't generally care about any whitespace $dom->preserveWhiteSpace = false; + + $dom->loadXML($xml,LIBXML_NOWARNING | LIBXML_NOERROR); if ($error = libxml_get_last_error()) { libxml_clear_errors(); diff --git a/3rdparty/Sabre/DAV/includes.php b/3rdparty/Sabre/DAV/includes.php index 6a4890677ea..6728f88ce76 100755..100644 --- a/3rdparty/Sabre/DAV/includes.php +++ b/3rdparty/Sabre/DAV/includes.php @@ -28,7 +28,7 @@ include __DIR__ . '/Locks/Backend/PDO.php'; include __DIR__ . '/Locks/LockInfo.php'; include __DIR__ . '/Node.php'; include __DIR__ . '/Property/IHref.php'; -include __DIR__ . '/Property.php'; +include __DIR__ . '/PropertyInterface.php'; include __DIR__ . '/Server.php'; include __DIR__ . '/ServerPlugin.php'; include __DIR__ . '/StringUtil.php'; @@ -60,7 +60,7 @@ include __DIR__ . '/Exception/NotFound.php'; include __DIR__ . '/Exception/NotImplemented.php'; include __DIR__ . '/Exception/PaymentRequired.php'; include __DIR__ . '/Exception/PreconditionFailed.php'; -include __DIR__ . '/Exception/ReportNotImplemented.php'; +include __DIR__ . '/Exception/ReportNotSupported.php'; include __DIR__ . '/Exception/RequestedRangeNotSatisfiable.php'; include __DIR__ . '/Exception/UnsupportedMediaType.php'; include __DIR__ . '/FS/Node.php'; @@ -72,18 +72,11 @@ include __DIR__ . '/IQuota.php'; include __DIR__ . '/Locks/Plugin.php'; include __DIR__ . '/Mount/Plugin.php'; include __DIR__ . '/ObjectTree.php'; -include __DIR__ . '/Property/GetLastModified.php'; -include __DIR__ . '/Property/Href.php'; -include __DIR__ . '/Property/HrefList.php'; -include __DIR__ . '/Property/LockDiscovery.php'; -include __DIR__ . '/Property/ResourceType.php'; -include __DIR__ . '/Property/Response.php'; -include __DIR__ . '/Property/ResponseList.php'; -include __DIR__ . '/Property/SupportedLock.php'; -include __DIR__ . '/Property/SupportedReportSet.php'; +include __DIR__ . '/PartialUpdate/IFile.php'; +include __DIR__ . '/PartialUpdate/Plugin.php'; +include __DIR__ . '/Property.php'; include __DIR__ . '/Tree/Filesystem.php'; include __DIR__ . '/Collection.php'; -include __DIR__ . '/Directory.php'; include __DIR__ . '/Exception/ConflictingLock.php'; include __DIR__ . '/Exception/FileNotFound.php'; include __DIR__ . '/File.php'; @@ -91,7 +84,15 @@ include __DIR__ . '/FS/Directory.php'; include __DIR__ . '/FS/File.php'; include __DIR__ . '/FSExt/Directory.php'; include __DIR__ . '/FSExt/File.php'; +include __DIR__ . '/Property/GetLastModified.php'; +include __DIR__ . '/Property/Href.php'; +include __DIR__ . '/Property/HrefList.php'; +include __DIR__ . '/Property/LockDiscovery.php'; +include __DIR__ . '/Property/ResourceType.php'; +include __DIR__ . '/Property/Response.php'; +include __DIR__ . '/Property/ResponseList.php'; +include __DIR__ . '/Property/SupportedLock.php'; +include __DIR__ . '/Property/SupportedReportSet.php'; include __DIR__ . '/SimpleCollection.php'; -include __DIR__ . '/SimpleDirectory.php'; include __DIR__ . '/SimpleFile.php'; // End includes diff --git a/3rdparty/Sabre/DAVACL/AbstractPrincipalCollection.php b/3rdparty/Sabre/DAVACL/AbstractPrincipalCollection.php index e05b7749805..f67eadad6e9 100755..100644 --- a/3rdparty/Sabre/DAVACL/AbstractPrincipalCollection.php +++ b/3rdparty/Sabre/DAVACL/AbstractPrincipalCollection.php @@ -107,7 +107,7 @@ abstract class Sabre_DAVACL_AbstractPrincipalCollection extends Sabre_DAV_Collec * * @param string $name * @throws Sabre_DAV_Exception_NotFound - * @return Sabre_DAV_IPrincipal + * @return Sabre_DAVACL_IPrincipal */ public function getChild($name) { diff --git a/3rdparty/Sabre/DAVACL/Exception/AceConflict.php b/3rdparty/Sabre/DAVACL/Exception/AceConflict.php index 4b9f93b0036..4b9f93b0036 100755..100644 --- a/3rdparty/Sabre/DAVACL/Exception/AceConflict.php +++ b/3rdparty/Sabre/DAVACL/Exception/AceConflict.php diff --git a/3rdparty/Sabre/DAVACL/Exception/NeedPrivileges.php b/3rdparty/Sabre/DAVACL/Exception/NeedPrivileges.php index 9b055dd9709..9b055dd9709 100755..100644 --- a/3rdparty/Sabre/DAVACL/Exception/NeedPrivileges.php +++ b/3rdparty/Sabre/DAVACL/Exception/NeedPrivileges.php diff --git a/3rdparty/Sabre/DAVACL/Exception/NoAbstract.php b/3rdparty/Sabre/DAVACL/Exception/NoAbstract.php index f44e3e32281..f44e3e32281 100755..100644 --- a/3rdparty/Sabre/DAVACL/Exception/NoAbstract.php +++ b/3rdparty/Sabre/DAVACL/Exception/NoAbstract.php diff --git a/3rdparty/Sabre/DAVACL/Exception/NotRecognizedPrincipal.php b/3rdparty/Sabre/DAVACL/Exception/NotRecognizedPrincipal.php index 8d1e38ca1b4..8d1e38ca1b4 100755..100644 --- a/3rdparty/Sabre/DAVACL/Exception/NotRecognizedPrincipal.php +++ b/3rdparty/Sabre/DAVACL/Exception/NotRecognizedPrincipal.php diff --git a/3rdparty/Sabre/DAVACL/Exception/NotSupportedPrivilege.php b/3rdparty/Sabre/DAVACL/Exception/NotSupportedPrivilege.php index 3b5d012d7fa..3b5d012d7fa 100755..100644 --- a/3rdparty/Sabre/DAVACL/Exception/NotSupportedPrivilege.php +++ b/3rdparty/Sabre/DAVACL/Exception/NotSupportedPrivilege.php diff --git a/3rdparty/Sabre/DAVACL/IACL.php b/3rdparty/Sabre/DAVACL/IACL.php index 003e6993483..356bb481d55 100755..100644 --- a/3rdparty/Sabre/DAVACL/IACL.php +++ b/3rdparty/Sabre/DAVACL/IACL.php @@ -48,7 +48,7 @@ interface Sabre_DAVACL_IACL extends Sabre_DAV_INode { /** * Updates the ACL * - * This method will receive a list of new ACE's. + * This method will receive a list of new ACE's as an array argument. * * @param array $acl * @return void diff --git a/3rdparty/Sabre/DAVACL/IPrincipal.php b/3rdparty/Sabre/DAVACL/IPrincipal.php index fc7605bf625..fc7605bf625 100755..100644 --- a/3rdparty/Sabre/DAVACL/IPrincipal.php +++ b/3rdparty/Sabre/DAVACL/IPrincipal.php diff --git a/3rdparty/Sabre/DAVACL/IPrincipalBackend.php b/3rdparty/Sabre/DAVACL/IPrincipalBackend.php index e798bf890c0..e798bf890c0 100755..100644 --- a/3rdparty/Sabre/DAVACL/IPrincipalBackend.php +++ b/3rdparty/Sabre/DAVACL/IPrincipalBackend.php diff --git a/3rdparty/Sabre/DAVACL/Plugin.php b/3rdparty/Sabre/DAVACL/Plugin.php index 5c828c6d97b..5b17c838475 100755..100644 --- a/3rdparty/Sabre/DAVACL/Plugin.php +++ b/3rdparty/Sabre/DAVACL/Plugin.php @@ -3,7 +3,7 @@ /** * SabreDAV ACL Plugin * - * This plugin provides funcitonality to enforce ACL permissions. + * This plugin provides functionality to enforce ACL permissions. * ACL is defined in RFC3744. * * In addition it also provides support for the {DAV:}current-user-principal @@ -102,11 +102,11 @@ class Sabre_DAVACL_Plugin extends Sabre_DAV_ServerPlugin { ); /** - * Any principal uri's added here, will automatically be added to the list - * of ACL's. They will effectively receive {DAV:}all privileges, as a + * Any principal uri's added here, will automatically be added to the list + * of ACL's. They will effectively receive {DAV:}all privileges, as a * protected privilege. - * - * @var array + * + * @var array */ public $adminPrincipals = array(); @@ -233,6 +233,7 @@ class Sabre_DAVACL_Plugin extends Sabre_DAV_ServerPlugin { $authPlugin = $this->server->getPlugin('auth'); if (is_null($authPlugin)) return null; + /** @var $authPlugin Sabre_DAV_Auth_Plugin */ $userName = $authPlugin->getCurrentUser(); if (!$userName) return null; @@ -242,6 +243,14 @@ class Sabre_DAVACL_Plugin extends Sabre_DAV_ServerPlugin { } /** + * This array holds a cache for all the principals that are associated with + * a single principal. + * + * @var array + */ + protected $currentUserPrincipalsCache = array(); + + /** * Returns a list of principals that's associated to the current * user, either directly or through group membership. * @@ -253,6 +262,11 @@ class Sabre_DAVACL_Plugin extends Sabre_DAV_ServerPlugin { if (is_null($currentUser)) return array(); + // First check our cache + if (isset($this->currentUserPrincipalsCache[$currentUser])) { + return $this->currentUserPrincipalsCache[$currentUser]; + } + $check = array($currentUser); $principals = array($currentUser); @@ -277,6 +291,9 @@ class Sabre_DAVACL_Plugin extends Sabre_DAV_ServerPlugin { } + // Store the result in the cache + $this->currentUserPrincipalsCache[$currentUser] = $principals; + return $principals; } @@ -771,7 +788,7 @@ class Sabre_DAVACL_Plugin extends Sabre_DAV_ServerPlugin { * @param array $requestedProperties * @param array $returnedProperties * @TODO really should be broken into multiple methods, or even a class. - * @return void + * @return bool */ public function beforeGetProperties($uri, Sabre_DAV_INode $node, &$requestedProperties, &$returnedProperties) { @@ -895,6 +912,18 @@ class Sabre_DAVACL_Plugin extends Sabre_DAV_ServerPlugin { $returnedProperties[200]['{DAV:}acl-restrictions'] = new Sabre_DAVACL_Property_AclRestrictions(); } + /* Adding ACL properties */ + if ($node instanceof Sabre_DAVACL_IACL) { + + if (false !== ($index = array_search('{DAV:}owner', $requestedProperties))) { + + unset($requestedProperties[$index]); + $returnedProperties[200]['{DAV:}owner'] = new Sabre_DAV_Property_Href($node->getOwner() . '/'); + + } + + } + } /** @@ -928,6 +957,9 @@ class Sabre_DAVACL_Plugin extends Sabre_DAV_ServerPlugin { } $node->setGroupMemberSet($memberSet); + // We must also clear our cache, just in case + + $this->currentUserPrincipalsCache = array(); $result[200]['{DAV:}group-member-set'] = null; unset($propertyDelta['{DAV:}group-member-set']); @@ -935,7 +967,7 @@ class Sabre_DAVACL_Plugin extends Sabre_DAV_ServerPlugin { } /** - * This method handels HTTP REPORT requests + * This method handles HTTP REPORT requests * * @param string $reportName * @param DOMNode $dom @@ -1268,10 +1300,12 @@ class Sabre_DAVACL_Plugin extends Sabre_DAV_ServerPlugin { } $result = $this->principalSearch($searchProperties, $requestedProperties, $uri); - $xml = $this->server->generateMultiStatus($result); - $this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8'); + $prefer = $this->server->getHTTPPRefer(); + $this->server->httpResponse->sendStatus(207); - $this->server->httpResponse->sendBody($xml); + $this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8'); + $this->server->httpResponse->setHeader('Vary','Brief,Prefer'); + $this->server->httpResponse->sendBody($this->server->generateMultiStatus($result, $prefer['return-minimal'])); } diff --git a/3rdparty/Sabre/DAVACL/Principal.php b/3rdparty/Sabre/DAVACL/Principal.php index 51c6658afd6..51c6658afd6 100755..100644 --- a/3rdparty/Sabre/DAVACL/Principal.php +++ b/3rdparty/Sabre/DAVACL/Principal.php diff --git a/3rdparty/Sabre/DAVACL/PrincipalBackend/PDO.php b/3rdparty/Sabre/DAVACL/PrincipalBackend/PDO.php index a76b4a9d727..a76b4a9d727 100755..100644 --- a/3rdparty/Sabre/DAVACL/PrincipalBackend/PDO.php +++ b/3rdparty/Sabre/DAVACL/PrincipalBackend/PDO.php diff --git a/3rdparty/Sabre/DAVACL/PrincipalCollection.php b/3rdparty/Sabre/DAVACL/PrincipalCollection.php index c3e4cb83f23..c3e4cb83f23 100755..100644 --- a/3rdparty/Sabre/DAVACL/PrincipalCollection.php +++ b/3rdparty/Sabre/DAVACL/PrincipalCollection.php diff --git a/3rdparty/Sabre/DAVACL/Property/Acl.php b/3rdparty/Sabre/DAVACL/Property/Acl.php index 05e1a690b3c..3f79a8d532e 100755..100644 --- a/3rdparty/Sabre/DAVACL/Property/Acl.php +++ b/3rdparty/Sabre/DAVACL/Property/Acl.php @@ -88,11 +88,11 @@ class Sabre_DAVACL_Property_Acl extends Sabre_DAV_Property { static public function unserialize(DOMElement $dom) { $privileges = array(); - $xaces = $dom->getElementsByTagNameNS('urn:DAV','ace'); + $xaces = $dom->getElementsByTagNameNS('DAV:','ace'); for($ii=0; $ii < $xaces->length; $ii++) { $xace = $xaces->item($ii); - $principal = $xace->getElementsByTagNameNS('urn:DAV','principal'); + $principal = $xace->getElementsByTagNameNS('DAV:','principal'); if ($principal->length !== 1) { throw new Sabre_DAV_Exception_BadRequest('Each {DAV:}ace element must have one {DAV:}principal element'); } @@ -116,17 +116,17 @@ class Sabre_DAVACL_Property_Acl extends Sabre_DAV_Property { $protected = false; - if ($xace->getElementsByTagNameNS('urn:DAV','protected')->length > 0) { + if ($xace->getElementsByTagNameNS('DAV:','protected')->length > 0) { $protected = true; } - $grants = $xace->getElementsByTagNameNS('urn:DAV','grant'); + $grants = $xace->getElementsByTagNameNS('DAV:','grant'); if ($grants->length < 1) { throw new Sabre_DAV_Exception_NotImplemented('Every {DAV:}ace element must have a {DAV:}grant element. {DAV:}deny is not yet supported'); } $grant = $grants->item(0); - $xprivs = $grant->getElementsByTagNameNS('urn:DAV','privilege'); + $xprivs = $grant->getElementsByTagNameNS('DAV:','privilege'); for($jj=0; $jj<$xprivs->length; $jj++) { $xpriv = $xprivs->item($jj); diff --git a/3rdparty/Sabre/DAVACL/Property/AclRestrictions.php b/3rdparty/Sabre/DAVACL/Property/AclRestrictions.php index a8b054956dd..a8b054956dd 100755..100644 --- a/3rdparty/Sabre/DAVACL/Property/AclRestrictions.php +++ b/3rdparty/Sabre/DAVACL/Property/AclRestrictions.php diff --git a/3rdparty/Sabre/DAVACL/Property/CurrentUserPrivilegeSet.php b/3rdparty/Sabre/DAVACL/Property/CurrentUserPrivilegeSet.php index 94a29640615..94a29640615 100755..100644 --- a/3rdparty/Sabre/DAVACL/Property/CurrentUserPrivilegeSet.php +++ b/3rdparty/Sabre/DAVACL/Property/CurrentUserPrivilegeSet.php diff --git a/3rdparty/Sabre/DAVACL/Property/Principal.php b/3rdparty/Sabre/DAVACL/Property/Principal.php index c36328a58e0..3f681742276 100755..100644 --- a/3rdparty/Sabre/DAVACL/Property/Principal.php +++ b/3rdparty/Sabre/DAVACL/Property/Principal.php @@ -15,7 +15,7 @@ class Sabre_DAVACL_Property_Principal extends Sabre_DAV_Property implements Sabre_DAV_Property_IHref { /** - * To specify a not-logged-in user, use the UNAUTHENTICTED principal + * To specify a not-logged-in user, use the UNAUTHENTICATED principal */ const UNAUTHENTICATED = 1; @@ -131,7 +131,7 @@ class Sabre_DAVACL_Property_Principal extends Sabre_DAV_Property implements Sabr * Deserializes a DOM element into a property object. * * @param DOMElement $dom - * @return Sabre_DAV_Property_Principal + * @return Sabre_DAVACL_Property_Principal */ static public function unserialize(DOMElement $dom) { diff --git a/3rdparty/Sabre/DAVACL/Property/SupportedPrivilegeSet.php b/3rdparty/Sabre/DAVACL/Property/SupportedPrivilegeSet.php index 276d57ae093..276d57ae093 100755..100644 --- a/3rdparty/Sabre/DAVACL/Property/SupportedPrivilegeSet.php +++ b/3rdparty/Sabre/DAVACL/Property/SupportedPrivilegeSet.php diff --git a/3rdparty/Sabre/DAVACL/Version.php b/3rdparty/Sabre/DAVACL/Version.php index 9950f748741..084a9c13c89 100755..100644 --- a/3rdparty/Sabre/DAVACL/Version.php +++ b/3rdparty/Sabre/DAVACL/Version.php @@ -14,7 +14,7 @@ class Sabre_DAVACL_Version { /** * Full version number */ - const VERSION = '1.6.0'; + const VERSION = '1.7.0'; /** * Stability : alpha, beta, stable diff --git a/3rdparty/Sabre/DAVACL/includes.php b/3rdparty/Sabre/DAVACL/includes.php index 28fa3eed225..28fa3eed225 100755..100644 --- a/3rdparty/Sabre/DAVACL/includes.php +++ b/3rdparty/Sabre/DAVACL/includes.php diff --git a/3rdparty/Sabre/HTTP/AWSAuth.php b/3rdparty/Sabre/HTTP/AWSAuth.php index fb8245c8cbf..fb8245c8cbf 100755..100644 --- a/3rdparty/Sabre/HTTP/AWSAuth.php +++ b/3rdparty/Sabre/HTTP/AWSAuth.php diff --git a/3rdparty/Sabre/HTTP/AbstractAuth.php b/3rdparty/Sabre/HTTP/AbstractAuth.php index 3bccabcd1c1..3bccabcd1c1 100755..100644 --- a/3rdparty/Sabre/HTTP/AbstractAuth.php +++ b/3rdparty/Sabre/HTTP/AbstractAuth.php diff --git a/3rdparty/Sabre/HTTP/BasicAuth.php b/3rdparty/Sabre/HTTP/BasicAuth.php index f90ed24f5d8..f90ed24f5d8 100755..100644 --- a/3rdparty/Sabre/HTTP/BasicAuth.php +++ b/3rdparty/Sabre/HTTP/BasicAuth.php diff --git a/3rdparty/Sabre/HTTP/DigestAuth.php b/3rdparty/Sabre/HTTP/DigestAuth.php index ee7f05c08ed..ee7f05c08ed 100755..100644 --- a/3rdparty/Sabre/HTTP/DigestAuth.php +++ b/3rdparty/Sabre/HTTP/DigestAuth.php diff --git a/3rdparty/Sabre/HTTP/Request.php b/3rdparty/Sabre/HTTP/Request.php index 4746ef77704..74d5ed3d7ee 100755..100644 --- a/3rdparty/Sabre/HTTP/Request.php +++ b/3rdparty/Sabre/HTTP/Request.php @@ -184,7 +184,7 @@ class Sabre_HTTP_Request { * This method returns a readable stream resource. * If the asString parameter is set to true, a string is sent instead. * - * @param bool asString + * @param bool $asString * @return resource */ public function getBody($asString = false) { diff --git a/3rdparty/Sabre/HTTP/Response.php b/3rdparty/Sabre/HTTP/Response.php index ffe9bda2082..9d436881bdf 100755..100644 --- a/3rdparty/Sabre/HTTP/Response.php +++ b/3rdparty/Sabre/HTTP/Response.php @@ -4,7 +4,7 @@ * Sabre_HTTP_Response * * @package Sabre - * @subpackage HTTP + * @subpackage HTTP * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License @@ -85,6 +85,9 @@ class Sabre_HTTP_Response { } + // @codeCoverageIgnoreStart + // We cannot reasonably test header() related methods. + /** * Sends an HTTP status header to the client * @@ -114,7 +117,9 @@ class Sabre_HTTP_Response { return header($name . ': ' . $value, $replace); else return false; + } + // @codeCoverageIgnoreEnd /** * Sets a bunch of HTTP Headers diff --git a/3rdparty/Sabre/HTTP/Util.php b/3rdparty/Sabre/HTTP/Util.php index 67bdd489e1e..67bdd489e1e 100755..100644 --- a/3rdparty/Sabre/HTTP/Util.php +++ b/3rdparty/Sabre/HTTP/Util.php diff --git a/3rdparty/Sabre/HTTP/Version.php b/3rdparty/Sabre/HTTP/Version.php index e6b4f7e5358..8ccd7c9edf6 100755..100644 --- a/3rdparty/Sabre/HTTP/Version.php +++ b/3rdparty/Sabre/HTTP/Version.php @@ -14,7 +14,7 @@ class Sabre_HTTP_Version { /** * Full version number */ - const VERSION = '1.6.4'; + const VERSION = '1.7.0'; /** * Stability : alpha, beta, stable diff --git a/3rdparty/Sabre/HTTP/includes.php b/3rdparty/Sabre/HTTP/includes.php index 9d34bf3a8be..9d34bf3a8be 100755..100644 --- a/3rdparty/Sabre/HTTP/includes.php +++ b/3rdparty/Sabre/HTTP/includes.php diff --git a/3rdparty/Sabre/VObject/Component.php b/3rdparty/Sabre/VObject/Component.php index b78a26133fa..7604e3a9bb8 100755..100644 --- a/3rdparty/Sabre/VObject/Component.php +++ b/3rdparty/Sabre/VObject/Component.php @@ -1,5 +1,7 @@ <?php +namespace Sabre\VObject; + /** * VObject Component * @@ -7,13 +9,11 @@ * VEVENT, VTODO and also VCALENDAR. It starts with BEGIN:COMPONENTNAME and * ends with END:COMPONENTNAME * - * @package Sabre - * @subpackage VObject * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ -class Sabre_VObject_Component extends Sabre_VObject_Element { +class Component extends Node { /** * Name, for example VEVENT @@ -30,18 +30,20 @@ class Sabre_VObject_Component extends Sabre_VObject_Element { public $children = array(); /** - * If coponents are added to this map, they will be automatically mapped + * If components are added to this map, they will be automatically mapped * to their respective classes, if parsed by the reader or constructed with * the 'create' method. * * @var array */ static public $classMap = array( - 'VCALENDAR' => 'Sabre_VObject_Component_VCalendar', - 'VEVENT' => 'Sabre_VObject_Component_VEvent', - 'VTODO' => 'Sabre_VObject_Component_VTodo', - 'VJOURNAL' => 'Sabre_VObject_Component_VJournal', - 'VALARM' => 'Sabre_VObject_Component_VAlarm', + 'VALARM' => 'Sabre\\VObject\\Component\\VAlarm', + 'VCALENDAR' => 'Sabre\\VObject\\Component\\VCalendar', + 'VCARD' => 'Sabre\\VObject\\Component\\VCard', + 'VEVENT' => 'Sabre\\VObject\\Component\\VEvent', + 'VJOURNAL' => 'Sabre\\VObject\\Component\\VJournal', + 'VTODO' => 'Sabre\\VObject\\Component\\VTodo', + 'VFREEBUSY' => 'Sabre\\VObject\\Component\\VFreeBusy', ); /** @@ -50,7 +52,7 @@ class Sabre_VObject_Component extends Sabre_VObject_Element { * * @param string $name * @param string $value - * @return Sabre_VObject_Component + * @return Component */ static public function create($name, $value = null) { @@ -71,9 +73,9 @@ class Sabre_VObject_Component extends Sabre_VObject_Element { * be overridden with the iterator argument * * @param string $name - * @param Sabre_VObject_ElementList $iterator + * @param ElementList $iterator */ - public function __construct($name, Sabre_VObject_ElementList $iterator = null) { + public function __construct($name, ElementList $iterator = null) { $this->name = strtoupper($name); if (!is_null($iterator)) $this->iterator = $iterator; @@ -94,40 +96,54 @@ class Sabre_VObject_Component extends Sabre_VObject_Element { * * This is solely used by the childrenSort method. * - * A higher score means the item will be higher in the list + * A higher score means the item will be lower in the list. + * To avoid score collisions, each "score category" has a reasonable + * space to accomodate elements. The $key is added to the $score to + * preserve the original relative order of elements. * - * @param Sabre_VObject_Node $n + * @param int $key + * @param array $array * @return int */ - $sortScore = function($n) { + $sortScore = function($key, $array) { + + if ($array[$key] instanceof Component) { - if ($n instanceof Sabre_VObject_Component) { // We want to encode VTIMEZONE first, this is a personal // preference. - if ($n->name === 'VTIMEZONE') { - return 1; + if ($array[$key]->name === 'VTIMEZONE') { + $score=300000000; + return $score+$key; } else { - return 0; + $score=400000000; + return $score+$key; } } else { + // Properties get encoded first // VCARD version 4.0 wants the VERSION property to appear first - if ($n->name === 'VERSION') { - return 3; - } else { - return 2; + if ($array[$key] instanceof Property) { + if ($array[$key]->name === 'VERSION') { + $score=100000000; + return $score+$key; + } else { + // All other properties + $score=200000000; + return $score+$key; + } } } }; - usort($this->children, function($a, $b) use ($sortScore) { + $tmp = $this->children; + uksort($this->children, function($a, $b) use ($sortScore, $tmp) { - $sA = $sortScore($a); - $sB = $sortScore($b); + $sA = $sortScore($a, $tmp); + $sB = $sortScore($b, $tmp); if ($sA === $sB) return 0; - return ($sA > $sB) ? -1 : 1; + return ($sA < $sB) ? -1 : 1; }); @@ -143,8 +159,8 @@ class Sabre_VObject_Component extends Sabre_VObject_Element { * * You can call this method with the following syntaxes: * - * add(Sabre_VObject_Element $element) - * add(string $name, $value) + * add(Node $node) + * add(string $name, $value, array $parameters = array()) * * The first version adds an Element * The second adds a property as a string. @@ -153,26 +169,23 @@ class Sabre_VObject_Component extends Sabre_VObject_Element { * @param mixed $itemValue * @return void */ - public function add($item, $itemValue = null) { + public function add($item, $itemValue = null, array $parameters = array()) { - if ($item instanceof Sabre_VObject_Element) { + if ($item instanceof Node) { if (!is_null($itemValue)) { - throw new InvalidArgumentException('The second argument must not be specified, when passing a VObject'); + throw new \InvalidArgumentException('The second argument must not be specified, when passing a VObject Node'); } $item->parent = $this; $this->children[] = $item; } elseif(is_string($item)) { - if (!is_scalar($itemValue)) { - throw new InvalidArgumentException('The second argument must be scalar'); - } - $item = Sabre_VObject_Property::create($item,$itemValue); + $item = Property::create($item,$itemValue, $parameters); $item->parent = $this; $this->children[] = $item; } else { - throw new InvalidArgumentException('The first argument must either be a Sabre_VObject_Element or a string'); + throw new \InvalidArgumentException('The first argument must either be a \\Sabre\\VObject\\Node or a string'); } @@ -181,11 +194,11 @@ class Sabre_VObject_Component extends Sabre_VObject_Element { /** * Returns an iterable list of children * - * @return Sabre_VObject_ElementList + * @return ElementList */ public function children() { - return new Sabre_VObject_ElementList($this->children); + return new ElementList($this->children); } @@ -218,7 +231,7 @@ class Sabre_VObject_Component extends Sabre_VObject_Element { if ( strtoupper($child->name) === $name && - (is_null($group) || ( $child instanceof Sabre_VObject_Property && strtoupper($child->group) === $group)) + (is_null($group) || ( $child instanceof Property && strtoupper($child->group) === $group)) ) { $result[$key] = $child; @@ -241,7 +254,7 @@ class Sabre_VObject_Component extends Sabre_VObject_Element { $result = array(); foreach($this->children as $child) { - if ($child instanceof Sabre_VObject_Component) { + if ($child instanceof Component) { $result[] = $child; } } @@ -250,6 +263,33 @@ class Sabre_VObject_Component extends Sabre_VObject_Element { } + /** + * Validates the node for correctness. + * + * The following options are supported: + * - Node::REPAIR - If something is broken, and automatic repair may + * be attempted. + * + * An array is returned with warnings. + * + * Every item in the array has the following properties: + * * level - (number between 1 and 3 with severity information) + * * message - (human readable message) + * * node - (reference to the offending node) + * + * @param int $options + * @return array + */ + public function validate($options = 0) { + + $result = array(); + foreach($this->children as $child) { + $result = array_merge($result, $child->validate($options)); + } + return $result; + + } + /* Magic property accessors {{{ */ /** @@ -259,7 +299,7 @@ class Sabre_VObject_Component extends Sabre_VObject_Element { * null is returned. * * @param string $name - * @return Sabre_VObject_Property + * @return Property */ public function __get($name) { @@ -268,8 +308,8 @@ class Sabre_VObject_Component extends Sabre_VObject_Element { return null; } else { $firstMatch = current($matches); - /** @var $firstMatch Sabre_VObject_Property */ - $firstMatch->setIterator(new Sabre_VObject_ElementList(array_values($matches))); + /** @var $firstMatch Property */ + $firstMatch->setIterator(new ElementList(array_values($matches))); return $firstMatch; } @@ -291,7 +331,7 @@ class Sabre_VObject_Component extends Sabre_VObject_Element { /** * Using the setter method you can add properties or subcomponents * - * You can either pass a Sabre_VObject_Component, Sabre_VObject_Property + * You can either pass a Component, Property * object, or a string to automatically create a Property. * * If the item already exists, it will be removed. If you want to add @@ -306,7 +346,7 @@ class Sabre_VObject_Component extends Sabre_VObject_Element { $matches = $this->select($name); $overWrite = count($matches)?key($matches):null; - if ($value instanceof Sabre_VObject_Component || $value instanceof Sabre_VObject_Property) { + if ($value instanceof Component || $value instanceof Property) { $value->parent = $this; if (!is_null($overWrite)) { $this->children[$overWrite] = $value; @@ -314,7 +354,7 @@ class Sabre_VObject_Component extends Sabre_VObject_Element { $this->children[] = $value; } } elseif (is_scalar($value)) { - $property = Sabre_VObject_Property::create($name,$value); + $property = Property::create($name,$value); $property->parent = $this; if (!is_null($overWrite)) { $this->children[$overWrite] = $property; @@ -322,7 +362,7 @@ class Sabre_VObject_Component extends Sabre_VObject_Element { $this->children[] = $property; } } else { - throw new InvalidArgumentException('You must pass a Sabre_VObject_Component, Sabre_VObject_Property or scalar type'); + throw new \InvalidArgumentException('You must pass a \\Sabre\\VObject\\Component, \\Sabre\\VObject\\Property or scalar type'); } } diff --git a/3rdparty/Sabre/VObject/Component/VAlarm.php b/3rdparty/Sabre/VObject/Component/VAlarm.php index ebb4a9b18f6..383e16eef14 100755..100644 --- a/3rdparty/Sabre/VObject/Component/VAlarm.php +++ b/3rdparty/Sabre/VObject/Component/VAlarm.php @@ -1,17 +1,18 @@ <?php +namespace Sabre\VObject\Component; +use Sabre\VObject; + /** * VAlarm component * * This component contains some additional functionality specific for VALARMs. * - * @package Sabre - * @subpackage VObject * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ -class Sabre_VObject_Component_VAlarm extends Sabre_VObject_Component { +class VAlarm extends VObject\Component { /** * Returns a DateTime object when this alarm is going to trigger. @@ -24,12 +25,19 @@ class Sabre_VObject_Component_VAlarm extends Sabre_VObject_Component { $trigger = $this->TRIGGER; if(!isset($trigger['VALUE']) || strtoupper($trigger['VALUE']) === 'DURATION') { - $triggerDuration = Sabre_VObject_DateTimeParser::parseDuration($this->TRIGGER); + $triggerDuration = VObject\DateTimeParser::parseDuration($this->TRIGGER); $related = (isset($trigger['RELATED']) && strtoupper($trigger['RELATED']) == 'END') ? 'END' : 'START'; $parentComponent = $this->parent; if ($related === 'START') { - $effectiveTrigger = clone $parentComponent->DTSTART->getDateTime(); + + if ($parentComponent->name === 'VTODO') { + $propName = 'DUE'; + } else { + $propName = 'DTSTART'; + } + + $effectiveTrigger = clone $parentComponent->$propName->getDateTime(); $effectiveTrigger->add($triggerDuration); } else { if ($parentComponent->name === 'VTODO') { @@ -37,7 +45,7 @@ class Sabre_VObject_Component_VAlarm extends Sabre_VObject_Component { } elseif ($parentComponent->name === 'VEVENT') { $endProp = 'DTEND'; } else { - throw new Sabre_DAV_Exception('time-range filters on VALARM components are only supported when they are a child of VTODO or VEVENT'); + throw new \LogicException('time-range filters on VALARM components are only supported when they are a child of VTODO or VEVENT'); } if (isset($parentComponent->$endProp)) { @@ -45,7 +53,7 @@ class Sabre_VObject_Component_VAlarm extends Sabre_VObject_Component { $effectiveTrigger->add($triggerDuration); } elseif (isset($parentComponent->DURATION)) { $effectiveTrigger = clone $parentComponent->DTSTART->getDateTime(); - $duration = Sabre_VObject_DateTimeParser::parseDuration($parentComponent->DURATION); + $duration = VObject\DateTimeParser::parseDuration($parentComponent->DURATION); $effectiveTrigger->add($duration); $effectiveTrigger->add($triggerDuration); } else { @@ -67,22 +75,22 @@ class Sabre_VObject_Component_VAlarm extends Sabre_VObject_Component { * The rules used to determine if an event falls within the specified * time-range is based on the CalDAV specification. * - * @param DateTime $start - * @param DateTime $end + * @param \DateTime $start + * @param \DateTime $end * @return bool */ - public function isInTimeRange(DateTime $start, DateTime $end) { + public function isInTimeRange(\DateTime $start, \DateTime $end) { $effectiveTrigger = $this->getEffectiveTriggerTime(); if (isset($this->DURATION)) { - $duration = Sabre_VObject_DateTimeParser::parseDuration($this->DURATION); + $duration = VObject\DateTimeParser::parseDuration($this->DURATION); $repeat = (string)$this->repeat; if (!$repeat) { $repeat = 1; } - $period = new DatePeriod($effectiveTrigger, $duration, (int)$repeat); + $period = new \DatePeriod($effectiveTrigger, $duration, (int)$repeat); foreach($period as $occurrence) { @@ -98,5 +106,3 @@ class Sabre_VObject_Component_VAlarm extends Sabre_VObject_Component { } } - -?> diff --git a/3rdparty/Sabre/VObject/Component/VCalendar.php b/3rdparty/Sabre/VObject/Component/VCalendar.php index f3be29afdbb..73f2f6d34fc 100755..100644 --- a/3rdparty/Sabre/VObject/Component/VCalendar.php +++ b/3rdparty/Sabre/VObject/Component/VCalendar.php @@ -1,17 +1,19 @@ <?php +namespace Sabre\VObject\Component; + +use Sabre\VObject; + /** * The VCalendar component * * This component adds functionality to a component, specific for a VCALENDAR. * - * @package Sabre - * @subpackage VObject * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ -class Sabre_VObject_Component_VCalendar extends Sabre_VObject_Component { +class VCalendar extends VObject\Component { /** * Returns a list of all 'base components'. For instance, if an Event has @@ -28,7 +30,7 @@ class Sabre_VObject_Component_VCalendar extends Sabre_VObject_Component { $components = array(); foreach($this->children as $component) { - if (!$component instanceof Sabre_VObject_Component) + if (!$component instanceof VObject\Component) continue; if (isset($component->{'RECURRENCE-ID'})) @@ -69,7 +71,7 @@ class Sabre_VObject_Component_VCalendar extends Sabre_VObject_Component { * @param DateTime $end * @return void */ - public function expand(DateTime $start, DateTime $end) { + public function expand(\DateTime $start, \DateTime $end) { $newEvents = array(); @@ -91,10 +93,10 @@ class Sabre_VObject_Component_VCalendar extends Sabre_VObject_Component { $uid = (string)$vevent->uid; if (!$uid) { - throw new LogicException('Event did not have a UID!'); + throw new \LogicException('Event did not have a UID!'); } - $it = new Sabre_VObject_RecurrenceIterator($this, $vevent->uid); + $it = new VObject\RecurrenceIterator($this, $vevent->uid); $it->fastForward($start); while($it->valid() && $it->getDTStart() < $end) { @@ -114,9 +116,9 @@ class Sabre_VObject_Component_VCalendar extends Sabre_VObject_Component { foreach($newEvents as $newEvent) { foreach($newEvent->children as $child) { - if ($child instanceof Sabre_VObject_Property_DateTime && - $child->getDateType() == Sabre_VObject_Property_DateTime::LOCALTZ) { - $child->setDateTime($child->getDateTime(),Sabre_VObject_Property_DateTime::UTC); + if ($child instanceof VObject\Property\DateTime && + $child->getDateType() == VObject\Property\DateTime::LOCALTZ) { + $child->setDateTime($child->getDateTime(),VObject\Property\DateTime::UTC); } } @@ -129,5 +131,112 @@ class Sabre_VObject_Component_VCalendar extends Sabre_VObject_Component { } + /** + * Validates the node for correctness. + * An array is returned with warnings. + * + * Every item in the array has the following properties: + * * level - (number between 1 and 3 with severity information) + * * message - (human readable message) + * * node - (reference to the offending node) + * + * @return array + */ + /* + public function validate() { + + $warnings = array(); + + $version = $this->select('VERSION'); + if (count($version)!==1) { + $warnings[] = array( + 'level' => 1, + 'message' => 'The VERSION property must appear in the VCALENDAR component exactly 1 time', + 'node' => $this, + ); + } else { + if ((string)$this->VERSION !== '2.0') { + $warnings[] = array( + 'level' => 1, + 'message' => 'Only iCalendar version 2.0 as defined in rfc5545 is supported.', + 'node' => $this, + ); + } + } + $version = $this->select('PRODID'); + if (count($version)!==1) { + $warnings[] = array( + 'level' => 2, + 'message' => 'The PRODID property must appear in the VCALENDAR component exactly 1 time', + 'node' => $this, + ); + } + if (count($this->CALSCALE) > 1) { + $warnings[] = array( + 'level' => 2, + 'message' => 'The CALSCALE property must not be specified more than once.', + 'node' => $this, + ); + } + if (count($this->METHOD) > 1) { + $warnings[] = array( + 'level' => 2, + 'message' => 'The METHOD property must not be specified more than once.', + 'node' => $this, + ); + } + + $allowedComponents = array( + 'VEVENT', + 'VTODO', + 'VJOURNAL', + 'VFREEBUSY', + 'VTIMEZONE', + ); + $allowedProperties = array( + 'PRODID', + 'VERSION', + 'CALSCALE', + 'METHOD', + ); + $componentsFound = 0; + foreach($this->children as $child) { + if($child instanceof Component) { + $componentsFound++; + if (!in_array($child->name, $allowedComponents)) { + $warnings[] = array( + 'level' => 1, + 'message' => 'The ' . $child->name . " component is not allowed in the VCALENDAR component", + 'node' => $this, + ); + } + } + if ($child instanceof Property) { + if (!in_array($child->name, $allowedProperties)) { + $warnings[] = array( + 'level' => 2, + 'message' => 'The ' . $child->name . " property is not allowed in the VCALENDAR component", + 'node' => $this, + ); + } + } + } + + if ($componentsFound===0) { + $warnings[] = array( + 'level' => 1, + 'message' => 'An iCalendar object must have at least 1 component.', + 'node' => $this, + ); + } + + return array_merge( + $warnings, + parent::validate() + ); + + } + */ + } diff --git a/3rdparty/Sabre/VObject/Component/VCard.php b/3rdparty/Sabre/VObject/Component/VCard.php new file mode 100644 index 00000000000..b2926985550 --- /dev/null +++ b/3rdparty/Sabre/VObject/Component/VCard.php @@ -0,0 +1,105 @@ +<?php + +namespace Sabre\VObject\Component; + +use Sabre\VObject; + +/** + * The VCard component + * + * This component represents the BEGIN:VCARD and END:VCARD found in every + * vcard. + * + * @copyright Copyright (C) 2007-2012 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 VCard extends VObject\Component { + + /** + * VCards with version 2.1, 3.0 and 4.0 are found. + * + * If the VCARD doesn't know its version, 4.0 is assumed. + */ + const DEFAULT_VERSION = '4.0'; + + /** + * Validates the node for correctness. + * + * The following options are supported: + * - Node::REPAIR - If something is broken, and automatic repair may + * be attempted. + * + * An array is returned with warnings. + * + * Every item in the array has the following properties: + * * level - (number between 1 and 3 with severity information) + * * message - (human readable message) + * * node - (reference to the offending node) + * + * @param int $options + * @return array + */ + public function validate($options = 0) { + + $warnings = array(); + + $version = $this->select('VERSION'); + if (count($version)!==1) { + $warnings[] = array( + 'level' => 1, + 'message' => 'The VERSION property must appear in the VCARD component exactly 1 time', + 'node' => $this, + ); + if ($options & self::REPAIR) { + $this->VERSION = self::DEFAULT_VERSION; + } + } else { + $version = (string)$this->VERSION; + if ($version!=='2.1' && $version!=='3.0' && $version!=='4.0') { + $warnings[] = array( + 'level' => 1, + 'message' => 'Only vcard version 4.0 (RFC6350), version 3.0 (RFC2426) or version 2.1 (icm-vcard-2.1) are supported.', + 'node' => $this, + ); + if ($options & self::REPAIR) { + $this->VERSION = '4.0'; + } + } + + } + $version = $this->select('FN'); + if (count($version)!==1) { + $warnings[] = array( + 'level' => 1, + 'message' => 'The FN property must appear in the VCARD component exactly 1 time', + 'node' => $this, + ); + if (($options & self::REPAIR) && count($version) === 0) { + // We're going to try to see if we can use the contents of the + // N property. + if (isset($this->N)) { + $value = explode(';', (string)$this->N); + if (isset($value[1]) && $value[1]) { + $this->FN = $value[1] . ' ' . $value[0]; + } else { + $this->FN = $value[0]; + } + + // Otherwise, the ORG property may work + } elseif (isset($this->ORG)) { + $this->FN = (string)$this->ORG; + } + + } + } + + return array_merge( + parent::validate($options), + $warnings + ); + + } + +} + diff --git a/3rdparty/Sabre/VObject/Component/VEvent.php b/3rdparty/Sabre/VObject/Component/VEvent.php index d6b910874d0..9d10966e203 100755..100644 --- a/3rdparty/Sabre/VObject/Component/VEvent.php +++ b/3rdparty/Sabre/VObject/Component/VEvent.php @@ -1,17 +1,18 @@ <?php +namespace Sabre\VObject\Component; +use Sabre\VObject; + /** * VEvent component * * This component contains some additional functionality specific for VEVENT's. * - * @package Sabre - * @subpackage VObject * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ -class Sabre_VObject_Component_VEvent extends Sabre_VObject_Component { +class VEvent extends VObject\Component { /** * Returns true or false depending on if the event falls in the specified @@ -20,14 +21,14 @@ class Sabre_VObject_Component_VEvent extends Sabre_VObject_Component { * The rules used to determine if an event falls within the specified * time-range is based on the CalDAV specification. * - * @param DateTime $start - * @param DateTime $end + * @param \DateTime $start + * @param \DateTime $end * @return bool */ - public function isInTimeRange(DateTime $start, DateTime $end) { + public function isInTimeRange(\DateTime $start, \DateTime $end) { if ($this->RRULE) { - $it = new Sabre_VObject_RecurrenceIterator($this); + $it = new VObject\RecurrenceIterator($this); $it->fastForward($start); // We fast-forwarded to a spot where the end-time of the @@ -53,8 +54,8 @@ class Sabre_VObject_Component_VEvent extends Sabre_VObject_Component { } elseif (isset($this->DURATION)) { $effectiveEnd = clone $effectiveStart; - $effectiveEnd->add( Sabre_VObject_DateTimeParser::parseDuration($this->DURATION) ); - } elseif ($this->DTSTART->getDateType() == Sabre_VObject_Element_DateTime::DATE) { + $effectiveEnd->add( VObject\DateTimeParser::parseDuration($this->DURATION) ); + } elseif ($this->DTSTART->getDateType() == VObject\Property\DateTime::DATE) { $effectiveEnd = clone $effectiveStart; $effectiveEnd->modify('+1 day'); } else { @@ -67,5 +68,3 @@ class Sabre_VObject_Component_VEvent extends Sabre_VObject_Component { } } - -?> diff --git a/3rdparty/Sabre/VObject/Component/VFreeBusy.php b/3rdparty/Sabre/VObject/Component/VFreeBusy.php new file mode 100644 index 00000000000..d6da52cbd77 --- /dev/null +++ b/3rdparty/Sabre/VObject/Component/VFreeBusy.php @@ -0,0 +1,68 @@ +<?php + +namespace Sabre\VObject\Component; + +use Sabre\VObject; + +/** + * The VFreeBusy component + * + * This component adds functionality to a component, specific for VFREEBUSY + * components. + * + * @copyright Copyright (C) 2007-2012 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 VFreeBusy extends VObject\Component { + + /** + * Checks based on the contained FREEBUSY information, if a timeslot is + * available. + * + * @param DateTime $start + * @param Datetime $end + * @return bool + */ + public function isFree(\DateTime $start, \Datetime $end) { + + foreach($this->select('FREEBUSY') as $freebusy) { + + // We are only interested in FBTYPE=BUSY (the default), + // FBTYPE=BUSY-TENTATIVE or FBTYPE=BUSY-UNAVAILABLE. + if (isset($freebusy['FBTYPE']) && strtoupper(substr((string)$freebusy['FBTYPE'],0,4))!=='BUSY') { + continue; + } + + // The freebusy component can hold more than 1 value, separated by + // commas. + $periods = explode(',', (string)$freebusy); + + foreach($periods as $period) { + // Every period is formatted as [start]/[end]. The start is an + // absolute UTC time, the end may be an absolute UTC time, or + // duration (relative) value. + list($busyStart, $busyEnd) = explode('/', $period); + + $busyStart = VObject\DateTimeParser::parse($busyStart); + $busyEnd = VObject\DateTimeParser::parse($busyEnd); + if ($busyEnd instanceof \DateInterval) { + $tmp = clone $busyStart; + $tmp->add($busyEnd); + $busyEnd = $tmp; + } + + if($start < $busyEnd && $end > $busyStart) { + return false; + } + + } + + } + + return true; + + } + +} + diff --git a/3rdparty/Sabre/VObject/Component/VJournal.php b/3rdparty/Sabre/VObject/Component/VJournal.php index 22b3ec921e5..f104a1f66ed 100755..100644 --- a/3rdparty/Sabre/VObject/Component/VJournal.php +++ b/3rdparty/Sabre/VObject/Component/VJournal.php @@ -1,17 +1,19 @@ <?php +namespace Sabre\VObject\Component; + +use Sabre\VObject; + /** * VJournal component * * This component contains some additional functionality specific for VJOURNALs. * - * @package Sabre - * @subpackage VObject * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ -class Sabre_VObject_Component_VJournal extends Sabre_VObject_Component { +class VJournal extends VObject\Component { /** * Returns true or false depending on if the event falls in the specified @@ -24,12 +26,12 @@ class Sabre_VObject_Component_VJournal extends Sabre_VObject_Component { * @param DateTime $end * @return bool */ - public function isInTimeRange(DateTime $start, DateTime $end) { + public function isInTimeRange(\DateTime $start, \DateTime $end) { $dtstart = isset($this->DTSTART)?$this->DTSTART->getDateTime():null; if ($dtstart) { $effectiveEnd = clone $dtstart; - if ($this->DTSTART->getDateType() == Sabre_VObject_Element_DateTime::DATE) { + if ($this->DTSTART->getDateType() == VObject\Property\DateTime::DATE) { $effectiveEnd->modify('+1 day'); } @@ -42,5 +44,3 @@ class Sabre_VObject_Component_VJournal extends Sabre_VObject_Component { } } - -?> diff --git a/3rdparty/Sabre/VObject/Component/VTodo.php b/3rdparty/Sabre/VObject/Component/VTodo.php index 79d06298d7f..5f879aea435 100755..100644 --- a/3rdparty/Sabre/VObject/Component/VTodo.php +++ b/3rdparty/Sabre/VObject/Component/VTodo.php @@ -1,17 +1,19 @@ <?php +namespace Sabre\VObject\Component; + +use Sabre\VObject; + /** * VTodo component * * This component contains some additional functionality specific for VTODOs. * - * @package Sabre - * @subpackage VObject * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ -class Sabre_VObject_Component_VTodo extends Sabre_VObject_Component { +class VTodo extends VObject\Component { /** * Returns true or false depending on if the event falls in the specified @@ -24,10 +26,10 @@ class Sabre_VObject_Component_VTodo extends Sabre_VObject_Component { * @param DateTime $end * @return bool */ - public function isInTimeRange(DateTime $start, DateTime $end) { + public function isInTimeRange(\DateTime $start, \DateTime $end) { $dtstart = isset($this->DTSTART)?$this->DTSTART->getDateTime():null; - $duration = isset($this->DURATION)?Sabre_VObject_DateTimeParser::parseDuration($this->DURATION):null; + $duration = isset($this->DURATION)?VObject\DateTimeParser::parseDuration($this->DURATION):null; $due = isset($this->DUE)?$this->DUE->getDateTime():null; $completed = isset($this->COMPLETED)?$this->COMPLETED->getDateTime():null; $created = isset($this->CREATED)?$this->CREATED->getDateTime():null; @@ -64,5 +66,3 @@ class Sabre_VObject_Component_VTodo extends Sabre_VObject_Component { } } - -?> diff --git a/3rdparty/Sabre/VObject/DateTimeParser.php b/3rdparty/Sabre/VObject/DateTimeParser.php index 23a4bb69916..d09ded96768 100755..100644 --- a/3rdparty/Sabre/VObject/DateTimeParser.php +++ b/3rdparty/Sabre/VObject/DateTimeParser.php @@ -1,18 +1,18 @@ <?php +namespace Sabre\VObject; + /** * DateTimeParser * * This class is responsible for parsing the several different date and time * formats iCalendar and vCards have. * - * @package Sabre - * @subpackage VObject * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ -class Sabre_VObject_DateTimeParser { +class DateTimeParser { /** * Parses an iCalendar (rfc5545) formatted datetime and returns a DateTime object @@ -25,22 +25,22 @@ class Sabre_VObject_DateTimeParser { * @param DateTimeZone $tz * @return DateTime */ - static public function parseDateTime($dt,DateTimeZone $tz = null) { + static public function parseDateTime($dt,\DateTimeZone $tz = null) { // Format is YYYYMMDD + "T" + hhmmss - $result = preg_match('/^([1-3][0-9]{3})([0-1][0-9])([0-3][0-9])T([0-2][0-9])([0-5][0-9])([0-5][0-9])([Z]?)$/',$dt,$matches); + $result = preg_match('/^([1-4][0-9]{3})([0-1][0-9])([0-3][0-9])T([0-2][0-9])([0-5][0-9])([0-5][0-9])([Z]?)$/',$dt,$matches); if (!$result) { - throw new Sabre_DAV_Exception_BadRequest('The supplied iCalendar datetime value is incorrect: ' . $dt); + throw new \LogicException('The supplied iCalendar datetime value is incorrect: ' . $dt); } if ($matches[7]==='Z' || is_null($tz)) { - $tz = new DateTimeZone('UTC'); + $tz = new \DateTimeZone('UTC'); } - $date = new DateTime($matches[1] . '-' . $matches[2] . '-' . $matches[3] . ' ' . $matches[4] . ':' . $matches[5] .':' . $matches[6], $tz); + $date = new \DateTime($matches[1] . '-' . $matches[2] . '-' . $matches[3] . ' ' . $matches[4] . ':' . $matches[5] .':' . $matches[6], $tz); // Still resetting the timezone, to normalize everything to UTC - $date->setTimeZone(new DateTimeZone('UTC')); + $date->setTimeZone(new \DateTimeZone('UTC')); return $date; } @@ -54,13 +54,13 @@ class Sabre_VObject_DateTimeParser { static public function parseDate($date) { // Format is YYYYMMDD - $result = preg_match('/^([1-3][0-9]{3})([0-1][0-9])([0-3][0-9])$/',$date,$matches); + $result = preg_match('/^([1-4][0-9]{3})([0-1][0-9])([0-3][0-9])$/',$date,$matches); if (!$result) { - throw new Sabre_DAV_Exception_BadRequest('The supplied iCalendar date value is incorrect: ' . $date); + throw new \LogicException('The supplied iCalendar date value is incorrect: ' . $date); } - $date = new DateTime($matches[1] . '-' . $matches[2] . '-' . $matches[3], new DateTimeZone('UTC')); + $date = new \DateTime($matches[1] . '-' . $matches[2] . '-' . $matches[3], new \DateTimeZone('UTC')); return $date; } @@ -79,7 +79,7 @@ class Sabre_VObject_DateTimeParser { $result = preg_match('/^(?P<plusminus>\+|-)?P((?P<week>\d+)W)?((?P<day>\d+)D)?(T((?P<hour>\d+)H)?((?P<minute>\d+)M)?((?P<second>\d+)S)?)?$/', $duration, $matches); if (!$result) { - throw new Sabre_DAV_Exception_BadRequest('The supplied iCalendar duration value is incorrect: ' . $duration); + throw new \LogicException('The supplied iCalendar duration value is incorrect: ' . $duration); } if (!$asString) { @@ -128,7 +128,7 @@ class Sabre_VObject_DateTimeParser { if ($duration==='P') { $duration = 'PT0S'; } - $iv = new DateInterval($duration); + $iv = new \DateInterval($duration); if ($invert) $iv->invert = true; return $iv; diff --git a/3rdparty/Sabre/VObject/Element.php b/3rdparty/Sabre/VObject/Element.php deleted file mode 100755 index e20ff0b353c..00000000000 --- a/3rdparty/Sabre/VObject/Element.php +++ /dev/null @@ -1,16 +0,0 @@ -<?php - -/** - * Base class for all elements - * - * @package Sabre - * @subpackage VObject - * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. - * @author Evert Pot (http://www.rooftopsolutions.nl/) - * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License - */ -abstract class Sabre_VObject_Element extends Sabre_VObject_Node { - - public $parent = null; - -} diff --git a/3rdparty/Sabre/VObject/Element/DateTime.php b/3rdparty/Sabre/VObject/Element/DateTime.php deleted file mode 100755 index 5e5eb7ab6f2..00000000000 --- a/3rdparty/Sabre/VObject/Element/DateTime.php +++ /dev/null @@ -1,37 +0,0 @@ -<?php - -/** - * DateTime property - * - * this class got renamed to Sabre_VObject_Property_DateTime - * - * @package Sabre - * @subpackage VObject - * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. - * @author Evert Pot (http://www.rooftopsolutions.nl/) - * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License - * @deprecated - */ -class Sabre_VObject_Element_DateTime extends Sabre_VObject_Property_DateTime { - - /** - * Local 'floating' time - */ - const LOCAL = 1; - - /** - * UTC-based time - */ - const UTC = 2; - - /** - * Local time plus timezone - */ - const LOCALTZ = 3; - - /** - * Only a date, time is ignored - */ - const DATE = 4; - -} diff --git a/3rdparty/Sabre/VObject/Element/MultiDateTime.php b/3rdparty/Sabre/VObject/Element/MultiDateTime.php deleted file mode 100755 index 8a12ced94a8..00000000000 --- a/3rdparty/Sabre/VObject/Element/MultiDateTime.php +++ /dev/null @@ -1,17 +0,0 @@ -<?php - -/** - * Multi-DateTime property - * - * This class got renamed to Sabre_VObject_Property_MultiDateTime - * - * @package Sabre - * @subpackage VObject - * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. - * @author Evert Pot (http://www.rooftopsolutions.nl/) - * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License - * @deprecated - */ -class Sabre_VObject_Element_MultiDateTime extends Sabre_VObject_Property_MultiDateTime { - -} diff --git a/3rdparty/Sabre/VObject/ElementList.php b/3rdparty/Sabre/VObject/ElementList.php index 7e508db20f0..b7f1c8eee22 100755..100644 --- a/3rdparty/Sabre/VObject/ElementList.php +++ b/3rdparty/Sabre/VObject/ElementList.php @@ -1,18 +1,18 @@ <?php +namespace Sabre\VObject; + /** * VObject ElementList * * This class represents a list of elements. Lists are the result of queries, * such as doing $vcalendar->vevent where there's multiple VEVENT objects. * - * @package Sabre - * @subpackage VObject * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ -class Sabre_VObject_ElementList implements Iterator, Countable, ArrayAccess { +class ElementList implements \Iterator, \Countable, \ArrayAccess { /** * Inner elements @@ -44,7 +44,7 @@ class Sabre_VObject_ElementList implements Iterator, Countable, ArrayAccess { /** * Returns current item in iteration * - * @return Sabre_VObject_Element + * @return Element */ public function current() { @@ -149,7 +149,7 @@ class Sabre_VObject_ElementList implements Iterator, Countable, ArrayAccess { */ public function offsetSet($offset,$value) { - throw new LogicException('You can not add new objects to an ElementList'); + throw new \LogicException('You can not add new objects to an ElementList'); } @@ -163,7 +163,7 @@ class Sabre_VObject_ElementList implements Iterator, Countable, ArrayAccess { */ public function offsetUnset($offset) { - throw new LogicException('You can not remove objects from an ElementList'); + throw new \LogicException('You can not remove objects from an ElementList'); } diff --git a/3rdparty/Sabre/VObject/FreeBusyGenerator.php b/3rdparty/Sabre/VObject/FreeBusyGenerator.php index 1c96a64a004..c607d119ced 100755..100644 --- a/3rdparty/Sabre/VObject/FreeBusyGenerator.php +++ b/3rdparty/Sabre/VObject/FreeBusyGenerator.php @@ -1,5 +1,7 @@ <?php +namespace Sabre\VObject; + /** * This class helps with generating FREEBUSY reports based on existing sets of * objects. @@ -10,13 +12,11 @@ * VFREEBUSY components are described in RFC5545, The rules for what should * go in a single freebusy report is taken from RFC4791, section 7.10. * - * @package Sabre - * @subpackage VObject * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ -class Sabre_VObject_FreeBusyGenerator { +class FreeBusyGenerator { /** * Input objects @@ -42,11 +42,34 @@ class Sabre_VObject_FreeBusyGenerator { /** * VCALENDAR object * - * @var Sabre_VObject_Component + * @var Component */ protected $baseObject; /** + * Creates the generator. + * + * Check the setTimeRange and setObjects methods for details about the + * arguments. + * + * @param DateTime $start + * @param DateTime $end + * @param mixed $objects + * @return void + */ + public function __construct(\DateTime $start = null, \DateTime $end = null, $objects = null) { + + if ($start && $end) { + $this->setTimeRange($start, $end); + } + + if ($objects) { + $this->setObjects($objects); + } + + } + + /** * Sets the VCALENDAR object. * * If this is set, it will not be generated for you. You are responsible @@ -54,10 +77,10 @@ class Sabre_VObject_FreeBusyGenerator { * * The VFREEBUSY object will be automatically added though. * - * @param Sabre_VObject_Component $vcalendar + * @param Component $vcalendar * @return void */ - public function setBaseObject(Sabre_VObject_Component $vcalendar) { + public function setBaseObject(Component $vcalendar) { $this->baseObject = $vcalendar; @@ -66,22 +89,28 @@ class Sabre_VObject_FreeBusyGenerator { /** * Sets the input objects * - * Every object must either be a string or a Sabre_VObject_Component. + * You must either specify a valendar object as a strong, or as the parse + * Component. + * It's also possible to specify multiple objects as an array. * - * @param array $objects + * @param mixed $objects * @return void */ - public function setObjects(array $objects) { + public function setObjects($objects) { + + if (!is_array($objects)) { + $objects = array($objects); + } $this->objects = array(); foreach($objects as $object) { if (is_string($object)) { - $this->objects[] = Sabre_VObject_Reader::read($object); - } elseif ($object instanceof Sabre_VObject_Component) { + $this->objects[] = Reader::read($object); + } elseif ($object instanceof Component) { $this->objects[] = $object; } else { - throw new InvalidArgumentException('You can only pass strings or Sabre_VObject_Component arguments to setObjects'); + throw new \InvalidArgumentException('You can only pass strings or \\Sabre\\VObject\\Component arguments to setObjects'); } } @@ -97,7 +126,7 @@ class Sabre_VObject_FreeBusyGenerator { * @param DateTime $end * @return void */ - public function setTimeRange(DateTime $start = null, DateTime $end = null) { + public function setTimeRange(\DateTime $start = null, \DateTime $end = null) { $this->start = $start; $this->end = $end; @@ -108,7 +137,7 @@ class Sabre_VObject_FreeBusyGenerator { * Parses the input data and returns a correct VFREEBUSY object, wrapped in * a VCALENDAR. * - * @return Sabre_VObject_Component + * @return Component */ public function getResult() { @@ -140,7 +169,7 @@ class Sabre_VObject_FreeBusyGenerator { if ($component->RRULE) { - $iterator = new Sabre_VObject_RecurrenceIterator($object, (string)$component->uid); + $iterator = new RecurrenceIterator($object, (string)$component->uid); if ($this->start) { $iterator->fastForward($this->start); } @@ -172,10 +201,10 @@ class Sabre_VObject_FreeBusyGenerator { if (isset($component->DTEND)) { $endTime = $component->DTEND->getDateTime(); } elseif (isset($component->DURATION)) { - $duration = Sabre_VObject_DateTimeParser::parseDuration((string)$component->DURATION); + $duration = DateTimeParser::parseDuration((string)$component->DURATION); $endTime = clone $startTime; $endTime->add($duration); - } elseif ($component->DTSTART->getDateType() === Sabre_VObject_Property_DateTime::DATE) { + } elseif ($component->DTSTART->getDateType() === Property\DateTime::DATE) { $endTime = clone $startTime; $endTime->modify('+1 day'); } else { @@ -212,14 +241,14 @@ class Sabre_VObject_FreeBusyGenerator { $values = explode(',', $freebusy); foreach($values as $value) { list($startTime, $endTime) = explode('/', $value); - $startTime = Sabre_VObject_DateTimeParser::parseDateTime($startTime); + $startTime = DateTimeParser::parseDateTime($startTime); if (substr($endTime,0,1)==='P' || substr($endTime,0,2)==='-P') { - $duration = Sabre_VObject_DateTimeParser::parseDuration($endTime); + $duration = DateTimeParser::parseDuration($endTime); $endTime = clone $startTime; $endTime->add($duration); } else { - $endTime = Sabre_VObject_DateTimeParser::parseDateTime($endTime); + $endTime = DateTimeParser::parseDateTime($endTime); } if($this->start && $this->start > $endTime) continue; @@ -248,39 +277,35 @@ class Sabre_VObject_FreeBusyGenerator { if ($this->baseObject) { $calendar = $this->baseObject; } else { - $calendar = new Sabre_VObject_Component('VCALENDAR'); + $calendar = new Component('VCALENDAR'); $calendar->version = '2.0'; - if (Sabre_DAV_Server::$exposeVersion) { - $calendar->prodid = '-//SabreDAV//Sabre VObject ' . Sabre_VObject_Version::VERSION . '//EN'; - } else { - $calendar->prodid = '-//SabreDAV//Sabre VObject//EN'; - } + $calendar->prodid = '-//Sabre//Sabre VObject ' . Version::VERSION . '//EN'; $calendar->calscale = 'GREGORIAN'; } - $vfreebusy = new Sabre_VObject_Component('VFREEBUSY'); + $vfreebusy = new Component('VFREEBUSY'); $calendar->add($vfreebusy); if ($this->start) { - $dtstart = new Sabre_VObject_Property_DateTime('DTSTART'); - $dtstart->setDateTime($this->start,Sabre_VObject_Property_DateTime::UTC); + $dtstart = new Property\DateTime('DTSTART'); + $dtstart->setDateTime($this->start,Property\DateTime::UTC); $vfreebusy->add($dtstart); } if ($this->end) { - $dtend = new Sabre_VObject_Property_DateTime('DTEND'); - $dtend->setDateTime($this->start,Sabre_VObject_Property_DateTime::UTC); + $dtend = new Property\DateTime('DTEND'); + $dtend->setDateTime($this->end,Property\DateTime::UTC); $vfreebusy->add($dtend); } - $dtstamp = new Sabre_VObject_Property_DateTime('DTSTAMP'); - $dtstamp->setDateTime(new DateTime('now'), Sabre_VObject_Property_DateTime::UTC); + $dtstamp = new Property\DateTime('DTSTAMP'); + $dtstamp->setDateTime(new \DateTime('now'), Property\DateTime::UTC); $vfreebusy->add($dtstamp); foreach($busyTimes as $busyTime) { - $busyTime[0]->setTimeZone(new DateTimeZone('UTC')); - $busyTime[1]->setTimeZone(new DateTimeZone('UTC')); + $busyTime[0]->setTimeZone(new \DateTimeZone('UTC')); + $busyTime[1]->setTimeZone(new \DateTimeZone('UTC')); - $prop = new Sabre_VObject_Property( + $prop = new Property( 'FREEBUSY', $busyTime[0]->format('Ymd\\THis\\Z') . '/' . $busyTime[1]->format('Ymd\\THis\\Z') ); diff --git a/3rdparty/Sabre/VObject/Node.php b/3rdparty/Sabre/VObject/Node.php index d89e01b56c6..5d2e1ce300f 100755..100644 --- a/3rdparty/Sabre/VObject/Node.php +++ b/3rdparty/Sabre/VObject/Node.php @@ -1,15 +1,20 @@ <?php +namespace Sabre\VObject; + /** * Base class for all nodes * - * @package Sabre - * @subpackage VObject * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ -abstract class Sabre_VObject_Node implements IteratorAggregate, ArrayAccess, Countable { +abstract class Node implements \IteratorAggregate, \ArrayAccess, \Countable { + + /** + * The following constants are used by the validate() method. + */ + const REPAIR = 1; /** * Turns the object back into a serialized blob. @@ -21,30 +26,53 @@ abstract class Sabre_VObject_Node implements IteratorAggregate, ArrayAccess, Cou /** * Iterator override * - * @var Sabre_VObject_ElementList + * @var ElementList */ protected $iterator = null; /** * A link to the parent node * - * @var Sabre_VObject_Node + * @var Node */ public $parent = null; + /** + * Validates the node for correctness. + * + * The following options are supported: + * - Node::REPAIR - If something is broken, and automatic repair may + * be attempted. + * + * An array is returned with warnings. + * + * Every item in the array has the following properties: + * * level - (number between 1 and 3 with severity information) + * * message - (human readable message) + * * node - (reference to the offending node) + * + * @param int $options + * @return array + */ + public function validate($options = 0) { + + return array(); + + } + /* {{{ IteratorAggregator interface */ /** * Returns the iterator for this object * - * @return Sabre_VObject_ElementList + * @return ElementList */ public function getIterator() { if (!is_null($this->iterator)) return $this->iterator; - return new Sabre_VObject_ElementList(array($this)); + return new ElementList(array($this)); } @@ -53,10 +81,10 @@ abstract class Sabre_VObject_Node implements IteratorAggregate, ArrayAccess, Cou * * Note that this is not actually part of the iterator interface * - * @param Sabre_VObject_ElementList $iterator + * @param ElementList $iterator * @return void */ - public function setIterator(Sabre_VObject_ElementList $iterator) { + public function setIterator(ElementList $iterator) { $this->iterator = $iterator; @@ -125,9 +153,14 @@ abstract class Sabre_VObject_Node implements IteratorAggregate, ArrayAccess, Cou public function offsetSet($offset,$value) { $iterator = $this->getIterator(); - return $iterator->offsetSet($offset,$value); + $iterator->offsetSet($offset,$value); + // @codeCoverageIgnoreStart + // + // This method always throws an exception, so we ignore the closing + // brace } + // @codeCoverageIgnoreEnd /** * Sets an item through ArrayAccess. @@ -140,9 +173,14 @@ abstract class Sabre_VObject_Node implements IteratorAggregate, ArrayAccess, Cou public function offsetUnset($offset) { $iterator = $this->getIterator(); - return $iterator->offsetUnset($offset); + $iterator->offsetUnset($offset); + // @codeCoverageIgnoreStart + // + // This method always throws an exception, so we ignore the closing + // brace } + // @codeCoverageIgnoreEnd /* }}} */ diff --git a/3rdparty/Sabre/VObject/Parameter.php b/3rdparty/Sabre/VObject/Parameter.php index 2e39af5f78a..d6d7c54c3bd 100755..100644 --- a/3rdparty/Sabre/VObject/Parameter.php +++ b/3rdparty/Sabre/VObject/Parameter.php @@ -1,5 +1,7 @@ <?php +namespace Sabre\VObject; + /** * VObject Parameter * @@ -8,13 +10,11 @@ * DTSTART;VALUE=DATE:20101108 * VALUE=DATE would be the parameter name and value. * - * @package Sabre - * @subpackage VObject * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ -class Sabre_VObject_Parameter extends Sabre_VObject_Node { +class Parameter extends Node { /** * Parameter name @@ -38,6 +38,10 @@ class Sabre_VObject_Parameter extends Sabre_VObject_Node { */ public function __construct($name, $value = null) { + if (!is_scalar($value) && !is_null($value)) { + throw new \InvalidArgumentException('The value argument must be a scalar value or null'); + } + $this->name = strtoupper($name); $this->value = $value; diff --git a/3rdparty/Sabre/VObject/ParseException.php b/3rdparty/Sabre/VObject/ParseException.php index 1b5e95bf16e..91386fec536 100755..100644 --- a/3rdparty/Sabre/VObject/ParseException.php +++ b/3rdparty/Sabre/VObject/ParseException.php @@ -1,12 +1,12 @@ <?php +namespace Sabre\VObject; + /** - * Exception thrown by Sabre_VObject_Reader if an invalid object was attempted to be parsed. + * Exception thrown by Reader if an invalid object was attempted to be parsed. * - * @package Sabre - * @subpackage VObject * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ -class Sabre_VObject_ParseException extends Exception { } +class ParseException extends \Exception { } diff --git a/3rdparty/Sabre/VObject/Property.php b/3rdparty/Sabre/VObject/Property.php index ce74fe3865b..6f364a73257 100755..100644 --- a/3rdparty/Sabre/VObject/Property.php +++ b/3rdparty/Sabre/VObject/Property.php @@ -1,5 +1,7 @@ <?php +namespace Sabre\VObject; + /** * VObject Property * @@ -11,13 +13,11 @@ * * Parameters can be accessed using the ArrayAccess interface. * - * @package Sabre - * @subpackage VObject * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ -class Sabre_VObject_Property extends Sabre_VObject_Element { +class Property extends Node { /** * Propertyname @@ -57,27 +57,37 @@ class Sabre_VObject_Property extends Sabre_VObject_Element { * @var array */ static public $classMap = array( - 'COMPLETED' => 'Sabre_VObject_Property_DateTime', - 'CREATED' => 'Sabre_VObject_Property_DateTime', - 'DTEND' => 'Sabre_VObject_Property_DateTime', - 'DTSTAMP' => 'Sabre_VObject_Property_DateTime', - 'DTSTART' => 'Sabre_VObject_Property_DateTime', - 'DUE' => 'Sabre_VObject_Property_DateTime', - 'EXDATE' => 'Sabre_VObject_Property_MultiDateTime', - 'LAST-MODIFIED' => 'Sabre_VObject_Property_DateTime', - 'RECURRENCE-ID' => 'Sabre_VObject_Property_DateTime', - 'TRIGGER' => 'Sabre_VObject_Property_DateTime', + 'COMPLETED' => 'Sabre\\VObject\\Property\\DateTime', + 'CREATED' => 'Sabre\\VObject\\Property\\DateTime', + 'DTEND' => 'Sabre\\VObject\\Property\\DateTime', + 'DTSTAMP' => 'Sabre\\VObject\\Property\\DateTime', + 'DTSTART' => 'Sabre\\VObject\\Property\\DateTime', + 'DUE' => 'Sabre\\VObject\\Property\\DateTime', + 'EXDATE' => 'Sabre\\VObject\\Property\\MultiDateTime', + 'LAST-MODIFIED' => 'Sabre\\VObject\\Property\\DateTime', + 'RECURRENCE-ID' => 'Sabre\\VObject\\Property\\DateTime', + 'TRIGGER' => 'Sabre\\VObject\\Property\\DateTime', + 'N' => 'Sabre\\VObject\\Property\\Compound', + 'ORG' => 'Sabre\\VObject\\Property\\Compound', + 'ADR' => 'Sabre\\VObject\\Property\\Compound', + 'CATEGORIES' => 'Sabre\\VObject\\Property\\Compound', ); /** * Creates the new property by name, but in addition will also see if * there's a class mapped to the property name. * + * Parameters can be specified with the optional third argument. Parameters + * must be a key->value map of the parameter name, and value. If the value + * is specified as an array, it is assumed that multiple parameters with + * the same name should be added. + * * @param string $name * @param string $value - * @return void + * @param array $parameters + * @return Property */ - static public function create($name, $value = null) { + static public function create($name, $value = null, array $parameters = array()) { $name = strtoupper($name); $shortName = $name; @@ -87,9 +97,9 @@ class Sabre_VObject_Property extends Sabre_VObject_Element { } if (isset(self::$classMap[$shortName])) { - return new self::$classMap[$shortName]($name, $value); + return new self::$classMap[$shortName]($name, $value, $parameters); } else { - return new self($name, $value); + return new self($name, $value, $parameters); } } @@ -97,14 +107,20 @@ class Sabre_VObject_Property extends Sabre_VObject_Element { /** * Creates a new property object * - * By default this object will iterate over its own children, but this can - * be overridden with the iterator argument + * Parameters can be specified with the optional third argument. Parameters + * must be a key->value map of the parameter name, and value. If the value + * is specified as an array, it is assumed that multiple parameters with + * the same name should be added. * * @param string $name * @param string $value - * @param Sabre_VObject_ElementList $iterator + * @param array $parameters */ - public function __construct($name, $value = null, $iterator = null) { + public function __construct($name, $value = null, array $parameters = array()) { + + if (!is_scalar($value) && !is_null($value)) { + throw new \InvalidArgumentException('The value argument must be scalar or null'); + } $name = strtoupper($name); $group = null; @@ -113,12 +129,21 @@ class Sabre_VObject_Property extends Sabre_VObject_Element { } $this->name = $name; $this->group = $group; - if (!is_null($iterator)) $this->iterator = $iterator; $this->setValue($value); - } + foreach($parameters as $paramName => $paramValues) { + if (!is_array($paramValues)) { + $paramValues = array($paramValues); + } + foreach($paramValues as $paramValue) { + $this->add($paramName, $paramValue); + } + + } + + } /** * Updates the internal value @@ -142,13 +167,12 @@ class Sabre_VObject_Property extends Sabre_VObject_Element { $str = $this->name; if ($this->group) $str = $this->group . '.' . $this->name; - if (count($this->parameters)) { - foreach($this->parameters as $param) { + foreach($this->parameters as $param) { - $str.=';' . $param->serialize(); + $str.=';' . $param->serialize(); - } } + $src = array( '\\', "\n", @@ -180,7 +204,7 @@ class Sabre_VObject_Property extends Sabre_VObject_Element { * * You can call this method with the following syntaxes: * - * add(Sabre_VObject_Parameter $element) + * add(Parameter $element) * add(string $name, $value) * * The first version adds an Parameter @@ -192,24 +216,21 @@ class Sabre_VObject_Property extends Sabre_VObject_Element { */ public function add($item, $itemValue = null) { - if ($item instanceof Sabre_VObject_Parameter) { + if ($item instanceof Parameter) { if (!is_null($itemValue)) { - throw new InvalidArgumentException('The second argument must not be specified, when passing a VObject'); + throw new \InvalidArgumentException('The second argument must not be specified, when passing a VObject'); } $item->parent = $this; $this->parameters[] = $item; } elseif(is_string($item)) { - if (!is_scalar($itemValue) && !is_null($itemValue)) { - throw new InvalidArgumentException('The second argument must be scalar'); - } - $parameter = new Sabre_VObject_Parameter($item,$itemValue); + $parameter = new Parameter($item,$itemValue); $parameter->parent = $this; $this->parameters[] = $parameter; } else { - throw new InvalidArgumentException('The first argument must either be a Sabre_VObject_Element or a string'); + throw new \InvalidArgumentException('The first argument must either be a Node a string'); } @@ -240,7 +261,7 @@ class Sabre_VObject_Property extends Sabre_VObject_Element { * Returns a parameter, or parameter list. * * @param string $name - * @return Sabre_VObject_Element + * @return Node */ public function offsetGet($name) { @@ -258,7 +279,7 @@ class Sabre_VObject_Property extends Sabre_VObject_Element { } elseif (count($result)===1) { return $result[0]; } else { - $result[0]->setIterator(new Sabre_VObject_ElementList($result)); + $result[0]->setIterator(new ElementList($result)); return $result[0]; } @@ -273,25 +294,25 @@ class Sabre_VObject_Property extends Sabre_VObject_Element { */ public function offsetSet($name, $value) { - if (is_int($name)) return parent::offsetSet($name, $value); + if (is_int($name)) parent::offsetSet($name, $value); if (is_scalar($value)) { if (!is_string($name)) - throw new InvalidArgumentException('A parameter name must be specified. This means you cannot use the $array[]="string" to add parameters.'); + throw new \InvalidArgumentException('A parameter name must be specified. This means you cannot use the $array[]="string" to add parameters.'); $this->offsetUnset($name); - $parameter = new Sabre_VObject_Parameter($name, $value); + $parameter = new Parameter($name, $value); $parameter->parent = $this; $this->parameters[] = $parameter; - } elseif ($value instanceof Sabre_VObject_Parameter) { + } elseif ($value instanceof Parameter) { if (!is_null($name)) - throw new InvalidArgumentException('Don\'t specify a parameter name if you\'re passing a Sabre_VObject_Parameter. Add using $array[]=$parameterObject.'); + throw new \InvalidArgumentException('Don\'t specify a parameter name if you\'re passing a \\Sabre\\VObject\\Parameter. Add using $array[]=$parameterObject.'); $value->parent = $this; $this->parameters[] = $value; } else { - throw new InvalidArgumentException('You can only add parameters to the property object'); + throw new \InvalidArgumentException('You can only add parameters to the property object'); } } @@ -304,7 +325,7 @@ class Sabre_VObject_Property extends Sabre_VObject_Element { */ public function offsetUnset($name) { - if (is_int($name)) return parent::offsetUnset($name); + if (is_int($name)) parent::offsetUnset($name); $name = strtoupper($name); foreach($this->parameters as $key=>$parameter) { @@ -345,4 +366,65 @@ class Sabre_VObject_Property extends Sabre_VObject_Element { } + /** + * Validates the node for correctness. + * + * The following options are supported: + * - Node::REPAIR - If something is broken, and automatic repair may + * be attempted. + * + * An array is returned with warnings. + * + * Every item in the array has the following properties: + * * level - (number between 1 and 3 with severity information) + * * message - (human readable message) + * * node - (reference to the offending node) + * + * @param int $options + * @return array + */ + public function validate($options = 0) { + + $warnings = array(); + + // Checking if our value is UTF-8 + if (!StringUtil::isUTF8($this->value)) { + $warnings[] = array( + 'level' => 1, + 'message' => 'Property is not valid UTF-8!', + 'node' => $this, + ); + if ($options & self::REPAIR) { + $this->value = StringUtil::convertToUTF8($this->value); + } + } + + // Checking if the propertyname does not contain any invalid bytes. + if (!preg_match('/^([A-Z0-9-]+)$/', $this->name)) { + $warnings[] = array( + 'level' => 1, + 'message' => 'The propertyname: ' . $this->name . ' contains invalid characters. Only A-Z, 0-9 and - are allowed', + 'node' => $this, + ); + if ($options & self::REPAIR) { + // Uppercasing and converting underscores to dashes. + $this->name = strtoupper( + str_replace('_', '-', $this->name) + ); + // Removing every other invalid character + $this->name = preg_replace('/([^A-Z0-9-])/u', '', $this->name); + + } + + } + + // Validating inner parameters + foreach($this->parameters as $param) { + $warnings = array_merge($warnings, $param->validate($options)); + } + + return $warnings; + + } + } diff --git a/3rdparty/Sabre/VObject/Property/Compound.php b/3rdparty/Sabre/VObject/Property/Compound.php new file mode 100644 index 00000000000..e2c18e7726e --- /dev/null +++ b/3rdparty/Sabre/VObject/Property/Compound.php @@ -0,0 +1,129 @@ +<?php + +namespace Sabre\VObject\Property; + +use Sabre\VObject; + +/** +* Compound property. +* +* This class adds (de)serialization of compound properties to/from arrays. +* +* Currently the following properties from RFC 6350 are mapped to use this +* class: +* +* N: Section 6.2.2 +* ADR: Section 6.3.1 +* ORG: Section 6.6.4 +* CATEGORIES: Section 6.7.1 +* +* In order to use this correctly, you must call setParts and getParts to +* retrieve and modify dates respectively. +* +* @author Thomas Tanghus (http://tanghus.net/) +* @author Lars Kneschke +* @author Evert Pot (http://www.rooftopsolutions.nl/) +* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. +* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License +*/ + +/** +* This class represents a compound property in a vCard. +*/ +class Compound extends VObject\Property { + + /** + * If property names are added to this map, they will be (de)serialised as arrays + * using the getParts() and setParts() methods. + * The keys are the property names, values are delimiter chars. + * + * @var array + */ + static public $delimiterMap = array( + 'N' => ';', + 'ADR' => ';', + 'ORG' => ';', + 'CATEGORIES' => ',', + ); + + /** + * The currently used delimiter. + * + * @var string + */ + protected $delimiter = null; + + /** + * Get a compound value as an array. + * + * @param $name string + * @return array + */ + public function getParts() { + + if (is_null($this->value)) { + return array(); + } + + $delimiter = $this->getDelimiter(); + + // split by any $delimiter which is NOT prefixed by a slash. + // Note that this is not a a perfect solution. If a value is prefixed + // by two slashes, it should actually be split anyway. + // + // Hopefully we can fix this better in a future version, where we can + // break compatibility a bit. + $compoundValues = preg_split("/(?<!\\\)$delimiter/", $this->value); + + // remove slashes from any semicolon and comma left escaped in the single values + $compoundValues = array_map( + function($val) { + return strtr($val, array('\,' => ',', '\;' => ';')); + }, $compoundValues); + + return $compoundValues; + + } + + /** + * Returns the delimiter for this property. + * + * @return string + */ + public function getDelimiter() { + + if (!$this->delimiter) { + if (isset(self::$delimiterMap[$this->name])) { + $this->delimiter = self::$delimiterMap[$this->name]; + } else { + // To be a bit future proof, we are going to default the + // delimiter to ; + $this->delimiter = ';'; + } + } + return $this->delimiter; + + } + + /** + * Set a compound value as an array. + * + * + * @param $name string + * @return array + */ + public function setParts(array $values) { + + // add slashes to all semicolons and commas in the single values + $values = array_map( + function($val) { + return strtr($val, array(',' => '\,', ';' => '\;')); + }, $values); + + $this->setValue( + implode($this->getDelimiter(), $values) + ); + + } + +} diff --git a/3rdparty/Sabre/VObject/Property/DateTime.php b/3rdparty/Sabre/VObject/Property/DateTime.php index fe2372caa81..556cd441d8a 100755..100644 --- a/3rdparty/Sabre/VObject/Property/DateTime.php +++ b/3rdparty/Sabre/VObject/Property/DateTime.php @@ -1,5 +1,9 @@ <?php +namespace Sabre\VObject\Property; + +use Sabre\VObject; + /** * DateTime property * @@ -13,13 +17,11 @@ * If you use the 'value' or properties directly, this object does not keep * reference and results might appear incorrectly. * - * @package Sabre - * @subpackage VObject * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ -class Sabre_VObject_Property_DateTime extends Sabre_VObject_Property { +class DateTime extends VObject\Property { /** * Local 'floating' time @@ -44,7 +46,7 @@ class Sabre_VObject_Property_DateTime extends Sabre_VObject_Property { /** * DateTime representation * - * @var DateTime + * @var \DateTime */ protected $dateTime; @@ -58,11 +60,11 @@ class Sabre_VObject_Property_DateTime extends Sabre_VObject_Property { /** * Updates the Date and Time. * - * @param DateTime $dt + * @param \DateTime $dt * @param int $dateType * @return void */ - public function setDateTime(DateTime $dt, $dateType = self::LOCALTZ) { + public function setDateTime(\DateTime $dt, $dateType = self::LOCALTZ) { switch($dateType) { @@ -73,7 +75,7 @@ class Sabre_VObject_Property_DateTime extends Sabre_VObject_Property { $this->offsetSet('VALUE','DATE-TIME'); break; case self::UTC : - $dt->setTimeZone(new DateTimeZone('UTC')); + $dt->setTimeZone(new \DateTimeZone('UTC')); $this->setValue($dt->format('Ymd\\THis\\Z')); $this->offsetUnset('VALUE'); $this->offsetUnset('TZID'); @@ -93,7 +95,7 @@ class Sabre_VObject_Property_DateTime extends Sabre_VObject_Property { $this->offsetSet('VALUE','DATE'); break; default : - throw new InvalidArgumentException('You must pass a valid dateType constant'); + throw new \InvalidArgumentException('You must pass a valid dateType constant'); } $this->dateTime = $dt; @@ -106,7 +108,7 @@ class Sabre_VObject_Property_DateTime extends Sabre_VObject_Property { * * If no value was set, this method returns null. * - * @return DateTime|null + * @return \DateTime|null */ public function getDateTime() { @@ -152,11 +154,11 @@ class Sabre_VObject_Property_DateTime extends Sabre_VObject_Property { * * @param string|null $propertyValue The string to parse (yymmdd or * ymmddThhmmss, etc..) - * @param Sabre_VObject_Property|null $property The instance of the + * @param \Sabre\VObject\Property|null $property The instance of the * property we're parsing. * @return array */ - static public function parseData($propertyValue, Sabre_VObject_Property $property = null) { + static public function parseData($propertyValue, VObject\Property $property = null) { if (is_null($propertyValue)) { return array(null, null); @@ -167,14 +169,14 @@ class Sabre_VObject_Property_DateTime extends Sabre_VObject_Property { $regex = "/^$date(T$time(?P<isutc>Z)?)?$/"; if (!preg_match($regex, $propertyValue, $matches)) { - throw new InvalidArgumentException($propertyValue . ' is not a valid DateTime or Date string'); + throw new \InvalidArgumentException($propertyValue . ' is not a valid \DateTime or Date string'); } if (!isset($matches['hour'])) { // Date-only return array( self::DATE, - new DateTime($matches['year'] . '-' . $matches['month'] . '-' . $matches['date'] . ' 00:00:00'), + new \DateTime($matches['year'] . '-' . $matches['month'] . '-' . $matches['date'] . ' 00:00:00', new \DateTimeZone('UTC')), ); } @@ -187,8 +189,8 @@ class Sabre_VObject_Property_DateTime extends Sabre_VObject_Property { $matches['second']; if (isset($matches['isutc'])) { - $dt = new DateTime($dateStr,new DateTimeZone('UTC')); - $dt->setTimeZone(new DateTimeZone('UTC')); + $dt = new \DateTime($dateStr,new \DateTimeZone('UTC')); + $dt->setTimeZone(new \DateTimeZone('UTC')); return array( self::UTC, $dt @@ -198,56 +200,27 @@ class Sabre_VObject_Property_DateTime extends Sabre_VObject_Property { // Finding the timezone. $tzid = $property['TZID']; if (!$tzid) { + // This was a floating time string. This implies we use the + // timezone from date_default_timezone_set / date.timezone ini + // setting. return array( self::LOCAL, - new DateTime($dateStr) + new \DateTime($dateStr) ); } - try { - // tzid an Olson identifier? - $tz = new DateTimeZone($tzid->value); - } catch (Exception $e) { - - // Not an Olson id, we're going to try to find the information - // through the time zone name map. - $newtzid = Sabre_VObject_WindowsTimezoneMap::lookup($tzid->value); - if (is_null($newtzid)) { - - // Not a well known time zone name either, we're going to try - // to find the information through the VTIMEZONE object. - - // First we find the root object - $root = $property; - while($root->parent) { - $root = $root->parent; - } - - if (isset($root->VTIMEZONE)) { - foreach($root->VTIMEZONE as $vtimezone) { - if (((string)$vtimezone->TZID) == $tzid) { - if (isset($vtimezone->{'X-LIC-LOCATION'})) { - $newtzid = (string)$vtimezone->{'X-LIC-LOCATION'}; - } else { - // No libical location specified. As a last resort we could - // try matching $vtimezone's DST rules against all known - // time zones returned by DateTimeZone::list* - - // TODO - } - } - } - } - } - - try { - $tz = new DateTimeZone($newtzid); - } catch (Exception $e) { - // If all else fails, we use the default PHP timezone - $tz = new DateTimeZone(date_default_timezone_get()); - } + // To look up the timezone, we must first find the VCALENDAR component. + $root = $property; + while($root->parent) { + $root = $root->parent; } - $dt = new DateTime($dateStr, $tz); + if ($root->name === 'VCALENDAR') { + $tz = VObject\TimeZoneUtil::getTimeZone((string)$tzid, $root); + } else { + $tz = VObject\TimeZoneUtil::getTimeZone((string)$tzid); + } + + $dt = new \DateTime($dateStr, $tz); $dt->setTimeZone($tz); return array( diff --git a/3rdparty/Sabre/VObject/Property/MultiDateTime.php b/3rdparty/Sabre/VObject/Property/MultiDateTime.php index ae53ab6a617..629ef4a1340 100755..100644 --- a/3rdparty/Sabre/VObject/Property/MultiDateTime.php +++ b/3rdparty/Sabre/VObject/Property/MultiDateTime.php @@ -1,5 +1,9 @@ <?php +namespace Sabre\VObject\Property; + +use Sabre\VObject; + /** * Multi-DateTime property * @@ -13,13 +17,11 @@ * If you use the 'value' or properties directly, this object does not keep * reference and results might appear incorrectly. * - * @package Sabre - * @subpackage VObject * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ -class Sabre_VObject_Property_MultiDateTime extends Sabre_VObject_Property { +class MultiDateTime extends VObject\Property { /** * DateTime representation @@ -31,7 +33,7 @@ class Sabre_VObject_Property_MultiDateTime extends Sabre_VObject_Property { /** * dateType * - * This is one of the Sabre_VObject_Property_DateTime constants. + * This is one of the Sabre\VObject\Property\DateTime constants. * * @var int */ @@ -44,17 +46,17 @@ class Sabre_VObject_Property_MultiDateTime extends Sabre_VObject_Property { * @param int $dateType * @return void */ - public function setDateTimes(array $dt, $dateType = Sabre_VObject_Property_DateTime::LOCALTZ) { + public function setDateTimes(array $dt, $dateType = VObject\Property\DateTime::LOCALTZ) { foreach($dt as $i) - if (!$i instanceof DateTime) - throw new InvalidArgumentException('You must pass an array of DateTime objects'); + if (!$i instanceof \DateTime) + throw new \InvalidArgumentException('You must pass an array of DateTime objects'); $this->offsetUnset('VALUE'); $this->offsetUnset('TZID'); switch($dateType) { - case Sabre_VObject_Property_DateTime::LOCAL : + case DateTime::LOCAL : $val = array(); foreach($dt as $i) { $val[] = $i->format('Ymd\\THis'); @@ -62,16 +64,16 @@ class Sabre_VObject_Property_MultiDateTime extends Sabre_VObject_Property { $this->setValue(implode(',',$val)); $this->offsetSet('VALUE','DATE-TIME'); break; - case Sabre_VObject_Property_DateTime::UTC : + case DateTime::UTC : $val = array(); foreach($dt as $i) { - $i->setTimeZone(new DateTimeZone('UTC')); + $i->setTimeZone(new \DateTimeZone('UTC')); $val[] = $i->format('Ymd\\THis\\Z'); } $this->setValue(implode(',',$val)); $this->offsetSet('VALUE','DATE-TIME'); break; - case Sabre_VObject_Property_DateTime::LOCALTZ : + case DateTime::LOCALTZ : $val = array(); foreach($dt as $i) { $val[] = $i->format('Ymd\\THis'); @@ -80,7 +82,7 @@ class Sabre_VObject_Property_MultiDateTime extends Sabre_VObject_Property { $this->offsetSet('VALUE','DATE-TIME'); $this->offsetSet('TZID', $dt[0]->getTimeZone()->getName()); break; - case Sabre_VObject_Property_DateTime::DATE : + case DateTime::DATE : $val = array(); foreach($dt as $i) { $val[] = $i->format('Ymd'); @@ -89,7 +91,7 @@ class Sabre_VObject_Property_MultiDateTime extends Sabre_VObject_Property { $this->offsetSet('VALUE','DATE'); break; default : - throw new InvalidArgumentException('You must pass a valid dateType constant'); + throw new \InvalidArgumentException('You must pass a valid dateType constant'); } $this->dateTimes = $dt; @@ -121,7 +123,7 @@ class Sabre_VObject_Property_MultiDateTime extends Sabre_VObject_Property { list( $type, $dt - ) = Sabre_VObject_Property_DateTime::parseData($val, $this); + ) = DateTime::parseData($val, $this); $dts[] = $dt; $this->dateType = $type; } @@ -154,7 +156,7 @@ class Sabre_VObject_Property_MultiDateTime extends Sabre_VObject_Property { list( $type, $dt - ) = Sabre_VObject_Property_DateTime::parseData($val, $this); + ) = DateTime::parseData($val, $this); $dts[] = $dt; $this->dateType = $type; } diff --git a/3rdparty/Sabre/VObject/Reader.php b/3rdparty/Sabre/VObject/Reader.php index eea73fa3dce..a24590cb388 100755..100644 --- a/3rdparty/Sabre/VObject/Reader.php +++ b/3rdparty/Sabre/VObject/Reader.php @@ -1,5 +1,7 @@ <?php +namespace Sabre\VObject; + /** * VCALENDAR/VCARD reader * @@ -8,21 +10,38 @@ * TODO: this class currently completely works 'statically'. This is pointless, * and defeats OOP principals. Needs refactoring in a future version. * - * @package Sabre - * @subpackage VObject * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ -class Sabre_VObject_Reader { +class Reader { + + /** + * If this option is passed to the reader, it will be less strict about the + * validity of the lines. + * + * Currently using this option just means, that it will accept underscores + * in property names. + */ + const OPTION_FORGIVING = 1; + + /** + * If this option is turned on, any lines we cannot parse will be ignored + * by the reader. + */ + const OPTION_IGNORE_INVALID_LINES = 2; /** * Parses the file and returns the top component * + * The options argument is a bitfield. Pass any of the OPTIONS constant to + * alter the parsers' behaviour. + * * @param string $data - * @return Sabre_VObject_Element + * @param int $options + * @return Node */ - static function read($data) { + static function read($data, $options = 0) { // Normalizing newlines $data = str_replace(array("\r","\n\n"), array("\n","\n"), $data); @@ -48,7 +67,7 @@ class Sabre_VObject_Reader { reset($lines2); - return self::readLine($lines2); + return self::readLine($lines2, $options); } @@ -58,37 +77,45 @@ class Sabre_VObject_Reader { * This method receives the full array of lines. The array pointer is used * to traverse. * + * This method returns null if an invalid line was encountered, and the + * IGNORE_INVALID_LINES option was turned on. + * * @param array $lines - * @return Sabre_VObject_Element + * @param int $options See the OPTIONS constants. + * @return Node */ - static private function readLine(&$lines) { + static private function readLine(&$lines, $options = 0) { $line = current($lines); $lineNr = key($lines); next($lines); // Components - if (stripos($line,"BEGIN:")===0) { + if (strtoupper(substr($line,0,6)) === "BEGIN:") { $componentName = strtoupper(substr($line,6)); - $obj = Sabre_VObject_Component::create($componentName); + $obj = Component::create($componentName); $nextLine = current($lines); - while(stripos($nextLine,"END:")!==0) { - - $obj->add(self::readLine($lines)); + while(strtoupper(substr($nextLine,0,4))!=="END:") { + $parsedLine = self::readLine($lines, $options); $nextLine = current($lines); + if (is_null($parsedLine)) { + continue; + } + $obj->add($parsedLine); + if ($nextLine===false) - throw new Sabre_VObject_ParseException('Invalid VObject. Document ended prematurely.'); + throw new ParseException('Invalid VObject. Document ended prematurely.'); } // Checking component name of the 'END:' line. if (substr($nextLine,4)!==$obj->name) { - throw new Sabre_VObject_ParseException('Invalid VObject, expected: "END:' . $obj->name . '" got: "' . $nextLine . '"'); + throw new ParseException('Invalid VObject, expected: "END:' . $obj->name . '" got: "' . $nextLine . '"'); } next($lines); @@ -99,19 +126,26 @@ class Sabre_VObject_Reader { // Properties //$result = preg_match('/(?P<name>[A-Z0-9-]+)(?:;(?P<parameters>^(?<!:):))(.*)$/',$line,$matches); - - $token = '[A-Z0-9-\.]+'; + if ($options & self::OPTION_FORGIVING) { + $token = '[A-Z0-9-\._]+'; + } else { + $token = '[A-Z0-9-\.]+'; + } $parameters = "(?:;(?P<parameters>([^:^\"]|\"([^\"]*)\")*))?"; $regex = "/^(?P<name>$token)$parameters:(?P<value>.*)$/i"; $result = preg_match($regex,$line,$matches); if (!$result) { - throw new Sabre_VObject_ParseException('Invalid VObject, line ' . ($lineNr+1) . ' did not follow the icalendar/vcard format'); + if ($options & self::OPTION_IGNORE_INVALID_LINES) { + return null; + } else { + throw new ParseException('Invalid VObject, line ' . ($lineNr+1) . ' did not follow the icalendar/vcard format'); + } } $propertyName = strtoupper($matches['name']); - $propertyValue = preg_replace_callback('#(\\\\(\\\\|N|n|;|,))#',function($matches) { + $propertyValue = preg_replace_callback('#(\\\\(\\\\|N|n))#',function($matches) { if ($matches[2]==='n' || $matches[2]==='N') { return "\n"; } else { @@ -119,7 +153,7 @@ class Sabre_VObject_Reader { } }, $matches['value']); - $obj = Sabre_VObject_Property::create($propertyName, $propertyValue); + $obj = Property::create($propertyName, $propertyValue); if ($matches['parameters']) { @@ -137,7 +171,7 @@ class Sabre_VObject_Reader { /** * Reads a parameter list from a property * - * This method returns an array of Sabre_VObject_Parameter + * This method returns an array of Parameter * * @param string $parameters * @return array @@ -171,7 +205,7 @@ class Sabre_VObject_Reader { } }, $value); - $params[] = new Sabre_VObject_Parameter($match['paramName'], $value); + $params[] = new Parameter($match['paramName'], $value); } diff --git a/3rdparty/Sabre/VObject/RecurrenceIterator.php b/3rdparty/Sabre/VObject/RecurrenceIterator.php index 740270dd8f0..46c7f447806 100755..100644 --- a/3rdparty/Sabre/VObject/RecurrenceIterator.php +++ b/3rdparty/Sabre/VObject/RecurrenceIterator.php @@ -1,5 +1,7 @@ <?php +namespace Sabre\VObject; + /** * This class is used to determine new for a recurring event, when the next * events occur. @@ -37,13 +39,11 @@ * you may get unexpected results. The effect is that in some applications the * specified recurrence may look incorrect, or is missing. * - * @package Sabre - * @subpackage VObject * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ -class Sabre_VObject_RecurrenceIterator implements Iterator { +class RecurrenceIterator implements \Iterator { /** * The initial event date @@ -82,7 +82,7 @@ class Sabre_VObject_RecurrenceIterator implements Iterator { /** * Base event * - * @var Sabre_VObject_Component_VEvent + * @var Component\VEvent */ public $baseEvent; @@ -97,7 +97,7 @@ class Sabre_VObject_RecurrenceIterator implements Iterator { /** * list of events that are 'overridden'. * - * This is an array of Sabre_VObject_Component_VEvent objects. + * This is an array of Component\VEvent objects. * * @var array */ @@ -281,7 +281,7 @@ class Sabre_VObject_RecurrenceIterator implements Iterator { * If the current iteration of the event is an overriden event, this * property will hold the VObject * - * @var Sabre_Component_VObject + * @var Component */ private $currentOverriddenEvent; @@ -300,14 +300,14 @@ class Sabre_VObject_RecurrenceIterator implements Iterator { * You should pass a VCALENDAR component, as well as the UID of the event * we're going to traverse. * - * @param Sabre_VObject_Component $vcal + * @param Component $vcal * @param string|null $uid */ - public function __construct(Sabre_VObject_Component $vcal, $uid=null) { + public function __construct(Component $vcal, $uid=null) { if (is_null($uid)) { if ($vcal->name === 'VCALENDAR') { - throw new InvalidArgumentException('If you pass a VCALENDAR object, you must pass a uid argument as well'); + throw new \InvalidArgumentException('If you pass a VCALENDAR object, you must pass a uid argument as well'); } $components = array($vcal); $uid = (string)$vcal->uid; @@ -325,7 +325,7 @@ class Sabre_VObject_RecurrenceIterator implements Iterator { } } if (!$this->baseEvent) { - throw new InvalidArgumentException('Could not find a base event with uid: ' . $uid); + throw new \InvalidArgumentException('Could not find a base event with uid: ' . $uid); } $this->startDate = clone $this->baseEvent->DTSTART->getDateTime(); @@ -336,8 +336,8 @@ class Sabre_VObject_RecurrenceIterator implements Iterator { } else { $this->endDate = clone $this->startDate; if (isset($this->baseEvent->DURATION)) { - $this->endDate->add(Sabre_VObject_DateTimeParser::parse($this->baseEvent->DURATION->value)); - } elseif ($this->baseEvent->DTSTART->getDateType()===Sabre_VObject_Property_DateTime::DATE) { + $this->endDate->add(DateTimeParser::parse($this->baseEvent->DURATION->value)); + } elseif ($this->baseEvent->DTSTART->getDateType()===Property\DateTime::DATE) { $this->endDate->modify('+1 day'); } } @@ -347,7 +347,11 @@ class Sabre_VObject_RecurrenceIterator implements Iterator { $parts = explode(';', $rrule); - foreach($parts as $part) { + // If no rrule was specified, we create a default setting + if (!$rrule) { + $this->frequency = 'daily'; + $this->count = 1; + } else foreach($parts as $part) { list($key, $value) = explode('=', $part, 2); @@ -358,14 +362,14 @@ class Sabre_VObject_RecurrenceIterator implements Iterator { strtolower($value), array('secondly','minutely','hourly','daily','weekly','monthly','yearly') )) { - throw new InvalidArgumentException('Unknown value for FREQ=' . strtoupper($value)); + throw new \InvalidArgumentException('Unknown value for FREQ=' . strtoupper($value)); } $this->frequency = strtolower($value); break; case 'UNTIL' : - $this->until = Sabre_VObject_DateTimeParser::parse($value); + $this->until = DateTimeParser::parse($value); break; case 'COUNT' : @@ -374,6 +378,9 @@ class Sabre_VObject_RecurrenceIterator implements Iterator { case 'INTERVAL' : $this->interval = (int)$value; + if ($this->interval < 1) { + throw new \InvalidArgumentException('INTERVAL in RRULE must be a positive integer!'); + } break; case 'BYSECOND' : @@ -427,7 +434,7 @@ class Sabre_VObject_RecurrenceIterator implements Iterator { foreach(explode(',', (string)$exDate) as $exceptionDate) { $this->exceptionDates[] = - Sabre_VObject_DateTimeParser::parse($exceptionDate, $this->startDate->getTimeZone()); + DateTimeParser::parse($exceptionDate, $this->startDate->getTimeZone()); } @@ -485,7 +492,7 @@ class Sabre_VObject_RecurrenceIterator implements Iterator { * * This method always returns a cloned instance. * - * @return void + * @return Component\VEvent */ public function getEventObject() { @@ -561,7 +568,7 @@ class Sabre_VObject_RecurrenceIterator implements Iterator { * @param DateTime $dt * @return void */ - public function fastForward(DateTime $dt) { + public function fastForward(\DateTime $dt) { while($this->valid() && $this->getDTEnd() <= $dt) { $this->next(); @@ -570,6 +577,17 @@ class Sabre_VObject_RecurrenceIterator implements Iterator { } /** + * Returns true if this recurring event never ends. + * + * @return bool + */ + public function isInfinite() { + + return !$this->count && !$this->until; + + } + + /** * Goes on to the next iteration * * @return void @@ -632,7 +650,7 @@ class Sabre_VObject_RecurrenceIterator implements Iterator { } - // Checking overriden events + // Checking overridden events foreach($this->overriddenEvents as $index=>$event) { if ($index > $previousStamp && $index <= $currentStamp) { @@ -880,7 +898,7 @@ class Sabre_VObject_RecurrenceIterator implements Iterator { // The first occurrence that's higher than the current // day of the month wins. // If we advanced to the next month or year, the first - // occurence is always correct. + // occurrence is always correct. if ($occurrence > $currentDayOfMonth || $advancedToNewMonth) { break 2; } diff --git a/3rdparty/Sabre/VObject/Splitter/ICalendar.php b/3rdparty/Sabre/VObject/Splitter/ICalendar.php new file mode 100644 index 00000000000..7a9a63e1b21 --- /dev/null +++ b/3rdparty/Sabre/VObject/Splitter/ICalendar.php @@ -0,0 +1,111 @@ +<?php + +namespace Sabre\VObject\Splitter; + +use Sabre\VObject; + +/** + * Splitter + * + * This class is responsible for splitting up iCalendar objects. + * + * This class expects a single VCALENDAR object with one or more + * calendar-objects inside. Objects with identical UID's will be combined into + * a single object. + * + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. + * @author Dominik Tobschall + * @author Armin Hackmann + * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License + */ +class ICalendar implements SplitterInterface { + + /** + * Timezones + * + * @var array + */ + protected $vtimezones = array(); + + /** + * iCalendar objects + * + * @var array + */ + protected $objects = array(); + + /** + * Constructor + * + * The splitter should receive an readable file stream as it's input. + * + * @param resource $input + */ + public function __construct($input) { + + $data = VObject\Reader::read(stream_get_contents($input)); + $vtimezones = array(); + $components = array(); + + foreach($data->children as $component) { + if (!$component instanceof VObject\Component) { + continue; + } + + // Get all timezones + if ($component->name === 'VTIMEZONE') { + $this->vtimezones[(string)$component->TZID] = $component; + continue; + } + + // Get component UID for recurring Events search + if($component->UID) { + $uid = (string)$component->UID; + } else { + // Generating a random UID + $uid = sha1(microtime()) . '-vobjectimport'; + } + + // Take care of recurring events + if (!array_key_exists($uid, $this->objects)) { + $this->objects[$uid] = VObject\Component::create('VCALENDAR'); + } + + $this->objects[$uid]->add(clone $component); + } + + } + + /** + * Every time getNext() is called, a new object will be parsed, until we + * hit the end of the stream. + * + * When the end is reached, null will be returned. + * + * @return Sabre\VObject\Component|null + */ + public function getNext() { + + if($object=array_shift($this->objects)) { + + // create our baseobject + $object->version = '2.0'; + $object->prodid = '-//Sabre//Sabre VObject ' . VObject\Version::VERSION . '//EN'; + $object->calscale = 'GREGORIAN'; + + // add vtimezone information to obj (if we have it) + foreach ($this->vtimezones as $vtimezone) { + $object->add($vtimezone); + } + + return $object; + + } else { + + return null; + + } + + } + +} diff --git a/3rdparty/Sabre/VObject/Splitter/SplitterInterface.php b/3rdparty/Sabre/VObject/Splitter/SplitterInterface.php new file mode 100644 index 00000000000..9f7a82450e3 --- /dev/null +++ b/3rdparty/Sabre/VObject/Splitter/SplitterInterface.php @@ -0,0 +1,39 @@ +<?php + +namespace Sabre\VObject\Splitter; + +/** + * VObject splitter + * + * The splitter is responsible for reading a large vCard or iCalendar object, + * and splitting it into multiple objects. + * + * This is for example for Card and CalDAV, which require every event and vcard + * to exist in their own objects, instead of one large one. + * + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. + * @author Dominik Tobschall + * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License + */ +interface SplitterInterface { + + /** + * Constructor + * + * The splitter should receive an readable file stream as it's input. + * + * @param resource $input + */ + function __construct($input); + + /** + * Every time getNext() is called, a new object will be parsed, until we + * hit the end of the stream. + * + * When the end is reached, null will be returned. + * + * @return Sabre\VObject\Component|null + */ + function getNext(); + +} diff --git a/3rdparty/Sabre/VObject/Splitter/VCard.php b/3rdparty/Sabre/VObject/Splitter/VCard.php new file mode 100644 index 00000000000..829491ed07a --- /dev/null +++ b/3rdparty/Sabre/VObject/Splitter/VCard.php @@ -0,0 +1,76 @@ +<?php + +namespace Sabre\VObject\Splitter; + +use Sabre\VObject; + +/** + * Splitter + * + * This class is responsible for splitting up VCard objects. + * + * It is assumed that the input stream contains 1 or more VCARD objects. This + * class checks for BEGIN:VCARD and END:VCARD and parses each encountered + * component individually. + * + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. + * @author Dominik Tobschall + * @author Armin Hackmann + * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License + */ +class VCard implements SplitterInterface { + + /** + * File handle + * + * @var resource + */ + protected $input; + + /** + * Constructor + * + * The splitter should receive an readable file stream as it's input. + * + * @param resource $input + */ + public function __construct($input) { + + $this->input = $input; + + } + + /** + * Every time getNext() is called, a new object will be parsed, until we + * hit the end of the stream. + * + * When the end is reached, null will be returned. + * + * @return Sabre\VObject\Component|null + */ + public function getNext() { + + $vcard = ''; + + do { + + if (feof($this->input)) { + return false; + } + + $line = fgets($this->input); + $vcard .= $line; + + } while(strtoupper(substr($line,0,4))!=="END:"); + + $object = VObject\Reader::read($vcard); + + if($object->name !== 'VCARD') { + throw new \InvalidArgumentException("Thats no vCard!", 1); + } + + return $object; + + } + +} diff --git a/3rdparty/Sabre/VObject/StringUtil.php b/3rdparty/Sabre/VObject/StringUtil.php new file mode 100644 index 00000000000..886a7135d67 --- /dev/null +++ b/3rdparty/Sabre/VObject/StringUtil.php @@ -0,0 +1,61 @@ +<?php + +namespace Sabre\VObject; + +/** + * Useful utilities for working with various strings. + * + * @copyright Copyright (C) 2007-2012 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 StringUtil { + + /** + * Returns true or false depending on if a string is valid UTF-8 + * + * @param string $str + * @return bool + */ + static function isUTF8($str) { + + // First check.. mb_check_encoding + if (!mb_check_encoding($str, 'UTF-8')) { + return false; + } + + // Control characters + if (preg_match('%(?:[\x00-\x08\x0B-\x0C\x0E\x0F])%', $str)) { + return false; + } + + return true; + + } + + /** + * This method tries its best to convert the input string to UTF-8. + * + * Currently only ISO-5991-1 input and UTF-8 input is supported, but this + * may be expanded upon if we receive other examples. + * + * @param string $str + * @return string + */ + static function convertToUTF8($str) { + + $encoding = mb_detect_encoding($str , array('UTF-8','ISO-8859-1'), true); + + if ($encoding === 'ISO-8859-1') { + $newStr = utf8_encode($str); + } else { + $newStr = $str; + } + + // Removing any control characters + return (preg_replace('%(?:[\x00-\x08\x0B-\x0C\x0E\x0F])%', '', $newStr)); + + } + +} + diff --git a/3rdparty/Sabre/VObject/TimeZoneUtil.php b/3rdparty/Sabre/VObject/TimeZoneUtil.php new file mode 100644 index 00000000000..ba73cf34a79 --- /dev/null +++ b/3rdparty/Sabre/VObject/TimeZoneUtil.php @@ -0,0 +1,351 @@ +<?php + +namespace Sabre\VObject; + +/** + * Time zone name translation + * + * This file translates well-known time zone names into "Olson database" time zone names. + * + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. + * @author Frank Edelhaeuser (fedel@users.sourceforge.net) + * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License + */ +class TimeZoneUtil { + + public static $map = array( + + // from http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/zone_tzid.html + // snapshot taken on 2012/01/16 + + // windows + 'AUS Central Standard Time'=>'Australia/Darwin', + 'AUS Eastern Standard Time'=>'Australia/Sydney', + 'Afghanistan Standard Time'=>'Asia/Kabul', + 'Alaskan Standard Time'=>'America/Anchorage', + 'Arab Standard Time'=>'Asia/Riyadh', + 'Arabian Standard Time'=>'Asia/Dubai', + 'Arabic Standard Time'=>'Asia/Baghdad', + 'Argentina Standard Time'=>'America/Buenos_Aires', + 'Armenian Standard Time'=>'Asia/Yerevan', + 'Atlantic Standard Time'=>'America/Halifax', + 'Azerbaijan Standard Time'=>'Asia/Baku', + 'Azores Standard Time'=>'Atlantic/Azores', + 'Bangladesh Standard Time'=>'Asia/Dhaka', + 'Canada Central Standard Time'=>'America/Regina', + 'Cape Verde Standard Time'=>'Atlantic/Cape_Verde', + 'Caucasus Standard Time'=>'Asia/Yerevan', + 'Cen. Australia Standard Time'=>'Australia/Adelaide', + 'Central America Standard Time'=>'America/Guatemala', + 'Central Asia Standard Time'=>'Asia/Almaty', + 'Central Brazilian Standard Time'=>'America/Cuiaba', + 'Central Europe Standard Time'=>'Europe/Budapest', + 'Central European Standard Time'=>'Europe/Warsaw', + 'Central Pacific Standard Time'=>'Pacific/Guadalcanal', + 'Central Standard Time'=>'America/Chicago', + 'Central Standard Time (Mexico)'=>'America/Mexico_City', + 'China Standard Time'=>'Asia/Shanghai', + 'Dateline Standard Time'=>'Etc/GMT+12', + 'E. Africa Standard Time'=>'Africa/Nairobi', + 'E. Australia Standard Time'=>'Australia/Brisbane', + 'E. Europe Standard Time'=>'Europe/Minsk', + 'E. South America Standard Time'=>'America/Sao_Paulo', + 'Eastern Standard Time'=>'America/New_York', + 'Egypt Standard Time'=>'Africa/Cairo', + 'Ekaterinburg Standard Time'=>'Asia/Yekaterinburg', + 'FLE Standard Time'=>'Europe/Kiev', + 'Fiji Standard Time'=>'Pacific/Fiji', + 'GMT Standard Time'=>'Europe/London', + 'GTB Standard Time'=>'Europe/Istanbul', + 'Georgian Standard Time'=>'Asia/Tbilisi', + 'Greenland Standard Time'=>'America/Godthab', + 'Greenwich Standard Time'=>'Atlantic/Reykjavik', + 'Hawaiian Standard Time'=>'Pacific/Honolulu', + 'India Standard Time'=>'Asia/Calcutta', + 'Iran Standard Time'=>'Asia/Tehran', + 'Israel Standard Time'=>'Asia/Jerusalem', + 'Jordan Standard Time'=>'Asia/Amman', + 'Kamchatka Standard Time'=>'Asia/Kamchatka', + 'Korea Standard Time'=>'Asia/Seoul', + 'Magadan Standard Time'=>'Asia/Magadan', + 'Mauritius Standard Time'=>'Indian/Mauritius', + 'Mexico Standard Time'=>'America/Mexico_City', + 'Mexico Standard Time 2'=>'America/Chihuahua', + 'Mid-Atlantic Standard Time'=>'Etc/GMT+2', + 'Middle East Standard Time'=>'Asia/Beirut', + 'Montevideo Standard Time'=>'America/Montevideo', + 'Morocco Standard Time'=>'Africa/Casablanca', + 'Mountain Standard Time'=>'America/Denver', + 'Mountain Standard Time (Mexico)'=>'America/Chihuahua', + 'Myanmar Standard Time'=>'Asia/Rangoon', + 'N. Central Asia Standard Time'=>'Asia/Novosibirsk', + 'Namibia Standard Time'=>'Africa/Windhoek', + 'Nepal Standard Time'=>'Asia/Katmandu', + 'New Zealand Standard Time'=>'Pacific/Auckland', + 'Newfoundland Standard Time'=>'America/St_Johns', + 'North Asia East Standard Time'=>'Asia/Irkutsk', + 'North Asia Standard Time'=>'Asia/Krasnoyarsk', + 'Pacific SA Standard Time'=>'America/Santiago', + 'Pacific Standard Time'=>'America/Los_Angeles', + 'Pacific Standard Time (Mexico)'=>'America/Santa_Isabel', + 'Pakistan Standard Time'=>'Asia/Karachi', + 'Paraguay Standard Time'=>'America/Asuncion', + 'Romance Standard Time'=>'Europe/Paris', + 'Russian Standard Time'=>'Europe/Moscow', + 'SA Eastern Standard Time'=>'America/Cayenne', + 'SA Pacific Standard Time'=>'America/Bogota', + 'SA Western Standard Time'=>'America/La_Paz', + 'SE Asia Standard Time'=>'Asia/Bangkok', + 'Samoa Standard Time'=>'Pacific/Apia', + 'Singapore Standard Time'=>'Asia/Singapore', + 'South Africa Standard Time'=>'Africa/Johannesburg', + 'Sri Lanka Standard Time'=>'Asia/Colombo', + 'Syria Standard Time'=>'Asia/Damascus', + 'Taipei Standard Time'=>'Asia/Taipei', + 'Tasmania Standard Time'=>'Australia/Hobart', + 'Tokyo Standard Time'=>'Asia/Tokyo', + 'Tonga Standard Time'=>'Pacific/Tongatapu', + 'US Eastern Standard Time'=>'America/Indianapolis', + 'US Mountain Standard Time'=>'America/Phoenix', + 'UTC'=>'Etc/GMT', + 'UTC+12'=>'Etc/GMT-12', + 'UTC-02'=>'Etc/GMT+2', + 'UTC-11'=>'Etc/GMT+11', + 'Ulaanbaatar Standard Time'=>'Asia/Ulaanbaatar', + 'Venezuela Standard Time'=>'America/Caracas', + 'Vladivostok Standard Time'=>'Asia/Vladivostok', + 'W. Australia Standard Time'=>'Australia/Perth', + 'W. Central Africa Standard Time'=>'Africa/Lagos', + 'W. Europe Standard Time'=>'Europe/Berlin', + 'West Asia Standard Time'=>'Asia/Tashkent', + 'West Pacific Standard Time'=>'Pacific/Port_Moresby', + 'Yakutsk Standard Time'=>'Asia/Yakutsk', + + // Microsoft exchange timezones + // Source: + // http://msdn.microsoft.com/en-us/library/ms988620%28v=exchg.65%29.aspx + // + // Correct timezones deduced with help from: + // http://en.wikipedia.org/wiki/List_of_tz_database_time_zones + 'Universal Coordinated Time' => 'UTC', + 'Casablanca, Monrovia' => 'Africa/Casablanca', + 'Greenwich Mean Time: Dublin, Edinburgh, Lisbon, London' => 'Europe/Lisbon', + 'Greenwich Mean Time; Dublin, Edinburgh, London' => 'Europe/London', + 'Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna' => 'Europe/Berlin', + 'Belgrade, Pozsony, Budapest, Ljubljana, Prague' => 'Europe/Prague', + 'Brussels, Copenhagen, Madrid, Paris' => 'Europe/Paris', + 'Paris, Madrid, Brussels, Copenhagen' => 'Europe/Paris', + 'Prague, Central Europe' => 'Europe/Prague', + 'Sarajevo, Skopje, Sofija, Vilnius, Warsaw, Zagreb' => 'Europe/Sarajevo', + 'West Central Africa' => 'Africa/Luanda', // This was a best guess + 'Athens, Istanbul, Minsk' => 'Europe/Athens', + 'Bucharest' => 'Europe/Bucharest', + 'Cairo' => 'Africa/Cairo', + 'Harare, Pretoria' => 'Africa/Harare', + 'Helsinki, Riga, Tallinn' => 'Europe/Helsinki', + 'Israel, Jerusalem Standard Time' => 'Asia/Jerusalem', + 'Baghdad' => 'Asia/Baghdad', + 'Arab, Kuwait, Riyadh' => 'Asia/Kuwait', + 'Moscow, St. Petersburg, Volgograd' => 'Europe/Moscow', + 'East Africa, Nairobi' => 'Africa/Nairobi', + 'Tehran' => 'Asia/Tehran', + 'Abu Dhabi, Muscat' => 'Asia/Muscat', // Best guess + 'Baku, Tbilisi, Yerevan' => 'Asia/Baku', + 'Kabul' => 'Asia/Kabul', + 'Ekaterinburg' => 'Asia/Yekaterinburg', + 'Islamabad, Karachi, Tashkent' => 'Asia/Karachi', + 'Kolkata, Chennai, Mumbai, New Delhi, India Standard Time' => 'Asia/Calcutta', + 'Kathmandu, Nepal' => 'Asia/Kathmandu', + 'Almaty, Novosibirsk, North Central Asia' => 'Asia/Almaty', + 'Astana, Dhaka' => 'Asia/Dhaka', + 'Sri Jayawardenepura, Sri Lanka' => 'Asia/Colombo', + 'Rangoon' => 'Asia/Rangoon', + 'Bangkok, Hanoi, Jakarta' => 'Asia/Bangkok', + 'Krasnoyarsk' => 'Asia/Krasnoyarsk', + 'Beijing, Chongqing, Hong Kong SAR, Urumqi' => 'Asia/Shanghai', + 'Irkutsk, Ulaan Bataar' => 'Asia/Irkutsk', + 'Kuala Lumpur, Singapore' => 'Asia/Singapore', + 'Perth, Western Australia' => 'Australia/Perth', + 'Taipei' => 'Asia/Taipei', + 'Osaka, Sapporo, Tokyo' => 'Asia/Tokyo', + 'Seoul, Korea Standard time' => 'Asia/Seoul', + 'Yakutsk' => 'Asia/Yakutsk', + 'Adelaide, Central Australia' => 'Australia/Adelaide', + 'Darwin' => 'Australia/Darwin', + 'Brisbane, East Australia' => 'Australia/Brisbane', + 'Canberra, Melbourne, Sydney, Hobart (year 2000 only)' => 'Australia/Sydney', + 'Guam, Port Moresby' => 'Pacific/Guam', + 'Hobart, Tasmania' => 'Australia/Hobart', + 'Vladivostok' => 'Asia/Vladivostok', + 'Magadan, Solomon Is., New Caledonia' => 'Asia/Magadan', + 'Auckland, Wellington' => 'Pacific/Auckland', + 'Fiji Islands, Kamchatka, Marshall Is.' => 'Pacific/Fiji', + 'Nuku\'alofa, Tonga' => 'Pacific/Tongatapu', + 'Azores' => 'Atlantic/Azores', + 'Cape Verde Is.' => 'Atlantic/Cape_Verde', + 'Mid-Atlantic' => 'America/Noronha', + 'Brasilia' => 'America/Sao_Paulo', // Best guess + 'Buenos Aires' => 'America/Argentina/Buenos_Aires', + 'Greenland' => 'America/Godthab', + 'Newfoundland' => 'America/St_Johns', + 'Atlantic Time (Canada)' => 'America/Halifax', + 'Caracas, La Paz' => 'America/Caracas', + 'Santiago' => 'America/Santiago', + 'Bogota, Lima, Quito' => 'America/Bogota', + 'Eastern Time (US & Canada)' => 'America/New_York', + 'Indiana (East)' => 'America/Indiana/Indianapolis', + 'Central America' => 'America/Guatemala', + 'Central Time (US & Canada)' => 'America/Chicago', + 'Mexico City, Tegucigalpa' => 'America/Mexico_City', + 'Saskatchewan' => 'America/Edmonton', + 'Arizona' => 'America/Phoenix', + 'Mountain Time (US & Canada)' => 'America/Denver', // Best guess + 'Pacific Time (US & Canada); Tijuana' => 'America/Los_Angeles', // Best guess + 'Alaska' => 'America/Anchorage', + 'Hawaii' => 'Pacific/Honolulu', + 'Midway Island, Samoa' => 'Pacific/Midway', + 'Eniwetok, Kwajalein, Dateline Time' => 'Pacific/Kwajalein', + + ); + + public static $microsoftExchangeMap = array( + 0 => 'UTC', + 31 => 'Africa/Casablanca', + 2 => 'Europe/Lisbon', + 1 => 'Europe/London', + 4 => 'Europe/Berlin', + 6 => 'Europe/Prague', + 3 => 'Europe/Paris', + 69 => 'Africa/Luanda', // This was a best guess + 7 => 'Europe/Athens', + 5 => 'Europe/Bucharest', + 49 => 'Africa/Cairo', + 50 => 'Africa/Harare', + 59 => 'Europe/Helsinki', + 27 => 'Asia/Jerusalem', + 26 => 'Asia/Baghdad', + 74 => 'Asia/Kuwait', + 51 => 'Europe/Moscow', + 56 => 'Africa/Nairobi', + 25 => 'Asia/Tehran', + 24 => 'Asia/Muscat', // Best guess + 54 => 'Asia/Baku', + 48 => 'Asia/Kabul', + 58 => 'Asia/Yekaterinburg', + 47 => 'Asia/Karachi', + 23 => 'Asia/Calcutta', + 62 => 'Asia/Kathmandu', + 46 => 'Asia/Almaty', + 71 => 'Asia/Dhaka', + 66 => 'Asia/Colombo', + 61 => 'Asia/Rangoon', + 22 => 'Asia/Bangkok', + 64 => 'Asia/Krasnoyarsk', + 45 => 'Asia/Shanghai', + 63 => 'Asia/Irkutsk', + 21 => 'Asia/Singapore', + 73 => 'Australia/Perth', + 75 => 'Asia/Taipei', + 20 => 'Asia/Tokyo', + 72 => 'Asia/Seoul', + 70 => 'Asia/Yakutsk', + 19 => 'Australia/Adelaide', + 44 => 'Australia/Darwin', + 18 => 'Australia/Brisbane', + 76 => 'Australia/Sydney', + 43 => 'Pacific/Guam', + 42 => 'Australia/Hobart', + 68 => 'Asia/Vladivostok', + 41 => 'Asia/Magadan', + 17 => 'Pacific/Auckland', + 40 => 'Pacific/Fiji', + 67 => 'Pacific/Tongatapu', + 29 => 'Atlantic/Azores', + 53 => 'Atlantic/Cape_Verde', + 30 => 'America/Noronha', + 8 => 'America/Sao_Paulo', // Best guess + 32 => 'America/Argentina/Buenos_Aires', + 60 => 'America/Godthab', + 28 => 'America/St_Johns', + 9 => 'America/Halifax', + 33 => 'America/Caracas', + 65 => 'America/Santiago', + 35 => 'America/Bogota', + 10 => 'America/New_York', + 34 => 'America/Indiana/Indianapolis', + 55 => 'America/Guatemala', + 11 => 'America/Chicago', + 37 => 'America/Mexico_City', + 36 => 'America/Edmonton', + 38 => 'America/Phoenix', + 12 => 'America/Denver', // Best guess + 13 => 'America/Los_Angeles', // Best guess + 14 => 'America/Anchorage', + 15 => 'Pacific/Honolulu', + 16 => 'Pacific/Midway', + 39 => 'Pacific/Kwajalein', + ); + + /** + * This method will try to find out the correct timezone for an iCalendar + * date-time value. + * + * You must pass the contents of the TZID parameter, as well as the full + * calendar. + * + * If the lookup fails, this method will return UTC. + * + * @param string $tzid + * @param Sabre\VObject\Component $vcalendar + * @return DateTimeZone + */ + static public function getTimeZone($tzid, Component $vcalendar = null) { + + // First we will just see if the tzid is a support timezone identifier. + try { + return new \DateTimeZone($tzid); + } catch (\Exception $e) { + } + + // Next, we check if the tzid is somewhere in our tzid map. + if (isset(self::$map[$tzid])) { + return new \DateTimeZone(self::$map[$tzid]); + } + + if ($vcalendar) { + + // If that didn't work, we will scan VTIMEZONE objects + foreach($vcalendar->select('VTIMEZONE') as $vtimezone) { + + if ((string)$vtimezone->TZID === $tzid) { + + // Some clients add 'X-LIC-LOCATION' with the olson name. + if (isset($vtimezone->{'X-LIC-LOCATION'})) { + try { + return new \DateTimeZone($vtimezone->{'X-LIC-LOCATION'}); + } catch (\Exception $e) { + } + + } + // Microsoft may add a magic number, which we also have an + // answer for. + if (isset($vtimezone->{'X-MICROSOFT-CDO-TZID'})) { + if (isset(self::$microsoftExchangeMap[(int)$vtimezone->{'X-MICROSOFT-CDO-TZID'}->value])) { + return new \DateTimeZone(self::$microsoftExchangeMap[(int)$vtimezone->{'X-MICROSOFT-CDO-TZID'}->value]); + } + } + } + + } + + } + + // If we got all the way here, we default to UTC. + return new \DateTimeZone(date_default_timezone_get()); + + + } + + +} diff --git a/3rdparty/Sabre/VObject/Version.php b/3rdparty/Sabre/VObject/Version.php index 9ee03d87118..0065b7abc95 100755..100644 --- a/3rdparty/Sabre/VObject/Version.php +++ b/3rdparty/Sabre/VObject/Version.php @@ -1,20 +1,20 @@ <?php +namespace Sabre\VObject; + /** * This class contains the version number for the VObject package * - * @package Sabre - * @subpackage VObject * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ -class Sabre_VObject_Version { +class Version { /** * Full version number */ - const VERSION = '1.3.4'; + const VERSION = '2.0.2'; /** * Stability : alpha, beta, stable diff --git a/3rdparty/Sabre/VObject/WindowsTimezoneMap.php b/3rdparty/Sabre/VObject/WindowsTimezoneMap.php deleted file mode 100755 index 5e1cc5d479b..00000000000 --- a/3rdparty/Sabre/VObject/WindowsTimezoneMap.php +++ /dev/null @@ -1,128 +0,0 @@ -<?php - -/** - * Time zone name translation - * - * This file translates well-known time zone names into "Olson database" time zone names. - * - * @package Sabre - * @subpackage VObject - * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. - * @author Frank Edelhaeuser (fedel@users.sourceforge.net) - * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License - */ -class Sabre_VObject_WindowsTimezoneMap { - - protected static $map = array( - - // from http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/zone_tzid.html - // snapshot taken on 2012/01/16 - - // windows - 'AUS Central Standard Time'=>'Australia/Darwin', - 'AUS Eastern Standard Time'=>'Australia/Sydney', - 'Afghanistan Standard Time'=>'Asia/Kabul', - 'Alaskan Standard Time'=>'America/Anchorage', - 'Arab Standard Time'=>'Asia/Riyadh', - 'Arabian Standard Time'=>'Asia/Dubai', - 'Arabic Standard Time'=>'Asia/Baghdad', - 'Argentina Standard Time'=>'America/Buenos_Aires', - 'Armenian Standard Time'=>'Asia/Yerevan', - 'Atlantic Standard Time'=>'America/Halifax', - 'Azerbaijan Standard Time'=>'Asia/Baku', - 'Azores Standard Time'=>'Atlantic/Azores', - 'Bangladesh Standard Time'=>'Asia/Dhaka', - 'Canada Central Standard Time'=>'America/Regina', - 'Cape Verde Standard Time'=>'Atlantic/Cape_Verde', - 'Caucasus Standard Time'=>'Asia/Yerevan', - 'Cen. Australia Standard Time'=>'Australia/Adelaide', - 'Central America Standard Time'=>'America/Guatemala', - 'Central Asia Standard Time'=>'Asia/Almaty', - 'Central Brazilian Standard Time'=>'America/Cuiaba', - 'Central Europe Standard Time'=>'Europe/Budapest', - 'Central European Standard Time'=>'Europe/Warsaw', - 'Central Pacific Standard Time'=>'Pacific/Guadalcanal', - 'Central Standard Time'=>'America/Chicago', - 'Central Standard Time (Mexico)'=>'America/Mexico_City', - 'China Standard Time'=>'Asia/Shanghai', - 'Dateline Standard Time'=>'Etc/GMT+12', - 'E. Africa Standard Time'=>'Africa/Nairobi', - 'E. Australia Standard Time'=>'Australia/Brisbane', - 'E. Europe Standard Time'=>'Europe/Minsk', - 'E. South America Standard Time'=>'America/Sao_Paulo', - 'Eastern Standard Time'=>'America/New_York', - 'Egypt Standard Time'=>'Africa/Cairo', - 'Ekaterinburg Standard Time'=>'Asia/Yekaterinburg', - 'FLE Standard Time'=>'Europe/Kiev', - 'Fiji Standard Time'=>'Pacific/Fiji', - 'GMT Standard Time'=>'Europe/London', - 'GTB Standard Time'=>'Europe/Istanbul', - 'Georgian Standard Time'=>'Asia/Tbilisi', - 'Greenland Standard Time'=>'America/Godthab', - 'Greenwich Standard Time'=>'Atlantic/Reykjavik', - 'Hawaiian Standard Time'=>'Pacific/Honolulu', - 'India Standard Time'=>'Asia/Calcutta', - 'Iran Standard Time'=>'Asia/Tehran', - 'Israel Standard Time'=>'Asia/Jerusalem', - 'Jordan Standard Time'=>'Asia/Amman', - 'Kamchatka Standard Time'=>'Asia/Kamchatka', - 'Korea Standard Time'=>'Asia/Seoul', - 'Magadan Standard Time'=>'Asia/Magadan', - 'Mauritius Standard Time'=>'Indian/Mauritius', - 'Mexico Standard Time'=>'America/Mexico_City', - 'Mexico Standard Time 2'=>'America/Chihuahua', - 'Mid-Atlantic Standard Time'=>'Etc/GMT+2', - 'Middle East Standard Time'=>'Asia/Beirut', - 'Montevideo Standard Time'=>'America/Montevideo', - 'Morocco Standard Time'=>'Africa/Casablanca', - 'Mountain Standard Time'=>'America/Denver', - 'Mountain Standard Time (Mexico)'=>'America/Chihuahua', - 'Myanmar Standard Time'=>'Asia/Rangoon', - 'N. Central Asia Standard Time'=>'Asia/Novosibirsk', - 'Namibia Standard Time'=>'Africa/Windhoek', - 'Nepal Standard Time'=>'Asia/Katmandu', - 'New Zealand Standard Time'=>'Pacific/Auckland', - 'Newfoundland Standard Time'=>'America/St_Johns', - 'North Asia East Standard Time'=>'Asia/Irkutsk', - 'North Asia Standard Time'=>'Asia/Krasnoyarsk', - 'Pacific SA Standard Time'=>'America/Santiago', - 'Pacific Standard Time'=>'America/Los_Angeles', - 'Pacific Standard Time (Mexico)'=>'America/Santa_Isabel', - 'Pakistan Standard Time'=>'Asia/Karachi', - 'Paraguay Standard Time'=>'America/Asuncion', - 'Romance Standard Time'=>'Europe/Paris', - 'Russian Standard Time'=>'Europe/Moscow', - 'SA Eastern Standard Time'=>'America/Cayenne', - 'SA Pacific Standard Time'=>'America/Bogota', - 'SA Western Standard Time'=>'America/La_Paz', - 'SE Asia Standard Time'=>'Asia/Bangkok', - 'Samoa Standard Time'=>'Pacific/Apia', - 'Singapore Standard Time'=>'Asia/Singapore', - 'South Africa Standard Time'=>'Africa/Johannesburg', - 'Sri Lanka Standard Time'=>'Asia/Colombo', - 'Syria Standard Time'=>'Asia/Damascus', - 'Taipei Standard Time'=>'Asia/Taipei', - 'Tasmania Standard Time'=>'Australia/Hobart', - 'Tokyo Standard Time'=>'Asia/Tokyo', - 'Tonga Standard Time'=>'Pacific/Tongatapu', - 'US Eastern Standard Time'=>'America/Indianapolis', - 'US Mountain Standard Time'=>'America/Phoenix', - 'UTC'=>'Etc/GMT', - 'UTC+12'=>'Etc/GMT-12', - 'UTC-02'=>'Etc/GMT+2', - 'UTC-11'=>'Etc/GMT+11', - 'Ulaanbaatar Standard Time'=>'Asia/Ulaanbaatar', - 'Venezuela Standard Time'=>'America/Caracas', - 'Vladivostok Standard Time'=>'Asia/Vladivostok', - 'W. Australia Standard Time'=>'Australia/Perth', - 'W. Central Africa Standard Time'=>'Africa/Lagos', - 'W. Europe Standard Time'=>'Europe/Berlin', - 'West Asia Standard Time'=>'Asia/Tashkent', - 'West Pacific Standard Time'=>'Pacific/Port_Moresby', - 'Yakutsk Standard Time'=>'Asia/Yakutsk', - ); - - static public function lookup($tzid) { - return isset(self::$map[$tzid]) ? self::$map[$tzid] : null; - } -} diff --git a/3rdparty/Sabre/VObject/includes.php b/3rdparty/Sabre/VObject/includes.php index 0177a8f1ba6..b74bd3b6cbf 100755..100644 --- a/3rdparty/Sabre/VObject/includes.php +++ b/3rdparty/Sabre/VObject/includes.php @@ -1,15 +1,11 @@ <?php /** - * Sabre_VObject includes file + * Includes file * - * Including this file will automatically include all files from the VObject - * package. + * This file includes the entire VObject library in one go. + * The benefit is that an autoloader is not needed, which is often faster. * - * This often allows faster loadtimes, as autoload-speed is often quite slow. - * - * @package Sabre - * @subpackage VObject * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License @@ -22,20 +18,23 @@ include __DIR__ . '/FreeBusyGenerator.php'; include __DIR__ . '/Node.php'; include __DIR__ . '/Parameter.php'; include __DIR__ . '/ParseException.php'; +include __DIR__ . '/Property.php'; include __DIR__ . '/Reader.php'; include __DIR__ . '/RecurrenceIterator.php'; +include __DIR__ . '/Splitter/SplitterInterface.php'; +include __DIR__ . '/StringUtil.php'; +include __DIR__ . '/TimeZoneUtil.php'; include __DIR__ . '/Version.php'; -include __DIR__ . '/WindowsTimezoneMap.php'; -include __DIR__ . '/Element.php'; -include __DIR__ . '/Property.php'; +include __DIR__ . '/Splitter/VCard.php'; include __DIR__ . '/Component.php'; +include __DIR__ . '/Property/Compound.php'; include __DIR__ . '/Property/DateTime.php'; include __DIR__ . '/Property/MultiDateTime.php'; +include __DIR__ . '/Splitter/ICalendar.php'; include __DIR__ . '/Component/VAlarm.php'; include __DIR__ . '/Component/VCalendar.php'; include __DIR__ . '/Component/VEvent.php'; +include __DIR__ . '/Component/VFreeBusy.php'; include __DIR__ . '/Component/VJournal.php'; include __DIR__ . '/Component/VTodo.php'; -include __DIR__ . '/Element/DateTime.php'; -include __DIR__ . '/Element/MultiDateTime.php'; // End includes diff --git a/3rdparty/Sabre/autoload.php b/3rdparty/Sabre/autoload.php index c7b537d83d3..505bd36efb0 100755..100644 --- a/3rdparty/Sabre/autoload.php +++ b/3rdparty/Sabre/autoload.php @@ -1,31 +1,27 @@ <?php /** - * SabreDAV's PHP autoloader + * SabreDAV's autoloader * - * If you love the autoloader, and don't care as much about performance, this - * file register a new autoload function using spl_autoload_register. + * This file is kept for backwards compatibility purposes. + * SabreDAV now uses the composer autoloader. + * + * You should stop including this file, and include 'vendor/autoload.php' + * instead. * * @package Sabre * @subpackage DAV + * @deprecated Will be removed in a future version! * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ /** - * @param string $className - * @return void + * We are assuming that the composer autoloader is just 2 directories up. + * + * This is not the case when sabredav is installed as a dependency. But, in + * those cases it's not expected that people will look for this file anyway. */ -function Sabre_autoload($className) { - - if(strpos($className,'Sabre_')===0) { - - include dirname(__FILE__) . '/' . str_replace('_','/',substr($className,6)) . '.php'; - - } - -} - -spl_autoload_register('Sabre_autoload'); +require __DIR__ . '/../../vendor/autoload.php'; diff --git a/apps/files/templates/index.php_orig b/apps/files/templates/index.php_orig new file mode 100644 index 00000000000..aff484f0a7a --- /dev/null +++ b/apps/files/templates/index.php_orig @@ -0,0 +1,90 @@ +<!--[if IE 8]><style>input[type="checkbox"]{padding:0;}table td{position:static !important;}</style><![endif]--> +<div id="controls"> + <?php echo($_['breadcrumb']); ?> + <?php if ($_['isCreatable']):?> + <div class="actions <?php if (isset($_['files']) and count($_['files'])==0):?>emptyfolder<?php endif; ?>"> + <div id='new' class='button'> + <a><?php echo $l->t('New');?></a> + <ul class="popup popupTop"> + <li style="background-image:url('<?php echo OCP\mimetype_icon('text/plain') ?>')" data-type='file'><p><?php echo $l->t('Text file');?></p></li> + <li style="background-image:url('<?php echo OCP\mimetype_icon('dir') ?>')" data-type='folder'><p><?php echo $l->t('Folder');?></p></li> + <li style="background-image:url('<?php echo OCP\image_path('core','actions/public.png') ?>')" data-type='web'><p><?php echo $l->t('From url');?></p></li> + </ul> + </div> + <div class="file_upload_wrapper svg"> + <form data-upload-id='1' class="file_upload_form" action="<?php echo OCP\Util::linkTo('files', 'ajax/upload.php'); ?>" method="post" enctype="multipart/form-data" target="file_upload_target_1"> + <input type="hidden" name="MAX_FILE_SIZE" value="<?php echo $_['uploadMaxFilesize'] ?>" id="max_upload"> + <input type="hidden" class="max_human_file_size" value="(max <?php echo $_['uploadMaxHumanFilesize']; ?>)"> + <input type="hidden" name="dir" value="<?php echo $_['dir'] ?>" id="dir"> + <input class="file_upload_start" type="file" name='files[]'/> + <a href="#" class="file_upload_button_wrapper" onclick="return false;" title="<?php echo $l->t('Upload'); echo ' max. '.$_['uploadMaxHumanFilesize'] ?>"></a> + <button class="file_upload_filename"></button> + <iframe name="file_upload_target_1" class='file_upload_target' src=""></iframe> + </form> + </div> + <div id="upload"> + <div id="uploadprogressbar"></div> + <input type="button" class="stop" style="display:none" value="<?php echo $l->t('Cancel upload');?>" onclick="javascript:Files.cancelUploads();" /> + </div> + + </div> + <div id="file_action_panel"></div> + <?php else:?> + <input type="hidden" name="dir" value="<?php echo $_['dir'] ?>" id="dir"> + <?php endif;?> + <input type="hidden" name="permissions" value="<?php echo $_['permissions']; ?>" id="permissions"> +</div> +<div id='notification'></div> + +<?php if (isset($_['files']) and $_['isCreatable'] and count($_['files'])==0):?> + <div id="emptyfolder"><?php echo $l->t('Nothing in here. Upload something!')?></div> +<?php endif; ?> + +<table> + <thead> + <tr> + <th id='headerName'> + <input type="checkbox" id="select_all" /> + <span class='name'><?php echo $l->t( 'Name' ); ?></span> + <span class='selectedActions'> +<!-- <a href="" class="share"><img class='svg' alt="Share" src="<?php echo OCP\image_path("core", "actions/share.svg"); ?>" /> <?php echo $l->t('Share')?></a> --> + <?php if($_['allowZipDownload']) : ?> + <a href="" class="download"><img class='svg' alt="Download" src="<?php echo OCP\image_path("core", "actions/download.svg"); ?>" /> <?php echo $l->t('Download')?></a> + <?php endif; ?> + </span> + </th> + <th id="headerSize"><?php echo $l->t( 'Size' ); ?></th> + <th id="headerDate"> + <span id="modified"><?php echo $l->t( 'Modified' ); ?></span> + <?php if ($_['permissions'] & OCP\Share::PERMISSION_DELETE): ?> +<!-- NOTE: Temporary fix to allow unsharing of files in root of Shared folder --> + <?php if ($_['dir'] == '/Shared'): ?> + <span class="selectedActions"><a href="" class="delete"><?php echo $l->t('Unshare')?> <img class="svg" alt="<?php echo $l->t('Unshare')?>" src="<?php echo OCP\image_path("core", "actions/delete.svg"); ?>" /></a></span> + <?php else: ?> + <span class="selectedActions"><a href="" class="delete"><?php echo $l->t('Delete')?> <img class="svg" alt="<?php echo $l->t('Delete')?>" src="<?php echo OCP\image_path("core", "actions/delete.svg"); ?>" /></a></span> + <?php endif; ?> + <?php endif; ?> + </th> + </tr> + </thead> + <tbody id="fileList"> + <?php echo($_['fileList']); ?> + </tbody> +</table> +<div id="editor"></div> +<div id="uploadsize-message" title="<?php echo $l->t('Upload too large')?>"> + <p> + <?php echo $l->t('The files you are trying to upload exceed the maximum size for file uploads on this server.');?> + </p> +</div> +<div id="scanning-message"> + <h3> + <?php echo $l->t('Files are being scanned, please wait.');?> <span id='scan-count'></span> + </h3> + <p> + <?php echo $l->t('Current scanning');?> <span id='scan-current'></span> + </p> +</div> + +<!-- config hints for javascript --> +<input type="hidden" name="allowZipDownload" id="allowZipDownload" value="<?php echo $_['allowZipDownload']; ?>" /> diff --git a/apps/files/templates/index.php_test b/apps/files/templates/index.php_test new file mode 100644 index 00000000000..a509333aa45 --- /dev/null +++ b/apps/files/templates/index.php_test @@ -0,0 +1,90 @@ +<!--[if IE 8]><style>input[type="checkbox"]{padding:0;}table td{position:static !important;}</style><![endif]--> +<div id="controls"> + <?php echo($_['breadcrumb']); ?> + <?php if ($_['isCreatable']):?> + <div class="actions <?php if (isset($_['files']) and count($_['files'])==0):?>emptyfolder<?php endif; ?>"> + <div id='new' class='button'> + <a><?php echo $l->t('New');?></a> + <ul class="popup popupTop"> + <li style="background-image:url('<?php echo OCP\mimetype_icon('text/plain') ?>')" data-type='file'><p><?php echo $l->t('Text file');?></p></li> + <li style="background-image:url('<?php echo OCP\mimetype_icon('dir') ?>')" data-type='folder'><p><?php echo $l->t('Folder');?></p></li> + <li style="background-image:url('<?php echo OCP\image_path('core','actions/public.png') ?>')" data-type='web'><p><?php echo $l->t('From url');?></p></li> + </ul> + </div> + <div class="file_upload_wrapper svg"> + <form data-upload-id='1' class="file_upload_form" action="<?php echo OCP\Util::linkTo('files', 'ajax/upload.php'); ?>" method="post" enctype="multipart/form-data" target="file_upload_target_1"> +<!-- <input type="hidden" name="MAX_FILE_SIZE" value="4000000000" id="max_upload"> --> + <input type="hidden" class="max_human_file_size" value="(max <?php echo $_['uploadMaxHumanFilesize']; ?>)"> + <input type="hidden" name="dir" value="<?php echo $_['dir'] ?>" id="dir"> + <input class="file_upload_start" type="file" name='files[]'/> + <a href="#" class="file_upload_button_wrapper" onclick="return false;" title="<?php echo $l->t('Upload'); echo ' max. '.$_['uploadMaxHumanFilesize'] ?>"></a> + <button class="file_upload_filename"></button> + <iframe name="file_upload_target_1" class='file_upload_target' src=""></iframe> + </form> + </div> + <div id="upload"> + <div id="uploadprogressbar"></div> + <input type="button" class="stop" style="display:none" value="<?php echo $l->t('Cancel upload');?>" onclick="javascript:Files.cancelUploads();" /> + </div> + + </div> + <div id="file_action_panel"></div> + <?php else:?> + <input type="hidden" name="dir" value="<?php echo $_['dir'] ?>" id="dir"> + <?php endif;?> + <input type="hidden" name="permissions" value="<?php echo $_['permissions']; ?>" id="permissions"> +</div> +<div id='notification'></div> + +<?php if (isset($_['files']) and $_['isCreatable'] and count($_['files'])==0):?> + <div id="emptyfolder"><?php echo $l->t('Nothing in here. Upload something!')?></div> +<?php endif; ?> + +<table> + <thead> + <tr> + <th id='headerName'> + <input type="checkbox" id="select_all" /> + <span class='name'><?php echo $l->t( 'Name' ); ?></span> + <span class='selectedActions'> +<!-- <a href="" class="share"><img class='svg' alt="Share" src="<?php echo OCP\image_path("core", "actions/share.svg"); ?>" /> <?php echo $l->t('Share')?></a> --> + <?php if($_['allowZipDownload']) : ?> + <a href="" class="download"><img class='svg' alt="Download" src="<?php echo OCP\image_path("core", "actions/download.svg"); ?>" /> <?php echo $l->t('Download')?></a> + <?php endif; ?> + </span> + </th> + <th id="headerSize"><?php echo $l->t( 'Size' ); ?></th> + <th id="headerDate"> + <span id="modified"><?php echo $l->t( 'Modified' ); ?></span> + <?php if ($_['permissions'] & OCP\Share::PERMISSION_DELETE): ?> +<!-- NOTE: Temporary fix to allow unsharing of files in root of Shared folder --> + <?php if ($_['dir'] == '/Shared'): ?> + <span class="selectedActions"><a href="" class="delete"><?php echo $l->t('Unshare')?> <img class="svg" alt="<?php echo $l->t('Unshare')?>" src="<?php echo OCP\image_path("core", "actions/delete.svg"); ?>" /></a></span> + <?php else: ?> + <span class="selectedActions"><a href="" class="delete"><?php echo $l->t('Delete')?> <img class="svg" alt="<?php echo $l->t('Delete')?>" src="<?php echo OCP\image_path("core", "actions/delete.svg"); ?>" /></a></span> + <?php endif; ?> + <?php endif; ?> + </th> + </tr> + </thead> + <tbody id="fileList"> + <?php echo($_['fileList']); ?> + </tbody> +</table> +<div id="editor"></div> +<div id="uploadsize-message" title="<?php echo $l->t('Upload too large')?>"> + <p> + <?php echo $l->t('The files you are trying to upload exceed the maximum size for file uploads on this server.');?> + </p> +</div> +<div id="scanning-message"> + <h3> + <?php echo $l->t('Files are being scanned, please wait.');?> <span id='scan-count'></span> + </h3> + <p> + <?php echo $l->t('Current scanning');?> <span id='scan-current'></span> + </p> +</div> + +<!-- config hints for javascript --> +<input type="hidden" name="allowZipDownload" id="allowZipDownload" value="<?php echo $_['allowZipDownload']; ?>" /> diff --git a/lib/base.php b/lib/base.php index a1ad4f6dc0a..bd8829e1ab6 100644 --- a/lib/base.php +++ b/lib/base.php @@ -93,6 +93,11 @@ class OC{ elseif(strpos($className, 'Sabre_')===0) { $path = str_replace('_', '/', $className) . '.php'; } + + elseif(strpos($className, 'Sabre\\VObject')===0) { + $path = '3rdparty/'.str_replace('\\', '/', $className) . '.php'; + } + elseif(strpos($className, 'Test_')===0) { $path = 'tests/lib/'.strtolower(str_replace('_', '/', substr($className, 5)) . '.php'); }else{ diff --git a/lib/vobject.php b/lib/vobject.php index b5a04b4bf65..5070c3f1ee6 100644 --- a/lib/vobject.php +++ b/lib/vobject.php @@ -24,11 +24,11 @@ * This class provides a streamlined interface to the Sabre VObject classes */ class OC_VObject{ - /** @var Sabre_VObject_Component */ + /** @var Sabre\VObject\Component */ protected $vobject; /** - * @returns Sabre_VObject_Component + * @returns Sabre\VObject\Component */ public function getVObject() { return $this->vobject; @@ -41,9 +41,9 @@ class OC_VObject{ */ public static function parse($data) { try { - Sabre_VObject_Property::$classMap['LAST-MODIFIED'] = 'Sabre_VObject_Property_DateTime'; - $vobject = Sabre_VObject_Reader::read($data); - if ($vobject instanceof Sabre_VObject_Component) { + Sabre\VObject\Property::$classMap['LAST-MODIFIED'] = 'Sabre\VObject\Property\DateTime'; + $vobject = Sabre\VObject\Reader::read($data); + if ($vobject instanceof Sabre\VObject\Component) { $vobject = new OC_VObject($vobject); } return $vobject; @@ -89,13 +89,13 @@ class OC_VObject{ /** * Constuctor - * @param Sabre_VObject_Component or string + * @param Sabre\VObject\Component or string */ public function __construct($vobject_or_name) { if (is_object($vobject_or_name)) { $this->vobject = $vobject_or_name; } else { - $this->vobject = new Sabre_VObject_Component($vobject_or_name); + $this->vobject = new Sabre\VObject\Component($vobject_or_name); } } @@ -117,9 +117,9 @@ class OC_VObject{ if(is_array($value)) { $value = OC_VObject::escapeSemicolons($value); } - $property = new Sabre_VObject_Property( $name, $value ); + $property = new Sabre\VObject\Property( $name, $value ); foreach($parameters as $name => $value) { - $property->parameters[] = new Sabre_VObject_Parameter($name, $value); + $property->parameters[] = new Sabre\VObject\Parameter($name, $value); } $this->vobject->add($property); @@ -150,12 +150,12 @@ class OC_VObject{ * @param int $dateType * @return void */ - public function setDateTime($name, $datetime, $dateType=Sabre_VObject_Property_DateTime::LOCALTZ) { + public function setDateTime($name, $datetime, $dateType=Sabre\VObject\Property\DateTime::LOCALTZ) { if ($datetime == 'now') { $datetime = new DateTime(); } if ($datetime instanceof DateTime) { - $datetime_element = new Sabre_VObject_Property_DateTime($name); + $datetime_element = new Sabre\VObject\Property\DateTime($name); $datetime_element->setDateTime($datetime, $dateType); $this->vobject->__set($name, $datetime_element); }else{ @@ -183,7 +183,7 @@ class OC_VObject{ return $this->vobject->children; } $return = $this->vobject->__get($name); - if ($return instanceof Sabre_VObject_Component) { + if ($return instanceof Sabre\VObject\Component) { $return = new OC_VObject($return); } return $return; |