From 183cc22501b75ab8819971f70b88dbc010026ac1 Mon Sep 17 00:00:00 2001 From: Lukas Reschke Date: Sat, 13 Oct 2012 22:03:44 +0200 Subject: [PATCH] Update SabreDAV to 1.7.1 --- 3rdparty/Sabre/CalDAV/Backend/Abstract.php | 182 ++- .../Sabre/CalDAV/Backend/BackendInterface.php | 231 ++++ .../CalDAV/Backend/NotificationSupport.php | 47 + 3rdparty/Sabre/CalDAV/Backend/PDO.php | 337 +++++- .../Sabre/CalDAV/Backend/SharingSupport.php | 238 ++++ 3rdparty/Sabre/CalDAV/Calendar.php | 63 +- 3rdparty/Sabre/CalDAV/CalendarObject.php | 22 +- 3rdparty/Sabre/CalDAV/CalendarQueryParser.php | 12 +- .../Sabre/CalDAV/CalendarQueryValidator.php | 28 +- 3rdparty/Sabre/CalDAV/CalendarRootNode.php | 13 +- .../CalDAV/Exception/InvalidComponentType.php | 32 + 3rdparty/Sabre/CalDAV/ICSExportPlugin.php | 6 +- 3rdparty/Sabre/CalDAV/ICalendar.php | 21 +- 3rdparty/Sabre/CalDAV/IShareableCalendar.php | 48 + 3rdparty/Sabre/CalDAV/ISharedCalendar.php | 22 + .../Sabre/CalDAV/Notifications/Collection.php | 169 +++ .../CalDAV/Notifications/ICollection.php | 22 + 3rdparty/Sabre/CalDAV/Notifications/INode.php | 38 + .../Notifications/INotificationType.php | 43 + 3rdparty/Sabre/CalDAV/Notifications/Node.php | 188 +++ .../Notifications/Notification/Invite.php | 276 +++++ .../Notification/InviteReply.php | 216 ++++ .../Notification/SystemStatus.php | 179 +++ 3rdparty/Sabre/CalDAV/Plugin.php | 587 ++++++++-- .../CalDAV/Property/AllowedSharingModes.php | 72 ++ 3rdparty/Sabre/CalDAV/Property/Invite.php | 173 +++ .../Property/ScheduleCalendarTransp.php | 99 ++ 3rdparty/Sabre/CalDAV/Schedule/IMip.php | 18 +- 3rdparty/Sabre/CalDAV/Schedule/Outbox.php | 10 +- 3rdparty/Sabre/CalDAV/Server.php | 68 -- 3rdparty/Sabre/CalDAV/ShareableCalendar.php | 72 ++ 3rdparty/Sabre/CalDAV/SharedCalendar.php | 98 ++ 3rdparty/Sabre/CalDAV/SharingPlugin.php | 475 ++++++++ 3rdparty/Sabre/CalDAV/UserCalendars.php | 64 +- 3rdparty/Sabre/CalDAV/Version.php | 2 +- 3rdparty/Sabre/CalDAV/includes.php | 25 +- 3rdparty/Sabre/CardDAV/AddressBook.php | 4 +- 3rdparty/Sabre/CardDAV/Card.php | 16 +- 3rdparty/Sabre/CardDAV/Plugin.php | 55 +- 3rdparty/Sabre/CardDAV/VCFExportPlugin.php | 107 ++ 3rdparty/Sabre/CardDAV/Version.php | 2 +- 3rdparty/Sabre/CardDAV/includes.php | 1 + 3rdparty/Sabre/DAV/Browser/Plugin.php | 2 +- 3rdparty/Sabre/DAV/Client.php | 69 +- 3rdparty/Sabre/DAV/Collection.php | 6 +- 3rdparty/Sabre/DAV/Directory.php | 17 - ...Implemented.php => ReportNotSupported.php} | 4 +- 3rdparty/Sabre/DAV/FS/Directory.php | 5 +- 3rdparty/Sabre/DAV/FSExt/Directory.php | 5 +- 3rdparty/Sabre/DAV/FSExt/File.php | 32 +- 3rdparty/Sabre/DAV/ICollection.php | 5 +- 3rdparty/Sabre/DAV/IFile.php | 2 +- 3rdparty/Sabre/DAV/Locks/Plugin.php | 1 + 3rdparty/Sabre/DAV/Node.php | 2 +- 3rdparty/Sabre/DAV/ObjectTree.php | 26 +- 3rdparty/Sabre/DAV/PartialUpdate/IFile.php | 38 + 3rdparty/Sabre/DAV/PartialUpdate/Plugin.php | 209 ++++ 3rdparty/Sabre/DAV/Property.php | 12 +- 3rdparty/Sabre/DAV/Property/Href.php | 2 +- 3rdparty/Sabre/DAV/Property/HrefList.php | 2 +- 3rdparty/Sabre/DAV/Property/Response.php | 2 +- 3rdparty/Sabre/DAV/PropertyInterface.php | 21 + 3rdparty/Sabre/DAV/Server.php | 175 ++- 3rdparty/Sabre/DAV/ServerPlugin.php | 2 +- 3rdparty/Sabre/DAV/SimpleCollection.php | 5 +- 3rdparty/Sabre/DAV/SimpleDirectory.php | 21 - 3rdparty/Sabre/DAV/Version.php | 2 +- 3rdparty/Sabre/DAV/XMLUtil.php | 29 +- 3rdparty/Sabre/DAV/includes.php | 27 +- .../DAVACL/AbstractPrincipalCollection.php | 2 +- 3rdparty/Sabre/DAVACL/IACL.php | 2 +- 3rdparty/Sabre/DAVACL/Plugin.php | 54 +- 3rdparty/Sabre/DAVACL/Property/Acl.php | 10 +- 3rdparty/Sabre/DAVACL/Property/Principal.php | 4 +- 3rdparty/Sabre/DAVACL/Version.php | 2 +- 3rdparty/Sabre/HTTP/Request.php | 2 +- 3rdparty/Sabre/HTTP/Response.php | 7 +- 3rdparty/Sabre/HTTP/Version.php | 2 +- 3rdparty/Sabre/VObject/Component.php | 365 ------ 3rdparty/Sabre/VObject/Component/VAlarm.php | 102 -- .../Sabre/VObject/Component/VCalendar.php | 133 --- 3rdparty/Sabre/VObject/Component/VEvent.php | 71 -- 3rdparty/Sabre/VObject/Component/VJournal.php | 46 - 3rdparty/Sabre/VObject/Component/VTodo.php | 68 -- 3rdparty/Sabre/VObject/DateTimeParser.php | 181 --- 3rdparty/Sabre/VObject/Element.php | 16 - 3rdparty/Sabre/VObject/Element/DateTime.php | 37 - .../Sabre/VObject/Element/MultiDateTime.php | 17 - 3rdparty/Sabre/VObject/ElementList.php | 172 --- 3rdparty/Sabre/VObject/FreeBusyGenerator.php | 297 ----- 3rdparty/Sabre/VObject/Node.php | 149 --- 3rdparty/Sabre/VObject/Parameter.php | 84 -- 3rdparty/Sabre/VObject/ParseException.php | 12 - 3rdparty/Sabre/VObject/Property.php | 348 ------ 3rdparty/Sabre/VObject/Property/DateTime.php | 260 ---- .../Sabre/VObject/Property/MultiDateTime.php | 166 --- 3rdparty/Sabre/VObject/Reader.php | 183 --- 3rdparty/Sabre/VObject/RecurrenceIterator.php | 1043 ----------------- 3rdparty/Sabre/VObject/Version.php | 24 - 3rdparty/Sabre/VObject/WindowsTimezoneMap.php | 128 -- 3rdparty/Sabre/VObject/includes.php | 41 - 3rdparty/Sabre/autoload.php | 28 +- 102 files changed, 4658 insertions(+), 4502 deletions(-) create mode 100755 3rdparty/Sabre/CalDAV/Backend/BackendInterface.php create mode 100755 3rdparty/Sabre/CalDAV/Backend/NotificationSupport.php create mode 100755 3rdparty/Sabre/CalDAV/Backend/SharingSupport.php create mode 100755 3rdparty/Sabre/CalDAV/Exception/InvalidComponentType.php create mode 100755 3rdparty/Sabre/CalDAV/IShareableCalendar.php create mode 100755 3rdparty/Sabre/CalDAV/ISharedCalendar.php create mode 100755 3rdparty/Sabre/CalDAV/Notifications/Collection.php create mode 100755 3rdparty/Sabre/CalDAV/Notifications/ICollection.php create mode 100755 3rdparty/Sabre/CalDAV/Notifications/INode.php create mode 100755 3rdparty/Sabre/CalDAV/Notifications/INotificationType.php create mode 100755 3rdparty/Sabre/CalDAV/Notifications/Node.php create mode 100755 3rdparty/Sabre/CalDAV/Notifications/Notification/Invite.php create mode 100755 3rdparty/Sabre/CalDAV/Notifications/Notification/InviteReply.php create mode 100755 3rdparty/Sabre/CalDAV/Notifications/Notification/SystemStatus.php create mode 100755 3rdparty/Sabre/CalDAV/Property/AllowedSharingModes.php create mode 100755 3rdparty/Sabre/CalDAV/Property/Invite.php create mode 100755 3rdparty/Sabre/CalDAV/Property/ScheduleCalendarTransp.php delete mode 100755 3rdparty/Sabre/CalDAV/Server.php create mode 100755 3rdparty/Sabre/CalDAV/ShareableCalendar.php create mode 100755 3rdparty/Sabre/CalDAV/SharedCalendar.php create mode 100755 3rdparty/Sabre/CalDAV/SharingPlugin.php create mode 100755 3rdparty/Sabre/CardDAV/VCFExportPlugin.php delete mode 100755 3rdparty/Sabre/DAV/Directory.php rename 3rdparty/Sabre/DAV/Exception/{ReportNotImplemented.php => ReportNotSupported.php} (87%) create mode 100755 3rdparty/Sabre/DAV/PartialUpdate/IFile.php create mode 100755 3rdparty/Sabre/DAV/PartialUpdate/Plugin.php create mode 100755 3rdparty/Sabre/DAV/PropertyInterface.php delete mode 100755 3rdparty/Sabre/DAV/SimpleDirectory.php delete mode 100755 3rdparty/Sabre/VObject/Component.php delete mode 100755 3rdparty/Sabre/VObject/Component/VAlarm.php delete mode 100755 3rdparty/Sabre/VObject/Component/VCalendar.php delete mode 100755 3rdparty/Sabre/VObject/Component/VEvent.php delete mode 100755 3rdparty/Sabre/VObject/Component/VJournal.php delete mode 100755 3rdparty/Sabre/VObject/Component/VTodo.php delete mode 100755 3rdparty/Sabre/VObject/DateTimeParser.php delete mode 100755 3rdparty/Sabre/VObject/Element.php delete mode 100755 3rdparty/Sabre/VObject/Element/DateTime.php delete mode 100755 3rdparty/Sabre/VObject/Element/MultiDateTime.php delete mode 100755 3rdparty/Sabre/VObject/ElementList.php delete mode 100755 3rdparty/Sabre/VObject/FreeBusyGenerator.php delete mode 100755 3rdparty/Sabre/VObject/Node.php delete mode 100755 3rdparty/Sabre/VObject/Parameter.php delete mode 100755 3rdparty/Sabre/VObject/ParseException.php delete mode 100755 3rdparty/Sabre/VObject/Property.php delete mode 100755 3rdparty/Sabre/VObject/Property/DateTime.php delete mode 100755 3rdparty/Sabre/VObject/Property/MultiDateTime.php delete mode 100755 3rdparty/Sabre/VObject/Reader.php delete mode 100755 3rdparty/Sabre/VObject/RecurrenceIterator.php delete mode 100755 3rdparty/Sabre/VObject/Version.php delete mode 100755 3rdparty/Sabre/VObject/WindowsTimezoneMap.php delete mode 100755 3rdparty/Sabre/VObject/includes.php diff --git a/3rdparty/Sabre/CalDAV/Backend/Abstract.php b/3rdparty/Sabre/CalDAV/Backend/Abstract.php index 7aba1d69ff..88e5b4a1a0 100755 --- a/3rdparty/Sabre/CalDAV/Backend/Abstract.php +++ b/3rdparty/Sabre/CalDAV/Backend/Abstract.php @@ -1,47 +1,19 @@ getCalendarObjects($calendarId); + + $validator = new Sabre_CalDAV_CalendarQueryValidator(); + + foreach($objects as $object) { + + if ($this->validateFilterForObject($object, $filters)) { + $result[] = $object['uri']; + } + + } + + return $result; + + } /** - * Returns information from a single calendar object, based on it's object - * uri. + * This method validates if a filters (as passed to calendarQuery) matches + * the given object. * - * The returned array must have the same keys as getCalendarObjects. The - * 'calendardata' object is required here though, while it's not required - * for getCalendarObjects. - * - * @param string $calendarId - * @param string $objectUri - * @return array + * @param array $object + * @param array $filter + * @return bool */ - abstract function getCalendarObject($calendarId,$objectUri); + protected function validateFilterForObject(array $object, array $filters) { - /** - * Creates a new calendar object. - * - * @param string $calendarId - * @param string $objectUri - * @param string $calendarData - * @return void - */ - abstract function createCalendarObject($calendarId,$objectUri,$calendarData); + // 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']); + } - /** - * 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); + $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); + + } - /** - * Deletes an existing calendar object. - * - * @param string $calendarId - * @param string $objectUri - * @return void - */ - abstract function deleteCalendarObject($calendarId,$objectUri); } diff --git a/3rdparty/Sabre/CalDAV/Backend/BackendInterface.php b/3rdparty/Sabre/CalDAV/Backend/BackendInterface.php new file mode 100755 index 0000000000..881538ab60 --- /dev/null +++ b/3rdparty/Sabre/CalDAV/Backend/BackendInterface.php @@ -0,0 +1,231 @@ + 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 100755 index 0000000000..d5a1409d9b --- /dev/null +++ b/3rdparty/Sabre/CalDAV/Backend/NotificationSupport.php @@ -0,0 +1,47 @@ + $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,16 +246,24 @@ 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; + } } @@ -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 100755 index 0000000000..f73f0ff3d8 --- /dev/null +++ b/3rdparty/Sabre/CalDAV/Backend/SharingSupport.php @@ -0,0 +1,238 @@ +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 72f0a578d1..40bd8588c7 100755 --- 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 bd0d343382..b95095f96f 100755 --- a/3rdparty/Sabre/CalDAV/CalendarQueryParser.php +++ b/3rdparty/Sabre/CalDAV/CalendarQueryParser.php @@ -1,5 +1,7 @@ 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 8f674840e8..53e86fc509 100755 --- a/3rdparty/Sabre/CalDAV/CalendarQueryValidator.php +++ b/3rdparty/Sabre/CalDAV/CalendarQueryValidator.php @@ -1,5 +1,7 @@ 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 3907913cc7..eb62eea75a 100755 --- a/3rdparty/Sabre/CalDAV/CalendarRootNode.php +++ b/3rdparty/Sabre/CalDAV/CalendarRootNode.php @@ -1,14 +1,15 @@ caldavBackend = $caldavBackend; diff --git a/3rdparty/Sabre/CalDAV/Exception/InvalidComponentType.php b/3rdparty/Sabre/CalDAV/Exception/InvalidComponentType.php new file mode 100755 index 0000000000..4ac617d22f --- /dev/null +++ b/3rdparty/Sabre/CalDAV/Exception/InvalidComponentType.php @@ -0,0 +1,32 @@ +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 ec42b406b2..d3e4e7b720 100755 --- a/3rdparty/Sabre/CalDAV/ICSExportPlugin.php +++ b/3rdparty/Sabre/CalDAV/ICSExportPlugin.php @@ -1,5 +1,7 @@ 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 15d51ebcf7..40aa9f9579 100755 --- 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/IShareableCalendar.php b/3rdparty/Sabre/CalDAV/IShareableCalendar.php new file mode 100755 index 0000000000..5b55788c01 --- /dev/null +++ b/3rdparty/Sabre/CalDAV/IShareableCalendar.php @@ -0,0 +1,48 @@ +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 100755 index 0000000000..eb873af3f9 --- /dev/null +++ b/3rdparty/Sabre/CalDAV/Notifications/ICollection.php @@ -0,0 +1,22 @@ +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 100755 index 0000000000..a6b36203f3 --- /dev/null +++ b/3rdparty/Sabre/CalDAV/Notifications/Notification/Invite.php @@ -0,0 +1,276 @@ +$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 100755 index 0000000000..e935aa5aa1 --- /dev/null +++ b/3rdparty/Sabre/CalDAV/Notifications/Notification/InviteReply.php @@ -0,0 +1,216 @@ +$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 100755 index 0000000000..f09ed3525f --- /dev/null +++ b/3rdparty/Sabre/CalDAV/Notifications/Notification/SystemStatus.php @@ -0,0 +1,179 @@ +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 c56ab38484..f3d11969c8 100755 --- a/3rdparty/Sabre/CalDAV/Plugin.php +++ b/3rdparty/Sabre/CalDAV/Plugin.php @@ -1,5 +1,7 @@ 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) { + + $requestedCalendarData = true; + $requestedProperties = $parser->requestedProperties; + + if (!in_array('{urn:ietf:params:xml:ns:caldav}calendar-data', $requestedProperties)) { + + // We always retrieve calendar-data, as we need it for filtering. + $requestedProperties[] = '{urn:ietf:params:xml:ns:caldav}calendar-data'; + + // If calendar-data wasn't explicitly requested, we need to remove + // it after processing. + $requestedCalendarData = false; + } + + $properties = $this->server->getPropertiesForPath( + $this->server->getRequestUri(), + $requestedProperties, + 0 + ); + + // This array should have only 1 element, the first calendar + // object. + $properties = current($properties); + + // 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 calendar-data wasn't explicitly requested, we need to remove - // it after processing. - $requestedCalendarData = false; } + // If we're dealing with a calendar, the calendar itself is responsible + // for the calendar-query. + if ($node instanceof Sabre_CalDAV_ICalendar && $depth = 1) { - // These are the list of nodes that potentially match the requirement - $candidateNodes = $this->server->getPropertiesForPath( - $this->server->getRequestUri(), - $requestedProperties, - $this->server->getHTTPDepth(0) - ); + $nodePaths = $node->calendarQuery($parser->filters); - $verifiedNodes = array(); + foreach($nodePaths as $path) { - $validator = new Sabre_CalDAV_CalendarQueryValidator(); + list($properties) = + $this->server->getPropertiesForPath($this->server->getRequestUri() . '/' . $path, $parser->requestedProperties); - foreach($candidateNodes as $node) { - - // If the node didn't have a calendar-data property, it must not be a calendar object - if (!isset($node[200]['{urn:ietf:params:xml:ns:caldav}calendar-data'])) - continue; - - $vObject = Sabre_VObject_Reader::read($node[200]['{urn:ietf:params:xml:ns:caldav}calendar-data']); - if ($validator->validate($vObject,$parser->filters)) { - - if (!$requestedCalendarData) { - unset($node[200]['{urn:ietf:params:xml:ns:caldav}calendar-data']); - } if ($parser->expand) { + // 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); - } - return $obj; - }, $calendar->getChildren()); + // 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, + )); - $generator = new Sabre_VObject_FreeBusyGenerator(); + $objects = array_map(function($url) use ($calendar) { + $obj = $calendar->getChild($url)->get(); + return $obj; + }, $urls); + + $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) { + 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 + */ + 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; - } /** @@ -877,6 +1060,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 diff --git a/3rdparty/Sabre/CalDAV/Property/AllowedSharingModes.php b/3rdparty/Sabre/CalDAV/Property/AllowedSharingModes.php new file mode 100755 index 0000000000..efe751732c --- /dev/null +++ b/3rdparty/Sabre/CalDAV/Property/AllowedSharingModes.php @@ -0,0 +1,72 @@ +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 100755 index 0000000000..4ed94877df --- /dev/null +++ b/3rdparty/Sabre/CalDAV/Property/Invite.php @@ -0,0 +1,173 @@ +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 100755 index 0000000000..76c1dbaec2 --- /dev/null +++ b/3rdparty/Sabre/CalDAV/Property/ScheduleCalendarTransp.php @@ -0,0 +1,99 @@ +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/Schedule/IMip.php b/3rdparty/Sabre/CalDAV/Schedule/IMip.php index 37e75fcc4a..92c3c097c1 100755 --- a/3rdparty/Sabre/CalDAV/Schedule/IMip.php +++ b/3rdparty/Sabre/CalDAV/Schedule/IMip.php @@ -1,5 +1,7 @@ diff --git a/3rdparty/Sabre/CalDAV/Schedule/Outbox.php b/3rdparty/Sabre/CalDAV/Schedule/Outbox.php index 014c37230d..462aa527e2 100755 --- 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 @@ -103,6 +103,11 @@ class Sabre_CalDAV_Schedule_Outbox extends Sabre_DAV_Directory implements Sabre_ 'principal' => $this->getOwner(), 'protected' => true, ), + array( + 'privilege' => '{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}schedule-post-vevent', + 'principal' => $this->getOwner(), + 'protected' => true, + ), array( 'privilege' => '{DAV:}read', 'principal' => $this->getOwner(), @@ -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 325e3d80a7..0000000000 --- a/3rdparty/Sabre/CalDAV/Server.php +++ /dev/null @@ -1,68 +0,0 @@ -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 100755 index 0000000000..0e44885c62 --- /dev/null +++ b/3rdparty/Sabre/CalDAV/ShareableCalendar.php @@ -0,0 +1,72 @@ +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 100755 index 0000000000..9000697d3e --- /dev/null +++ b/3rdparty/Sabre/CalDAV/SharedCalendar.php @@ -0,0 +1,98 @@ +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 100755 index 0000000000..31df8057b2 --- /dev/null +++ b/3rdparty/Sabre/CalDAV/SharingPlugin.php @@ -0,0 +1,475 @@ +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 b8d3f0573f..3194e6677a 100755 --- 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 ace9901c08..0ad14fa086 100755 --- 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 1ecb870a0e..8b38e398f6 100755 --- 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 12297175a8..8d545114d9 100755 --- 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/Card.php b/3rdparty/Sabre/CardDAV/Card.php index d7c6633383..0e35d321eb 100755 --- 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/Plugin.php b/3rdparty/Sabre/CardDAV/Plugin.php index 96def6dd96..12bccaec4f 100755 --- a/3rdparty/Sabre/CardDAV/Plugin.php +++ b/3rdparty/Sabre/CardDAV/Plugin.php @@ -1,5 +1,7 @@ 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; @@ -615,6 +624,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 diff --git a/3rdparty/Sabre/CardDAV/VCFExportPlugin.php b/3rdparty/Sabre/CardDAV/VCFExportPlugin.php new file mode 100755 index 0000000000..8850fef8af --- /dev/null +++ b/3rdparty/Sabre/CardDAV/VCFExportPlugin.php @@ -0,0 +1,107 @@ +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 d0623f0d3e..6b70a2df9b 100755 --- 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 c3b8c04b07..08b4f76bd0 100755 --- 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/Browser/Plugin.php b/3rdparty/Sabre/DAV/Browser/Plugin.php index 09bbdd2ae0..b6440ab634 100755 --- 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/Client.php b/3rdparty/Sabre/DAV/Client.php index 9a428765e9..9d9f4c96b0 100755 --- 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 @@ -87,6 +100,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 * @@ -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 776c22531b..c7648a8a52 100755 --- 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 6db8febc02..0000000000 --- a/3rdparty/Sabre/DAV/Directory.php +++ /dev/null @@ -1,17 +0,0 @@ -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/ICollection.php b/3rdparty/Sabre/DAV/ICollection.php index 4626038a66..94431a0c49 100755 --- 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/IFile.php b/3rdparty/Sabre/DAV/IFile.php index 478f822ae7..1eca8986a5 100755 --- 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/Locks/Plugin.php b/3rdparty/Sabre/DAV/Locks/Plugin.php index 035b3a6386..957ac506a9 100755 --- 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/Node.php b/3rdparty/Sabre/DAV/Node.php index 070b7176af..3b95dfec2f 100755 --- 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 bce5146390..3b7f222d64 100755 --- 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 100755 index 0000000000..cf5ad55c6d --- /dev/null +++ b/3rdparty/Sabre/DAV/PartialUpdate/IFile.php @@ -0,0 +1,38 @@ +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 1cfada3236..6487bf44bc 100755 --- 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/Href.php b/3rdparty/Sabre/DAV/Property/Href.php index dac564f24d..cd1d867f71 100755 --- 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 7a52272e88..282c452ca0 100755 --- 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/Response.php b/3rdparty/Sabre/DAV/Property/Response.php index 88afbcfb26..9f21163d12 100755 --- 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/PropertyInterface.php b/3rdparty/Sabre/DAV/PropertyInterface.php new file mode 100755 index 0000000000..515072cbd3 --- /dev/null +++ b/3rdparty/Sabre/DAV/PropertyInterface.php @@ -0,0 +1,21 @@ +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[a-z0-9-]+)(?:=(?P[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 131863d13f..120569ffcc 100755 --- 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 4acf971caa..79e2eaaacd 100755 --- 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 621222ebc5..0000000000 --- a/3rdparty/Sabre/DAV/SimpleDirectory.php +++ /dev/null @@ -1,21 +0,0 @@ -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; @@ -64,29 +60,11 @@ 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 6a4890677e..6728f88ce7 100755 --- 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,6 +72,18 @@ include __DIR__ . '/IQuota.php'; include __DIR__ . '/Locks/Plugin.php'; include __DIR__ . '/Mount/Plugin.php'; include __DIR__ . '/ObjectTree.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__ . '/Exception/ConflictingLock.php'; +include __DIR__ . '/Exception/FileNotFound.php'; +include __DIR__ . '/File.php'; +include __DIR__ . '/FS/Directory.php'; +include __DIR__ . '/FS/File.php'; +include __DIR__ . '/FSExt/Directory.php'; +include __DIR__ . '/FSExt/File.php'; include __DIR__ . '/Property/GetLastModified.php'; include __DIR__ . '/Property/Href.php'; include __DIR__ . '/Property/HrefList.php'; @@ -81,17 +93,6 @@ include __DIR__ . '/Property/Response.php'; include __DIR__ . '/Property/ResponseList.php'; include __DIR__ . '/Property/SupportedLock.php'; include __DIR__ . '/Property/SupportedReportSet.php'; -include __DIR__ . '/Tree/Filesystem.php'; -include __DIR__ . '/Collection.php'; -include __DIR__ . '/Directory.php'; -include __DIR__ . '/Exception/ConflictingLock.php'; -include __DIR__ . '/Exception/FileNotFound.php'; -include __DIR__ . '/File.php'; -include __DIR__ . '/FS/Directory.php'; -include __DIR__ . '/FS/File.php'; -include __DIR__ . '/FSExt/Directory.php'; -include __DIR__ . '/FSExt/File.php'; include __DIR__ . '/SimpleCollection.php'; -include __DIR__ . '/SimpleDirectory.php'; include __DIR__ . '/SimpleFile.php'; // End includes diff --git a/3rdparty/Sabre/DAVACL/AbstractPrincipalCollection.php b/3rdparty/Sabre/DAVACL/AbstractPrincipalCollection.php index e05b774980..f67eadad6e 100755 --- 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/IACL.php b/3rdparty/Sabre/DAVACL/IACL.php index 003e699348..356bb481d5 100755 --- 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/Plugin.php b/3rdparty/Sabre/DAVACL/Plugin.php index 5c828c6d97..5b17c83847 100755 --- 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; @@ -241,6 +242,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/Property/Acl.php b/3rdparty/Sabre/DAVACL/Property/Acl.php index 05e1a690b3..3f79a8d532 100755 --- 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/Principal.php b/3rdparty/Sabre/DAVACL/Property/Principal.php index c36328a58e..3f68174227 100755 --- 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/Version.php b/3rdparty/Sabre/DAVACL/Version.php index 9950f74874..084a9c13c8 100755 --- 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/HTTP/Request.php b/3rdparty/Sabre/HTTP/Request.php index 4746ef7770..74d5ed3d7e 100755 --- 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 ffe9bda208..9d436881bd 100755 --- 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/Version.php b/3rdparty/Sabre/HTTP/Version.php index e6b4f7e535..8ccd7c9edf 100755 --- 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/VObject/Component.php b/3rdparty/Sabre/VObject/Component.php deleted file mode 100755 index b78a26133f..0000000000 --- a/3rdparty/Sabre/VObject/Component.php +++ /dev/null @@ -1,365 +0,0 @@ - 'Sabre_VObject_Component_VCalendar', - 'VEVENT' => 'Sabre_VObject_Component_VEvent', - 'VTODO' => 'Sabre_VObject_Component_VTodo', - 'VJOURNAL' => 'Sabre_VObject_Component_VJournal', - 'VALARM' => 'Sabre_VObject_Component_VAlarm', - ); - - /** - * Creates the new component by name, but in addition will also see if - * there's a class mapped to the property name. - * - * @param string $name - * @param string $value - * @return Sabre_VObject_Component - */ - static public function create($name, $value = null) { - - $name = strtoupper($name); - - if (isset(self::$classMap[$name])) { - return new self::$classMap[$name]($name, $value); - } else { - return new self($name, $value); - } - - } - - /** - * Creates a new component. - * - * By default this object will iterate over its own children, but this can - * be overridden with the iterator argument - * - * @param string $name - * @param Sabre_VObject_ElementList $iterator - */ - public function __construct($name, Sabre_VObject_ElementList $iterator = null) { - - $this->name = strtoupper($name); - if (!is_null($iterator)) $this->iterator = $iterator; - - } - - /** - * Turns the object back into a serialized blob. - * - * @return string - */ - public function serialize() { - - $str = "BEGIN:" . $this->name . "\r\n"; - - /** - * Gives a component a 'score' for sorting purposes. - * - * This is solely used by the childrenSort method. - * - * A higher score means the item will be higher in the list - * - * @param Sabre_VObject_Node $n - * @return int - */ - $sortScore = function($n) { - - if ($n instanceof Sabre_VObject_Component) { - // We want to encode VTIMEZONE first, this is a personal - // preference. - if ($n->name === 'VTIMEZONE') { - return 1; - } else { - return 0; - } - } else { - // VCARD version 4.0 wants the VERSION property to appear first - if ($n->name === 'VERSION') { - return 3; - } else { - return 2; - } - } - - }; - - usort($this->children, function($a, $b) use ($sortScore) { - - $sA = $sortScore($a); - $sB = $sortScore($b); - - if ($sA === $sB) return 0; - - return ($sA > $sB) ? -1 : 1; - - }); - - foreach($this->children as $child) $str.=$child->serialize(); - $str.= "END:" . $this->name . "\r\n"; - - return $str; - - } - - /** - * Adds a new component or element - * - * You can call this method with the following syntaxes: - * - * add(Sabre_VObject_Element $element) - * add(string $name, $value) - * - * The first version adds an Element - * The second adds a property as a string. - * - * @param mixed $item - * @param mixed $itemValue - * @return void - */ - public function add($item, $itemValue = null) { - - if ($item instanceof Sabre_VObject_Element) { - if (!is_null($itemValue)) { - throw new InvalidArgumentException('The second argument must not be specified, when passing a VObject'); - } - $item->parent = $this; - $this->children[] = $item; - } elseif(is_string($item)) { - - if (!is_scalar($itemValue)) { - throw new InvalidArgumentException('The second argument must be scalar'); - } - $item = Sabre_VObject_Property::create($item,$itemValue); - $item->parent = $this; - $this->children[] = $item; - - } else { - - throw new InvalidArgumentException('The first argument must either be a Sabre_VObject_Element or a string'); - - } - - } - - /** - * Returns an iterable list of children - * - * @return Sabre_VObject_ElementList - */ - public function children() { - - return new Sabre_VObject_ElementList($this->children); - - } - - /** - * Returns an array with elements that match the specified name. - * - * This function is also aware of MIME-Directory groups (as they appear in - * vcards). This means that if a property is grouped as "HOME.EMAIL", it - * will also be returned when searching for just "EMAIL". If you want to - * search for a property in a specific group, you can select on the entire - * string ("HOME.EMAIL"). If you want to search on a specific property that - * has not been assigned a group, specify ".EMAIL". - * - * Keys are retained from the 'children' array, which may be confusing in - * certain cases. - * - * @param string $name - * @return array - */ - public function select($name) { - - $group = null; - $name = strtoupper($name); - if (strpos($name,'.')!==false) { - list($group,$name) = explode('.', $name, 2); - } - - $result = array(); - foreach($this->children as $key=>$child) { - - if ( - strtoupper($child->name) === $name && - (is_null($group) || ( $child instanceof Sabre_VObject_Property && strtoupper($child->group) === $group)) - ) { - - $result[$key] = $child; - - } - } - - reset($result); - return $result; - - } - - /** - * This method only returns a list of sub-components. Properties are - * ignored. - * - * @return array - */ - public function getComponents() { - - $result = array(); - foreach($this->children as $child) { - if ($child instanceof Sabre_VObject_Component) { - $result[] = $child; - } - } - - return $result; - - } - - /* Magic property accessors {{{ */ - - /** - * Using 'get' you will either get a property or component, - * - * If there were no child-elements found with the specified name, - * null is returned. - * - * @param string $name - * @return Sabre_VObject_Property - */ - public function __get($name) { - - $matches = $this->select($name); - if (count($matches)===0) { - return null; - } else { - $firstMatch = current($matches); - /** @var $firstMatch Sabre_VObject_Property */ - $firstMatch->setIterator(new Sabre_VObject_ElementList(array_values($matches))); - return $firstMatch; - } - - } - - /** - * This method checks if a sub-element with the specified name exists. - * - * @param string $name - * @return bool - */ - public function __isset($name) { - - $matches = $this->select($name); - return count($matches)>0; - - } - - /** - * Using the setter method you can add properties or subcomponents - * - * You can either pass a Sabre_VObject_Component, Sabre_VObject_Property - * object, or a string to automatically create a Property. - * - * If the item already exists, it will be removed. If you want to add - * a new item with the same name, always use the add() method. - * - * @param string $name - * @param mixed $value - * @return void - */ - public function __set($name, $value) { - - $matches = $this->select($name); - $overWrite = count($matches)?key($matches):null; - - if ($value instanceof Sabre_VObject_Component || $value instanceof Sabre_VObject_Property) { - $value->parent = $this; - if (!is_null($overWrite)) { - $this->children[$overWrite] = $value; - } else { - $this->children[] = $value; - } - } elseif (is_scalar($value)) { - $property = Sabre_VObject_Property::create($name,$value); - $property->parent = $this; - if (!is_null($overWrite)) { - $this->children[$overWrite] = $property; - } else { - $this->children[] = $property; - } - } else { - throw new InvalidArgumentException('You must pass a Sabre_VObject_Component, Sabre_VObject_Property or scalar type'); - } - - } - - /** - * Removes all properties and components within this component. - * - * @param string $name - * @return void - */ - public function __unset($name) { - - $matches = $this->select($name); - foreach($matches as $k=>$child) { - - unset($this->children[$k]); - $child->parent = null; - - } - - } - - /* }}} */ - - /** - * This method is automatically called when the object is cloned. - * Specifically, this will ensure all child elements are also cloned. - * - * @return void - */ - public function __clone() { - - foreach($this->children as $key=>$child) { - $this->children[$key] = clone $child; - $this->children[$key]->parent = $this; - } - - } - -} diff --git a/3rdparty/Sabre/VObject/Component/VAlarm.php b/3rdparty/Sabre/VObject/Component/VAlarm.php deleted file mode 100755 index ebb4a9b18f..0000000000 --- a/3rdparty/Sabre/VObject/Component/VAlarm.php +++ /dev/null @@ -1,102 +0,0 @@ -TRIGGER; - if(!isset($trigger['VALUE']) || strtoupper($trigger['VALUE']) === 'DURATION') { - $triggerDuration = Sabre_VObject_DateTimeParser::parseDuration($this->TRIGGER); - $related = (isset($trigger['RELATED']) && strtoupper($trigger['RELATED']) == 'END') ? 'END' : 'START'; - - $parentComponent = $this->parent; - if ($related === 'START') { - $effectiveTrigger = clone $parentComponent->DTSTART->getDateTime(); - $effectiveTrigger->add($triggerDuration); - } else { - if ($parentComponent->name === 'VTODO') { - $endProp = 'DUE'; - } elseif ($parentComponent->name === 'VEVENT') { - $endProp = 'DTEND'; - } else { - throw new Sabre_DAV_Exception('time-range filters on VALARM components are only supported when they are a child of VTODO or VEVENT'); - } - - if (isset($parentComponent->$endProp)) { - $effectiveTrigger = clone $parentComponent->$endProp->getDateTime(); - $effectiveTrigger->add($triggerDuration); - } elseif (isset($parentComponent->DURATION)) { - $effectiveTrigger = clone $parentComponent->DTSTART->getDateTime(); - $duration = Sabre_VObject_DateTimeParser::parseDuration($parentComponent->DURATION); - $effectiveTrigger->add($duration); - $effectiveTrigger->add($triggerDuration); - } else { - $effectiveTrigger = clone $parentComponent->DTSTART->getDateTime(); - $effectiveTrigger->add($triggerDuration); - } - } - } else { - $effectiveTrigger = $trigger->getDateTime(); - } - return $effectiveTrigger; - - } - - /** - * Returns true or false depending on if the event falls in the specified - * time-range. This is used for filtering purposes. - * - * The rules used to determine if an event falls within the specified - * time-range is based on the CalDAV specification. - * - * @param DateTime $start - * @param DateTime $end - * @return bool - */ - public function isInTimeRange(DateTime $start, DateTime $end) { - - $effectiveTrigger = $this->getEffectiveTriggerTime(); - - if (isset($this->DURATION)) { - $duration = Sabre_VObject_DateTimeParser::parseDuration($this->DURATION); - $repeat = (string)$this->repeat; - if (!$repeat) { - $repeat = 1; - } - - $period = new DatePeriod($effectiveTrigger, $duration, (int)$repeat); - - foreach($period as $occurrence) { - - if ($start <= $occurrence && $end > $occurrence) { - return true; - } - } - return false; - } else { - return ($start <= $effectiveTrigger && $end > $effectiveTrigger); - } - - } - -} - -?> diff --git a/3rdparty/Sabre/VObject/Component/VCalendar.php b/3rdparty/Sabre/VObject/Component/VCalendar.php deleted file mode 100755 index f3be29afdb..0000000000 --- a/3rdparty/Sabre/VObject/Component/VCalendar.php +++ /dev/null @@ -1,133 +0,0 @@ -children as $component) { - - if (!$component instanceof Sabre_VObject_Component) - continue; - - if (isset($component->{'RECURRENCE-ID'})) - continue; - - if ($componentName && $component->name !== strtoupper($componentName)) - continue; - - if ($component->name === 'VTIMEZONE') - continue; - - $components[] = $component; - - } - - return $components; - - } - - /** - * If this calendar object, has events with recurrence rules, this method - * can be used to expand the event into multiple sub-events. - * - * Each event will be stripped from it's recurrence information, and only - * the instances of the event in the specified timerange will be left - * alone. - * - * In addition, this method will cause timezone information to be stripped, - * and normalized to UTC. - * - * This method will alter the VCalendar. This cannot be reversed. - * - * This functionality is specifically used by the CalDAV standard. It is - * possible for clients to request expand events, if they are rather simple - * clients and do not have the possibility to calculate recurrences. - * - * @param DateTime $start - * @param DateTime $end - * @return void - */ - public function expand(DateTime $start, DateTime $end) { - - $newEvents = array(); - - foreach($this->select('VEVENT') as $key=>$vevent) { - - if (isset($vevent->{'RECURRENCE-ID'})) { - unset($this->children[$key]); - continue; - } - - - if (!$vevent->rrule) { - unset($this->children[$key]); - if ($vevent->isInTimeRange($start, $end)) { - $newEvents[] = $vevent; - } - continue; - } - - $uid = (string)$vevent->uid; - if (!$uid) { - throw new LogicException('Event did not have a UID!'); - } - - $it = new Sabre_VObject_RecurrenceIterator($this, $vevent->uid); - $it->fastForward($start); - - while($it->valid() && $it->getDTStart() < $end) { - - if ($it->getDTEnd() > $start) { - - $newEvents[] = $it->getEventObject(); - - } - $it->next(); - - } - unset($this->children[$key]); - - } - - foreach($newEvents as $newEvent) { - - foreach($newEvent->children as $child) { - if ($child instanceof Sabre_VObject_Property_DateTime && - $child->getDateType() == Sabre_VObject_Property_DateTime::LOCALTZ) { - $child->setDateTime($child->getDateTime(),Sabre_VObject_Property_DateTime::UTC); - } - } - - $this->add($newEvent); - - } - - // Removing all VTIMEZONE components - unset($this->VTIMEZONE); - - } - -} - diff --git a/3rdparty/Sabre/VObject/Component/VEvent.php b/3rdparty/Sabre/VObject/Component/VEvent.php deleted file mode 100755 index d6b910874d..0000000000 --- a/3rdparty/Sabre/VObject/Component/VEvent.php +++ /dev/null @@ -1,71 +0,0 @@ -RRULE) { - $it = new Sabre_VObject_RecurrenceIterator($this); - $it->fastForward($start); - - // We fast-forwarded to a spot where the end-time of the - // recurrence instance exceeded the start of the requested - // time-range. - // - // If the starttime of the recurrence did not exceed the - // end of the time range as well, we have a match. - return ($it->getDTStart() < $end && $it->getDTEnd() > $start); - - } - - $effectiveStart = $this->DTSTART->getDateTime(); - if (isset($this->DTEND)) { - - // The DTEND property is considered non inclusive. So for a 3 day - // event in july, dtstart and dtend would have to be July 1st and - // July 4th respectively. - // - // See: - // http://tools.ietf.org/html/rfc5545#page-54 - $effectiveEnd = $this->DTEND->getDateTime(); - - } elseif (isset($this->DURATION)) { - $effectiveEnd = clone $effectiveStart; - $effectiveEnd->add( Sabre_VObject_DateTimeParser::parseDuration($this->DURATION) ); - } elseif ($this->DTSTART->getDateType() == Sabre_VObject_Element_DateTime::DATE) { - $effectiveEnd = clone $effectiveStart; - $effectiveEnd->modify('+1 day'); - } else { - $effectiveEnd = clone $effectiveStart; - } - return ( - ($start <= $effectiveEnd) && ($end > $effectiveStart) - ); - - } - -} - -?> diff --git a/3rdparty/Sabre/VObject/Component/VJournal.php b/3rdparty/Sabre/VObject/Component/VJournal.php deleted file mode 100755 index 22b3ec921e..0000000000 --- a/3rdparty/Sabre/VObject/Component/VJournal.php +++ /dev/null @@ -1,46 +0,0 @@ -DTSTART)?$this->DTSTART->getDateTime():null; - if ($dtstart) { - $effectiveEnd = clone $dtstart; - if ($this->DTSTART->getDateType() == Sabre_VObject_Element_DateTime::DATE) { - $effectiveEnd->modify('+1 day'); - } - - return ($start <= $effectiveEnd && $end > $dtstart); - - } - return false; - - - } - -} - -?> diff --git a/3rdparty/Sabre/VObject/Component/VTodo.php b/3rdparty/Sabre/VObject/Component/VTodo.php deleted file mode 100755 index 79d06298d7..0000000000 --- a/3rdparty/Sabre/VObject/Component/VTodo.php +++ /dev/null @@ -1,68 +0,0 @@ -DTSTART)?$this->DTSTART->getDateTime():null; - $duration = isset($this->DURATION)?Sabre_VObject_DateTimeParser::parseDuration($this->DURATION):null; - $due = isset($this->DUE)?$this->DUE->getDateTime():null; - $completed = isset($this->COMPLETED)?$this->COMPLETED->getDateTime():null; - $created = isset($this->CREATED)?$this->CREATED->getDateTime():null; - - if ($dtstart) { - if ($duration) { - $effectiveEnd = clone $dtstart; - $effectiveEnd->add($duration); - return $start <= $effectiveEnd && $end > $dtstart; - } elseif ($due) { - return - ($start < $due || $start <= $dtstart) && - ($end > $dtstart || $end >= $due); - } else { - return $start <= $dtstart && $end > $dtstart; - } - } - if ($due) { - return ($start < $due && $end >= $due); - } - if ($completed && $created) { - return - ($start <= $created || $start <= $completed) && - ($end >= $created || $end >= $completed); - } - if ($completed) { - return ($start <= $completed && $end >= $completed); - } - if ($created) { - return ($end > $created); - } - return true; - - } - -} - -?> diff --git a/3rdparty/Sabre/VObject/DateTimeParser.php b/3rdparty/Sabre/VObject/DateTimeParser.php deleted file mode 100755 index 23a4bb6991..0000000000 --- a/3rdparty/Sabre/VObject/DateTimeParser.php +++ /dev/null @@ -1,181 +0,0 @@ -setTimeZone(new DateTimeZone('UTC')); - return $date; - - } - - /** - * Parses an iCalendar (rfc5545) formatted date and returns a DateTime object - * - * @param string $date - * @return DateTime - */ - static public function parseDate($date) { - - // Format is YYYYMMDD - $result = preg_match('/^([1-3][0-9]{3})([0-1][0-9])([0-3][0-9])$/',$date,$matches); - - if (!$result) { - throw new Sabre_DAV_Exception_BadRequest('The supplied iCalendar date value is incorrect: ' . $date); - } - - $date = new DateTime($matches[1] . '-' . $matches[2] . '-' . $matches[3], new DateTimeZone('UTC')); - return $date; - - } - - /** - * Parses an iCalendar (RFC5545) formatted duration value. - * - * This method will either return a DateTimeInterval object, or a string - * suitable for strtotime or DateTime::modify. - * - * @param string $duration - * @param bool $asString - * @return DateInterval|string - */ - static public function parseDuration($duration, $asString = false) { - - $result = preg_match('/^(?P\+|-)?P((?P\d+)W)?((?P\d+)D)?(T((?P\d+)H)?((?P\d+)M)?((?P\d+)S)?)?$/', $duration, $matches); - if (!$result) { - throw new Sabre_DAV_Exception_BadRequest('The supplied iCalendar duration value is incorrect: ' . $duration); - } - - if (!$asString) { - $invert = false; - if ($matches['plusminus']==='-') { - $invert = true; - } - - - $parts = array( - 'week', - 'day', - 'hour', - 'minute', - 'second', - ); - foreach($parts as $part) { - $matches[$part] = isset($matches[$part])&&$matches[$part]?(int)$matches[$part]:0; - } - - - // We need to re-construct the $duration string, because weeks and - // days are not supported by DateInterval in the same string. - $duration = 'P'; - $days = $matches['day']; - if ($matches['week']) { - $days+=$matches['week']*7; - } - if ($days) - $duration.=$days . 'D'; - - if ($matches['minute'] || $matches['second'] || $matches['hour']) { - $duration.='T'; - - if ($matches['hour']) - $duration.=$matches['hour'].'H'; - - if ($matches['minute']) - $duration.=$matches['minute'].'M'; - - if ($matches['second']) - $duration.=$matches['second'].'S'; - - } - - if ($duration==='P') { - $duration = 'PT0S'; - } - $iv = new DateInterval($duration); - if ($invert) $iv->invert = true; - - return $iv; - - } - - - - $parts = array( - 'week', - 'day', - 'hour', - 'minute', - 'second', - ); - - $newDur = ''; - foreach($parts as $part) { - if (isset($matches[$part]) && $matches[$part]) { - $newDur.=' '.$matches[$part] . ' ' . $part . 's'; - } - } - - $newDur = ($matches['plusminus']==='-'?'-':'+') . trim($newDur); - if ($newDur === '+') { $newDur = '+0 seconds'; }; - return $newDur; - - } - - /** - * Parses either a Date or DateTime, or Duration value. - * - * @param string $date - * @param DateTimeZone|string $referenceTZ - * @return DateTime|DateInterval - */ - static public function parse($date, $referenceTZ = null) { - - if ($date[0]==='P' || ($date[0]==='-' && $date[1]==='P')) { - return self::parseDuration($date); - } elseif (strlen($date)===8) { - return self::parseDate($date); - } else { - return self::parseDateTime($date, $referenceTZ); - } - - } - - -} diff --git a/3rdparty/Sabre/VObject/Element.php b/3rdparty/Sabre/VObject/Element.php deleted file mode 100755 index e20ff0b353..0000000000 --- a/3rdparty/Sabre/VObject/Element.php +++ /dev/null @@ -1,16 +0,0 @@ -vevent where there's multiple VEVENT objects. - * - * @package Sabre - * @subpackage VObject - * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. - * @author Evert Pot (http://www.rooftopsolutions.nl/) - * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License - */ -class Sabre_VObject_ElementList implements Iterator, Countable, ArrayAccess { - - /** - * Inner elements - * - * @var array - */ - protected $elements = array(); - - /** - * Creates the element list. - * - * @param array $elements - */ - public function __construct(array $elements) { - - $this->elements = $elements; - - } - - /* {{{ Iterator interface */ - - /** - * Current position - * - * @var int - */ - private $key = 0; - - /** - * Returns current item in iteration - * - * @return Sabre_VObject_Element - */ - public function current() { - - return $this->elements[$this->key]; - - } - - /** - * To the next item in the iterator - * - * @return void - */ - public function next() { - - $this->key++; - - } - - /** - * Returns the current iterator key - * - * @return int - */ - public function key() { - - return $this->key; - - } - - /** - * Returns true if the current position in the iterator is a valid one - * - * @return bool - */ - public function valid() { - - return isset($this->elements[$this->key]); - - } - - /** - * Rewinds the iterator - * - * @return void - */ - public function rewind() { - - $this->key = 0; - - } - - /* }}} */ - - /* {{{ Countable interface */ - - /** - * Returns the number of elements - * - * @return int - */ - public function count() { - - return count($this->elements); - - } - - /* }}} */ - - /* {{{ ArrayAccess Interface */ - - - /** - * Checks if an item exists through ArrayAccess. - * - * @param int $offset - * @return bool - */ - public function offsetExists($offset) { - - return isset($this->elements[$offset]); - - } - - /** - * Gets an item through ArrayAccess. - * - * @param int $offset - * @return mixed - */ - public function offsetGet($offset) { - - return $this->elements[$offset]; - - } - - /** - * Sets an item through ArrayAccess. - * - * @param int $offset - * @param mixed $value - * @return void - */ - public function offsetSet($offset,$value) { - - throw new LogicException('You can not add new objects to an ElementList'); - - } - - /** - * Sets an item through ArrayAccess. - * - * This method just forwards the request to the inner iterator - * - * @param int $offset - * @return void - */ - public function offsetUnset($offset) { - - throw new LogicException('You can not remove objects from an ElementList'); - - } - - /* }}} */ - -} diff --git a/3rdparty/Sabre/VObject/FreeBusyGenerator.php b/3rdparty/Sabre/VObject/FreeBusyGenerator.php deleted file mode 100755 index 1c96a64a00..0000000000 --- a/3rdparty/Sabre/VObject/FreeBusyGenerator.php +++ /dev/null @@ -1,297 +0,0 @@ -baseObject = $vcalendar; - - } - - /** - * Sets the input objects - * - * Every object must either be a string or a Sabre_VObject_Component. - * - * @param array $objects - * @return void - */ - public function setObjects(array $objects) { - - $this->objects = array(); - foreach($objects as $object) { - - if (is_string($object)) { - $this->objects[] = Sabre_VObject_Reader::read($object); - } elseif ($object instanceof Sabre_VObject_Component) { - $this->objects[] = $object; - } else { - throw new InvalidArgumentException('You can only pass strings or Sabre_VObject_Component arguments to setObjects'); - } - - } - - } - - /** - * Sets the time range - * - * Any freebusy object falling outside of this time range will be ignored. - * - * @param DateTime $start - * @param DateTime $end - * @return void - */ - public function setTimeRange(DateTime $start = null, DateTime $end = null) { - - $this->start = $start; - $this->end = $end; - - } - - /** - * Parses the input data and returns a correct VFREEBUSY object, wrapped in - * a VCALENDAR. - * - * @return Sabre_VObject_Component - */ - public function getResult() { - - $busyTimes = array(); - - foreach($this->objects as $object) { - - foreach($object->getBaseComponents() as $component) { - - switch($component->name) { - - case 'VEVENT' : - - $FBTYPE = 'BUSY'; - if (isset($component->TRANSP) && (strtoupper($component->TRANSP) === 'TRANSPARENT')) { - break; - } - if (isset($component->STATUS)) { - $status = strtoupper($component->STATUS); - if ($status==='CANCELLED') { - break; - } - if ($status==='TENTATIVE') { - $FBTYPE = 'BUSY-TENTATIVE'; - } - } - - $times = array(); - - if ($component->RRULE) { - - $iterator = new Sabre_VObject_RecurrenceIterator($object, (string)$component->uid); - if ($this->start) { - $iterator->fastForward($this->start); - } - - $maxRecurrences = 200; - - while($iterator->valid() && --$maxRecurrences) { - - $startTime = $iterator->getDTStart(); - if ($this->end && $startTime > $this->end) { - break; - } - $times[] = array( - $iterator->getDTStart(), - $iterator->getDTEnd(), - ); - - $iterator->next(); - - } - - } else { - - $startTime = $component->DTSTART->getDateTime(); - if ($this->end && $startTime > $this->end) { - break; - } - $endTime = null; - if (isset($component->DTEND)) { - $endTime = $component->DTEND->getDateTime(); - } elseif (isset($component->DURATION)) { - $duration = Sabre_VObject_DateTimeParser::parseDuration((string)$component->DURATION); - $endTime = clone $startTime; - $endTime->add($duration); - } elseif ($component->DTSTART->getDateType() === Sabre_VObject_Property_DateTime::DATE) { - $endTime = clone $startTime; - $endTime->modify('+1 day'); - } else { - // The event had no duration (0 seconds) - break; - } - - $times[] = array($startTime, $endTime); - - } - - foreach($times as $time) { - - if ($this->end && $time[0] > $this->end) break; - if ($this->start && $time[1] < $this->start) break; - - $busyTimes[] = array( - $time[0], - $time[1], - $FBTYPE, - ); - } - break; - - case 'VFREEBUSY' : - foreach($component->FREEBUSY as $freebusy) { - - $fbType = isset($freebusy['FBTYPE'])?strtoupper($freebusy['FBTYPE']):'BUSY'; - - // Skipping intervals marked as 'free' - if ($fbType==='FREE') - continue; - - $values = explode(',', $freebusy); - foreach($values as $value) { - list($startTime, $endTime) = explode('/', $value); - $startTime = Sabre_VObject_DateTimeParser::parseDateTime($startTime); - - if (substr($endTime,0,1)==='P' || substr($endTime,0,2)==='-P') { - $duration = Sabre_VObject_DateTimeParser::parseDuration($endTime); - $endTime = clone $startTime; - $endTime->add($duration); - } else { - $endTime = Sabre_VObject_DateTimeParser::parseDateTime($endTime); - } - - if($this->start && $this->start > $endTime) continue; - if($this->end && $this->end < $startTime) continue; - $busyTimes[] = array( - $startTime, - $endTime, - $fbType - ); - - } - - - } - break; - - - - } - - - } - - } - - if ($this->baseObject) { - $calendar = $this->baseObject; - } else { - $calendar = new Sabre_VObject_Component('VCALENDAR'); - $calendar->version = '2.0'; - if (Sabre_DAV_Server::$exposeVersion) { - $calendar->prodid = '-//SabreDAV//Sabre VObject ' . Sabre_VObject_Version::VERSION . '//EN'; - } else { - $calendar->prodid = '-//SabreDAV//Sabre VObject//EN'; - } - $calendar->calscale = 'GREGORIAN'; - } - - $vfreebusy = new Sabre_VObject_Component('VFREEBUSY'); - $calendar->add($vfreebusy); - - if ($this->start) { - $dtstart = new Sabre_VObject_Property_DateTime('DTSTART'); - $dtstart->setDateTime($this->start,Sabre_VObject_Property_DateTime::UTC); - $vfreebusy->add($dtstart); - } - if ($this->end) { - $dtend = new Sabre_VObject_Property_DateTime('DTEND'); - $dtend->setDateTime($this->start,Sabre_VObject_Property_DateTime::UTC); - $vfreebusy->add($dtend); - } - $dtstamp = new Sabre_VObject_Property_DateTime('DTSTAMP'); - $dtstamp->setDateTime(new DateTime('now'), Sabre_VObject_Property_DateTime::UTC); - $vfreebusy->add($dtstamp); - - foreach($busyTimes as $busyTime) { - - $busyTime[0]->setTimeZone(new DateTimeZone('UTC')); - $busyTime[1]->setTimeZone(new DateTimeZone('UTC')); - - $prop = new Sabre_VObject_Property( - 'FREEBUSY', - $busyTime[0]->format('Ymd\\THis\\Z') . '/' . $busyTime[1]->format('Ymd\\THis\\Z') - ); - $prop['FBTYPE'] = $busyTime[2]; - $vfreebusy->add($prop); - - } - - return $calendar; - - } - -} - diff --git a/3rdparty/Sabre/VObject/Node.php b/3rdparty/Sabre/VObject/Node.php deleted file mode 100755 index d89e01b56c..0000000000 --- a/3rdparty/Sabre/VObject/Node.php +++ /dev/null @@ -1,149 +0,0 @@ -iterator)) - return $this->iterator; - - return new Sabre_VObject_ElementList(array($this)); - - } - - /** - * Sets the overridden iterator - * - * Note that this is not actually part of the iterator interface - * - * @param Sabre_VObject_ElementList $iterator - * @return void - */ - public function setIterator(Sabre_VObject_ElementList $iterator) { - - $this->iterator = $iterator; - - } - - /* }}} */ - - /* {{{ Countable interface */ - - /** - * Returns the number of elements - * - * @return int - */ - public function count() { - - $it = $this->getIterator(); - return $it->count(); - - } - - /* }}} */ - - /* {{{ ArrayAccess Interface */ - - - /** - * Checks if an item exists through ArrayAccess. - * - * This method just forwards the request to the inner iterator - * - * @param int $offset - * @return bool - */ - public function offsetExists($offset) { - - $iterator = $this->getIterator(); - return $iterator->offsetExists($offset); - - } - - /** - * Gets an item through ArrayAccess. - * - * This method just forwards the request to the inner iterator - * - * @param int $offset - * @return mixed - */ - public function offsetGet($offset) { - - $iterator = $this->getIterator(); - return $iterator->offsetGet($offset); - - } - - /** - * Sets an item through ArrayAccess. - * - * This method just forwards the request to the inner iterator - * - * @param int $offset - * @param mixed $value - * @return void - */ - public function offsetSet($offset,$value) { - - $iterator = $this->getIterator(); - return $iterator->offsetSet($offset,$value); - - } - - /** - * Sets an item through ArrayAccess. - * - * This method just forwards the request to the inner iterator - * - * @param int $offset - * @return void - */ - public function offsetUnset($offset) { - - $iterator = $this->getIterator(); - return $iterator->offsetUnset($offset); - - } - - /* }}} */ - -} diff --git a/3rdparty/Sabre/VObject/Parameter.php b/3rdparty/Sabre/VObject/Parameter.php deleted file mode 100755 index 2e39af5f78..0000000000 --- a/3rdparty/Sabre/VObject/Parameter.php +++ /dev/null @@ -1,84 +0,0 @@ -name = strtoupper($name); - $this->value = $value; - - } - - /** - * Turns the object back into a serialized blob. - * - * @return string - */ - public function serialize() { - - if (is_null($this->value)) { - return $this->name; - } - $src = array( - '\\', - "\n", - ';', - ',', - ); - $out = array( - '\\\\', - '\n', - '\;', - '\,', - ); - - return $this->name . '=' . str_replace($src, $out, $this->value); - - } - - /** - * Called when this object is being cast to a string - * - * @return string - */ - public function __toString() { - - return $this->value; - - } - -} diff --git a/3rdparty/Sabre/VObject/ParseException.php b/3rdparty/Sabre/VObject/ParseException.php deleted file mode 100755 index 1b5e95bf16..0000000000 --- a/3rdparty/Sabre/VObject/ParseException.php +++ /dev/null @@ -1,12 +0,0 @@ - 'Sabre_VObject_Property_DateTime', - 'CREATED' => 'Sabre_VObject_Property_DateTime', - 'DTEND' => 'Sabre_VObject_Property_DateTime', - 'DTSTAMP' => 'Sabre_VObject_Property_DateTime', - 'DTSTART' => 'Sabre_VObject_Property_DateTime', - 'DUE' => 'Sabre_VObject_Property_DateTime', - 'EXDATE' => 'Sabre_VObject_Property_MultiDateTime', - 'LAST-MODIFIED' => 'Sabre_VObject_Property_DateTime', - 'RECURRENCE-ID' => 'Sabre_VObject_Property_DateTime', - 'TRIGGER' => 'Sabre_VObject_Property_DateTime', - ); - - /** - * Creates the new property by name, but in addition will also see if - * there's a class mapped to the property name. - * - * @param string $name - * @param string $value - * @return void - */ - static public function create($name, $value = null) { - - $name = strtoupper($name); - $shortName = $name; - $group = null; - if (strpos($shortName,'.')!==false) { - list($group, $shortName) = explode('.', $shortName); - } - - if (isset(self::$classMap[$shortName])) { - return new self::$classMap[$shortName]($name, $value); - } else { - return new self($name, $value); - } - - } - - /** - * Creates a new property object - * - * By default this object will iterate over its own children, but this can - * be overridden with the iterator argument - * - * @param string $name - * @param string $value - * @param Sabre_VObject_ElementList $iterator - */ - public function __construct($name, $value = null, $iterator = null) { - - $name = strtoupper($name); - $group = null; - if (strpos($name,'.')!==false) { - list($group, $name) = explode('.', $name); - } - $this->name = $name; - $this->group = $group; - if (!is_null($iterator)) $this->iterator = $iterator; - $this->setValue($value); - - } - - - - /** - * Updates the internal value - * - * @param string $value - * @return void - */ - public function setValue($value) { - - $this->value = $value; - - } - - /** - * Turns the object back into a serialized blob. - * - * @return string - */ - public function serialize() { - - $str = $this->name; - if ($this->group) $str = $this->group . '.' . $this->name; - - if (count($this->parameters)) { - foreach($this->parameters as $param) { - - $str.=';' . $param->serialize(); - - } - } - $src = array( - '\\', - "\n", - ); - $out = array( - '\\\\', - '\n', - ); - $str.=':' . str_replace($src, $out, $this->value); - - $out = ''; - while(strlen($str)>0) { - if (strlen($str)>75) { - $out.= mb_strcut($str,0,75,'utf-8') . "\r\n"; - $str = ' ' . mb_strcut($str,75,strlen($str),'utf-8'); - } else { - $out.=$str . "\r\n"; - $str=''; - break; - } - } - - return $out; - - } - - /** - * Adds a new componenten or element - * - * You can call this method with the following syntaxes: - * - * add(Sabre_VObject_Parameter $element) - * add(string $name, $value) - * - * The first version adds an Parameter - * The second adds a property as a string. - * - * @param mixed $item - * @param mixed $itemValue - * @return void - */ - public function add($item, $itemValue = null) { - - if ($item instanceof Sabre_VObject_Parameter) { - if (!is_null($itemValue)) { - throw new InvalidArgumentException('The second argument must not be specified, when passing a VObject'); - } - $item->parent = $this; - $this->parameters[] = $item; - } elseif(is_string($item)) { - - if (!is_scalar($itemValue) && !is_null($itemValue)) { - throw new InvalidArgumentException('The second argument must be scalar'); - } - $parameter = new Sabre_VObject_Parameter($item,$itemValue); - $parameter->parent = $this; - $this->parameters[] = $parameter; - - } else { - - throw new InvalidArgumentException('The first argument must either be a Sabre_VObject_Element or a string'); - - } - - } - - /* ArrayAccess interface {{{ */ - - /** - * Checks if an array element exists - * - * @param mixed $name - * @return bool - */ - public function offsetExists($name) { - - if (is_int($name)) return parent::offsetExists($name); - - $name = strtoupper($name); - - foreach($this->parameters as $parameter) { - if ($parameter->name == $name) return true; - } - return false; - - } - - /** - * Returns a parameter, or parameter list. - * - * @param string $name - * @return Sabre_VObject_Element - */ - public function offsetGet($name) { - - if (is_int($name)) return parent::offsetGet($name); - $name = strtoupper($name); - - $result = array(); - foreach($this->parameters as $parameter) { - if ($parameter->name == $name) - $result[] = $parameter; - } - - if (count($result)===0) { - return null; - } elseif (count($result)===1) { - return $result[0]; - } else { - $result[0]->setIterator(new Sabre_VObject_ElementList($result)); - return $result[0]; - } - - } - - /** - * Creates a new parameter - * - * @param string $name - * @param mixed $value - * @return void - */ - public function offsetSet($name, $value) { - - if (is_int($name)) return parent::offsetSet($name, $value); - - if (is_scalar($value)) { - if (!is_string($name)) - throw new InvalidArgumentException('A parameter name must be specified. This means you cannot use the $array[]="string" to add parameters.'); - - $this->offsetUnset($name); - $parameter = new Sabre_VObject_Parameter($name, $value); - $parameter->parent = $this; - $this->parameters[] = $parameter; - - } elseif ($value instanceof Sabre_VObject_Parameter) { - if (!is_null($name)) - throw new InvalidArgumentException('Don\'t specify a parameter name if you\'re passing a Sabre_VObject_Parameter. Add using $array[]=$parameterObject.'); - - $value->parent = $this; - $this->parameters[] = $value; - } else { - throw new InvalidArgumentException('You can only add parameters to the property object'); - } - - } - - /** - * Removes one or more parameters with the specified name - * - * @param string $name - * @return void - */ - public function offsetUnset($name) { - - if (is_int($name)) return parent::offsetUnset($name); - $name = strtoupper($name); - - foreach($this->parameters as $key=>$parameter) { - if ($parameter->name == $name) { - $parameter->parent = null; - unset($this->parameters[$key]); - } - - } - - } - - /* }}} */ - - /** - * Called when this object is being cast to a string - * - * @return string - */ - public function __toString() { - - return (string)$this->value; - - } - - /** - * This method is automatically called when the object is cloned. - * Specifically, this will ensure all child elements are also cloned. - * - * @return void - */ - public function __clone() { - - foreach($this->parameters as $key=>$child) { - $this->parameters[$key] = clone $child; - $this->parameters[$key]->parent = $this; - } - - } - -} diff --git a/3rdparty/Sabre/VObject/Property/DateTime.php b/3rdparty/Sabre/VObject/Property/DateTime.php deleted file mode 100755 index fe2372caa8..0000000000 --- a/3rdparty/Sabre/VObject/Property/DateTime.php +++ /dev/null @@ -1,260 +0,0 @@ -setValue($dt->format('Ymd\\THis')); - $this->offsetUnset('VALUE'); - $this->offsetUnset('TZID'); - $this->offsetSet('VALUE','DATE-TIME'); - break; - case self::UTC : - $dt->setTimeZone(new DateTimeZone('UTC')); - $this->setValue($dt->format('Ymd\\THis\\Z')); - $this->offsetUnset('VALUE'); - $this->offsetUnset('TZID'); - $this->offsetSet('VALUE','DATE-TIME'); - break; - case self::LOCALTZ : - $this->setValue($dt->format('Ymd\\THis')); - $this->offsetUnset('VALUE'); - $this->offsetUnset('TZID'); - $this->offsetSet('VALUE','DATE-TIME'); - $this->offsetSet('TZID', $dt->getTimeZone()->getName()); - break; - case self::DATE : - $this->setValue($dt->format('Ymd')); - $this->offsetUnset('VALUE'); - $this->offsetUnset('TZID'); - $this->offsetSet('VALUE','DATE'); - break; - default : - throw new InvalidArgumentException('You must pass a valid dateType constant'); - - } - $this->dateTime = $dt; - $this->dateType = $dateType; - - } - - /** - * Returns the current DateTime value. - * - * If no value was set, this method returns null. - * - * @return DateTime|null - */ - public function getDateTime() { - - if ($this->dateTime) - return $this->dateTime; - - list( - $this->dateType, - $this->dateTime - ) = self::parseData($this->value, $this); - return $this->dateTime; - - } - - /** - * Returns the type of Date format. - * - * This method returns one of the format constants. If no date was set, - * this method will return null. - * - * @return int|null - */ - public function getDateType() { - - if ($this->dateType) - return $this->dateType; - - list( - $this->dateType, - $this->dateTime, - ) = self::parseData($this->value, $this); - return $this->dateType; - - } - - /** - * Parses the internal data structure to figure out what the current date - * and time is. - * - * The returned array contains two elements: - * 1. A 'DateType' constant (as defined on this class), or null. - * 2. A DateTime object (or null) - * - * @param string|null $propertyValue The string to parse (yymmdd or - * ymmddThhmmss, etc..) - * @param Sabre_VObject_Property|null $property The instance of the - * property we're parsing. - * @return array - */ - static public function parseData($propertyValue, Sabre_VObject_Property $property = null) { - - if (is_null($propertyValue)) { - return array(null, null); - } - - $date = '(?P[1-2][0-9]{3})(?P[0-1][0-9])(?P[0-3][0-9])'; - $time = '(?P[0-2][0-9])(?P[0-5][0-9])(?P[0-5][0-9])'; - $regex = "/^$date(T$time(?PZ)?)?$/"; - - if (!preg_match($regex, $propertyValue, $matches)) { - throw new InvalidArgumentException($propertyValue . ' is not a valid DateTime or Date string'); - } - - if (!isset($matches['hour'])) { - // Date-only - return array( - self::DATE, - new DateTime($matches['year'] . '-' . $matches['month'] . '-' . $matches['date'] . ' 00:00:00'), - ); - } - - $dateStr = - $matches['year'] .'-' . - $matches['month'] . '-' . - $matches['date'] . ' ' . - $matches['hour'] . ':' . - $matches['minute'] . ':' . - $matches['second']; - - if (isset($matches['isutc'])) { - $dt = new DateTime($dateStr,new DateTimeZone('UTC')); - $dt->setTimeZone(new DateTimeZone('UTC')); - return array( - self::UTC, - $dt - ); - } - - // Finding the timezone. - $tzid = $property['TZID']; - if (!$tzid) { - return array( - self::LOCAL, - new DateTime($dateStr) - ); - } - - try { - // tzid an Olson identifier? - $tz = new DateTimeZone($tzid->value); - } catch (Exception $e) { - - // Not an Olson id, we're going to try to find the information - // through the time zone name map. - $newtzid = Sabre_VObject_WindowsTimezoneMap::lookup($tzid->value); - if (is_null($newtzid)) { - - // Not a well known time zone name either, we're going to try - // to find the information through the VTIMEZONE object. - - // First we find the root object - $root = $property; - while($root->parent) { - $root = $root->parent; - } - - if (isset($root->VTIMEZONE)) { - foreach($root->VTIMEZONE as $vtimezone) { - if (((string)$vtimezone->TZID) == $tzid) { - if (isset($vtimezone->{'X-LIC-LOCATION'})) { - $newtzid = (string)$vtimezone->{'X-LIC-LOCATION'}; - } else { - // No libical location specified. As a last resort we could - // try matching $vtimezone's DST rules against all known - // time zones returned by DateTimeZone::list* - - // TODO - } - } - } - } - } - - try { - $tz = new DateTimeZone($newtzid); - } catch (Exception $e) { - // If all else fails, we use the default PHP timezone - $tz = new DateTimeZone(date_default_timezone_get()); - } - } - $dt = new DateTime($dateStr, $tz); - $dt->setTimeZone($tz); - - return array( - self::LOCALTZ, - $dt - ); - - } - -} diff --git a/3rdparty/Sabre/VObject/Property/MultiDateTime.php b/3rdparty/Sabre/VObject/Property/MultiDateTime.php deleted file mode 100755 index ae53ab6a61..0000000000 --- a/3rdparty/Sabre/VObject/Property/MultiDateTime.php +++ /dev/null @@ -1,166 +0,0 @@ -offsetUnset('VALUE'); - $this->offsetUnset('TZID'); - switch($dateType) { - - case Sabre_VObject_Property_DateTime::LOCAL : - $val = array(); - foreach($dt as $i) { - $val[] = $i->format('Ymd\\THis'); - } - $this->setValue(implode(',',$val)); - $this->offsetSet('VALUE','DATE-TIME'); - break; - case Sabre_VObject_Property_DateTime::UTC : - $val = array(); - foreach($dt as $i) { - $i->setTimeZone(new DateTimeZone('UTC')); - $val[] = $i->format('Ymd\\THis\\Z'); - } - $this->setValue(implode(',',$val)); - $this->offsetSet('VALUE','DATE-TIME'); - break; - case Sabre_VObject_Property_DateTime::LOCALTZ : - $val = array(); - foreach($dt as $i) { - $val[] = $i->format('Ymd\\THis'); - } - $this->setValue(implode(',',$val)); - $this->offsetSet('VALUE','DATE-TIME'); - $this->offsetSet('TZID', $dt[0]->getTimeZone()->getName()); - break; - case Sabre_VObject_Property_DateTime::DATE : - $val = array(); - foreach($dt as $i) { - $val[] = $i->format('Ymd'); - } - $this->setValue(implode(',',$val)); - $this->offsetSet('VALUE','DATE'); - break; - default : - throw new InvalidArgumentException('You must pass a valid dateType constant'); - - } - $this->dateTimes = $dt; - $this->dateType = $dateType; - - } - - /** - * Returns the current DateTime value. - * - * If no value was set, this method returns null. - * - * @return array|null - */ - public function getDateTimes() { - - if ($this->dateTimes) - return $this->dateTimes; - - $dts = array(); - - if (!$this->value) { - $this->dateTimes = null; - $this->dateType = null; - return null; - } - - foreach(explode(',',$this->value) as $val) { - list( - $type, - $dt - ) = Sabre_VObject_Property_DateTime::parseData($val, $this); - $dts[] = $dt; - $this->dateType = $type; - } - $this->dateTimes = $dts; - return $this->dateTimes; - - } - - /** - * Returns the type of Date format. - * - * This method returns one of the format constants. If no date was set, - * this method will return null. - * - * @return int|null - */ - public function getDateType() { - - if ($this->dateType) - return $this->dateType; - - if (!$this->value) { - $this->dateTimes = null; - $this->dateType = null; - return null; - } - - $dts = array(); - foreach(explode(',',$this->value) as $val) { - list( - $type, - $dt - ) = Sabre_VObject_Property_DateTime::parseData($val, $this); - $dts[] = $dt; - $this->dateType = $type; - } - $this->dateTimes = $dts; - return $this->dateType; - - } - -} diff --git a/3rdparty/Sabre/VObject/Reader.php b/3rdparty/Sabre/VObject/Reader.php deleted file mode 100755 index eea73fa3dc..0000000000 --- a/3rdparty/Sabre/VObject/Reader.php +++ /dev/null @@ -1,183 +0,0 @@ -add(self::readLine($lines)); - - $nextLine = current($lines); - - if ($nextLine===false) - throw new Sabre_VObject_ParseException('Invalid VObject. Document ended prematurely.'); - - } - - // Checking component name of the 'END:' line. - if (substr($nextLine,4)!==$obj->name) { - throw new Sabre_VObject_ParseException('Invalid VObject, expected: "END:' . $obj->name . '" got: "' . $nextLine . '"'); - } - next($lines); - - return $obj; - - } - - // Properties - //$result = preg_match('/(?P[A-Z0-9-]+)(?:;(?P^(?([^:^\"]|\"([^\"]*)\")*))?"; - $regex = "/^(?P$token)$parameters:(?P.*)$/i"; - - $result = preg_match($regex,$line,$matches); - - if (!$result) { - throw new Sabre_VObject_ParseException('Invalid VObject, line ' . ($lineNr+1) . ' did not follow the icalendar/vcard format'); - } - - $propertyName = strtoupper($matches['name']); - $propertyValue = preg_replace_callback('#(\\\\(\\\\|N|n|;|,))#',function($matches) { - if ($matches[2]==='n' || $matches[2]==='N') { - return "\n"; - } else { - return $matches[2]; - } - }, $matches['value']); - - $obj = Sabre_VObject_Property::create($propertyName, $propertyValue); - - if ($matches['parameters']) { - - foreach(self::readParameters($matches['parameters']) as $param) { - $obj->add($param); - } - - } - - return $obj; - - - } - - /** - * Reads a parameter list from a property - * - * This method returns an array of Sabre_VObject_Parameter - * - * @param string $parameters - * @return array - */ - static private function readParameters($parameters) { - - $token = '[A-Z0-9-]+'; - - $paramValue = '(?P[^\"^;]*|"[^"]*")'; - - $regex = "/(?<=^|;)(?P$token)(=$paramValue(?=$|;))?/i"; - preg_match_all($regex, $parameters, $matches, PREG_SET_ORDER); - - $params = array(); - foreach($matches as $match) { - - $value = isset($match['paramValue'])?$match['paramValue']:null; - - if (isset($value[0])) { - // Stripping quotes, if needed - if ($value[0] === '"') $value = substr($value,1,strlen($value)-2); - } else { - $value = ''; - } - - $value = preg_replace_callback('#(\\\\(\\\\|N|n|;|,))#',function($matches) { - if ($matches[2]==='n' || $matches[2]==='N') { - return "\n"; - } else { - return $matches[2]; - } - }, $value); - - $params[] = new Sabre_VObject_Parameter($match['paramName'], $value); - - } - - return $params; - - } - - -} diff --git a/3rdparty/Sabre/VObject/RecurrenceIterator.php b/3rdparty/Sabre/VObject/RecurrenceIterator.php deleted file mode 100755 index 740270dd8f..0000000000 --- a/3rdparty/Sabre/VObject/RecurrenceIterator.php +++ /dev/null @@ -1,1043 +0,0 @@ - 0, - 'MO' => 1, - 'TU' => 2, - 'WE' => 3, - 'TH' => 4, - 'FR' => 5, - 'SA' => 6, - ); - - /** - * Mappings between the day number and english day name. - * - * @var array - */ - private $dayNames = array( - 0 => 'Sunday', - 1 => 'Monday', - 2 => 'Tuesday', - 3 => 'Wednesday', - 4 => 'Thursday', - 5 => 'Friday', - 6 => 'Saturday', - ); - - /** - * If the current iteration of the event is an overriden event, this - * property will hold the VObject - * - * @var Sabre_Component_VObject - */ - private $currentOverriddenEvent; - - /** - * This property may contain the date of the next not-overridden event. - * This date is calculated sometimes a bit early, before overridden events - * are evaluated. - * - * @var DateTime - */ - private $nextDate; - - /** - * Creates the iterator - * - * You should pass a VCALENDAR component, as well as the UID of the event - * we're going to traverse. - * - * @param Sabre_VObject_Component $vcal - * @param string|null $uid - */ - public function __construct(Sabre_VObject_Component $vcal, $uid=null) { - - if (is_null($uid)) { - if ($vcal->name === 'VCALENDAR') { - throw new InvalidArgumentException('If you pass a VCALENDAR object, you must pass a uid argument as well'); - } - $components = array($vcal); - $uid = (string)$vcal->uid; - } else { - $components = $vcal->select('VEVENT'); - } - foreach($components as $component) { - if ((string)$component->uid == $uid) { - if (isset($component->{'RECURRENCE-ID'})) { - $this->overriddenEvents[$component->DTSTART->getDateTime()->getTimeStamp()] = $component; - $this->overriddenDates[] = $component->{'RECURRENCE-ID'}->getDateTime(); - } else { - $this->baseEvent = $component; - } - } - } - if (!$this->baseEvent) { - throw new InvalidArgumentException('Could not find a base event with uid: ' . $uid); - } - - $this->startDate = clone $this->baseEvent->DTSTART->getDateTime(); - - $this->endDate = null; - if (isset($this->baseEvent->DTEND)) { - $this->endDate = clone $this->baseEvent->DTEND->getDateTime(); - } else { - $this->endDate = clone $this->startDate; - if (isset($this->baseEvent->DURATION)) { - $this->endDate->add(Sabre_VObject_DateTimeParser::parse($this->baseEvent->DURATION->value)); - } elseif ($this->baseEvent->DTSTART->getDateType()===Sabre_VObject_Property_DateTime::DATE) { - $this->endDate->modify('+1 day'); - } - } - $this->currentDate = clone $this->startDate; - - $rrule = (string)$this->baseEvent->RRULE; - - $parts = explode(';', $rrule); - - foreach($parts as $part) { - - list($key, $value) = explode('=', $part, 2); - - switch(strtoupper($key)) { - - case 'FREQ' : - if (!in_array( - strtolower($value), - array('secondly','minutely','hourly','daily','weekly','monthly','yearly') - )) { - throw new InvalidArgumentException('Unknown value for FREQ=' . strtoupper($value)); - - } - $this->frequency = strtolower($value); - break; - - case 'UNTIL' : - $this->until = Sabre_VObject_DateTimeParser::parse($value); - break; - - case 'COUNT' : - $this->count = (int)$value; - break; - - case 'INTERVAL' : - $this->interval = (int)$value; - break; - - case 'BYSECOND' : - $this->bySecond = explode(',', $value); - break; - - case 'BYMINUTE' : - $this->byMinute = explode(',', $value); - break; - - case 'BYHOUR' : - $this->byHour = explode(',', $value); - break; - - case 'BYDAY' : - $this->byDay = explode(',', strtoupper($value)); - break; - - case 'BYMONTHDAY' : - $this->byMonthDay = explode(',', $value); - break; - - case 'BYYEARDAY' : - $this->byYearDay = explode(',', $value); - break; - - case 'BYWEEKNO' : - $this->byWeekNo = explode(',', $value); - break; - - case 'BYMONTH' : - $this->byMonth = explode(',', $value); - break; - - case 'BYSETPOS' : - $this->bySetPos = explode(',', $value); - break; - - case 'WKST' : - $this->weekStart = strtoupper($value); - break; - - } - - } - - // Parsing exception dates - if (isset($this->baseEvent->EXDATE)) { - foreach($this->baseEvent->EXDATE as $exDate) { - - foreach(explode(',', (string)$exDate) as $exceptionDate) { - - $this->exceptionDates[] = - Sabre_VObject_DateTimeParser::parse($exceptionDate, $this->startDate->getTimeZone()); - - } - - } - - } - - } - - /** - * Returns the current item in the list - * - * @return DateTime - */ - public function current() { - - if (!$this->valid()) return null; - return clone $this->currentDate; - - } - - /** - * This method returns the startdate for the current iteration of the - * event. - * - * @return DateTime - */ - public function getDtStart() { - - if (!$this->valid()) return null; - return clone $this->currentDate; - - } - - /** - * This method returns the enddate for the current iteration of the - * event. - * - * @return DateTime - */ - public function getDtEnd() { - - if (!$this->valid()) return null; - $dtEnd = clone $this->currentDate; - $dtEnd->add( $this->startDate->diff( $this->endDate ) ); - return clone $dtEnd; - - } - - /** - * Returns a VEVENT object with the updated start and end date. - * - * Any recurrence information is removed, and this function may return an - * 'overridden' event instead. - * - * This method always returns a cloned instance. - * - * @return void - */ - public function getEventObject() { - - if ($this->currentOverriddenEvent) { - return clone $this->currentOverriddenEvent; - } - $event = clone $this->baseEvent; - unset($event->RRULE); - unset($event->EXDATE); - unset($event->RDATE); - unset($event->EXRULE); - - $event->DTSTART->setDateTime($this->getDTStart(), $event->DTSTART->getDateType()); - if (isset($event->DTEND)) { - $event->DTEND->setDateTime($this->getDtEnd(), $event->DTSTART->getDateType()); - } - if ($this->counter > 0) { - $event->{'RECURRENCE-ID'} = (string)$event->DTSTART; - } - - return $event; - - } - - /** - * Returns the current item number - * - * @return int - */ - public function key() { - - return $this->counter; - - } - - /** - * Whether or not there is a 'next item' - * - * @return bool - */ - public function valid() { - - if (!is_null($this->count)) { - return $this->counter < $this->count; - } - if (!is_null($this->until)) { - return $this->currentDate <= $this->until; - } - return true; - - } - - /** - * Resets the iterator - * - * @return void - */ - public function rewind() { - - $this->currentDate = clone $this->startDate; - $this->counter = 0; - - } - - /** - * This method allows you to quickly go to the next occurrence after the - * specified date. - * - * Note that this checks the current 'endDate', not the 'stardDate'. This - * means that if you forward to January 1st, the iterator will stop at the - * first event that ends *after* January 1st. - * - * @param DateTime $dt - * @return void - */ - public function fastForward(DateTime $dt) { - - while($this->valid() && $this->getDTEnd() <= $dt) { - $this->next(); - } - - } - - /** - * Goes on to the next iteration - * - * @return void - */ - public function next() { - - /* - if (!is_null($this->count) && $this->counter >= $this->count) { - $this->currentDate = null; - }*/ - - - $previousStamp = $this->currentDate->getTimeStamp(); - - while(true) { - - $this->currentOverriddenEvent = null; - - // If we have a next date 'stored', we use that - if ($this->nextDate) { - $this->currentDate = $this->nextDate; - $currentStamp = $this->currentDate->getTimeStamp(); - $this->nextDate = null; - } else { - - // Otherwise, we calculate it - switch($this->frequency) { - - case 'daily' : - $this->nextDaily(); - break; - - case 'weekly' : - $this->nextWeekly(); - break; - - case 'monthly' : - $this->nextMonthly(); - break; - - case 'yearly' : - $this->nextYearly(); - break; - - } - $currentStamp = $this->currentDate->getTimeStamp(); - - // Checking exception dates - foreach($this->exceptionDates as $exceptionDate) { - if ($this->currentDate == $exceptionDate) { - $this->counter++; - continue 2; - } - } - foreach($this->overriddenDates as $overriddenDate) { - if ($this->currentDate == $overriddenDate) { - continue 2; - } - } - - } - - // Checking overriden events - foreach($this->overriddenEvents as $index=>$event) { - if ($index > $previousStamp && $index <= $currentStamp) { - - // We're moving the 'next date' aside, for later use. - $this->nextDate = clone $this->currentDate; - - $this->currentDate = $event->DTSTART->getDateTime(); - $this->currentOverriddenEvent = $event; - - break; - } - } - - break; - - } - - /* - if (!is_null($this->until)) { - if($this->currentDate > $this->until) { - $this->currentDate = null; - } - }*/ - - $this->counter++; - - } - - /** - * Does the processing for advancing the iterator for daily frequency. - * - * @return void - */ - protected function nextDaily() { - - if (!$this->byDay) { - $this->currentDate->modify('+' . $this->interval . ' days'); - return; - } - - $recurrenceDays = array(); - foreach($this->byDay as $byDay) { - - // The day may be preceeded with a positive (+n) or - // negative (-n) integer. However, this does not make - // sense in 'weekly' so we ignore it here. - $recurrenceDays[] = $this->dayMap[substr($byDay,-2)]; - - } - - do { - - $this->currentDate->modify('+' . $this->interval . ' days'); - - // Current day of the week - $currentDay = $this->currentDate->format('w'); - - } while (!in_array($currentDay, $recurrenceDays)); - - } - - /** - * Does the processing for advancing the iterator for weekly frequency. - * - * @return void - */ - protected function nextWeekly() { - - if (!$this->byDay) { - $this->currentDate->modify('+' . $this->interval . ' weeks'); - return; - } - - $recurrenceDays = array(); - foreach($this->byDay as $byDay) { - - // The day may be preceeded with a positive (+n) or - // negative (-n) integer. However, this does not make - // sense in 'weekly' so we ignore it here. - $recurrenceDays[] = $this->dayMap[substr($byDay,-2)]; - - } - - // Current day of the week - $currentDay = $this->currentDate->format('w'); - - // First day of the week: - $firstDay = $this->dayMap[$this->weekStart]; - - $time = array( - $this->currentDate->format('H'), - $this->currentDate->format('i'), - $this->currentDate->format('s') - ); - - // Increasing the 'current day' until we find our next - // occurrence. - while(true) { - - $currentDay++; - - if ($currentDay>6) { - $currentDay = 0; - } - - // We need to roll over to the next week - if ($currentDay === $firstDay) { - $this->currentDate->modify('+' . $this->interval . ' weeks'); - - // We need to go to the first day of this week, but only if we - // are not already on this first day of this week. - if($this->currentDate->format('w') != $firstDay) { - $this->currentDate->modify('last ' . $this->dayNames[$this->dayMap[$this->weekStart]]); - $this->currentDate->setTime($time[0],$time[1],$time[2]); - } - } - - // We have a match - if (in_array($currentDay ,$recurrenceDays)) { - $this->currentDate->modify($this->dayNames[$currentDay]); - $this->currentDate->setTime($time[0],$time[1],$time[2]); - break; - } - - } - - } - - /** - * Does the processing for advancing the iterator for monthly frequency. - * - * @return void - */ - protected function nextMonthly() { - - $currentDayOfMonth = $this->currentDate->format('j'); - if (!$this->byMonthDay && !$this->byDay) { - - // If the current day is higher than the 28th, rollover can - // occur to the next month. We Must skip these invalid - // entries. - if ($currentDayOfMonth < 29) { - $this->currentDate->modify('+' . $this->interval . ' months'); - } else { - $increase = 0; - do { - $increase++; - $tempDate = clone $this->currentDate; - $tempDate->modify('+ ' . ($this->interval*$increase) . ' months'); - } while ($tempDate->format('j') != $currentDayOfMonth); - $this->currentDate = $tempDate; - } - return; - } - - while(true) { - - $occurrences = $this->getMonthlyOccurrences(); - - foreach($occurrences as $occurrence) { - - // The first occurrence thats higher than the current - // day of the month wins. - if ($occurrence > $currentDayOfMonth) { - break 2; - } - - } - - // If we made it all the way here, it means there were no - // valid occurrences, and we need to advance to the next - // month. - $this->currentDate->modify('first day of this month'); - $this->currentDate->modify('+ ' . $this->interval . ' months'); - - // This goes to 0 because we need to start counting at hte - // beginning. - $currentDayOfMonth = 0; - - } - - $this->currentDate->setDate($this->currentDate->format('Y'), $this->currentDate->format('n'), $occurrence); - - } - - /** - * Does the processing for advancing the iterator for yearly frequency. - * - * @return void - */ - protected function nextYearly() { - - $currentMonth = $this->currentDate->format('n'); - $currentYear = $this->currentDate->format('Y'); - $currentDayOfMonth = $this->currentDate->format('j'); - - // No sub-rules, so we just advance by year - if (!$this->byMonth) { - - // Unless it was a leap day! - if ($currentMonth==2 && $currentDayOfMonth==29) { - - $counter = 0; - do { - $counter++; - // Here we increase the year count by the interval, until - // we hit a date that's also in a leap year. - // - // We could just find the next interval that's dividable by - // 4, but that would ignore the rule that there's no leap - // year every year that's dividable by a 100, but not by - // 400. (1800, 1900, 2100). So we just rely on the datetime - // functions instead. - $nextDate = clone $this->currentDate; - $nextDate->modify('+ ' . ($this->interval*$counter) . ' years'); - } while ($nextDate->format('n')!=2); - $this->currentDate = $nextDate; - - return; - - } - - // The easiest form - $this->currentDate->modify('+' . $this->interval . ' years'); - return; - - } - - $currentMonth = $this->currentDate->format('n'); - $currentYear = $this->currentDate->format('Y'); - $currentDayOfMonth = $this->currentDate->format('j'); - - $advancedToNewMonth = false; - - // If we got a byDay or getMonthDay filter, we must first expand - // further. - if ($this->byDay || $this->byMonthDay) { - - while(true) { - - $occurrences = $this->getMonthlyOccurrences(); - - foreach($occurrences as $occurrence) { - - // The first occurrence that's higher than the current - // day of the month wins. - // If we advanced to the next month or year, the first - // occurence is always correct. - if ($occurrence > $currentDayOfMonth || $advancedToNewMonth) { - break 2; - } - - } - - // If we made it here, it means we need to advance to - // the next month or year. - $currentDayOfMonth = 1; - $advancedToNewMonth = true; - do { - - $currentMonth++; - if ($currentMonth>12) { - $currentYear+=$this->interval; - $currentMonth = 1; - } - } while (!in_array($currentMonth, $this->byMonth)); - - $this->currentDate->setDate($currentYear, $currentMonth, $currentDayOfMonth); - - } - - // If we made it here, it means we got a valid occurrence - $this->currentDate->setDate($currentYear, $currentMonth, $occurrence); - return; - - } else { - - // These are the 'byMonth' rules, if there are no byDay or - // byMonthDay sub-rules. - do { - - $currentMonth++; - if ($currentMonth>12) { - $currentYear+=$this->interval; - $currentMonth = 1; - } - } while (!in_array($currentMonth, $this->byMonth)); - $this->currentDate->setDate($currentYear, $currentMonth, $currentDayOfMonth); - - return; - - } - - } - - /** - * Returns all the occurrences for a monthly frequency with a 'byDay' or - * 'byMonthDay' expansion for the current month. - * - * The returned list is an array of integers with the day of month (1-31). - * - * @return array - */ - protected function getMonthlyOccurrences() { - - $startDate = clone $this->currentDate; - - $byDayResults = array(); - - // Our strategy is to simply go through the byDays, advance the date to - // that point and add it to the results. - if ($this->byDay) foreach($this->byDay as $day) { - - $dayName = $this->dayNames[$this->dayMap[substr($day,-2)]]; - - // Dayname will be something like 'wednesday'. Now we need to find - // all wednesdays in this month. - $dayHits = array(); - - $checkDate = clone $startDate; - $checkDate->modify('first day of this month'); - $checkDate->modify($dayName); - - do { - $dayHits[] = $checkDate->format('j'); - $checkDate->modify('next ' . $dayName); - } while ($checkDate->format('n') === $startDate->format('n')); - - // So now we have 'all wednesdays' for month. It is however - // possible that the user only really wanted the 1st, 2nd or last - // wednesday. - if (strlen($day)>2) { - $offset = (int)substr($day,0,-2); - - if ($offset>0) { - // It is possible that the day does not exist, such as a - // 5th or 6th wednesday of the month. - if (isset($dayHits[$offset-1])) { - $byDayResults[] = $dayHits[$offset-1]; - } - } else { - - // if it was negative we count from the end of the array - $byDayResults[] = $dayHits[count($dayHits) + $offset]; - } - } else { - // There was no counter (first, second, last wednesdays), so we - // just need to add the all to the list). - $byDayResults = array_merge($byDayResults, $dayHits); - - } - - } - - $byMonthDayResults = array(); - if ($this->byMonthDay) foreach($this->byMonthDay as $monthDay) { - - // Removing values that are out of range for this month - if ($monthDay > $startDate->format('t') || - $monthDay < 0-$startDate->format('t')) { - continue; - } - if ($monthDay>0) { - $byMonthDayResults[] = $monthDay; - } else { - // Negative values - $byMonthDayResults[] = $startDate->format('t') + 1 + $monthDay; - } - } - - // If there was just byDay or just byMonthDay, they just specify our - // (almost) final list. If both were provided, then byDay limits the - // list. - if ($this->byMonthDay && $this->byDay) { - $result = array_intersect($byMonthDayResults, $byDayResults); - } elseif ($this->byMonthDay) { - $result = $byMonthDayResults; - } else { - $result = $byDayResults; - } - $result = array_unique($result); - sort($result, SORT_NUMERIC); - - // The last thing that needs checking is the BYSETPOS. If it's set, it - // means only certain items in the set survive the filter. - if (!$this->bySetPos) { - return $result; - } - - $filteredResult = array(); - foreach($this->bySetPos as $setPos) { - - if ($setPos<0) { - $setPos = count($result)-($setPos+1); - } - if (isset($result[$setPos-1])) { - $filteredResult[] = $result[$setPos-1]; - } - } - - sort($filteredResult, SORT_NUMERIC); - return $filteredResult; - - } - - -} - diff --git a/3rdparty/Sabre/VObject/Version.php b/3rdparty/Sabre/VObject/Version.php deleted file mode 100755 index 9ee03d8711..0000000000 --- a/3rdparty/Sabre/VObject/Version.php +++ /dev/null @@ -1,24 +0,0 @@ -'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 deleted file mode 100755 index 0177a8f1ba..0000000000 --- a/3rdparty/Sabre/VObject/includes.php +++ /dev/null @@ -1,41 +0,0 @@ -