diff --git a/.gitignore b/.gitignore index 68c48822e9..ac58f3e6a6 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,8 @@ data owncloud config/config.php +config/mount.php +apps/inc.php # just sane ignores .*.sw[po] diff --git a/.htaccess b/.htaccess index 11520d743d..adc6667d5b 100644 --- a/.htaccess +++ b/.htaccess @@ -1,5 +1,9 @@ ErrorDocument 403 /core/templates/403.php ErrorDocument 404 /core/templates/404.php +Redirect 301 /apps/calendar/caldav.php /remote/caldav.php +Redirect 301 /apps/contacts/carddav.php /remote/carddav.php +Redirect 301 /apps/files/webdav.php /remote/webdav.php +Redirect 301 /files/webdav.php /remote/webdav.php php_value upload_max_filesize 512M php_value post_max_size 512M @@ -10,6 +14,10 @@ php_value memory_limit 512M RewriteEngine on -RewriteRule .* - [env=HTTP_AUTHORIZATION:%{HTTP:Authorization},last] +RewriteRule .* - [env=HTTP_AUTHORIZATION:%{HTTP:Authorization}] +RewriteRule ^.well-known/carddav /remote/carddav.php [R] +RewriteRule ^.well-known/caldav /remote/caldav.php [R] + +RewriteRule ^apps/([^/]*)/(.*\.(css|php))$ index.php?app=$1&getfile=$2 [QSA,L] Options -Indexes diff --git a/3rdparty/Sabre.includes.php b/3rdparty/Sabre.includes.php index d41b287b77..c133437366 100644 --- a/3rdparty/Sabre.includes.php +++ b/3rdparty/Sabre.includes.php @@ -3,125 +3,24 @@ /** * Library include file * + * This file is deprecated, don't use it! + * Instead, use the specific includes files that are in the sub-packages. + * + * Sabre/DAV/includes.php + * Sabre/HTTP/includes.php + * + * etc.. + * * This file contains all includes to the rest of the SabreDAV library - * Make sure the lib/ directory is in PHP's include_path + * Make sure the lib/ directory is in PHP's include_path. * * @package Sabre - * @subpackage DAV - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. + * @deprecated Don't use this file, it will be remove in a future version + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ -/* Utilities */ -include 'Sabre/HTTP/Util.php'; -include 'Sabre/HTTP/Response.php'; -include 'Sabre/HTTP/Request.php'; -include 'Sabre/HTTP/AbstractAuth.php'; -include 'Sabre/HTTP/BasicAuth.php'; -include 'Sabre/HTTP/DigestAuth.php'; -include 'Sabre/HTTP/AWSAuth.php'; - -/* Version */ -include 'Sabre/DAV/Version.php'; -include 'Sabre/HTTP/Version.php'; - -/* Exceptions */ -include 'Sabre/DAV/Exception.php'; -include 'Sabre/DAV/Exception/BadRequest.php'; -include 'Sabre/DAV/Exception/Conflict.php'; -include 'Sabre/DAV/Exception/FileNotFound.php'; -include 'Sabre/DAV/Exception/InsufficientStorage.php'; -include 'Sabre/DAV/Exception/Locked.php'; -include 'Sabre/DAV/Exception/LockTokenMatchesRequestUri.php'; -include 'Sabre/DAV/Exception/MethodNotAllowed.php'; -include 'Sabre/DAV/Exception/NotImplemented.php'; -include 'Sabre/DAV/Exception/Forbidden.php'; -include 'Sabre/DAV/Exception/PreconditionFailed.php'; -include 'Sabre/DAV/Exception/RequestedRangeNotSatisfiable.php'; -include 'Sabre/DAV/Exception/UnsupportedMediaType.php'; -include 'Sabre/DAV/Exception/NotAuthenticated.php'; - -include 'Sabre/DAV/Exception/ConflictingLock.php'; -include 'Sabre/DAV/Exception/ReportNotImplemented.php'; -include 'Sabre/DAV/Exception/InvalidResourceType.php'; - -/* Properties */ -include 'Sabre/DAV/Property.php'; -include 'Sabre/DAV/Property/GetLastModified.php'; -include 'Sabre/DAV/Property/ResourceType.php'; -include 'Sabre/DAV/Property/SupportedLock.php'; -include 'Sabre/DAV/Property/LockDiscovery.php'; -include 'Sabre/DAV/Property/IHref.php'; -include 'Sabre/DAV/Property/Href.php'; -include 'Sabre/DAV/Property/HrefList.php'; -include 'Sabre/DAV/Property/SupportedReportSet.php'; -include 'Sabre/DAV/Property/Response.php'; -include 'Sabre/DAV/Property/ResponseList.php'; - -/* Node interfaces */ -include 'Sabre/DAV/INode.php'; -include 'Sabre/DAV/IFile.php'; -include 'Sabre/DAV/ICollection.php'; -include 'Sabre/DAV/IProperties.php'; -include 'Sabre/DAV/ILockable.php'; -include 'Sabre/DAV/IQuota.php'; -include 'Sabre/DAV/IExtendedCollection.php'; - -/* Node abstract implementations */ -include 'Sabre/DAV/Node.php'; -include 'Sabre/DAV/File.php'; -include 'Sabre/DAV/Collection.php'; -include 'Sabre/DAV/Directory.php'; - -/* Utilities */ -include 'Sabre/DAV/SimpleCollection.php'; -include 'Sabre/DAV/SimpleDirectory.php'; -include 'Sabre/DAV/XMLUtil.php'; -include 'Sabre/DAV/URLUtil.php'; - -/* Filesystem implementation */ -include 'Sabre/DAV/FS/Node.php'; -include 'Sabre/DAV/FS/File.php'; -include 'Sabre/DAV/FS/Directory.php'; - -/* Advanced filesystem implementation */ -include 'Sabre/DAV/FSExt/Node.php'; -include 'Sabre/DAV/FSExt/File.php'; -include 'Sabre/DAV/FSExt/Directory.php'; - -/* Trees */ -include 'Sabre/DAV/Tree.php'; -include 'Sabre/DAV/ObjectTree.php'; -include 'Sabre/DAV/Tree/Filesystem.php'; - -/* Server */ -include 'Sabre/DAV/Server.php'; -include 'Sabre/DAV/ServerPlugin.php'; - -/* Browser */ -include 'Sabre/DAV/Browser/Plugin.php'; -include 'Sabre/DAV/Browser/MapGetToPropFind.php'; -include 'Sabre/DAV/Browser/GuessContentType.php'; - -/* Locks */ -include 'Sabre/DAV/Locks/LockInfo.php'; -include 'Sabre/DAV/Locks/Plugin.php'; -include 'Sabre/DAV/Locks/Backend/Abstract.php'; -include 'Sabre/DAV/Locks/Backend/FS.php'; -include 'Sabre/DAV/Locks/Backend/PDO.php'; - -/* Temporary File Filter plugin */ -include 'Sabre/DAV/TemporaryFileFilterPlugin.php'; - -/* Authentication plugin */ -include 'Sabre/DAV/Auth/Plugin.php'; -include 'Sabre/DAV/Auth/IBackend.php'; -include 'Sabre/DAV/Auth/Backend/AbstractDigest.php'; -include 'Sabre/DAV/Auth/Backend/AbstractBasic.php'; -include 'Sabre/DAV/Auth/Backend/File.php'; -include 'Sabre/DAV/Auth/Backend/PDO.php'; - -/* DavMount plugin */ -include 'Sabre/DAV/Mount/Plugin.php'; +include 'Sabre/HTTP/includes.php'; +include 'Sabre/DAV/includes.php'; diff --git a/3rdparty/Sabre/CalDAV/Backend/Abstract.php b/3rdparty/Sabre/CalDAV/Backend/Abstract.php index b694eef49e..7aba1d69ff 100644 --- a/3rdparty/Sabre/CalDAV/Backend/Abstract.php +++ b/3rdparty/Sabre/CalDAV/Backend/Abstract.php @@ -2,10 +2,10 @@ /** * Abstract Calendaring backend. Extend this class to create your own backends. - * + * * @package Sabre * @subpackage CalDAV - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. + * @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 */ @@ -17,16 +17,16 @@ abstract class Sabre_CalDAV_Backend_Abstract { * Every project is an array with the following keys: * * id, a unique id that will be used by other functions to modify the * calendar. This can be the same as the uri or a database key. - * * uri, which the basename of the uri with which the calendar is + * * uri, which the basename of the uri with which the calendar is * accessed. - * * principalUri. The owner of the calendar. Almost always the same as + * * principaluri. The owner of the calendar. Almost always the same as * principalUri passed to this method. * * Furthermore it can contain webdav properties in clark notation. A very - * common one is '{DAV:}displayname'. + * common one is '{DAV:}displayname'. * - * @param string $principalUri - * @return array + * @param string $principalUri + * @return array */ abstract function getCalendarsForUser($principalUri); @@ -39,9 +39,9 @@ abstract class Sabre_CalDAV_Backend_Abstract { * @param string $principalUri * @param string $calendarUri * @param array $properties - * @return void + * @return void */ - abstract function createCalendar($principalUri,$calendarUri,array $properties); + abstract function createCalendar($principalUri,$calendarUri,array $properties); /** * Updates properties for a calendar. @@ -56,7 +56,7 @@ abstract class Sabre_CalDAV_Backend_Abstract { * If the operation was successful, true can be returned. * If the operation failed, false can be returned. * - * Deletion of a non-existant property is always succesful. + * Deletion of a non-existent property is always successful. * * Lastly, it is optional to return detailed information about any * failures. In this case an array should be returned with the following @@ -71,24 +71,24 @@ abstract class Sabre_CalDAV_Backend_Abstract { * ) * ) * - * In this example it was forbidden to update {DAV:}displayname. + * In this example it was forbidden to update {DAV:}displayname. * (403 Forbidden), which in turn also caused {DAV:}owner to fail * (424 Failed Dependency) because the request needs to be atomic. * * @param string $calendarId * @param array $mutations - * @return bool|array + * @return bool|array */ public function updateCalendar($calendarId, array $mutations) { - - return false; + + return false; } /** - * Delete a calendar and all it's objects - * - * @param string $calendarId + * Delete a calendar and all it's objects + * + * @param string $calendarId * @return void */ abstract function deleteCalendar($calendarId); @@ -98,22 +98,27 @@ abstract class Sabre_CalDAV_Backend_Abstract { * * Every item contains an array with the following keys: * * id - unique identifier which will be used for subsequent updates - * * calendardata - The iCalendar-compatible calnedar data + * * 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.: + * * 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 + * 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 + * The calendardata is also optional. If it's not returned + * 'getCalendarObject' will be called later, which *is* expected to return * calendardata. - * - * @param string $calendarId - * @return array + * + * If neither etag or size are specified, the calendardata will be + * used/fetched to determine these numbers. If both are specified the + * amount of times this is needed is reduced by a great degree. + * + * @param string $calendarId + * @return array */ abstract function getCalendarObjects($calendarId); @@ -121,41 +126,41 @@ abstract class Sabre_CalDAV_Backend_Abstract { * 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 + * 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 string $calendarId + * @param string $objectUri + * @return array */ abstract function getCalendarObject($calendarId,$objectUri); /** - * Creates a new calendar object. - * - * @param string $calendarId - * @param string $objectUri - * @param string $calendarData + * Creates a new calendar object. + * + * @param string $calendarId + * @param string $objectUri + * @param string $calendarData * @return void */ abstract function createCalendarObject($calendarId,$objectUri,$calendarData); /** - * Updates an existing calendarobject, based on it's uri. - * - * @param string $calendarId - * @param string $objectUri - * @param string $calendarData + * Updates an existing calendarobject, based on it's uri. + * + * @param string $calendarId + * @param string $objectUri + * @param string $calendarData * @return void */ abstract function updateCalendarObject($calendarId,$objectUri,$calendarData); /** - * Deletes an existing calendar object. - * - * @param string $calendarId - * @param string $objectUri + * Deletes an existing calendar object. + * + * @param string $calendarId + * @param string $objectUri * @return void */ abstract function deleteCalendarObject($calendarId,$objectUri); diff --git a/3rdparty/Sabre/CalDAV/Backend/PDO.php b/3rdparty/Sabre/CalDAV/Backend/PDO.php index 7b1b33b912..ddacf940c7 100644 --- a/3rdparty/Sabre/CalDAV/Backend/PDO.php +++ b/3rdparty/Sabre/CalDAV/Backend/PDO.php @@ -3,35 +3,35 @@ /** * PDO CalDAV backend * - * This backend is used to store calendar-data in a PDO database, such as + * This backend is used to store calendar-data in a PDO database, such as * sqlite or MySQL - * + * * @package Sabre * @subpackage CalDAV - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ class Sabre_CalDAV_Backend_PDO extends Sabre_CalDAV_Backend_Abstract { /** - * pdo - * + * pdo + * * @var PDO */ protected $pdo; /** - * The table name that will be used for calendars - * - * @var string + * The table name that will be used for calendars + * + * @var string */ protected $calendarTableName; /** - * The table name that will be used for calendar objects - * - * @var string + * The table name that will be used for calendar objects + * + * @var string */ protected $calendarObjectTableName; @@ -39,7 +39,7 @@ class Sabre_CalDAV_Backend_PDO extends Sabre_CalDAV_Backend_Abstract { * List of CalDAV properties, and how they map to database fieldnames * * Add your own properties by simply adding on to this array - * + * * @var array */ public $propertyMap = array( @@ -51,9 +51,11 @@ class Sabre_CalDAV_Backend_PDO extends Sabre_CalDAV_Backend_Abstract { ); /** - * Creates the backend - * - * @param PDO $pdo + * Creates the backend + * + * @param PDO $pdo + * @param string $calendarTableName + * @param string $calendarObjectTableName */ public function __construct(PDO $pdo, $calendarTableName = 'calendars', $calendarObjectTableName = 'calendarobjects') { @@ -69,16 +71,16 @@ class Sabre_CalDAV_Backend_PDO extends Sabre_CalDAV_Backend_Abstract { * Every project is an array with the following keys: * * id, a unique id that will be used by other functions to modify the * calendar. This can be the same as the uri or a database key. - * * uri, which the basename of the uri with which the calendar is + * * uri, which the basename of the uri with which the calendar is * accessed. - * * principalUri. The owner of the calendar. Almost always the same as + * * principaluri. The owner of the calendar. Almost always the same as * principalUri passed to this method. * * Furthermore it can contain webdav properties in clark notation. A very - * common one is '{DAV:}displayname'. + * common one is '{DAV:}displayname'. * - * @param string $principalUri - * @return array + * @param string $principalUri + * @return array */ public function getCalendarsForUser($principalUri) { @@ -89,15 +91,18 @@ class Sabre_CalDAV_Backend_PDO extends Sabre_CalDAV_Backend_Abstract { $fields[] = 'components'; $fields[] = 'principaluri'; - // Making fields a comma-delimited list + // Making fields a comma-delimited list $fields = implode(', ', $fields); - $stmt = $this->pdo->prepare("SELECT " . $fields . " FROM `".$this->calendarTableName."` WHERE principaluri = ?"); + $stmt = $this->pdo->prepare("SELECT " . $fields . " FROM ".$this->calendarTableName." WHERE principaluri = ? ORDER BY calendarorder ASC"); $stmt->execute(array($principalUri)); $calendars = array(); while($row = $stmt->fetch(PDO::FETCH_ASSOC)) { - $components = explode(',',$row['components']); + $components = array(); + if ($row['components']) { + $components = explode(',',$row['components']); + } $calendar = array( 'id' => $row['id'], @@ -106,7 +111,7 @@ class Sabre_CalDAV_Backend_PDO extends Sabre_CalDAV_Backend_Abstract { '{' . Sabre_CalDAV_Plugin::NS_CALENDARSERVER . '}getctag' => $row['ctag']?$row['ctag']:'0', '{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}supported-calendar-component-set' => new Sabre_CalDAV_Property_SupportedCalendarComponentSet($components), ); - + foreach($this->propertyMap as $xmlName=>$dbName) { $calendar[$xmlName] = $row[$dbName]; @@ -129,8 +134,9 @@ class Sabre_CalDAV_Backend_PDO extends Sabre_CalDAV_Backend_Abstract { * @param string $principalUri * @param string $calendarUri * @param array $properties + * @return string */ - public function createCalendar($principalUri,$calendarUri, array $properties) { + public function createCalendar($principalUri, $calendarUri, array $properties) { $fieldNames = array( 'principaluri', @@ -158,13 +164,12 @@ class Sabre_CalDAV_Backend_PDO extends Sabre_CalDAV_Backend_Abstract { foreach($this->propertyMap as $xmlName=>$dbName) { if (isset($properties[$xmlName])) { - $myValue = $properties[$xmlName]; $values[':' . $dbName] = $properties[$xmlName]; $fieldNames[] = $dbName; } } - $stmt = $this->pdo->prepare("INSERT INTO `".$this->calendarTableName."` (".implode(', ', $fieldNames).") VALUES (".implode(', ',array_keys($values)).")"); + $stmt = $this->pdo->prepare("INSERT INTO ".$this->calendarTableName." (".implode(', ', $fieldNames).") VALUES (".implode(', ',array_keys($values)).")"); $stmt->execute($values); return $this->pdo->lastInsertId(); @@ -184,7 +189,7 @@ class Sabre_CalDAV_Backend_PDO extends Sabre_CalDAV_Backend_Abstract { * If the operation was successful, true can be returned. * If the operation failed, false can be returned. * - * Deletion of a non-existant property is always succesful. + * Deletion of a non-existent property is always successful. * * Lastly, it is optional to return detailed information about any * failures. In this case an array should be returned with the following @@ -199,13 +204,13 @@ class Sabre_CalDAV_Backend_PDO extends Sabre_CalDAV_Backend_Abstract { * ) * ) * - * In this example it was forbidden to update {DAV:}displayname. + * In this example it was forbidden to update {DAV:}displayname. * (403 Forbidden), which in turn also caused {DAV:}owner to fail * (424 Failed Dependency) because the request needs to be atomic. * * @param string $calendarId - * @param array $mutations - * @return bool|array + * @param array $mutations + * @return bool|array */ public function updateCalendar($calendarId, array $mutations) { @@ -220,7 +225,7 @@ class Sabre_CalDAV_Backend_PDO extends Sabre_CalDAV_Backend_Abstract { foreach($mutations as $propertyName=>$propertyValue) { - // We don't know about this property. + // We don't know about this property. if (!isset($this->propertyMap[$propertyName])) { $hasError = true; $result[403][$propertyName] = null; @@ -230,7 +235,7 @@ class Sabre_CalDAV_Backend_PDO extends Sabre_CalDAV_Backend_Abstract { $fieldName = $this->propertyMap[$propertyName]; $newValues[$fieldName] = $propertyValue; - + } // If there were any errors we need to fail the request @@ -258,55 +263,60 @@ class Sabre_CalDAV_Backend_PDO extends Sabre_CalDAV_Backend_Abstract { } $valuesSql[] = 'ctag = ctag + 1'; - $stmt = $this->pdo->prepare("UPDATE `" . $this->calendarTableName . "` SET " . implode(', ',$valuesSql) . " WHERE id = ?"); - $newValues['id'] = $calendarId; + $stmt = $this->pdo->prepare("UPDATE " . $this->calendarTableName . " SET " . implode(', ',$valuesSql) . " WHERE id = ?"); + $newValues['id'] = $calendarId; $stmt->execute(array_values($newValues)); - return true; + return true; } /** - * Delete a calendar and all it's objects - * - * @param string $calendarId + * Delete a calendar and all it's objects + * + * @param string $calendarId * @return void */ public function deleteCalendar($calendarId) { - $stmt = $this->pdo->prepare('DELETE FROM `'.$this->calendarObjectTableName.'` WHERE calendarid = ?'); + $stmt = $this->pdo->prepare('DELETE FROM '.$this->calendarObjectTableName.' WHERE calendarid = ?'); $stmt->execute(array($calendarId)); - $stmt = $this->pdo->prepare('DELETE FROM `'.$this->calendarTableName.'` WHERE id = ?'); + $stmt = $this->pdo->prepare('DELETE FROM '.$this->calendarTableName.' WHERE id = ?'); $stmt->execute(array($calendarId)); } /** - * Returns all calendar objects within a calendar. + * 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 calnedar data + * * 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.: + * * 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 + * 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 + * The calendardata is also optional. If it's not returned + * 'getCalendarObject' will be called later, which *is* expected to return * calendardata. - * - * @param string $calendarId - * @return array + * + * If neither etag or size are specified, the calendardata will be + * used/fetched to determine these numbers. If both are specified the + * amount of times this is needed is reduced by a great degree. + * + * @param string $calendarId + * @return array */ public function getCalendarObjects($calendarId) { - $stmt = $this->pdo->prepare('SELECT * FROM `'.$this->calendarObjectTableName.'` WHERE calendarid = ?'); + $stmt = $this->pdo->prepare('SELECT * FROM '.$this->calendarObjectTableName.' WHERE calendarid = ?'); $stmt->execute(array($calendarId)); return $stmt->fetchAll(); @@ -316,68 +326,68 @@ class Sabre_CalDAV_Backend_PDO extends Sabre_CalDAV_Backend_Abstract { * 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 + * 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 string $calendarId + * @param string $objectUri + * @return array */ public function getCalendarObject($calendarId,$objectUri) { - $stmt = $this->pdo->prepare('SELECT * FROM `'.$this->calendarObjectTableName.'` WHERE calendarid = ? AND uri = ?'); + $stmt = $this->pdo->prepare('SELECT * FROM '.$this->calendarObjectTableName.' WHERE calendarid = ? AND uri = ?'); $stmt->execute(array($calendarId, $objectUri)); return $stmt->fetch(); } /** - * Creates a new calendar object. - * - * @param string $calendarId - * @param string $objectUri - * @param string $calendarData + * Creates a new calendar object. + * + * @param string $calendarId + * @param string $objectUri + * @param string $calendarData * @return void */ public function createCalendarObject($calendarId,$objectUri,$calendarData) { - $stmt = $this->pdo->prepare('INSERT INTO `'.$this->calendarObjectTableName.'` (calendarid, uri, calendardata, lastmodified) VALUES (?,?,?,?)'); + $stmt = $this->pdo->prepare('INSERT INTO '.$this->calendarObjectTableName.' (calendarid, uri, calendardata, lastmodified) VALUES (?,?,?,?)'); $stmt->execute(array($calendarId,$objectUri,$calendarData,time())); - $stmt = $this->pdo->prepare('UPDATE `'.$this->calendarTableName.'` SET ctag = ctag + 1 WHERE id = ?'); + $stmt = $this->pdo->prepare('UPDATE '.$this->calendarTableName.' SET ctag = ctag + 1 WHERE id = ?'); $stmt->execute(array($calendarId)); } /** - * Updates an existing calendarobject, based on it's uri. - * - * @param string $calendarId - * @param string $objectUri - * @param string $calendarData + * Updates an existing calendarobject, based on it's uri. + * + * @param string $calendarId + * @param string $objectUri + * @param string $calendarData * @return void */ public function updateCalendarObject($calendarId,$objectUri,$calendarData) { - $stmt = $this->pdo->prepare('UPDATE `'.$this->calendarObjectTableName.'` SET calendardata = ?, lastmodified = ? WHERE calendarid = ? AND uri = ?'); + $stmt = $this->pdo->prepare('UPDATE '.$this->calendarObjectTableName.' SET calendardata = ?, lastmodified = ? WHERE calendarid = ? AND uri = ?'); $stmt->execute(array($calendarData,time(),$calendarId,$objectUri)); - $stmt = $this->pdo->prepare('UPDATE `'.$this->calendarTableName.'` SET ctag = ctag + 1 WHERE id = ?'); + $stmt = $this->pdo->prepare('UPDATE '.$this->calendarTableName.' SET ctag = ctag + 1 WHERE id = ?'); $stmt->execute(array($calendarId)); } /** - * Deletes an existing calendar object. - * - * @param string $calendarId - * @param string $objectUri + * Deletes an existing calendar object. + * + * @param string $calendarId + * @param string $objectUri * @return void */ public function deleteCalendarObject($calendarId,$objectUri) { - $stmt = $this->pdo->prepare('DELETE FROM `'.$this->calendarObjectTableName.'` WHERE calendarid = ? AND uri = ?'); + $stmt = $this->pdo->prepare('DELETE FROM '.$this->calendarObjectTableName.' WHERE calendarid = ? AND uri = ?'); $stmt->execute(array($calendarId,$objectUri)); - $stmt = $this->pdo->prepare('UPDATE `'. $this->calendarTableName .'` SET ctag = ctag + 1 WHERE id = ?'); + $stmt = $this->pdo->prepare('UPDATE '. $this->calendarTableName .' SET ctag = ctag + 1 WHERE id = ?'); $stmt->execute(array($calendarId)); } diff --git a/3rdparty/Sabre/CalDAV/Calendar.php b/3rdparty/Sabre/CalDAV/Calendar.php index 0d2b387577..623df2dd1b 100644 --- a/3rdparty/Sabre/CalDAV/Calendar.php +++ b/3rdparty/Sabre/CalDAV/Calendar.php @@ -5,42 +5,42 @@ * * A calendar can contain multiple TODO and or Events. These are represented * as Sabre_CalDAV_CalendarObject objects. - * + * * @package Sabre * @subpackage CalDAV - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ class Sabre_CalDAV_Calendar implements Sabre_CalDAV_ICalendar, Sabre_DAV_IProperties, Sabre_DAVACL_IACL { /** - * This is an array with calendar information - * - * @var array + * This is an array with calendar information + * + * @var array */ protected $calendarInfo; /** - * CalDAV backend - * - * @var Sabre_CalDAV_Backend_Abstract + * CalDAV backend + * + * @var Sabre_CalDAV_Backend_Abstract */ protected $caldavBackend; /** * Principal backend - * + * * @var Sabre_DAVACL_IPrincipalBackend */ protected $principalBackend; /** - * Constructor - * - * @param Sabre_CalDAV_Backend_Abstract $caldavBackend - * @param array $calendarInfo - * @return void + * Constructor + * + * @param Sabre_DAVACL_IPrincipalBackend $principalBackend + * @param Sabre_CalDAV_Backend_Abstract $caldavBackend + * @param array $calendarInfo */ public function __construct(Sabre_DAVACL_IPrincipalBackend $principalBackend, Sabre_CalDAV_Backend_Abstract $caldavBackend, $calendarInfo) { @@ -52,9 +52,9 @@ class Sabre_CalDAV_Calendar implements Sabre_CalDAV_ICalendar, Sabre_DAV_IProper } /** - * Returns the name of the calendar - * - * @return string + * Returns the name of the calendar + * + * @return string */ public function getName() { @@ -63,10 +63,10 @@ class Sabre_CalDAV_Calendar implements Sabre_CalDAV_ICalendar, Sabre_DAV_IProper } /** - * Updates properties such as the display name and description - * - * @param array $mutations - * @return array + * Updates properties such as the display name and description + * + * @param array $mutations + * @return array */ public function updateProperties($mutations) { @@ -75,10 +75,10 @@ class Sabre_CalDAV_Calendar implements Sabre_CalDAV_ICalendar, Sabre_DAV_IProper } /** - * Returns the list of properties - * - * @param array $properties - * @return array + * Returns the list of properties + * + * @param array $requestedProperties + * @return array */ public function getProperties($requestedProperties) { @@ -86,16 +86,16 @@ class Sabre_CalDAV_Calendar implements Sabre_CalDAV_ICalendar, Sabre_DAV_IProper foreach($requestedProperties as $prop) switch($prop) { - case '{urn:ietf:params:xml:ns:caldav}supported-calendar-data' : - $response[$prop] = new Sabre_CalDAV_Property_SupportedCalendarData(); + case '{urn:ietf:params:xml:ns:caldav}supported-calendar-data' : + $response[$prop] = new Sabre_CalDAV_Property_SupportedCalendarData(); break; - case '{urn:ietf:params:xml:ns:caldav}supported-collation-set' : - $response[$prop] = new Sabre_CalDAV_Property_SupportedCollationSet(); + 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 : + default : if (isset($this->calendarInfo[$prop])) $response[$prop] = $this->calendarInfo[$prop]; break; @@ -108,22 +108,22 @@ class Sabre_CalDAV_Calendar implements Sabre_CalDAV_ICalendar, Sabre_DAV_IProper * Returns a calendar object * * The contained calendar objects are for example Events or Todo's. - * - * @param string $name - * @return Sabre_DAV_ICalendarObject + * + * @param string $name + * @return Sabre_DAV_ICalendarObject */ public function getChild($name) { $obj = $this->caldavBackend->getCalendarObject($this->calendarInfo['id'],$name); - if (!$obj) throw new Sabre_DAV_Exception_FileNotFound('Calendar object not found'); + if (!$obj) throw new Sabre_DAV_Exception_NotFound('Calendar object not found'); return new Sabre_CalDAV_CalendarObject($this->caldavBackend,$this->calendarInfo,$obj); } /** - * Returns the full list of calendar objects - * - * @return array + * Returns the full list of calendar objects + * + * @return array */ public function getChildren() { @@ -137,17 +137,17 @@ class Sabre_CalDAV_Calendar implements Sabre_CalDAV_ICalendar, Sabre_DAV_IProper } /** - * Checks if a child-node exists. - * - * @param string $name - * @return bool + * Checks if a child-node exists. + * + * @param string $name + * @return bool */ public function childExists($name) { $obj = $this->caldavBackend->getCalendarObject($this->calendarInfo['id'],$name); - if (!$obj) + if (!$obj) return false; - else + else return true; } @@ -156,8 +156,8 @@ class Sabre_CalDAV_Calendar implements Sabre_CalDAV_ICalendar, Sabre_DAV_IProper * Creates a new directory * * We actually block this, as subdirectories are not allowed in calendars. - * - * @param string $name + * + * @param string $name * @return void */ public function createDirectory($name) { @@ -170,32 +170,23 @@ class Sabre_CalDAV_Calendar implements Sabre_CalDAV_ICalendar, Sabre_DAV_IProper * Creates a new file * * The contents of the new file must be a valid ICalendar string. - * - * @param string $name - * @param resource $calendarData - * @return void + * + * @param string $name + * @param resource $calendarData + * @return string|null */ public function createFile($name,$calendarData = null) { - $calendarData = stream_get_contents($calendarData); - // Converting to UTF-8, if needed - $calendarData = Sabre_DAV_StringUtil::ensureUTF8($calendarData); - - $supportedComponents = $this->calendarInfo['{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}supported-calendar-component-set']; - if ($supportedComponents) { - $supportedComponents = $supportedComponents->getValue(); - } else { - $supportedComponents = null; + if (is_resource($calendarData)) { + $calendarData = stream_get_contents($calendarData); } - Sabre_CalDAV_ICalendarUtil::validateICalendarObject($calendarData, $supportedComponents); - - $this->caldavBackend->createCalendarObject($this->calendarInfo['id'],$name,$calendarData); + return $this->caldavBackend->createCalendarObject($this->calendarInfo['id'],$name,$calendarData); } /** - * Deletes the calendar. - * + * Deletes the calendar. + * * @return void */ public function delete() { @@ -205,10 +196,10 @@ class Sabre_CalDAV_Calendar implements Sabre_CalDAV_ICalendar, Sabre_DAV_IProper } /** - * Renames the calendar. Note that most calendars use the - * {DAV:}displayname to display a name to display a name. - * - * @param string $newName + * Renames the calendar. Note that most calendars use the + * {DAV:}displayname to display a name to display a name. + * + * @param string $newName * @return void */ public function setName($newName) { @@ -219,7 +210,7 @@ class Sabre_CalDAV_Calendar implements Sabre_CalDAV_ICalendar, Sabre_DAV_IProper /** * Returns the last modification date as a unix timestamp. - * + * * @return void */ public function getLastModified() { @@ -231,8 +222,8 @@ class Sabre_CalDAV_Calendar implements Sabre_CalDAV_ICalendar, Sabre_DAV_IProper /** * Returns the owner principal * - * This must be a url to a principal, or null if there's no owner - * + * This must be a url to a principal, or null if there's no owner + * * @return string|null */ public function getOwner() { @@ -245,8 +236,8 @@ class Sabre_CalDAV_Calendar implements Sabre_CalDAV_ICalendar, Sabre_DAV_IProper * Returns a group principal * * This must be a url to a principal, or null if there's no owner - * - * @return string|null + * + * @return string|null */ public function getGroup() { @@ -258,13 +249,13 @@ class Sabre_CalDAV_Calendar implements Sabre_CalDAV_ICalendar, Sabre_DAV_IProper * 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 + * * '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 + * * 'protected' (optional), indicating that this ACE is not allowed to + * be updated. + * + * @return array */ public function getACL() { @@ -294,6 +285,11 @@ class Sabre_CalDAV_Calendar implements Sabre_CalDAV_ICalendar, Sabre_DAV_IProper 'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-read', 'protected' => true, ), + array( + 'privilege' => '{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}read-free-busy', + 'principal' => '{DAV:}authenticated', + 'protected' => true, + ), ); @@ -302,9 +298,9 @@ class Sabre_CalDAV_Calendar implements Sabre_CalDAV_ICalendar, Sabre_DAV_IProper /** * Updates the ACL * - * This method will receive a list of new ACE's. - * - * @param array $acl + * This method will receive a list of new ACE's. + * + * @param array $acl * @return void */ public function setACL(array $acl) { @@ -313,6 +309,35 @@ class Sabre_CalDAV_Calendar implements Sabre_CalDAV_ICalendar, Sabre_DAV_IProper } + /** + * Returns the list of supported privileges for this node. + * + * The returned data structure is a list of nested privileges. + * See Sabre_DAVACL_Plugin::getDefaultSupportedPrivilegeSet for a simple + * standard structure. + * + * If null is returned from this method, the default privilege set is used, + * which is fine for most common usecases. + * + * @return array|null + */ + public function getSupportedPrivilegeSet() { + $default = Sabre_DAVACL_Plugin::getDefaultSupportedPrivilegeSet(); + + // We need to inject 'read-free-busy' in the tree, aggregated under + // {DAV:}read. + foreach($default['aggregates'] as &$agg) { + + if ($agg['privilege'] !== '{DAV:}read') continue; + + $agg['aggregates'][] = array( + 'privilege' => '{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}read-free-busy', + ); + + } + return $default; + + } } diff --git a/3rdparty/Sabre/CalDAV/CalendarObject.php b/3rdparty/Sabre/CalDAV/CalendarObject.php index 0c99f18deb..72f0a578d1 100644 --- a/3rdparty/Sabre/CalDAV/CalendarObject.php +++ b/3rdparty/Sabre/CalDAV/CalendarObject.php @@ -1,43 +1,43 @@ calendarInfo['{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}supported-calendar-component-set']; - if ($supportedComponents) { - $supportedComponents = $supportedComponents->getValue(); - } else { - $supportedComponents = null; } - Sabre_CalDAV_ICalendarUtil::validateICalendarObject($calendarData, $supportedComponents); - - $this->caldavBackend->updateCalendarObject($this->calendarInfo['id'],$this->objectData['uri'],$calendarData); + $etag = $this->caldavBackend->updateCalendarObject($this->calendarInfo['id'],$this->objectData['uri'],$calendarData); $this->objectData['calendardata'] = $calendarData; + $this->objectData['etag'] = $etag; + + return $etag; } /** - * Deletes the calendar object - * + * Deletes the calendar object + * * @return void */ public function delete() { @@ -121,9 +113,9 @@ class Sabre_CalDAV_CalendarObject extends Sabre_DAV_File implements Sabre_CalDAV } /** - * Returns the mime content-type - * - * @return string + * Returns the mime content-type + * + * @return string */ public function getContentType() { @@ -134,9 +126,9 @@ class Sabre_CalDAV_CalendarObject extends Sabre_DAV_File implements Sabre_CalDAV /** * Returns an ETag for this object. * - * The ETag is an arbritrary string, but MUST be surrounded by double-quotes. - * - * @return string + * The ETag is an arbitrary string, but MUST be surrounded by double-quotes. + * + * @return string */ public function getETag() { @@ -150,8 +142,8 @@ class Sabre_CalDAV_CalendarObject extends Sabre_DAV_File implements Sabre_CalDAV /** * Returns the last modification date as a unix timestamp - * - * @return time + * + * @return time */ public function getLastModified() { @@ -160,21 +152,25 @@ class Sabre_CalDAV_CalendarObject extends Sabre_DAV_File implements Sabre_CalDAV } /** - * Returns the size of this object in bytes - * + * Returns the size of this object in bytes + * * @return int */ public function getSize() { - return strlen($this->objectData['calendardata']); + if (array_key_exists('size',$this->objectData)) { + return $this->objectData['size']; + } else { + return strlen($this->get()); + } } /** * Returns the owner principal * - * This must be a url to a principal, or null if there's no owner - * + * This must be a url to a principal, or null if there's no owner + * * @return string|null */ public function getOwner() { @@ -187,8 +183,8 @@ class Sabre_CalDAV_CalendarObject extends Sabre_DAV_File implements Sabre_CalDAV * Returns a group principal * * This must be a url to a principal, or null if there's no owner - * - * @return string|null + * + * @return string|null */ public function getGroup() { @@ -200,13 +196,13 @@ class Sabre_CalDAV_CalendarObject extends Sabre_DAV_File implements Sabre_CalDAV * 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 + * * '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 + * * 'protected' (optional), indicating that this ACE is not allowed to + * be updated. + * + * @return array */ public function getACL() { @@ -244,9 +240,9 @@ class Sabre_CalDAV_CalendarObject extends Sabre_DAV_File implements Sabre_CalDAV /** * Updates the ACL * - * This method will receive a list of new ACE's. - * - * @param array $acl + * This method will receive a list of new ACE's. + * + * @param array $acl * @return void */ public function setACL(array $acl) { @@ -255,6 +251,23 @@ class Sabre_CalDAV_CalendarObject extends Sabre_DAV_File implements Sabre_CalDAV } + /** + * Returns the list of supported privileges for this node. + * + * The returned data structure is a list of nested privileges. + * See Sabre_DAVACL_Plugin::getDefaultSupportedPrivilegeSet for a simple + * standard structure. + * + * If null is returned from this method, the default privilege set is used, + * which is fine for most common usecases. + * + * @return array|null + */ + public function getSupportedPrivilegeSet() { + + return null; + + } } diff --git a/3rdparty/Sabre/CalDAV/CalendarQueryParser.php b/3rdparty/Sabre/CalDAV/CalendarQueryParser.php new file mode 100644 index 0000000000..bd0d343382 --- /dev/null +++ b/3rdparty/Sabre/CalDAV/CalendarQueryParser.php @@ -0,0 +1,296 @@ +dom = $dom; + + $this->xpath = new DOMXPath($dom); + $this->xpath->registerNameSpace('cal',Sabre_CalDAV_Plugin::NS_CALDAV); + $this->xpath->registerNameSpace('dav','urn:DAV'); + + } + + /** + * Parses the request. + * + * @return void + */ + public function parse() { + + $filterNode = null; + + $filter = $this->xpath->query('/cal:calendar-query/cal:filter'); + if ($filter->length !== 1) { + throw new Sabre_DAV_Exception_BadRequest('Only one filter element is allowed'); + } + + $compFilters = $this->parseCompFilters($filter->item(0)); + if (count($compFilters)!==1) { + throw new Sabre_DAV_Exception_BadRequest('There must be exactly 1 top-level comp-filter.'); + } + + $this->filters = $compFilters[0]; + $this->requestedProperties = array_keys(Sabre_DAV_XMLUtil::parseProperties($this->dom->firstChild)); + + $expand = $this->xpath->query('/cal:calendar-query/dav:prop/cal:calendar-data/cal:expand'); + if ($expand->length>0) { + $this->expand = $this->parseExpand($expand->item(0)); + } + + + } + + /** + * Parses all the 'comp-filter' elements from a node + * + * @param DOMElement $parentNode + * @return array + */ + protected function parseCompFilters(DOMElement $parentNode) { + + $compFilterNodes = $this->xpath->query('cal:comp-filter', $parentNode); + $result = array(); + + for($ii=0; $ii < $compFilterNodes->length; $ii++) { + + $compFilterNode = $compFilterNodes->item($ii); + + $compFilter = array(); + $compFilter['name'] = $compFilterNode->getAttribute('name'); + $compFilter['is-not-defined'] = $this->xpath->query('cal:is-not-defined', $compFilterNode)->length>0; + $compFilter['comp-filters'] = $this->parseCompFilters($compFilterNode); + $compFilter['prop-filters'] = $this->parsePropFilters($compFilterNode); + $compFilter['time-range'] = $this->parseTimeRange($compFilterNode); + + if ($compFilter['time-range'] && !in_array($compFilter['name'],array( + 'VEVENT', + 'VTODO', + 'VJOURNAL', + 'VFREEBUSY', + 'VALARM', + ))) { + throw new Sabre_DAV_Exception_BadRequest('The time-range filter is not defined for the ' . $compFilter['name'] . ' component'); + }; + + $result[] = $compFilter; + + } + + return $result; + + } + + /** + * Parses all the prop-filter elements from a node + * + * @param DOMElement $parentNode + * @return array + */ + protected function parsePropFilters(DOMElement $parentNode) { + + $propFilterNodes = $this->xpath->query('cal:prop-filter', $parentNode); + $result = array(); + + for ($ii=0; $ii < $propFilterNodes->length; $ii++) { + + $propFilterNode = $propFilterNodes->item($ii); + $propFilter = array(); + $propFilter['name'] = $propFilterNode->getAttribute('name'); + $propFilter['is-not-defined'] = $this->xpath->query('cal:is-not-defined', $propFilterNode)->length>0; + $propFilter['param-filters'] = $this->parseParamFilters($propFilterNode); + $propFilter['text-match'] = $this->parseTextMatch($propFilterNode); + $propFilter['time-range'] = $this->parseTimeRange($propFilterNode); + + $result[] = $propFilter; + + } + + return $result; + + } + + /** + * Parses the param-filter element + * + * @param DOMElement $parentNode + * @return array + */ + protected function parseParamFilters(DOMElement $parentNode) { + + $paramFilterNodes = $this->xpath->query('cal:param-filter', $parentNode); + $result = array(); + + for($ii=0;$ii<$paramFilterNodes->length;$ii++) { + + $paramFilterNode = $paramFilterNodes->item($ii); + $paramFilter = array(); + $paramFilter['name'] = $paramFilterNode->getAttribute('name'); + $paramFilter['is-not-defined'] = $this->xpath->query('cal:is-not-defined', $paramFilterNode)->length>0; + $paramFilter['text-match'] = $this->parseTextMatch($paramFilterNode); + + $result[] = $paramFilter; + + } + + return $result; + + } + + /** + * Parses the text-match element + * + * @param DOMElement $parentNode + * @return array|null + */ + protected function parseTextMatch(DOMElement $parentNode) { + + $textMatchNodes = $this->xpath->query('cal:text-match', $parentNode); + + if ($textMatchNodes->length === 0) + return null; + + $textMatchNode = $textMatchNodes->item(0); + $negateCondition = $textMatchNode->getAttribute('negate-condition'); + $negateCondition = $negateCondition==='yes'; + $collation = $textMatchNode->getAttribute('collation'); + if (!$collation) $collation = 'i;ascii-casemap'; + + return array( + 'negate-condition' => $negateCondition, + 'collation' => $collation, + 'value' => $textMatchNode->nodeValue + ); + + } + + /** + * Parses the time-range element + * + * @param DOMElement $parentNode + * @return array|null + */ + protected function parseTimeRange(DOMElement $parentNode) { + + $timeRangeNodes = $this->xpath->query('cal:time-range', $parentNode); + if ($timeRangeNodes->length === 0) { + return null; + } + + $timeRangeNode = $timeRangeNodes->item(0); + + if ($start = $timeRangeNode->getAttribute('start')) { + $start = Sabre_VObject_DateTimeParser::parseDateTime($start); + } else { + $start = null; + } + if ($end = $timeRangeNode->getAttribute('end')) { + $end = Sabre_VObject_DateTimeParser::parseDateTime($end); + } else { + $end = null; + } + + if (!is_null($start) && !is_null($end) && $end <= $start) { + throw new Sabre_DAV_Exception_BadRequest('The end-date must be larger than the start-date in the time-range filter'); + } + + return array( + 'start' => $start, + 'end' => $end, + ); + + } + + /** + * Parses the CALDAV:expand element + * + * @param DOMElement $parentNode + * @return void + */ + protected function parseExpand(DOMElement $parentNode) { + + $start = $parentNode->getAttribute('start'); + if(!$start) { + throw new Sabre_DAV_Exception_BadRequest('The "start" attribute is required for the CALDAV:expand element'); + } + $start = Sabre_VObject_DateTimeParser::parseDateTime($start); + + $end = $parentNode->getAttribute('end'); + if(!$end) { + throw new Sabre_DAV_Exception_BadRequest('The "end" attribute is required for the CALDAV:expand element'); + } + $end = Sabre_VObject_DateTimeParser::parseDateTime($end); + + if ($end <= $start) { + throw new Sabre_DAV_Exception_BadRequest('The end-date must be larger than the start-date in the expand element.'); + } + + return array( + 'start' => $start, + 'end' => $end, + ); + + } + +} diff --git a/3rdparty/Sabre/CalDAV/CalendarQueryValidator.php b/3rdparty/Sabre/CalDAV/CalendarQueryValidator.php new file mode 100644 index 0000000000..1bb6b5d53f --- /dev/null +++ b/3rdparty/Sabre/CalDAV/CalendarQueryValidator.php @@ -0,0 +1,347 @@ +name !== $filters['name']) { + return false; + } + + return + $this->validateCompFilters($vObject, $filters['comp-filters']) && + $this->validatePropFilters($vObject, $filters['prop-filters']); + + + } + + /** + * This method checks the validity of comp-filters. + * + * A list of comp-filters needs to be specified. Also the parent of the + * component we're checking should be specified, not the component to check + * itself. + * + * @param Sabre_VObject_Component $parent + * @param array $filters + * @return bool + */ + protected function validateCompFilters(Sabre_VObject_Component $parent, array $filters) { + + foreach($filters as $filter) { + + $isDefined = isset($parent->$filter['name']); + + if ($filter['is-not-defined']) { + + if ($isDefined) { + return false; + } else { + continue; + } + + } + if (!$isDefined) { + return false; + } + + if ($filter['time-range']) { + foreach($parent->$filter['name'] as $subComponent) { + if ($this->validateTimeRange($subComponent, $filter['time-range']['start'], $filter['time-range']['end'])) { + continue 2; + } + } + return false; + } + + if (!$filter['comp-filters'] && !$filter['prop-filters']) { + continue; + } + + // If there are sub-filters, we need to find at least one component + // for which the subfilters hold true. + foreach($parent->$filter['name'] as $subComponent) { + + if ( + $this->validateCompFilters($subComponent, $filter['comp-filters']) && + $this->validatePropFilters($subComponent, $filter['prop-filters'])) { + // We had a match, so this comp-filter succeeds + continue 2; + } + + } + + // If we got here it means there were sub-comp-filters or + // sub-prop-filters and there was no match. This means this filter + // needs to return false. + return false; + + } + + // If we got here it means we got through all comp-filters alive so the + // filters were all true. + return true; + + } + + /** + * This method checks the validity of prop-filters. + * + * A list of prop-filters needs to be specified. Also the parent of the + * property we're checking should be specified, not the property to check + * itself. + * + * @param Sabre_VObject_Component $parent + * @param array $filters + * @return bool + */ + protected function validatePropFilters(Sabre_VObject_Component $parent, array $filters) { + + foreach($filters as $filter) { + + $isDefined = isset($parent->$filter['name']); + + if ($filter['is-not-defined']) { + + if ($isDefined) { + return false; + } else { + continue; + } + + } + if (!$isDefined) { + return false; + } + + if ($filter['time-range']) { + foreach($parent->$filter['name'] as $subComponent) { + if ($this->validateTimeRange($subComponent, $filter['time-range']['start'], $filter['time-range']['end'])) { + continue 2; + } + } + return false; + } + + if (!$filter['param-filters'] && !$filter['text-match']) { + continue; + } + + // If there are sub-filters, we need to find at least one property + // for which the subfilters hold true. + foreach($parent->$filter['name'] as $subComponent) { + + if( + $this->validateParamFilters($subComponent, $filter['param-filters']) && + (!$filter['text-match'] || $this->validateTextMatch($subComponent, $filter['text-match'])) + ) { + // We had a match, so this prop-filter succeeds + continue 2; + } + + } + + // If we got here it means there were sub-param-filters or + // text-match filters and there was no match. This means the + // filter needs to return false. + return false; + + } + + // If we got here it means we got through all prop-filters alive so the + // filters were all true. + return true; + + } + + /** + * This method checks the validity of param-filters. + * + * A list of param-filters needs to be specified. Also the parent of the + * parameter we're checking should be specified, not the parameter to check + * itself. + * + * @param Sabre_VObject_Property $parent + * @param array $filters + * @return bool + */ + protected function validateParamFilters(Sabre_VObject_Property $parent, array $filters) { + + foreach($filters as $filter) { + + $isDefined = isset($parent[$filter['name']]); + + if ($filter['is-not-defined']) { + + if ($isDefined) { + return false; + } else { + continue; + } + + } + if (!$isDefined) { + return false; + } + + if (!$filter['text-match']) { + continue; + } + + // If there are sub-filters, we need to find at least one parameter + // for which the subfilters hold true. + foreach($parent[$filter['name']] as $subParam) { + + if($this->validateTextMatch($subParam,$filter['text-match'])) { + // We had a match, so this param-filter succeeds + continue 2; + } + + } + + // If we got here it means there was a text-match filter and there + // were no matches. This means the filter needs to return false. + return false; + + } + + // If we got here it means we got through all param-filters alive so the + // filters were all true. + return true; + + } + + /** + * This method checks the validity of a text-match. + * + * A single text-match should be specified as well as the specific property + * or parameter we need to validate. + * + * @param Sabre_VObject_Node $parent + * @param array $textMatch + * @return bool + */ + protected function validateTextMatch(Sabre_VObject_Node $parent, array $textMatch) { + + $value = (string)$parent; + + $isMatching = Sabre_DAV_StringUtil::textMatch($value, $textMatch['value'], $textMatch['collation']); + + return ($textMatch['negate-condition'] xor $isMatching); + + } + + /** + * Validates if a component matches the given time range. + * + * This is all based on the rules specified in rfc4791, which are quite + * complex. + * + * @param Sabre_VObject_Node $component + * @param DateTime $start + * @param DateTime $end + * @return bool + */ + protected function validateTimeRange(Sabre_VObject_Node $component, $start, $end) { + + if (is_null($start)) { + $start = new DateTime('1900-01-01'); + } + if (is_null($end)) { + $end = new DateTime('3000-01-01'); + } + + switch($component->name) { + + case 'VEVENT' : + case 'VTODO' : + case 'VJOURNAL' : + + return $component->isInTimeRange($start, $end); + + case 'VALARM' : + + // If the valarm is wrapped in a recurring event, we need to + // expand the recursions, and validate each. + // + // Our datamodel doesn't easily allow us to do this straight + // in the VALARM component code, so this is a hack, and an + // expensive one too. + if ($component->parent->name === 'VEVENT' && $component->parent->RRULE) { + // Fire up the iterator! + $it = new Sabre_VObject_RecurrenceIterator($component->parent->parent, (string)$component->parent->UID); + while($it->valid()) { + $expandedEvent = $it->getEventObject(); + + // We need to check from these expanded alarms, which + // one is the first to trigger. Based on this, we can + // determine if we can 'give up' expanding events. + $firstAlarm = null; + foreach($expandedEvent->VALARM as $expandedAlarm) { + $effectiveTrigger = $expandedAlarm->getEffectiveTriggerTime(); + if (!$firstAlarm || $effectiveTrigger < $firstAlarm) { + $firstAlarm = $effectiveTrigger; + } + if ($expandedAlarm->isInTimeRange($start, $end)) { + return true; + } + + } + if ($firstAlarm > $end) { + return false; + } + $it->next(); + } + return false; + } else { + return $component->isInTimeRange($start, $end); + } + + case 'VFREEBUSY' : + throw new Sabre_DAV_Exception_NotImplemented('time-range filters are currently not supported on ' . $component->name . ' components'); + + case 'COMPLETED' : + case 'CREATED' : + case 'DTEND' : + case 'DTSTAMP' : + case 'DTSTART' : + case 'DUE' : + case 'LAST-MODIFIED' : + return ($start <= $component->getDateTime() && $end >= $component->getDateTime()); + + + + default : + throw new Sabre_DAV_Exception_BadRequest('You cannot create a time-range filter on a ' . $component->name . ' component'); + + } + + } + +} diff --git a/3rdparty/Sabre/CalDAV/CalendarRootNode.php b/3rdparty/Sabre/CalDAV/CalendarRootNode.php index 69669a9d7f..3907913cc7 100644 --- a/3rdparty/Sabre/CalDAV/CalendarRootNode.php +++ b/3rdparty/Sabre/CalDAV/CalendarRootNode.php @@ -1,38 +1,38 @@ server->getPlugin('acl')) { + $aclPlugin->checkPrivileges($uri, '{DAV:}read'); + } + $this->server->httpResponse->setHeader('Content-Type','text/calendar'); $this->server->httpResponse->sendStatus(200); - $this->server->httpResponse->sendBody($this->generateICS($this->server->tree->getChildren($uri))); + + $nodes = $this->server->getPropertiesForPath($uri, array( + '{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}calendar-data', + ),1); + + $this->server->httpResponse->sendBody($this->generateICS($nodes)); // Returning false to break the event chain return false; @@ -65,16 +75,20 @@ class Sabre_CalDAV_ICSExportPlugin extends Sabre_DAV_ServerPlugin { } /** - * Merges all calendar objects, and builds one big ics export - * - * @param array $nodes - * @return void + * Merges all calendar objects, and builds one big ics export + * + * @param array $nodes + * @return string */ public function generateICS(array $nodes) { $calendar = new Sabre_VObject_Component('vcalendar'); $calendar->version = '2.0'; - $calendar->prodid = '-//SabreDAV//SabreDAV ' . Sabre_DAV_Version::VERSION . '//EN'; + if (Sabre_DAV_Server::$exposeVersion) { + $calendar->prodid = '-//SabreDAV//SabreDAV ' . Sabre_DAV_Version::VERSION . '//EN'; + } else { + $calendar->prodid = '-//SabreDAV//SabreDAV//EN'; + } $calendar->calscale = 'GREGORIAN'; $collectedTimezones = array(); @@ -84,7 +98,11 @@ class Sabre_CalDAV_ICSExportPlugin extends Sabre_DAV_ServerPlugin { foreach($nodes as $node) { - $nodeData = $node->get(); + if (!isset($node[200]['{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}calendar-data'])) { + continue; + } + $nodeData = $node[200]['{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}calendar-data']; + $nodeComp = Sabre_VObject_Reader::read($nodeData); foreach($nodeComp->children() as $child) { @@ -105,13 +123,10 @@ class Sabre_CalDAV_ICSExportPlugin extends Sabre_DAV_ServerPlugin { $collectedTimezones[] = $child->TZID; break; - } - } - } foreach($timezones as $tz) $calendar->add($tz); @@ -119,6 +134,6 @@ class Sabre_CalDAV_ICSExportPlugin extends Sabre_DAV_ServerPlugin { return $calendar->serialize(); - } + } } diff --git a/3rdparty/Sabre/CalDAV/ICalendar.php b/3rdparty/Sabre/CalDAV/ICalendar.php index 8193dff3a8..15d51ebcf7 100644 --- a/3rdparty/Sabre/CalDAV/ICalendar.php +++ b/3rdparty/Sabre/CalDAV/ICalendar.php @@ -4,15 +4,15 @@ * Calendar interface * * Implement this interface to allow a node to be recognized as an calendar. - * + * * @package Sabre * @subpackage CalDAV - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ interface Sabre_CalDAV_ICalendar extends Sabre_DAV_ICollection { - + } diff --git a/3rdparty/Sabre/CalDAV/ICalendarObject.php b/3rdparty/Sabre/CalDAV/ICalendarObject.php index 708300ad7b..280f982a31 100644 --- a/3rdparty/Sabre/CalDAV/ICalendarObject.php +++ b/3rdparty/Sabre/CalDAV/ICalendarObject.php @@ -1,20 +1,20 @@ registerXPathNameSpace('cal','urn:ietf:params:xml:ns:xcal'); - - // Check if there's only 1 component - $components = array('vevent','vtodo','vjournal','vfreebusy'); - $componentsFound = array(); - - foreach($components as $component) { - $test = $xcal->xpath('/cal:iCalendar/cal:vcalendar/cal:' . $component); - if (is_array($test)) $componentsFound = array_merge($componentsFound, $test); - } - if (count($componentsFound)<1) { - throw new Sabre_CalDAV_Exception_InvalidICalendarObject('One VEVENT, VTODO, VJOURNAL or VFREEBUSY must be specified. 0 found.'); - } - $component = $componentsFound[0]; - - if (is_null($allowedComponents)) return true; - - // Check if the component is allowed - $name = $component->getName(); - if (!in_array(strtoupper($name),$allowedComponents)) { - throw new Sabre_CalDAV_Exception_InvalidICalendarObject(strtoupper($name) . ' is not allowed in this calendar.'); - } - - if (count($xcal->xpath('/cal:iCalendar/cal:vcalendar/cal:method'))>0) { - throw new Sabre_CalDAV_Exception_InvalidICalendarObject('The METHOD property is not allowed in calendar objects'); - } - - return true; - - } - - /** - * Converts ICalendar data to XML. - * - * Properties are converted to lowercase xml elements. Parameters are; - * converted to attributes. BEGIN:VEVENT is converted to and - * END:VEVENT as well as other components. - * - * It's a very loose parser. If any line does not conform to the spec, it - * will simply be ignored. It will try to detect if \r\n or \n line endings - * are used. - * - * @todo Currently quoted attributes are not parsed correctly. - * @see http://tools.ietf.org/html/draft-royer-calsch-xcal-03 - * @param string $icalData - * @return string. - */ - static function toXCAL($icalData) { - - // Detecting line endings - $lb="\r\n"; - if (strpos($icalData,"\r\n")!==false) $lb = "\r\n"; - elseif (strpos($icalData,"\n")!==false) $lb = "\n"; - - // Splitting up items per line - $lines = explode($lb,$icalData); - - // Properties can be folded over 2 lines. In this case the second - // line will be preceeded by a space or tab. - $lines2 = array(); - foreach($lines as $line) { - - if (!$line) continue; - if ($line[0]===" " || $line[0]==="\t") { - $lines2[count($lines2)-1].=substr($line,1); - continue; - } - - $lines2[]=$line; - - } - - $xml = '' . "\n"; - $xml.= "\n"; - - $spaces = 2; - foreach($lines2 as $line) { - - $matches = array(); - // This matches PROPERTYNAME;ATTRIBUTES:VALUE - if (!preg_match('/^([^:^;]*)(?:;([^:]*))?:(.*)$/',$line,$matches)) - continue; - - $propertyName = strtolower($matches[1]); - $attributes = $matches[2]; - $value = $matches[3]; - - // If the line was in the format BEGIN:COMPONENT or END:COMPONENT, we need to special case it. - if ($propertyName === 'begin') { - $xml.=str_repeat(" ",$spaces); - $xml.='<' . strtolower($value) . ">\n"; - $spaces+=2; - continue; - } elseif ($propertyName === 'end') { - $spaces-=2; - $xml.=str_repeat(" ",$spaces); - $xml.='\n"; - continue; - } - - $xml.=str_repeat(" ",$spaces); - $xml.='<' . $propertyName; - if ($attributes) { - // There can be multiple attributes - $attributes = explode(';',$attributes); - foreach($attributes as $att) { - - list($attName,$attValue) = explode('=',$att,2); - $attName = strtolower($attName); - if ($attName === 'language') $attName='xml:lang'; - $xml.=' ' . $attName . '="' . htmlspecialchars($attValue) . '"'; - - } - } - - $xml.='>'. htmlspecialchars(trim($value)) . '\n"; - - } - $xml.=""; - return $xml; - - } - -} - diff --git a/3rdparty/Sabre/CalDAV/Plugin.php b/3rdparty/Sabre/CalDAV/Plugin.php index 02747c8395..d7d1d97051 100644 --- a/3rdparty/Sabre/CalDAV/Plugin.php +++ b/3rdparty/Sabre/CalDAV/Plugin.php @@ -8,8 +8,8 @@ * * @package Sabre * @subpackage CalDAV - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. - * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. + * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin { @@ -18,7 +18,7 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin { * This is the official CalDAV namespace */ const NS_CALDAV = 'urn:ietf:params:xml:ns:caldav'; - + /** * This is the namespace for the proprietary calendarserver extensions */ @@ -41,21 +41,48 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin { const CALENDAR_ROOT = 'calendars'; /** - * Reference to server object - * - * @var Sabre_DAV_Server + * Reference to server object + * + * @var Sabre_DAV_Server */ private $server; + /** + * The email handler for invites and other scheduling messages. + * + * @var Sabre_CalDAV_Schedule_IMip + */ + protected $imipHandler; + + /** + * Sets the iMIP handler. + * + * iMIP = The email transport of iCalendar scheduling messages. Setting + * this is optional, but if you want the server to allow invites to be sent + * out, you must set a handler. + * + * Specifically iCal will plain assume that the server supports this. If + * the server doesn't, iCal will display errors when inviting people to + * events. + * + * @param Sabre_CalDAV_Schedule_IMip $imipHandler + * @return void + */ + public function setIMipHandler(Sabre_CalDAV_Schedule_IMip $imipHandler) { + + $this->imipHandler = $imipHandler; + + } + /** * Use this method to tell the server this plugin defines additional * HTTP methods. * - * This method is passed a uri. It should only return HTTP methods that are + * This method is passed a uri. It should only return HTTP methods that are * available for the specified uri. * * @param string $uri - * @return array + * @return array */ public function getHTTPMethods($uri) { @@ -68,7 +95,7 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin { if ($node instanceof Sabre_DAV_IExtendedCollection) { try { $node->getChild($name); - } catch (Sabre_DAV_Exception_FileNotFound $e) { + } catch (Sabre_DAV_Exception_NotFound $e) { return array('MKCALENDAR'); } } @@ -77,9 +104,9 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin { } /** - * Returns a list of features for the DAV: HTTP header. - * - * @return array + * Returns a list of features for the DAV: HTTP header. + * + * @return array */ public function getFeatures() { @@ -89,11 +116,11 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin { /** * Returns a plugin name. - * + * * Using this name other plugins will be able to access other plugins - * using Sabre_DAV_Server::getPlugin - * - * @return string + * using Sabre_DAV_Server::getPlugin + * + * @return string */ public function getPluginName() { @@ -105,38 +132,46 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin { * Returns a list of reports this plugin supports. * * This will be used in the {DAV:}supported-report-set property. - * Note that you still need to subscribe to the 'report' event to actually - * implement them - * + * Note that you still need to subscribe to the 'report' event to actually + * implement them + * * @param string $uri - * @return array + * @return array */ public function getSupportedReportSet($uri) { $node = $this->server->tree->getNodeForPath($uri); + + $reports = array(); if ($node instanceof Sabre_CalDAV_ICalendar || $node instanceof Sabre_CalDAV_ICalendarObject) { - return array( - '{' . self::NS_CALDAV . '}calendar-multiget', - '{' . self::NS_CALDAV . '}calendar-query', - ); + $reports[] = '{' . self::NS_CALDAV . '}calendar-multiget'; + $reports[] = '{' . self::NS_CALDAV . '}calendar-query'; } - return array(); + if ($node instanceof Sabre_CalDAV_ICalendar) { + $reports[] = '{' . self::NS_CALDAV . '}free-busy-query'; + } + return $reports; } /** - * Initializes the plugin - * - * @param Sabre_DAV_Server $server + * Initializes the plugin + * + * @param Sabre_DAV_Server $server * @return void */ public function initialize(Sabre_DAV_Server $server) { $this->server = $server; + $server->subscribeEvent('unknownMethod',array($this,'unknownMethod')); //$server->subscribeEvent('unknownMethod',array($this,'unknownMethod2'),1000); $server->subscribeEvent('report',array($this,'report')); $server->subscribeEvent('beforeGetProperties',array($this,'beforeGetProperties')); + $server->subscribeEvent('onHTMLActionsPanel', array($this,'htmlActionsPanel')); + $server->subscribeEvent('onBrowserPostAction', array($this,'browserPostAction')); + $server->subscribeEvent('beforeWriteContent', array($this, 'beforeWriteContent')); + $server->subscribeEvent('beforeCreateFile', array($this, 'beforeCreateFile')); $server->xmlNamespaces[self::NS_CALDAV] = 'cal'; $server->xmlNamespaces[self::NS_CALENDARSERVER] = 'cs'; @@ -144,6 +179,7 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin { $server->propertyMap['{' . self::NS_CALDAV . '}supported-calendar-component-set'] = 'Sabre_CalDAV_Property_SupportedCalendarComponentSet'; $server->resourceTypeMapping['Sabre_CalDAV_ICalendar'] = '{urn:ietf:params:xml:ns:caldav}calendar'; + $server->resourceTypeMapping['Sabre_CalDAV_Schedule_IOutbox'] = '{urn:ietf:params:xml:ns:caldav}schedule-outbox'; $server->resourceTypeMapping['Sabre_CalDAV_Principal_ProxyRead'] = '{http://calendarserver.org/ns/}calendar-proxy-read'; $server->resourceTypeMapping['Sabre_CalDAV_Principal_ProxyWrite'] = '{http://calendarserver.org/ns/}calendar-proxy-write'; @@ -161,7 +197,10 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin { '{' . self::NS_CALDAV . '}calendar-data', // scheduling extension + '{' . self::NS_CALDAV . '}schedule-inbox-URL', + '{' . self::NS_CALDAV . '}schedule-outbox-URL', '{' . self::NS_CALDAV . '}calendar-user-address-set', + '{' . self::NS_CALDAV . '}calendar-user-type', // CalendarServer extensions '{' . self::NS_CALENDARSERVER . '}getctag', @@ -173,36 +212,55 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin { /** * This function handles support for the MKCALENDAR method - * - * @param string $method - * @return bool + * + * @param string $method + * @param string $uri + * @return bool */ public function unknownMethod($method, $uri) { - if ($method!=='MKCALENDAR') return; + switch ($method) { + case 'MKCALENDAR' : + $this->httpMkCalendar($uri); + // false is returned to stop the propagation of the + // unknownMethod event. + return false; + case 'POST' : + // Checking if we're talking to an outbox + try { + $node = $this->server->tree->getNodeForPath($uri); + } catch (Sabre_DAV_Exception_NotFound $e) { + return; + } + if (!$node instanceof Sabre_CalDAV_Schedule_IOutbox) + return; - $this->httpMkCalendar($uri); - // false is returned to stop the unknownMethod event - return false; + $this->outboxRequest($node); + return false; + + } } /** - * This functions handles REPORT requests specific to CalDAV - * - * @param string $reportName - * @param DOMNode $dom - * @return bool + * This functions handles REPORT requests specific to CalDAV + * + * @param string $reportName + * @param DOMNode $dom + * @return bool */ public function report($reportName,$dom) { - switch($reportName) { + switch($reportName) { case '{'.self::NS_CALDAV.'}calendar-multiget' : $this->calendarMultiGetReport($dom); return false; case '{'.self::NS_CALDAV.'}calendar-query' : $this->calendarQueryReport($dom); return false; + case '{'.self::NS_CALDAV.'}free-busy-query' : + $this->freeBusyQueryReport($dom); + return false; } @@ -212,9 +270,9 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin { /** * This function handles the MKCALENDAR HTTP method, which creates * a new calendar. - * + * * @param string $uri - * @return void + * @return void */ public function httpMkCalendar($uri) { @@ -238,7 +296,7 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin { foreach(Sabre_DAV_XMLUtil::parseProperties($child,$this->server->propertyMap) as $k=>$prop) { $properties[$k] = $prop; } - + } } @@ -254,9 +312,9 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin { * beforeGetProperties * * This method handler is invoked before any after properties for a - * resource are fetched. This allows us to add in any CalDAV specific - * properties. - * + * resource are fetched. This allows us to add in any CalDAV specific + * properties. + * * @param string $path * @param Sabre_DAV_INode $node * @param array $requestedProperties @@ -270,12 +328,21 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin { // calendar-home-set property $calHome = '{' . self::NS_CALDAV . '}calendar-home-set'; if (in_array($calHome,$requestedProperties)) { - $principalId = $node->getName(); + $principalId = $node->getName(); $calendarHomePath = self::CALENDAR_ROOT . '/' . $principalId . '/'; unset($requestedProperties[$calHome]); $returnedProperties[200][$calHome] = new Sabre_DAV_Property_Href($calendarHomePath); } + // schedule-outbox-URL property + $scheduleProp = '{' . self::NS_CALDAV . '}schedule-outbox-URL'; + if (in_array($scheduleProp,$requestedProperties)) { + $principalId = $node->getName(); + $outboxPath = self::CALENDAR_ROOT . '/' . $principalId . '/outbox'; + unset($requestedProperties[$scheduleProp]); + $returnedProperties[200][$scheduleProp] = new Sabre_DAV_Property_Href($outboxPath); + } + // calendar-user-address-set property $calProp = '{' . self::NS_CALDAV . '}calendar-user-address-set'; if (in_array($calProp,$requestedProperties)) { @@ -287,7 +354,7 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin { } - // These two properties are shortcuts for ical to easily find + // These two properties are shortcuts for ical to easily find // other principals this principal has access to. $propRead = '{' . self::NS_CALENDARSERVER . '}calendar-proxy-read-for'; $propWrite = '{' . self::NS_CALENDARSERVER . '}calendar-proxy-write-for'; @@ -301,8 +368,8 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin { $groupNode = $this->server->tree->getNodeForPath($group); - // If the node is either ap proxy-read or proxy-write - // group, we grab the parent principal and add it to the + // If the node is either ap proxy-read or proxy-write + // group, we grab the parent principal and add it to the // list. if ($groupNode instanceof Sabre_CalDAV_Principal_ProxyRead) { list($readList[]) = Sabre_DAV_URLUtil::splitPath($group); @@ -327,8 +394,8 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin { if ($node instanceof Sabre_CalDAV_ICalendarObject) { - // The calendar-data property is not supposed to be a 'real' - // property, but in large chunks of the spec it does act as such. + // The calendar-data property is not supposed to be a 'real' + // property, but in large chunks of the spec it does act as such. // Therefore we simply expose it as a property. $calDataProp = '{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}calendar-data'; if (in_array($calDataProp, $requestedProperties)) { @@ -350,18 +417,52 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin { * * This report is used by the client to fetch the content of a series * of urls. Effectively avoiding a lot of redundant requests. - * - * @param DOMNode $dom + * + * @param DOMNode $dom * @return void */ public function calendarMultiGetReport($dom) { $properties = array_keys(Sabre_DAV_XMLUtil::parseProperties($dom->firstChild)); - $hrefElems = $dom->getElementsByTagNameNS('urn:DAV','href'); + + $xpath = new DOMXPath($dom); + $xpath->registerNameSpace('cal',Sabre_CalDAV_Plugin::NS_CALDAV); + $xpath->registerNameSpace('dav','urn:DAV'); + + $expand = $xpath->query('/cal:calendar-multiget/dav:prop/cal:calendar-data/cal:expand'); + if ($expand->length>0) { + $expandElem = $expand->item(0); + $start = $expandElem->getAttribute('start'); + $end = $expandElem->getAttribute('end'); + if(!$start || !$end) { + throw new Sabre_DAV_Exception_BadRequest('The "start" and "end" attributes are required for the CALDAV:expand element'); + } + $start = Sabre_VObject_DateTimeParser::parseDateTime($start); + $end = Sabre_VObject_DateTimeParser::parseDateTime($end); + + if ($end <= $start) { + throw new Sabre_DAV_Exception_BadRequest('The end-date must be larger than the start-date in the expand element.'); + } + + $expand = true; + + } else { + + $expand = false; + + } + foreach($hrefElems as $elem) { $uri = $this->server->calculateUri($elem->nodeValue); list($objProps) = $this->server->getPropertiesForPath($uri,$properties); + + if ($expand && isset($objProps[200]['{' . self::NS_CALDAV . '}calendar-data'])) { + $vObject = Sabre_VObject_Reader::read($objProps[200]['{' . self::NS_CALDAV . '}calendar-data']); + $vObject->expand($start, $end); + $objProps[200]['{' . self::NS_CALDAV . '}calendar-data'] = $vObject->serialize(); + } + $propertyList[]=$objProps; } @@ -377,48 +478,57 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin { * * This report is used by clients to request calendar objects based on * complex conditions. - * - * @param DOMNode $dom + * + * @param DOMNode $dom * @return void */ public function calendarQueryReport($dom) { - $requestedProperties = array_keys(Sabre_DAV_XMLUtil::parseProperties($dom->firstChild)); - - $filterNode = $dom->getElementsByTagNameNS('urn:ietf:params:xml:ns:caldav','filter'); - if ($filterNode->length!==1) { - throw new Sabre_DAV_Exception_BadRequest('The calendar-query report must have a filter element'); - } - $filters = Sabre_CalDAV_XMLUtil::parseCalendarQueryFilters($filterNode->item(0)); + $parser = new Sabre_CalDAV_CalendarQueryParser($dom); + $parser->parse(); $requestedCalendarData = true; + $requestedProperties = $parser->requestedProperties; if (!in_array('{urn:ietf:params:xml:ns:caldav}calendar-data', $requestedProperties)) { + // We always retrieve calendar-data, as we need it for filtering. $requestedProperties[] = '{urn:ietf:params:xml:ns:caldav}calendar-data'; - // If calendar-data wasn't explicitly requested, we need to remove + // If calendar-data wasn't explicitly requested, we need to remove // it after processing. $requestedCalendarData = false; } // These are the list of nodes that potentially match the requirement - $candidateNodes = $this->server->getPropertiesForPath($this->server->getRequestUri(),$requestedProperties,$this->server->getHTTPDepth(0)); + $candidateNodes = $this->server->getPropertiesForPath( + $this->server->getRequestUri(), + $requestedProperties, + $this->server->getHTTPDepth(0) + ); $verifiedNodes = array(); + $validator = new Sabre_CalDAV_CalendarQueryValidator(); + foreach($candidateNodes as $node) { // If the node didn't have a calendar-data property, it must not be a calendar object - if (!isset($node[200]['{urn:ietf:params:xml:ns:caldav}calendar-data'])) continue; + 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 ($this->validateFilters($node[200]['{urn:ietf:params:xml:ns:caldav}calendar-data'],$filters)) { - if (!$requestedCalendarData) { unset($node[200]['{urn:ietf:params:xml:ns:caldav}calendar-data']); } + if ($parser->expand) { + $vObject->expand($parser->expand['start'], $parser->expand['end']); + $node[200]['{' . self::NS_CALDAV . '}calendar-data'] = $vObject->serialize(); + } $verifiedNodes[] = $node; - } + } } @@ -428,360 +538,291 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin { } - /** - * Verify if a list of filters applies to the calendar data object + * This method is responsible for parsing the request and generating the + * response for the CALDAV:free-busy-query REPORT. * - * The calendarData object must be a valid iCalendar blob. The list of - * filters must be formatted as parsed by Sabre_CalDAV_Plugin::parseCalendarQueryFilters - * - * @param string $calendarData - * @param array $filters - * @return bool - */ - public function validateFilters($calendarData,$filters) { - - // We are converting the calendar object to an XML structure - // This makes it far easier to parse - $xCalendarData = Sabre_CalDAV_ICalendarUtil::toXCal($calendarData); - $xml = simplexml_load_string($xCalendarData); - $xml->registerXPathNamespace('c','urn:ietf:params:xml:ns:xcal'); - - foreach($filters as $xpath=>$filter) { - - // if-not-defined comes first - if (isset($filter['is-not-defined'])) { - if (!$xml->xpath($xpath)) - continue; - else - return false; - - } - - $elem = $xml->xpath($xpath); - - if (!$elem) return false; - $elem = $elem[0]; - - if (isset($filter['time-range'])) { - - switch($elem->getName()) { - case 'vevent' : - $result = $this->validateTimeRangeFilterForEvent($xml,$xpath,$filter); - if ($result===false) return false; - break; - case 'vtodo' : - $result = $this->validateTimeRangeFilterForTodo($xml,$xpath,$filter); - if ($result===false) return false; - break; - case 'vjournal' : - case 'vfreebusy' : - case 'valarm' : - // TODO: not implemented - break; - - /* - - case 'vjournal' : - $result = $this->validateTimeRangeFilterForJournal($xml,$xpath,$filter); - if ($result===false) return false; - break; - case 'vfreebusy' : - $result = $this->validateTimeRangeFilterForFreeBusy($xml,$xpath,$filter); - if ($result===false) return false; - break; - case 'valarm' : - $result = $this->validateTimeRangeFilterForAlarm($xml,$xpath,$filter); - if ($result===false) return false; - break; - - */ - - } - - } - - if (isset($filter['text-match'])) { - $currentString = (string)$elem; - - $isMatching = Sabre_DAV_StringUtil::textMatch($currentString, $filter['text-match']['value'], $filter['text-match']['collation']); - if ($filter['text-match']['negate-condition'] && $isMatching) return false; - if (!$filter['text-match']['negate-condition'] && !$isMatching) return false; - - } - - } - return true; - - } - - /** - * Checks whether a time-range filter matches an event. - * - * @param SimpleXMLElement $xml Event as xml object - * @param string $currentXPath XPath to check - * @param array $currentFilter Filter information + * @param DOMNode $dom * @return void */ - private function validateTimeRangeFilterForEvent(SimpleXMLElement $xml,$currentXPath,array $currentFilter) { + protected function freeBusyQueryReport(DOMNode $dom) { - // Grabbing the DTSTART property - $xdtstart = $xml->xpath($currentXPath.'/c:dtstart'); - if (!count($xdtstart)) { - throw new Sabre_DAV_Exception_BadRequest('DTSTART property missing from calendar object'); - } + $start = null; + $end = null; - // The dtstart can be both a date, or datetime property - if ((string)$xdtstart[0]['value']==='DATE' || strlen((string)$xdtstart[0])===8) { - $isDateTime = false; - } else { - $isDateTime = true; - } + foreach($dom->firstChild->childNodes as $childNode) { - // Determining the timezone - if ($tzid = (string)$xdtstart[0]['tzid']) { - $tz = new DateTimeZone($tzid); - } else { - $tz = null; - } - if ($isDateTime) { - $dtstart = Sabre_CalDAV_XMLUtil::parseICalendarDateTime((string)$xdtstart[0],$tz); - } else { - $dtstart = Sabre_CalDAV_XMLUtil::parseICalendarDate((string)$xdtstart[0]); - } - - - // Grabbing the DTEND property - $xdtend = $xml->xpath($currentXPath.'/c:dtend'); - $dtend = null; - - if (count($xdtend)) { - // Determining the timezone - if ($tzid = (string)$xdtend[0]['tzid']) { - $tz = new DateTimeZone($tzid); - } else { - $tz = null; + $clark = Sabre_DAV_XMLUtil::toClarkNotation($childNode); + if ($clark == '{' . self::NS_CALDAV . '}time-range') { + $start = $childNode->getAttribute('start'); + $end = $childNode->getAttribute('end'); + break; } - // Since the VALUE prameter of both DTSTART and DTEND must be the same - // we can assume we don't need to check the VALUE paramter of DTEND. - if ($isDateTime) { - $dtend = Sabre_CalDAV_XMLUtil::parseICalendarDateTime((string)$xdtend[0],$tz); - } else { - $dtend = Sabre_CalDAV_XMLUtil::parseICalendarDate((string)$xdtend[0],$tz); - } - - } - - if (is_null($dtend)) { - // The DTEND property was not found. We will first see if the event has a duration - // property - - $xduration = $xml->xpath($currentXPath.'/c:duration'); - if (count($xduration)) { - $duration = Sabre_CalDAV_XMLUtil::parseICalendarDuration((string)$xduration[0]); - - // Making sure that the duration is bigger than 0 seconds. - $tempDT = clone $dtstart; - $tempDT->modify($duration); - if ($tempDT > $dtstart) { - - // use DTEND = DTSTART + DURATION - $dtend = $tempDT; - } else { - // use DTEND = DTSTART - $dtend = $dtstart; - } - - } + } + if ($start) { + $start = Sabre_VObject_DateTimeParser::parseDateTime($start); + } + if ($end) { + $end = Sabre_VObject_DateTimeParser::parseDateTime($end); } - if (is_null($dtend)) { - if ($isDateTime) { - // DTEND = DTSTART - $dtend = $dtstart; - } else { - // DTEND = DTSTART + 1 DAY - $dtend = clone $dtstart; - $dtend->modify('+1 day'); - } + if (!$start && !$end) { + throw new Sabre_DAV_Exception_BadRequest('The freebusy report must have a time-range filter'); } - // TODO: we need to properly parse RRULE's, but it's very difficult. - // For now, we're always returning events if they have an RRULE at all. - $rrule = $xml->xpath($currentXPath.'/c:rrule'); - $hasRrule = (count($rrule))>0; - - if (!is_null($currentFilter['time-range']['start']) && $currentFilter['time-range']['start'] >= $dtend) return false; - if (!is_null($currentFilter['time-range']['end']) && $currentFilter['time-range']['end'] <= $dtstart && !$hasRrule) return false; - return true; - + $acl = $this->server->getPlugin('acl'); + + if (!$acl) { + throw new Sabre_DAV_Exception('The ACL plugin must be loaded for free-busy queries to work'); + } + $uri = $this->server->getRequestUri(); + $acl->checkPrivileges($uri,'{' . self::NS_CALDAV . '}read-free-busy'); + + $calendar = $this->server->tree->getNodeForPath($uri); + if (!$calendar instanceof Sabre_CalDAV_ICalendar) { + throw new Sabre_DAV_Exception_NotImplemented('The free-busy-query REPORT is only implemented on calendars'); + } + + $objects = array_map(function($child) { + $obj = $child->get(); + if (is_resource($obj)) { + $obj = stream_get_contents($obj); + } + return $obj; + }, $calendar->getChildren()); + + $generator = new Sabre_VObject_FreeBusyGenerator(); + $generator->setObjects($objects); + $generator->setTimeRange($start, $end); + $result = $generator->getResult(); + $result = $result->serialize(); + + $this->server->httpResponse->sendStatus(200); + $this->server->httpResponse->setHeader('Content-Type', 'text/calendar'); + $this->server->httpResponse->setHeader('Content-Length', strlen($result)); + $this->server->httpResponse->sendBody($result); + } - private function validateTimeRangeFilterForTodo(SimpleXMLElement $xml,$currentXPath,array $filter) { + /** + * This method is triggered before a file gets updated with new content. + * + * This plugin uses this method to ensure that CalDAV objects receive + * valid calendar data. + * + * @param string $path + * @param Sabre_DAV_IFile $node + * @param resource $data + * @return void + */ + public function beforeWriteContent($path, Sabre_DAV_IFile $node, &$data) { - // Gathering all relevant elements + if (!$node instanceof Sabre_CalDAV_ICalendarObject) + return; - $dtStart = null; - $duration = null; - $due = null; - $completed = null; - $created = null; + $this->validateICalendar($data); - $xdt = $xml->xpath($currentXPath.'/c:dtstart'); - if (count($xdt)) { - // The dtstart can be both a date, or datetime property - if ((string)$xdt[0]['value']==='DATE') { - $isDateTime = false; - } else { - $isDateTime = true; - } + } - // Determining the timezone - if ($tzid = (string)$xdt[0]['tzid']) { - $tz = new DateTimeZone($tzid); - } else { - $tz = null; - } - if ($isDateTime) { - $dtStart = Sabre_CalDAV_XMLUtil::parseICalendarDateTime((string)$xdt[0],$tz); - } else { - $dtStart = Sabre_CalDAV_XMLUtil::parseICalendarDate((string)$xdt[0]); - } + /** + * This method is triggered before a new file is created. + * + * This plugin uses this method to ensure that newly created calendar + * objects contain valid calendar data. + * + * @param string $path + * @param resource $data + * @param Sabre_DAV_ICollection $parentNode + * @return void + */ + public function beforeCreateFile($path, &$data, Sabre_DAV_ICollection $parentNode) { + + if (!$parentNode instanceof Sabre_CalDAV_Calendar) + return; + + $this->validateICalendar($data); + + } + + /** + * Checks if the submitted iCalendar data is in fact, valid. + * + * An exception is thrown if it's not. + * + * @param resource|string $data + * @return void + */ + protected function validateICalendar(&$data) { + + // If it's a stream, we convert it to a string first. + if (is_resource($data)) { + $data = stream_get_contents($data); } - // Only need to grab duration if dtStart is set - if (!is_null($dtStart)) { + // Converting the data to unicode, if needed. + $data = Sabre_DAV_StringUtil::ensureUTF8($data); - $xduration = $xml->xpath($currentXPath.'/c:duration'); - if (count($xduration)) { - $duration = Sabre_CalDAV_XMLUtil::parseICalendarDuration((string)$xduration[0]); - } + try { + + $vobj = Sabre_VObject_Reader::read($data); + + } catch (Sabre_VObject_ParseException $e) { + + throw new Sabre_DAV_Exception_UnsupportedMediaType('This resource only supports valid iCalendar 2.0 data. Parse error: ' . $e->getMessage()); } - if (!is_null($dtStart) && !is_null($duration)) { + } - // Comparision from RFC 4791: - // (start <= DTSTART+DURATION) AND ((end > DTSTART) OR (end >= DTSTART+DURATION)) + /** + * This method handles POST requests to the schedule-outbox + * + * @param Sabre_CalDAV_Schedule_IOutbox $outboxNode + * @return void + */ + public function outboxRequest(Sabre_CalDAV_Schedule_IOutbox $outboxNode) { - $end = clone $dtStart; - $end->modify($duration); + $originator = $this->server->httpRequest->getHeader('Originator'); + $recipients = $this->server->httpRequest->getHeader('Recipient'); - if( (is_null($filter['time-range']['start']) || $filter['time-range']['start'] <= $end) && - (is_null($filter['time-range']['end']) || $filter['time-range']['end'] > $dtStart || $filter['time-range']['end'] >= $end) ) { - return true; - } else { - return false; + if (!$originator) { + throw new Sabre_DAV_Exception_BadRequest('The Originator: header must be specified when making POST requests'); + } + if (!$recipients) { + throw new Sabre_DAV_Exception_BadRequest('The Recipient: header must be specified when making POST requests'); + } + + if (!preg_match('/^mailto:(.*)@(.*)$/', $originator)) { + throw new Sabre_DAV_Exception_BadRequest('Originator must start with mailto: and must be valid email address'); + } + $originator = substr($originator,7); + + $recipients = explode(',',$recipients); + foreach($recipients as $k=>$recipient) { + + $recipient = trim($recipient); + if (!preg_match('/^mailto:(.*)@(.*)$/', $recipient)) { + throw new Sabre_DAV_Exception_BadRequest('Recipients must start with mailto: and must be valid email address'); } - + $recipient = substr($recipient, 7); + $recipients[$k] = $recipient; } - // Need to grab the DUE property - $xdt = $xml->xpath($currentXPath.'/c:due'); - if (count($xdt)) { - // The due property can be both a date, or datetime property - if ((string)$xdt[0]['value']==='DATE') { - $isDateTime = false; - } else { - $isDateTime = true; - } - // Determining the timezone - if ($tzid = (string)$xdt[0]['tzid']) { - $tz = new DateTimeZone($tzid); - } else { - $tz = null; - } - if ($isDateTime) { - $due = Sabre_CalDAV_XMLUtil::parseICalendarDateTime((string)$xdt[0],$tz); - } else { - $due = Sabre_CalDAV_XMLUtil::parseICalendarDate((string)$xdt[0]); - } + // We need to make sure that 'originator' matches one of the email + // addresses of the selected principal. + $principal = $outboxNode->getOwner(); + $props = $this->server->getProperties($principal,array( + '{' . self::NS_CALDAV . '}calendar-user-address-set', + )); + + $addresses = array(); + if (isset($props['{' . self::NS_CALDAV . '}calendar-user-address-set'])) { + $addresses = $props['{' . self::NS_CALDAV . '}calendar-user-address-set']->getHrefs(); } - if (!is_null($dtStart) && !is_null($due)) { - - // Comparision from RFC 4791: - // ((start < DUE) OR (start <= DTSTART)) AND ((end > DTSTART) OR (end >= DUE)) - - if( (is_null($filter['time-range']['start']) || $filter['time-range']['start'] < $due || $filter['time-range']['start'] < $dtstart) && - (is_null($filter['time-range']['end']) || $filter['time-range']['end'] >= $due) ) { - return true; - } else { - return false; - } - + if (!in_array('mailto:' . $originator, $addresses)) { + throw new Sabre_DAV_Exception_Forbidden('The addresses specified in the Originator header did not match any addresses in the owners calendar-user-address-set header'); } - if (!is_null($dtStart)) { - - // Comparision from RFC 4791 - // (start <= DTSTART) AND (end > DTSTART) - if ( (is_null($filter['time-range']['start']) || $filter['time-range']['start'] <= $dtStart) && - (is_null($filter['time-range']['end']) || $filter['time-range']['end'] > $dtStart) ) { - return true; - } else { - return false; - } - + 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()); } - if (!is_null($due)) { - - // Comparison from RFC 4791 - // (start < DUE) AND (end >= DUE) - if ( (is_null($filter['time-range']['start']) || $filter['time-range']['start'] < $due) && - (is_null($filter['time-range']['end']) || $filter['time-range']['end'] >= $due) ) { - return true; - } else { - return false; - } - - } - // Need to grab the COMPLETED property - $xdt = $xml->xpath($currentXPath.'/c:completed'); - if (count($xdt)) { - $completed = Sabre_CalDAV_XMLUtil::parseICalendarDateTime((string)$xdt[0]); - } - // Need to grab the CREATED property - $xdt = $xml->xpath($currentXPath.'/c:created'); - if (count($xdt)) { - $created = Sabre_CalDAV_XMLUtil::parseICalendarDateTime((string)$xdt[0]); - } - - if (!is_null($completed) && !is_null($created)) { - // Comparison from RFC 4791 - // ((start <= CREATED) OR (start <= COMPLETED)) AND ((end >= CREATED) OR (end >= COMPLETED)) - if( (is_null($filter['time-range']['start']) || $filter['time-range']['start'] <= $created || $filter['time-range']['start'] <= $completed) && - (is_null($filter['time-range']['end']) || $filter['time-range']['end'] >= $created || $filter['time-range']['end'] >= $completed)) { - return true; - } else { - return false; + // Checking for the object type + $componentType = null; + foreach($vObject->getComponents() as $component) { + if ($component->name !== 'VTIMEZONE') { + $componentType = $component->name; + break; } } - - if (!is_null($completed)) { - // Comparison from RFC 4791 - // (start <= COMPLETED) AND (end >= COMPLETED) - if( (is_null($filter['time-range']['start']) || $filter['time-range']['start'] <= $completed) && - (is_null($filter['time-range']['end']) || $filter['time-range']['end'] >= $completed)) { - return true; - } else { - return false; - } + if (is_null($componentType)) { + throw new Sabre_DAV_Exception_BadRequest('We expected at least one VTODO, VJOURNAL, VFREEBUSY or VEVENT component'); } - if (!is_null($created)) { - // Comparison from RFC 4791 - // (end > CREATED) - if( (is_null($filter['time-range']['end']) || $filter['time-range']['end'] > $created) ) { - return true; - } else { - return false; - } + // 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'); } - // Everything else is TRUE - return true; + if (in_array($method, array('REQUEST','REPLY','ADD','CANCEL')) && $componentType==='VEVENT') { + $this->iMIPMessage($originator, $recipients, $vObject); + $this->server->httpResponse->sendStatus(200); + $this->server->httpResponse->sendBody('Messages sent'); + } else { + throw new Sabre_DAV_Exception_NotImplemented('This iTIP method is currently not implemented'); + } + + } + + /** + * Sends an iMIP message by email. + * + * @param string $originator + * @param array $recipients + * @param Sabre_VObject_Component $vObject + * @return void + */ + protected function iMIPMessage($originator, array $recipients, Sabre_VObject_Component $vObject) { + + if (!$this->imipHandler) { + throw new Sabre_DAV_Exception_NotImplemented('No iMIP handler is setup on this server.'); + } + $this->imipHandler->sendMessage($originator, $recipients, $vObject); + + } + + /** + * This method is used to generate HTML output for the + * Sabre_DAV_Browser_Plugin. This allows us to generate an interface users + * can use to create new calendars. + * + * @param Sabre_DAV_INode $node + * @param string $output + * @return bool + */ + public function htmlActionsPanel(Sabre_DAV_INode $node, &$output) { + + if (!$node instanceof Sabre_CalDAV_UserCalendars) + return; + + $output.= '
+

Create new calendar

+ +
+
+ +
+ '; + + return false; + + } + + /** + * This method allows us to intercept the 'mkcalendar' sabreAction. This + * action enables the user to create new calendars from the browser plugin. + * + * @param string $uri + * @param string $action + * @param array $postVars + * @return bool + */ + public function browserPostAction($uri, $action, array $postVars) { + + if ($action!=='mkcalendar') + return; + + $resourceType = array('{DAV:}collection','{urn:ietf:params:xml:ns:caldav}calendar'); + $properties = array(); + if (isset($postVars['{DAV:}displayname'])) { + $properties['{DAV:}displayname'] = $postVars['{DAV:}displayname']; + } + $this->server->createCollection($uri . '/' . $postVars['name'],$resourceType,$properties); + return false; } diff --git a/3rdparty/Sabre/CalDAV/Principal/Collection.php b/3rdparty/Sabre/CalDAV/Principal/Collection.php index 13435b2448..abbefa5567 100644 --- a/3rdparty/Sabre/CalDAV/Principal/Collection.php +++ b/3rdparty/Sabre/CalDAV/Principal/Collection.php @@ -4,23 +4,23 @@ * Principal collection * * This is an alternative collection to the standard ACL principal collection. - * This collection adds support for the calendar-proxy-read and - * calendar-proxy-write sub-principals, as defined by the caldav-proxy + * This collection adds support for the calendar-proxy-read and + * calendar-proxy-write sub-principals, as defined by the caldav-proxy * specification. * * @package Sabre * @subpackage CalDAV - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ class Sabre_CalDAV_Principal_Collection extends Sabre_DAVACL_AbstractPrincipalCollection { /** - * Returns a child object based on principal information - * - * @param array $principalInfo - * @return Sabre_CalDAV_Principal_User + * Returns a child object based on principal information + * + * @param array $principalInfo + * @return Sabre_CalDAV_Principal_User */ public function getChildForPrincipal(array $principalInfo) { diff --git a/3rdparty/Sabre/CalDAV/Principal/ProxyRead.php b/3rdparty/Sabre/CalDAV/Principal/ProxyRead.php index f531d85d1f..4b3f035634 100644 --- a/3rdparty/Sabre/CalDAV/Principal/ProxyRead.php +++ b/3rdparty/Sabre/CalDAV/Principal/ProxyRead.php @@ -4,38 +4,38 @@ * ProxyRead principal * * This class represents a principal group, hosted under the main principal. - * This is needed to implement 'Calendar delegation' support. This class is + * This is needed to implement 'Calendar delegation' support. This class is * instantiated by Sabre_CalDAV_Principal_User. - * + * * @package Sabre * @subpackage CalDAV - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ class Sabre_CalDAV_Principal_ProxyRead implements Sabre_DAVACL_IPrincipal { /** - * Principal information from the parent principal. - * - * @var array + * Principal information from the parent principal. + * + * @var array */ protected $principalInfo; /** - * Principal backend - * - * @var Sabre_DAVACL_IPrincipalBackend + * Principal backend + * + * @var Sabre_DAVACL_IPrincipalBackend */ protected $principalBackend; /** * Creates the object. * - * Note that you MUST supply the parent principal information. - * - * @param Sabre_DAVACL_IPrincipalBackend $principalBackend - * @param array $principalInfo + * Note that you MUST supply the parent principal information. + * + * @param Sabre_DAVACL_IPrincipalBackend $principalBackend + * @param array $principalInfo */ public function __construct(Sabre_DAVACL_IPrincipalBackend $principalBackend, array $principalInfo) { @@ -46,8 +46,8 @@ class Sabre_CalDAV_Principal_ProxyRead implements Sabre_DAVACL_IPrincipal { /** * Returns this principals name. - * - * @return string + * + * @return string */ public function getName() { @@ -56,13 +56,13 @@ class Sabre_CalDAV_Principal_ProxyRead implements Sabre_DAVACL_IPrincipal { } /** - * Returns the last modification time + * Returns the last modification time * - * @return null + * @return null */ public function getLastModified() { - return null; + return null; } @@ -70,7 +70,7 @@ class Sabre_CalDAV_Principal_ProxyRead implements Sabre_DAVACL_IPrincipal { * Deletes the current node * * @throws Sabre_DAV_Exception_Forbidden - * @return void + * @return void */ public function delete() { @@ -80,7 +80,7 @@ class Sabre_CalDAV_Principal_ProxyRead implements Sabre_DAVACL_IPrincipal { /** * Renames the node - * + * * @throws Sabre_DAV_Exception_Forbidden * @param string $name The new name * @return void @@ -93,11 +93,11 @@ class Sabre_CalDAV_Principal_ProxyRead implements Sabre_DAVACL_IPrincipal { /** - * Returns a list of altenative urls for a principal - * + * Returns a list of alternative urls for a principal + * * This can for example be an email address, or ldap url. - * - * @return array + * + * @return array */ public function getAlternateUriSet() { @@ -106,41 +106,41 @@ class Sabre_CalDAV_Principal_ProxyRead implements Sabre_DAVACL_IPrincipal { } /** - * Returns the full principal url - * - * @return string + * Returns the full principal url + * + * @return string */ public function getPrincipalUrl() { - return $this->principalInfo['uri'] . '/' . $this->getName(); + return $this->principalInfo['uri'] . '/' . $this->getName(); } /** * Returns the list of group members - * + * * If this principal is a group, this function should return - * all member principal uri's for the group. - * + * all member principal uri's for the group. + * * @return array */ public function getGroupMemberSet() { - return $this->principalBackend->getGroupMemberSet($this->getPrincipalUrl()); + return $this->principalBackend->getGroupMemberSet($this->getPrincipalUrl()); } /** * Returns the list of groups this principal is member of - * + * * If this principal is a member of a (list of) groups, this function - * should return a list of principal uri's for it's members. - * - * @return array + * should return a list of principal uri's for it's members. + * + * @return array */ public function getGroupMembership() { - return $this->principalBackend->getGroupMembership($this->getPrincipalUrl()); + return $this->principalBackend->getGroupMembership($this->getPrincipalUrl()); } @@ -149,11 +149,11 @@ class Sabre_CalDAV_Principal_ProxyRead implements Sabre_DAVACL_IPrincipal { * * If this principal is a group, this method sets all the group members. * The list of members is always overwritten, never appended to. - * - * This method should throw an exception if the members could not be set. - * - * @param array $principals - * @return void + * + * This method should throw an exception if the members could not be set. + * + * @param array $principals + * @return void */ public function setGroupMemberSet(array $principals) { @@ -165,9 +165,9 @@ class Sabre_CalDAV_Principal_ProxyRead implements Sabre_DAVACL_IPrincipal { * Returns the displayname * * This should be a human readable name for the principal. - * If none is available, return the nodename. - * - * @return string + * If none is available, return the nodename. + * + * @return string */ public function getDisplayName() { diff --git a/3rdparty/Sabre/CalDAV/Principal/ProxyWrite.php b/3rdparty/Sabre/CalDAV/Principal/ProxyWrite.php index 4d8face206..dd0c2e86ed 100644 --- a/3rdparty/Sabre/CalDAV/Principal/ProxyWrite.php +++ b/3rdparty/Sabre/CalDAV/Principal/ProxyWrite.php @@ -4,38 +4,38 @@ * ProxyWrite principal * * This class represents a principal group, hosted under the main principal. - * This is needed to implement 'Calendar delegation' support. This class is + * This is needed to implement 'Calendar delegation' support. This class is * instantiated by Sabre_CalDAV_Principal_User. - * + * * @package Sabre * @subpackage CalDAV - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ class Sabre_CalDAV_Principal_ProxyWrite implements Sabre_DAVACL_IPrincipal { /** - * Parent principal information - * - * @var array + * Parent principal information + * + * @var array */ protected $principalInfo; /** - * Principal Backend - * - * @var Sabre_DAVACL_IPrincipalBackend + * Principal Backend + * + * @var Sabre_DAVACL_IPrincipalBackend */ protected $principalBackend; /** * Creates the object * - * Note that you MUST supply the parent principal information. - * - * @param Sabre_DAVACL_IPrincipalBackend $principalBackend - * @param array $principalInfo + * Note that you MUST supply the parent principal information. + * + * @param Sabre_DAVACL_IPrincipalBackend $principalBackend + * @param array $principalInfo */ public function __construct(Sabre_DAVACL_IPrincipalBackend $principalBackend, array $principalInfo) { @@ -46,8 +46,8 @@ class Sabre_CalDAV_Principal_ProxyWrite implements Sabre_DAVACL_IPrincipal { /** * Returns this principals name. - * - * @return string + * + * @return string */ public function getName() { @@ -56,13 +56,13 @@ class Sabre_CalDAV_Principal_ProxyWrite implements Sabre_DAVACL_IPrincipal { } /** - * Returns the last modification time + * Returns the last modification time * - * @return null + * @return null */ public function getLastModified() { - return null; + return null; } @@ -70,7 +70,7 @@ class Sabre_CalDAV_Principal_ProxyWrite implements Sabre_DAVACL_IPrincipal { * Deletes the current node * * @throws Sabre_DAV_Exception_Forbidden - * @return void + * @return void */ public function delete() { @@ -80,7 +80,7 @@ class Sabre_CalDAV_Principal_ProxyWrite implements Sabre_DAVACL_IPrincipal { /** * Renames the node - * + * * @throws Sabre_DAV_Exception_Forbidden * @param string $name The new name * @return void @@ -93,11 +93,11 @@ class Sabre_CalDAV_Principal_ProxyWrite implements Sabre_DAVACL_IPrincipal { /** - * Returns a list of altenative urls for a principal - * + * Returns a list of alternative urls for a principal + * * This can for example be an email address, or ldap url. - * - * @return array + * + * @return array */ public function getAlternateUriSet() { @@ -106,41 +106,41 @@ class Sabre_CalDAV_Principal_ProxyWrite implements Sabre_DAVACL_IPrincipal { } /** - * Returns the full principal url - * - * @return string + * Returns the full principal url + * + * @return string */ public function getPrincipalUrl() { - return $this->principalInfo['uri'] . '/' . $this->getName(); + return $this->principalInfo['uri'] . '/' . $this->getName(); } /** * Returns the list of group members - * + * * If this principal is a group, this function should return - * all member principal uri's for the group. - * + * all member principal uri's for the group. + * * @return array */ public function getGroupMemberSet() { - return $this->principalBackend->getGroupMemberSet($this->getPrincipalUrl()); + return $this->principalBackend->getGroupMemberSet($this->getPrincipalUrl()); } /** * Returns the list of groups this principal is member of - * + * * If this principal is a member of a (list of) groups, this function - * should return a list of principal uri's for it's members. - * - * @return array + * should return a list of principal uri's for it's members. + * + * @return array */ public function getGroupMembership() { - return $this->principalBackend->getGroupMembership($this->getPrincipalUrl()); + return $this->principalBackend->getGroupMembership($this->getPrincipalUrl()); } @@ -149,11 +149,11 @@ class Sabre_CalDAV_Principal_ProxyWrite implements Sabre_DAVACL_IPrincipal { * * If this principal is a group, this method sets all the group members. * The list of members is always overwritten, never appended to. - * - * This method should throw an exception if the members could not be set. - * - * @param array $principals - * @return void + * + * This method should throw an exception if the members could not be set. + * + * @param array $principals + * @return void */ public function setGroupMemberSet(array $principals) { @@ -165,9 +165,9 @@ class Sabre_CalDAV_Principal_ProxyWrite implements Sabre_DAVACL_IPrincipal { * Returns the displayname * * This should be a human readable name for the principal. - * If none is available, return the nodename. - * - * @return string + * If none is available, return the nodename. + * + * @return string */ public function getDisplayName() { diff --git a/3rdparty/Sabre/CalDAV/Principal/User.php b/3rdparty/Sabre/CalDAV/Principal/User.php index 034629b89b..8453b877a7 100644 --- a/3rdparty/Sabre/CalDAV/Principal/User.php +++ b/3rdparty/Sabre/CalDAV/Principal/User.php @@ -1,25 +1,25 @@ principalBackend->getPrincipalByPath($this->getPrincipalURL() . '/' . $name); + if (!$principal) { + throw new Sabre_DAV_Exception_NotFound('Node with name ' . $name . ' was not found'); + } if ($name === 'calendar-proxy-read') return new Sabre_CalDAV_Principal_ProxyRead($this->principalBackend, $this->principalProperties); if ($name === 'calendar-proxy-write') return new Sabre_CalDAV_Principal_ProxyWrite($this->principalBackend, $this->principalProperties); - throw new Sabre_DAV_Exception_FileNotFound('Node with name ' . $name . ' was not found'); + throw new Sabre_DAV_Exception_NotFound('Node with name ' . $name . ' was not found'); } /** - * Returns an array with all the child nodes - * - * @return Sabre_DAV_INode[] + * Returns an array with all the child nodes + * + * @return Sabre_DAV_INode[] */ public function getChildren() { - return array( - new Sabre_CalDAV_Principal_ProxyRead($this->principalBackend, $this->principalProperties), - new Sabre_CalDAV_Principal_ProxyWrite($this->principalBackend, $this->principalProperties), - ); + $r = array(); + if ($this->principalBackend->getPrincipalByPath($this->getPrincipalURL() . '/calendar-proxy-read')) { + $r[] = new Sabre_CalDAV_Principal_ProxyRead($this->principalBackend, $this->principalProperties); + } + if ($this->principalBackend->getPrincipalByPath($this->getPrincipalURL() . '/calendar-proxy-write')) { + $r[] = new Sabre_CalDAV_Principal_ProxyWrite($this->principalBackend, $this->principalProperties); + } + + return $r; } /** - * Checks if a child-node with the specified name exists - * - * @return bool + * Returns whether or not the child node exists + * + * @param string $name + * @return bool */ public function childExists($name) { - return $name === 'calendar-proxy-read' || $name === 'calendar-proxy-write'; + try { + $this->getChild($name); + return true; + } catch (Sabre_DAV_Exception_NotFound $e) { + return false; + } } @@ -89,33 +104,28 @@ class Sabre_CalDAV_Principal_User extends Sabre_DAVACL_Principal implements Sabr * 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 + * * '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 + * * 'protected' (optional), indicating that this ACE is not allowed to + * be updated. + * + * @return array */ public function getACL() { - return array( - array( - 'privilege' => '{DAV:}read', - 'principal' => $this->principalProperties['uri'], - 'protected' => true, - ), - array( - 'privilege' => '{DAV:}read', - 'principal' => $this->principalProperties['uri'] . '/calendar-proxy-read', - 'protected' => true, - ), - array( - 'privilege' => '{DAV:}read', - 'principal' => $this->principalProperties['uri'] . '/calendar-proxy-write', - 'protected' => true, - ), + $acl = parent::getACL(); + $acl[] = array( + 'privilege' => '{DAV:}read', + 'principal' => $this->principalProperties['uri'] . '/calendar-proxy-read', + 'protected' => true, ); + $acl[] = array( + 'privilege' => '{DAV:}read', + 'principal' => $this->principalProperties['uri'] . '/calendar-proxy-write', + 'protected' => true, + ); + return $acl; } diff --git a/3rdparty/Sabre/CalDAV/Property/SupportedCalendarComponentSet.php b/3rdparty/Sabre/CalDAV/Property/SupportedCalendarComponentSet.php index 1bbaca6b8a..2ea078d7da 100644 --- a/3rdparty/Sabre/CalDAV/Property/SupportedCalendarComponentSet.php +++ b/3rdparty/Sabre/CalDAV/Property/SupportedCalendarComponentSet.php @@ -3,40 +3,40 @@ /** * Supported component set property * - * This property is a representation of the supported-calendar_component-set + * This property is a representation of the supported-calendar_component-set * property in the CalDAV namespace. It simply requires an array of components, * such as VEVENT, VTODO * * @package Sabre * @subpackage CalDAV - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ class Sabre_CalDAV_Property_SupportedCalendarComponentSet extends Sabre_DAV_Property { /** - * List of supported components, such as "VEVENT, VTODO" - * - * @var array + * List of supported components, such as "VEVENT, VTODO" + * + * @var array */ private $components; /** - * Creates the property - * - * @param array $components + * Creates the property + * + * @param array $components */ public function __construct(array $components) { - $this->components = $components; + $this->components = $components; } /** - * Returns the list of supported components - * - * @return array + * Returns the list of supported components + * + * @return array */ public function getValue() { @@ -45,10 +45,10 @@ class Sabre_CalDAV_Property_SupportedCalendarComponentSet extends Sabre_DAV_Prop } /** - * Serializes the property in a DOMDocument - * - * @param Sabre_DAV_Server $server - * @param DOMElement $node + * 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) { @@ -58,7 +58,7 @@ class Sabre_CalDAV_Property_SupportedCalendarComponentSet extends Sabre_DAV_Prop $xcomp = $doc->createElement('cal:comp'); $xcomp->setAttribute('name',$component); - $node->appendChild($xcomp); + $node->appendChild($xcomp); } @@ -66,9 +66,9 @@ class Sabre_CalDAV_Property_SupportedCalendarComponentSet extends Sabre_DAV_Prop /** * Unserializes the DOMElement back into a Property class. - * - * @param DOMElement $node - * @return void + * + * @param DOMElement $node + * @return Sabre_CalDAV_Property_SupportedCalendarComponentSet */ static function unserialize(DOMElement $node) { diff --git a/3rdparty/Sabre/CalDAV/Property/SupportedCalendarData.php b/3rdparty/Sabre/CalDAV/Property/SupportedCalendarData.php index 5010ee6d52..1d848dd5cf 100644 --- a/3rdparty/Sabre/CalDAV/Property/SupportedCalendarData.php +++ b/3rdparty/Sabre/CalDAV/Property/SupportedCalendarData.php @@ -9,17 +9,17 @@ * * @package Sabre * @subpackage CalDAV - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ class Sabre_CalDAV_Property_SupportedCalendarData extends Sabre_DAV_Property { /** - * Serializes the property in a DOMDocument - * - * @param Sabre_DAV_Server $server - * @param DOMElement $node + * 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) { @@ -32,7 +32,7 @@ class Sabre_CalDAV_Property_SupportedCalendarData extends Sabre_DAV_Property { $caldata->setAttribute('content-type','text/calendar'); $caldata->setAttribute('version','2.0'); - $node->appendChild($caldata); + $node->appendChild($caldata); } } diff --git a/3rdparty/Sabre/CalDAV/Property/SupportedCollationSet.php b/3rdparty/Sabre/CalDAV/Property/SupportedCollationSet.php index e585e9db3d..24e84d4c17 100644 --- a/3rdparty/Sabre/CalDAV/Property/SupportedCollationSet.php +++ b/3rdparty/Sabre/CalDAV/Property/SupportedCollationSet.php @@ -4,27 +4,27 @@ * supported-collation-set property * * This property is a representation of the supported-collation-set property - * in the CalDAV namespace. + * in the CalDAV namespace. * * @package Sabre * @subpackage CalDAV - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ class Sabre_CalDAV_Property_SupportedCollationSet extends Sabre_DAV_Property { /** - * Serializes the property in a DOM document - * - * @param Sabre_DAV_Server $server - * @param DOMElement $node + * Serializes the property in a DOM document + * + * @param Sabre_DAV_Server $server + * @param DOMElement $node * @return void */ public function serialize(Sabre_DAV_Server $server,DOMElement $node) { $doc = $node->ownerDocument; - + $prefix = $node->lookupPrefix('urn:ietf:params:xml:ns:caldav'); if (!$prefix) $prefix = 'cal'; diff --git a/3rdparty/Sabre/CalDAV/Schedule/IMip.php b/3rdparty/Sabre/CalDAV/Schedule/IMip.php new file mode 100644 index 0000000000..37e75fcc4a --- /dev/null +++ b/3rdparty/Sabre/CalDAV/Schedule/IMip.php @@ -0,0 +1,104 @@ +senderEmail = $senderEmail; + + } + + /** + * Sends one or more iTip messages through email. + * + * @param string $originator + * @param array $recipients + * @param Sabre_VObject_Component $vObject + * @return void + */ + public function sendMessage($originator, array $recipients, Sabre_VObject_Component $vObject) { + + foreach($recipients as $recipient) { + + $to = $recipient; + $replyTo = $originator; + $subject = 'SabreDAV iTIP message'; + + switch(strtoupper($vObject->METHOD)) { + case 'REPLY' : + $subject = 'Response for: ' . $vObject->VEVENT->SUMMARY; + break; + case 'REQUEST' : + $subject = 'Invitation for: ' .$vObject->VEVENT->SUMMARY; + break; + case 'CANCEL' : + $subject = 'Cancelled event: ' . $vObject->VEVENT->SUMMARY; + break; + } + + $headers = array(); + $headers[] = 'Reply-To: ' . $replyTo; + $headers[] = 'From: ' . $this->senderEmail; + $headers[] = 'Content-Type: text/calendar; method=' . (string)$vObject->method . '; charset=utf-8'; + if (Sabre_DAV_Server::$exposeVersion) { + $headers[] = 'X-Sabre-Version: ' . Sabre_DAV_Version::VERSION . '-' . Sabre_DAV_Version::STABILITY; + } + + $vcalBody = $vObject->serialize(); + + $this->mail($to, $subject, $vcalBody, $headers); + + } + + } + + /** + * This function is reponsible for sending the actual email. + * + * @param string $to Recipient email address + * @param string $subject Subject of the email + * @param string $body iCalendar body + * @param array $headers List of headers + * @return void + */ + protected function mail($to, $subject, $body, array $headers) { + + mail($to, $subject, $body, implode("\r\n", $headers)); + + } + + +} + +?> diff --git a/3rdparty/Sabre/CalDAV/Schedule/IOutbox.php b/3rdparty/Sabre/CalDAV/Schedule/IOutbox.php new file mode 100644 index 0000000000..46d77514bc --- /dev/null +++ b/3rdparty/Sabre/CalDAV/Schedule/IOutbox.php @@ -0,0 +1,16 @@ +principalUri = $principalUri; + + } + + /** + * Returns the name of the node. + * + * This is used to generate the url. + * + * @return string + */ + public function getName() { + + return 'outbox'; + + } + + /** + * Returns an array with all the child nodes + * + * @return Sabre_DAV_INode[] + */ + public function getChildren() { + + return array(); + + } + + /** + * Returns the owner principal + * + * This must be a url to a principal, or null if there's no owner + * + * @return string|null + */ + public function getOwner() { + + return $this->principalUri; + + } + + /** + * Returns a group principal + * + * This must be a url to a principal, or null if there's no owner + * + * @return string|null + */ + public function getGroup() { + + return null; + + } + + /** + * Returns a list of ACE's for this node. + * + * Each ACE has the following properties: + * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are + * currently the only supported privileges + * * 'principal', a url to the principal who owns the node + * * 'protected' (optional), indicating that this ACE is not allowed to + * be updated. + * + * @return array + */ + public function getACL() { + + return array( + array( + 'privilege' => '{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}schedule-query-freebusy', + 'principal' => $this->getOwner(), + 'protected' => true, + ), + array( + 'privilege' => '{DAV:}read', + 'principal' => $this->getOwner(), + 'protected' => true, + ), + ); + + } + + /** + * Updates the ACL + * + * This method will receive a list of new ACE's. + * + * @param array $acl + * @return void + */ + public function setACL(array $acl) { + + throw new Sabre_DAV_Exception_MethodNotAllowed('You\'re not allowed to update the ACL'); + + } + + /** + * Returns the list of supported privileges for this node. + * + * The returned data structure is a list of nested privileges. + * See Sabre_DAVACL_Plugin::getDefaultSupportedPrivilegeSet for a simple + * standard structure. + * + * If null is returned from this method, the default privilege set is used, + * which is fine for most common usecases. + * + * @return array|null + */ + public function getSupportedPrivilegeSet() { + + $default = Sabre_DAVACL_Plugin::getDefaultSupportedPrivilegeSet(); + $default['aggregates'][] = array( + 'privilege' => '{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}schedule-query-freebusy', + ); + + return $default; + + } + +} diff --git a/3rdparty/Sabre/CalDAV/Server.php b/3rdparty/Sabre/CalDAV/Server.php index 969d69c627..325e3d80a7 100644 --- a/3rdparty/Sabre/CalDAV/Server.php +++ b/3rdparty/Sabre/CalDAV/Server.php @@ -3,17 +3,20 @@ /** * CalDAV server * + * Deprecated! Warning: This class is now officially deprecated + * * This script is a convenience script. It quickly sets up a WebDAV server * with caldav and ACL support, and it creates the root 'principals' and * 'calendars' collections. * - * Note that if you plan to do anything moderately complex, you are advised to - * not subclass this server, but use Sabre_DAV_Server directly instead. This + * Note that if you plan to do anything moderately complex, you are advised to + * not subclass this server, but use Sabre_DAV_Server directly instead. This * class is nothing more than an 'easy setup'. - * + * * @package Sabre * @subpackage CalDAV - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. + * @deprecated Don't use this class anymore, it will be removed in version 1.7. + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ @@ -22,17 +25,17 @@ class Sabre_CalDAV_Server extends Sabre_DAV_Server { /** * The authentication realm * - * Note that if this changes, the hashes in the auth backend must also - * be recalculated. - * + * Note that if this changes, the hashes in the auth backend must also + * be recalculated. + * * @var string */ public $authRealm = 'SabreDAV'; /** * Sets up the object. A PDO object must be passed to setup all the backends. - * - * @param PDO $pdo + * + * @param PDO $pdo */ public function __construct(PDO $pdo) { diff --git a/3rdparty/Sabre/CalDAV/UserCalendars.php b/3rdparty/Sabre/CalDAV/UserCalendars.php index f52d65e9a7..b8d3f0573f 100644 --- a/3rdparty/Sabre/CalDAV/UserCalendars.php +++ b/3rdparty/Sabre/CalDAV/UserCalendars.php @@ -1,68 +1,68 @@ principalBackend = $principalBackend; $this->caldavBackend = $caldavBackend; $this->principalInfo = $principalBackend->getPrincipalByPath($userUri); - + } /** - * Returns the name of this object - * + * Returns the name of this object + * * @return string */ public function getName() { - + list(,$name) = Sabre_DAV_URLUtil::splitPath($this->principalInfo['uri']); - return $name; + return $name; } /** - * Updates the name of this object - * - * @param string $name + * Updates the name of this object + * + * @param string $name * @return void */ public function setName($name) { @@ -72,8 +72,8 @@ class Sabre_CalDAV_UserCalendars implements Sabre_DAV_IExtendedCollection, Sabre } /** - * Deletes this object - * + * Deletes this object + * * @return void */ public function delete() { @@ -83,13 +83,13 @@ class Sabre_CalDAV_UserCalendars implements Sabre_DAV_IExtendedCollection, Sabre } /** - * Returns the last modification date - * - * @return int + * Returns the last modification date + * + * @return int */ public function getLastModified() { - return null; + return null; } @@ -97,9 +97,9 @@ class Sabre_CalDAV_UserCalendars implements Sabre_DAV_IExtendedCollection, Sabre * Creates a new file under this object. * * This is currently not allowed - * - * @param string $filename - * @param resource $data + * + * @param string $filename + * @param resource $data * @return void */ public function createFile($filename, $data=null) { @@ -112,8 +112,8 @@ class Sabre_CalDAV_UserCalendars implements Sabre_DAV_IExtendedCollection, Sabre * Creates a new directory under this object. * * This is currently not allowed. - * - * @param string $filename + * + * @param string $filename * @return void */ public function createDirectory($filename) { @@ -123,11 +123,11 @@ class Sabre_CalDAV_UserCalendars implements Sabre_DAV_IExtendedCollection, Sabre } /** - * Returns a single calendar, by name - * + * Returns a single calendar, by name + * * @param string $name * @todo needs optimizing - * @return Sabre_CalDAV_Calendar + * @return Sabre_CalDAV_Calendar */ public function getChild($name) { @@ -136,22 +136,22 @@ class Sabre_CalDAV_UserCalendars implements Sabre_DAV_IExtendedCollection, Sabre return $child; } - throw new Sabre_DAV_Exception_FileNotFound('Calendar with name \'' . $name . '\' could not be found'); + throw new Sabre_DAV_Exception_NotFound('Calendar with name \'' . $name . '\' could not be found'); } /** * Checks if a calendar exists. - * + * * @param string $name * @todo needs optimizing - * @return bool + * @return bool */ public function childExists($name) { foreach($this->getChildren() as $child) { if ($name==$child->getName()) - return true; + return true; } return false; @@ -160,8 +160,8 @@ class Sabre_CalDAV_UserCalendars implements Sabre_DAV_IExtendedCollection, Sabre /** * Returns a list of calendars - * - * @return array + * + * @return array */ public function getChildren() { @@ -170,15 +170,17 @@ class Sabre_CalDAV_UserCalendars implements Sabre_DAV_IExtendedCollection, Sabre foreach($calendars as $calendar) { $objs[] = new Sabre_CalDAV_Calendar($this->principalBackend, $this->caldavBackend, $calendar); } + $objs[] = new Sabre_CalDAV_Schedule_Outbox($this->principalInfo['uri']); return $objs; } /** * Creates a new calendar - * - * @param string $name - * @param string $properties + * + * @param string $name + * @param array $resourceType + * @param array $properties * @return void */ public function createExtendedCollection($name, array $resourceType, array $properties) { @@ -193,8 +195,8 @@ class Sabre_CalDAV_UserCalendars implements Sabre_DAV_IExtendedCollection, Sabre /** * Returns the owner principal * - * This must be a url to a principal, or null if there's no owner - * + * This must be a url to a principal, or null if there's no owner + * * @return string|null */ public function getOwner() { @@ -207,8 +209,8 @@ class Sabre_CalDAV_UserCalendars implements Sabre_DAV_IExtendedCollection, Sabre * Returns a group principal * * This must be a url to a principal, or null if there's no owner - * - * @return string|null + * + * @return string|null */ public function getGroup() { @@ -220,13 +222,13 @@ class Sabre_CalDAV_UserCalendars implements Sabre_DAV_IExtendedCollection, Sabre * 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 + * * '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 + * * 'protected' (optional), indicating that this ACE is not allowed to + * be updated. + * + * @return array */ public function getACL() { @@ -264,9 +266,9 @@ class Sabre_CalDAV_UserCalendars implements Sabre_DAV_IExtendedCollection, Sabre /** * Updates the ACL * - * This method will receive a list of new ACE's. - * - * @param array $acl + * This method will receive a list of new ACE's. + * + * @param array $acl * @return void */ public function setACL(array $acl) { @@ -275,6 +277,22 @@ class Sabre_CalDAV_UserCalendars implements Sabre_DAV_IExtendedCollection, Sabre } + /** + * Returns the list of supported privileges for this node. + * + * The returned data structure is a list of nested privileges. + * See Sabre_DAVACL_Plugin::getDefaultSupportedPrivilegeSet for a simple + * standard structure. + * + * If null is returned from this method, the default privilege set is used, + * which is fine for most common usecases. + * + * @return array|null + */ + public function getSupportedPrivilegeSet() { + return null; + + } } diff --git a/3rdparty/Sabre/CalDAV/Version.php b/3rdparty/Sabre/CalDAV/Version.php index df8fe1f6bd..939e903c89 100644 --- a/3rdparty/Sabre/CalDAV/Version.php +++ b/3rdparty/Sabre/CalDAV/Version.php @@ -2,10 +2,10 @@ /** * This class contains the Sabre_CalDAV version constants. - * + * * @package Sabre * @subpackage CalDAV - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. + * @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 */ @@ -14,7 +14,7 @@ class Sabre_CalDAV_Version { /** * Full version number */ - const VERSION = '1.5.3'; + const VERSION = '1.6.2'; /** * Stability : alpha, beta, stable diff --git a/3rdparty/Sabre/CalDAV/XMLUtil.php b/3rdparty/Sabre/CalDAV/XMLUtil.php deleted file mode 100644 index bf349a36aa..0000000000 --- a/3rdparty/Sabre/CalDAV/XMLUtil.php +++ /dev/null @@ -1,208 +0,0 @@ -childNodes as $child) { - - switch(Sabre_DAV_XMLUtil::toClarkNotation($child)) { - - case '{urn:ietf:params:xml:ns:caldav}comp-filter' : - case '{urn:ietf:params:xml:ns:caldav}prop-filter' : - - $filterName = $basePath . '/' . 'c:' . strtolower($child->getAttribute('name')); - $filters[$filterName] = array(); - - self::parseCalendarQueryFilters($child, $filterName,$filters); - break; - - case '{urn:ietf:params:xml:ns:caldav}time-range' : - - if ($start = $child->getAttribute('start')) { - $start = self::parseICalendarDateTime($start); - } else { - $start = null; - } - if ($end = $child->getAttribute('end')) { - $end = self::parseICalendarDateTime($end); - } else { - $end = null; - } - - if (!is_null($start) && !is_null($end) && $end <= $start) { - throw new Sabre_DAV_Exception_BadRequest('The end-date must be larger than the start-date in the time-range filter'); - } - - $filters[$basePath]['time-range'] = array( - 'start' => $start, - 'end' => $end - ); - break; - - case '{urn:ietf:params:xml:ns:caldav}is-not-defined' : - $filters[$basePath]['is-not-defined'] = true; - break; - - case '{urn:ietf:params:xml:ns:caldav}param-filter' : - - $filterName = $basePath . '/@' . strtolower($child->getAttribute('name')); - $filters[$filterName] = array(); - self::parseCalendarQueryFilters($child, $filterName, $filters); - break; - - case '{urn:ietf:params:xml:ns:caldav}text-match' : - - $collation = $child->getAttribute('collation'); - if (!$collation) $collation = 'i;ascii-casemap'; - - $filters[$basePath]['text-match'] = array( - 'collation' => ($collation == 'default'?'i;ascii-casemap':$collation), - 'negate-condition' => $child->getAttribute('negate-condition')==='yes', - 'value' => $child->nodeValue, - ); - break; - - } - - } - - return $filters; - - } - - /** - * Parses an iCalendar (rfc5545) formatted datetime and returns a DateTime object - * - * Specifying a reference timezone is optional. It will only be used - * if the non-UTC format is used. The argument is used as a reference, the - * returned DateTime object will still be in the UTC timezone. - * - * @param string $dt - * @param DateTimeZone $tz - * @return DateTime - */ - static public function parseICalendarDateTime($dt,DateTimeZone $tz = null) { - - // Format is YYYYMMDD + "T" + hhmmss - $result = preg_match('/^([1-3][0-9]{3})([0-1][0-9])([0-3][0-9])T([0-2][0-9])([0-5][0-9])([0-5][0-9])([Z]?)$/',$dt,$matches); - - if (!$result) { - throw new Sabre_DAV_Exception_BadRequest('The supplied iCalendar datetime value is incorrect: ' . $dt); - } - - if ($matches[7]==='Z' || is_null($tz)) { - $tz = new DateTimeZone('UTC'); - } - $date = new DateTime($matches[1] . '-' . $matches[2] . '-' . $matches[3] . ' ' . $matches[4] . ':' . $matches[5] .':' . $matches[6], $tz); - - // Still resetting the timezone, to normalize everything to UTC - $date->setTimeZone(new DateTimeZone('UTC')); - return $date; - - } - - /** - * Parses an iCalendar (rfc5545) formatted datetime and returns a DateTime object - * - * @param string $date - * @param DateTimeZone $tz - * @return DateTime - */ - static public function parseICalendarDate($date) { - - // Format is YYYYMMDD - $result = preg_match('/^([1-3][0-9]{3})([0-1][0-9])([0-3][0-9])$/',$date,$matches); - - if (!$result) { - throw new Sabre_DAV_Exception_BadRequest('The supplied iCalendar date value is incorrect: ' . $date); - } - - $date = new DateTime($matches[1] . '-' . $matches[2] . '-' . $matches[3], new DateTimeZone('UTC')); - return $date; - - } - - /** - * Parses an iCalendar (RFC5545) formatted duration and returns a string suitable - * for strtotime or DateTime::modify. - * - * NOTE: When we require PHP 5.3 this can be replaced by the DateTimeInterval object, which - * supports ISO 8601 Intervals, which is a superset of ICalendar durations. - * - * For now though, we're just gonna live with this messy system - * - * @param string $duration - * @return string - */ - static public function parseICalendarDuration($duration) { - - $result = preg_match('/^(?P\+|-)?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); - } - - $parts = array( - 'week', - 'day', - 'hour', - 'minute', - 'second', - ); - - $newDur = ''; - foreach($parts as $part) { - if (isset($matches[$part]) && $matches[$part]) { - $newDur.=' '.$matches[$part] . ' ' . $part . 's'; - } - } - - $newDur = ($matches['plusminus']==='-'?'-':'+') . trim($newDur); - return $newDur; - - } - -} diff --git a/3rdparty/Sabre/CalDAV/includes.php b/3rdparty/Sabre/CalDAV/includes.php new file mode 100644 index 0000000000..1ecb870a0e --- /dev/null +++ b/3rdparty/Sabre/CalDAV/includes.php @@ -0,0 +1,43 @@ +carddavBackend = $carddavBackend; $this->addressBookInfo = $addressBookInfo; @@ -42,9 +41,9 @@ class Sabre_CardDAV_AddressBook extends Sabre_DAV_Collection implements Sabre_Ca } /** - * Returns the name of the addressbook - * - * @return string + * Returns the name of the addressbook + * + * @return string */ public function getName() { @@ -55,21 +54,21 @@ class Sabre_CardDAV_AddressBook extends Sabre_DAV_Collection implements Sabre_Ca /** * Returns a card * - * @param string $name + * @param string $name * @return Sabre_DAV_Card */ public function getChild($name) { $obj = $this->carddavBackend->getCard($this->addressBookInfo['id'],$name); - if (!$obj) throw new Sabre_DAV_Exception_FileNotFound('Card not found'); + if (!$obj) throw new Sabre_DAV_Exception_NotFound('Card not found'); return new Sabre_CardDAV_Card($this->carddavBackend,$this->addressBookInfo,$obj); } /** * Returns the full list of cards - * - * @return array + * + * @return array */ public function getChildren() { @@ -85,9 +84,9 @@ class Sabre_CardDAV_AddressBook extends Sabre_DAV_Collection implements Sabre_Ca /** * Creates a new directory * - * We actually block this, as subdirectories are not allowed in addressbooks. - * - * @param string $name + * We actually block this, as subdirectories are not allowed in addressbooks. + * + * @param string $name * @return void */ public function createDirectory($name) { @@ -99,11 +98,13 @@ class Sabre_CardDAV_AddressBook extends Sabre_DAV_Collection implements Sabre_Ca /** * Creates a new file * - * The contents of the new file must be a valid VCARD - * - * @param string $name - * @param resource $vcardData - * @return void + * The contents of the new file must be a valid VCARD. + * + * This method may return an ETag. + * + * @param string $name + * @param resource $vcardData + * @return void|null */ public function createFile($name,$vcardData = null) { @@ -111,13 +112,13 @@ class Sabre_CardDAV_AddressBook extends Sabre_DAV_Collection implements Sabre_Ca // Converting to UTF-8, if needed $vcardData = Sabre_DAV_StringUtil::ensureUTF8($vcardData); - $this->carddavBackend->createCard($this->addressBookInfo['id'],$name,$vcardData); + return $this->carddavBackend->createCard($this->addressBookInfo['id'],$name,$vcardData); } /** - * Deletes the entire addressbook. - * + * Deletes the entire addressbook. + * * @return void */ public function delete() { @@ -128,8 +129,8 @@ class Sabre_CardDAV_AddressBook extends Sabre_DAV_Collection implements Sabre_Ca /** * Renames the addressbook - * - * @param string $newName + * + * @param string $newName * @return void */ public function setName($newName) { @@ -140,7 +141,7 @@ class Sabre_CardDAV_AddressBook extends Sabre_DAV_Collection implements Sabre_Ca /** * Returns the last modification date as a unix timestamp. - * + * * @return void */ public function getLastModified() { @@ -162,7 +163,7 @@ class Sabre_CardDAV_AddressBook extends Sabre_DAV_Collection implements Sabre_Ca * If the operation was successful, true can be returned. * If the operation failed, false can be returned. * - * Deletion of a non-existant property is always succesful. + * Deletion of a non-existent property is always successful. * * Lastly, it is optional to return detailed information about any * failures. In this case an array should be returned with the following @@ -177,16 +178,16 @@ class Sabre_CardDAV_AddressBook extends Sabre_DAV_Collection implements Sabre_Ca * ) * ) * - * In this example it was forbidden to update {DAV:}displayname. + * In this example it was forbidden to update {DAV:}displayname. * (403 Forbidden), which in turn also caused {DAV:}owner to fail * (424 Failed Dependency) because the request needs to be atomic. * - * @param array $mutations - * @return bool|array + * @param array $mutations + * @return bool|array */ public function updateProperties($mutations) { - return $this->carddavBackend->updateAddressBook($this->addressBookInfo['id'], $mutations); + return $this->carddavBackend->updateAddressBook($this->addressBookInfo['id'], $mutations); } @@ -198,8 +199,8 @@ class Sabre_CardDAV_AddressBook extends Sabre_DAV_Collection implements Sabre_Ca * * If the array is empty, it means 'all properties' were requested. * - * @param array $properties - * @return void + * @param array $properties + * @return array */ public function getProperties($properties) { @@ -221,8 +222,8 @@ class Sabre_CardDAV_AddressBook extends Sabre_DAV_Collection implements Sabre_Ca /** * Returns the owner principal * - * This must be a url to a principal, or null if there's no owner - * + * This must be a url to a principal, or null if there's no owner + * * @return string|null */ public function getOwner() { @@ -235,8 +236,8 @@ class Sabre_CardDAV_AddressBook extends Sabre_DAV_Collection implements Sabre_Ca * Returns a group principal * * This must be a url to a principal, or null if there's no owner - * - * @return string|null + * + * @return string|null */ public function getGroup() { @@ -248,13 +249,13 @@ class Sabre_CardDAV_AddressBook extends Sabre_DAV_Collection implements Sabre_Ca * 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 + * * '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 + * * 'protected' (optional), indicating that this ACE is not allowed to + * be updated. + * + * @return array */ public function getACL() { @@ -277,9 +278,9 @@ class Sabre_CardDAV_AddressBook extends Sabre_DAV_Collection implements Sabre_Ca /** * Updates the ACL * - * This method will receive a list of new ACE's. - * - * @param array $acl + * This method will receive a list of new ACE's. + * + * @param array $acl * @return void */ public function setACL(array $acl) { @@ -288,6 +289,22 @@ class Sabre_CardDAV_AddressBook extends Sabre_DAV_Collection implements Sabre_Ca } + /** + * Returns the list of supported privileges for this node. + * + * The returned data structure is a list of nested privileges. + * See Sabre_DAVACL_Plugin::getDefaultSupportedPrivilegeSet for a simple + * standard structure. + * + * If null is returned from this method, the default privilege set is used, + * which is fine for most common usecases. + * + * @return array|null + */ + public function getSupportedPrivilegeSet() { + return null; + + } } diff --git a/3rdparty/Sabre/CardDAV/AddressBookQueryParser.php b/3rdparty/Sabre/CardDAV/AddressBookQueryParser.php index 08adc3b815..85a4963127 100644 --- a/3rdparty/Sabre/CardDAV/AddressBookQueryParser.php +++ b/3rdparty/Sabre/CardDAV/AddressBookQueryParser.php @@ -3,12 +3,12 @@ /** * Parses the addressbook-query report request body. * - * Whoever designed this format, and the CalDAV equavalent even more so, + * Whoever designed this format, and the CalDAV equivalent even more so, * has no feel for design. - * + * * @package Sabre * @subpackage CardDAV - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. + * @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 */ @@ -19,8 +19,8 @@ class Sabre_CardDAV_AddressBookQueryParser { /** * List of requested properties the client wanted - * - * @var array + * + * @var array */ public $requestedProperties; @@ -28,44 +28,43 @@ class Sabre_CardDAV_AddressBookQueryParser { * The number of results the client wants * * null means it wasn't specified, which in most cases means 'all results'. - * - * @var int|null + * + * @var int|null */ public $limit; /** * List of property filters. * - * @var array + * @var array */ public $filters; /** * Either TEST_ANYOF or TEST_ALLOF - * - * @var string + * + * @var string */ public $test; /** * DOM Document - * - * @var DOMDocument + * + * @var DOMDocument */ protected $dom; /** - * DOM XPath object - * - * @var DOMXPath + * DOM XPath object + * + * @var DOMXPath */ protected $xpath; /** * Creates the parser - * - * @param DOMNode $dom - * @return void + * + * @param DOMDocument $dom */ public function __construct(DOMDocument $dom) { @@ -77,15 +76,14 @@ class Sabre_CardDAV_AddressBookQueryParser { } /** - * Parses the request. - * - * @param DOMNode $dom + * Parses the request. + * * @return void */ public function parse() { $filterNode = null; - + $limit = $this->xpath->evaluate('number(/card:addressbook-query/card:limit/card:nresults)'); if (is_nan($limit)) $limit = null; @@ -120,9 +118,9 @@ class Sabre_CardDAV_AddressBookQueryParser { /** * Parses the prop-filter xml element - * - * @param DOMElement $propFilterNode - * @return array + * + * @param DOMElement $propFilterNode + * @return array */ protected function parsePropFilterNode(DOMElement $propFilterNode) { @@ -157,13 +155,13 @@ class Sabre_CardDAV_AddressBookQueryParser { } /** - * Parses the param-filter element - * - * @param DOMElement $paramFilterNode - * @return array + * Parses the param-filter element + * + * @param DOMElement $paramFilterNode + * @return array */ public function parseParamFilterNode(DOMElement $paramFilterNode) { - + $paramFilter = array(); $paramFilter['name'] = $paramFilterNode->getAttribute('name'); $paramFilter['is-not-defined'] = $this->xpath->query('card:is-not-defined', $paramFilterNode)->length>0; @@ -174,15 +172,15 @@ class Sabre_CardDAV_AddressBookQueryParser { $paramFilter['text-match'] = $this->parseTextMatchNode($textMatch->item(0)); } - return $paramFilter; + return $paramFilter; } /** * Text match - * - * @param DOMElement $textMatchNode - * @return void + * + * @param DOMElement $textMatchNode + * @return array */ public function parseTextMatchNode(DOMElement $textMatchNode) { @@ -204,8 +202,8 @@ class Sabre_CardDAV_AddressBookQueryParser { 'match-type' => $matchType, 'value' => $textMatchNode->nodeValue ); - - } + + } } diff --git a/3rdparty/Sabre/CardDAV/AddressBookRoot.php b/3rdparty/Sabre/CardDAV/AddressBookRoot.php index 1a80efba35..9d37b15f08 100644 --- a/3rdparty/Sabre/CardDAV/AddressBookRoot.php +++ b/3rdparty/Sabre/CardDAV/AddressBookRoot.php @@ -1,45 +1,45 @@ pdo = $pdo; $this->addressBooksTableName = $addressBooksTableName; - $this->cardsTableName = $cardsTableName; + $this->cardsTableName = $cardsTableName; } /** - * Returns the list of addressbooks for a specific user. - * - * @param string $principalUri - * @return array + * Returns the list of addressbooks for a specific user. + * + * @param string $principalUri + * @return array */ public function getAddressBooksForUser($principalUri) { - $stmt = $this->pdo->prepare('SELECT id, uri, displayname, principaluri, description, ctag FROM `'.$this->addressBooksTableName.'` WHERE principaluri = ?'); - $result = $stmt->execute(array($principalUri)); + $stmt = $this->pdo->prepare('SELECT id, uri, displayname, principaluri, description, ctag FROM '.$this->addressBooksTableName.' WHERE principaluri = ?'); + $stmt->execute(array($principalUri)); $addressBooks = array(); @@ -65,7 +67,7 @@ class Sabre_CardDAV_Backend_PDO extends Sabre_CardDAV_Backend_Abstract { '{DAV:}displayname' => $row['displayname'], '{' . Sabre_CardDAV_Plugin::NS_CARDDAV . '}addressbook-description' => $row['description'], '{http://calendarserver.org/ns/}getctag' => $row['ctag'], - '{' . Sabre_CardDAV_Plugin::NS_CARDDAV . '}supported-address-data' => + '{' . Sabre_CardDAV_Plugin::NS_CARDDAV . '}supported-address-data' => new Sabre_CardDAV_Property_SupportedAddressData(), ); @@ -79,8 +81,8 @@ class Sabre_CardDAV_Backend_PDO extends Sabre_CardDAV_Backend_Abstract { /** * Updates an addressbook's properties * - * See Sabre_DAV_IProperties for a description of the mutations array, as - * well as the return value. + * See Sabre_DAV_IProperties for a description of the mutations array, as + * well as the return value. * * @param mixed $addressBookId * @param array $mutations @@ -88,7 +90,7 @@ class Sabre_CardDAV_Backend_PDO extends Sabre_CardDAV_Backend_Abstract { * @return bool|array */ public function updateAddressBook($addressBookId, array $mutations) { - + $updates = array(); foreach($mutations as $property=>$newValue) { @@ -101,7 +103,7 @@ class Sabre_CardDAV_Backend_PDO extends Sabre_CardDAV_Backend_Abstract { $updates['description'] = $newValue; break; default : - // If any unsupported values were being updated, we must + // If any unsupported values were being updated, we must // let the entire request fail. return false; } @@ -113,7 +115,7 @@ class Sabre_CardDAV_Backend_PDO extends Sabre_CardDAV_Backend_Abstract { return false; } - $query = 'UPDATE `' . $this->addressBooksTableName . '` SET ctag = ctag + 1 '; + $query = 'UPDATE ' . $this->addressBooksTableName . ' SET ctag = ctag + 1 '; foreach($updates as $key=>$value) { $query.=', `' . $key . '` = :' . $key . ' '; } @@ -129,11 +131,11 @@ class Sabre_CardDAV_Backend_PDO extends Sabre_CardDAV_Backend_Abstract { } /** - * Creates a new address book + * Creates a new address book * - * @param string $principalUri - * @param string $url Just the 'basename' of the url. - * @param array $properties + * @param string $principalUri + * @param string $url Just the 'basename' of the url. + * @param array $properties * @return void */ public function createAddressBook($principalUri, $url, array $properties) { @@ -160,7 +162,7 @@ class Sabre_CardDAV_Backend_PDO extends Sabre_CardDAV_Backend_Abstract { } - $query = 'INSERT INTO `' . $this->addressBooksTableName . '` (uri, displayname, description, principaluri, ctag) VALUES (:uri, :displayname, :description, :principaluri, 1)'; + $query = 'INSERT INTO ' . $this->addressBooksTableName . ' (uri, displayname, description, principaluri, ctag) VALUES (:uri, :displayname, :description, :principaluri, 1)'; $stmt = $this->pdo->prepare($query); $stmt->execute($values); @@ -169,44 +171,61 @@ class Sabre_CardDAV_Backend_PDO extends Sabre_CardDAV_Backend_Abstract { /** * Deletes an entire addressbook and all its contents * - * @param int $addressBookId + * @param int $addressBookId * @return void */ public function deleteAddressBook($addressBookId) { - $stmt = $this->pdo->prepare('DELETE FROM `' . $this->cardsTableName . '` WHERE addressbookid = ?'); + $stmt = $this->pdo->prepare('DELETE FROM ' . $this->cardsTableName . ' WHERE addressbookid = ?'); $stmt->execute(array($addressBookId)); - $stmt = $this->pdo->prepare('DELETE FROM `' . $this->addressBooksTableName . '` WHERE id = ?'); + $stmt = $this->pdo->prepare('DELETE FROM ' . $this->addressBooksTableName . ' WHERE id = ?'); $stmt->execute(array($addressBookId)); } /** - * Returns all cards for a specific addressbook id. - * - * @param mixed $addressbookId - * @return array + * Returns all cards for a specific addressbook id. + * + * This method should return the following properties for each card: + * * carddata - raw vcard data + * * uri - Some unique url + * * lastmodified - A unix timestamp + * + * It's recommended to also return the following properties: + * * etag - A unique etag. This must change every time the card changes. + * * size - The size of the card in bytes. + * + * If these last two properties are provided, less time will be spent + * calculating them. If they are specified, you can also ommit carddata. + * This may speed up certain requests, especially with large cards. + * + * @param mixed $addressbookId + * @return array */ public function getCards($addressbookId) { - $stmt = $this->pdo->prepare('SELECT id, carddata, uri, lastmodified FROM `' . $this->cardsTableName . '` WHERE addressbookid = ?'); + $stmt = $this->pdo->prepare('SELECT id, carddata, uri, lastmodified FROM ' . $this->cardsTableName . ' WHERE addressbookid = ?'); $stmt->execute(array($addressbookId)); return $stmt->fetchAll(PDO::FETCH_ASSOC); - + } + /** - * Returns a specfic card - * - * @param mixed $addressBookId - * @param string $cardUri - * @return array + * Returns a specfic card. + * + * The same set of properties must be returned as with getCards. The only + * exception is that 'carddata' is absolutely required. + * + * @param mixed $addressBookId + * @param string $cardUri + * @return array */ public function getCard($addressBookId, $cardUri) { - $stmt = $this->pdo->prepare('SELECT id, carddata, uri, lastmodified FROM `' . $this->cardsTableName . '` WHERE addressbookid = ? AND uri = ? LIMIT 1'); + $stmt = $this->pdo->prepare('SELECT id, carddata, uri, lastmodified FROM ' . $this->cardsTableName . ' WHERE addressbookid = ? AND uri = ? LIMIT 1'); $stmt->execute(array($addressBookId, $cardUri)); $result = $stmt->fetchAll(PDO::FETCH_ASSOC); @@ -216,59 +235,93 @@ class Sabre_CardDAV_Backend_PDO extends Sabre_CardDAV_Backend_Abstract { } /** - * Creates a new card - * - * @param mixed $addressBookId - * @param string $cardUri - * @param string $cardData - * @return bool + * Creates a new card. + * + * The addressbook id will be passed as the first argument. This is the + * same id as it is returned from the getAddressbooksForUser method. + * + * The cardUri is a base uri, and doesn't include the full path. The + * cardData argument is the vcard body, and is passed as a string. + * + * It is possible to return an ETag from this method. This ETag is for the + * newly created resource, and must be enclosed with double quotes (that + * is, the string itself must contain the double quotes). + * + * You should only return the ETag if you store the carddata as-is. If a + * subsequent GET request on the same card does not have the same body, + * byte-by-byte and you did return an ETag here, clients tend to get + * confused. + * + * If you don't return an ETag, you can just return null. + * + * @param mixed $addressBookId + * @param string $cardUri + * @param string $cardData + * @return string|null */ public function createCard($addressBookId, $cardUri, $cardData) { - $stmt = $this->pdo->prepare('INSERT INTO `' . $this->cardsTableName . '` (carddata, uri, lastmodified, addressbookid) VALUES (?, ?, ?, ?)'); + $stmt = $this->pdo->prepare('INSERT INTO ' . $this->cardsTableName . ' (carddata, uri, lastmodified, addressbookid) VALUES (?, ?, ?, ?)'); $result = $stmt->execute(array($cardData, $cardUri, time(), $addressBookId)); - $stmt2 = $this->pdo->prepare('UPDATE `' . $this->addressBooksTableName . '` SET ctag = ctag + 1 WHERE id = ?'); + $stmt2 = $this->pdo->prepare('UPDATE ' . $this->addressBooksTableName . ' SET ctag = ctag + 1 WHERE id = ?'); $stmt2->execute(array($addressBookId)); - return $result; + return '"' . md5($cardData) . '"'; } /** - * Updates a card - * - * @param mixed $addressBookId - * @param string $cardUri - * @param string $cardData - * @return bool + * Updates a card. + * + * The addressbook id will be passed as the first argument. This is the + * same id as it is returned from the getAddressbooksForUser method. + * + * The cardUri is a base uri, and doesn't include the full path. The + * cardData argument is the vcard body, and is passed as a string. + * + * It is possible to return an ETag from this method. This ETag should + * match that of the updated resource, and must be enclosed with double + * quotes (that is: the string itself must contain the actual quotes). + * + * You should only return the ETag if you store the carddata as-is. If a + * subsequent GET request on the same card does not have the same body, + * byte-by-byte and you did return an ETag here, clients tend to get + * confused. + * + * If you don't return an ETag, you can just return null. + * + * @param mixed $addressBookId + * @param string $cardUri + * @param string $cardData + * @return string|null */ public function updateCard($addressBookId, $cardUri, $cardData) { - $stmt = $this->pdo->prepare('UPDATE `' . $this->cardsTableName . '` SET carddata = ?, lastmodified = ? WHERE uri = ? AND addressbookid =?'); - $result = $stmt->execute(array($cardData, time(), $cardUri, $addressBookId)); + $stmt = $this->pdo->prepare('UPDATE ' . $this->cardsTableName . ' SET carddata = ?, lastmodified = ? WHERE uri = ? AND addressbookid =?'); + $stmt->execute(array($cardData, time(), $cardUri, $addressBookId)); - $stmt2 = $this->pdo->prepare('UPDATE `' . $this->addressBooksTableName . '` SET ctag = ctag + 1 WHERE id = ?'); + $stmt2 = $this->pdo->prepare('UPDATE ' . $this->addressBooksTableName . ' SET ctag = ctag + 1 WHERE id = ?'); $stmt2->execute(array($addressBookId)); - return $stmt->rowCount()===1; + return '"' . md5($cardData) . '"'; } /** * Deletes a card - * - * @param mixed $addressBookId - * @param string $cardUri - * @return bool + * + * @param mixed $addressBookId + * @param string $cardUri + * @return bool */ public function deleteCard($addressBookId, $cardUri) { - $stmt = $this->pdo->prepare('DELETE FROM `' . $this->cardsTableName . '` WHERE addressbookid = ? AND uri = ?'); + $stmt = $this->pdo->prepare('DELETE FROM ' . $this->cardsTableName . ' WHERE addressbookid = ? AND uri = ?'); $stmt->execute(array($addressBookId, $cardUri)); - $stmt2 = $this->pdo->prepare('UPDATE `' . $this->addressBooksTableName . '` SET ctag = ctag + 1 WHERE id = ?'); + $stmt2 = $this->pdo->prepare('UPDATE ' . $this->addressBooksTableName . ' SET ctag = ctag + 1 WHERE id = ?'); $stmt2->execute(array($addressBookId)); return $stmt->rowCount()===1; diff --git a/3rdparty/Sabre/CardDAV/Card.php b/3rdparty/Sabre/CardDAV/Card.php index 2844eaf7ed..d7c6633383 100644 --- a/3rdparty/Sabre/CardDAV/Card.php +++ b/3rdparty/Sabre/CardDAV/Card.php @@ -2,10 +2,10 @@ /** * The Card object represents a single Card from an addressbook - * + * * @package Sabre * @subpackage CardDAV - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. + * @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 */ @@ -13,29 +13,29 @@ class Sabre_CardDAV_Card extends Sabre_DAV_File implements Sabre_CardDAV_ICard, /** * CardDAV backend - * - * @var Sabre_CardDAV_Backend_Abstract + * + * @var Sabre_CardDAV_Backend_Abstract */ private $carddavBackend; /** * Array with information about this Card - * - * @var array + * + * @var array */ private $cardData; /** - * Array with information about the containing addressbook - * - * @var array + * Array with information about the containing addressbook + * + * @var array */ private $addressBookInfo; /** - * Constructor - * - * @param Sabre_CardDAV_Backend_Abstract $carddavBackend + * Constructor + * + * @param Sabre_CardDAV_Backend_Abstract $carddavBackend * @param array $addressBookInfo * @param array $cardData */ @@ -48,9 +48,9 @@ class Sabre_CardDAV_Card extends Sabre_DAV_File implements Sabre_CardDAV_ICard, } /** - * Returns the uri for this object - * - * @return string + * Returns the uri for this object + * + * @return string */ public function getName() { @@ -59,25 +59,26 @@ class Sabre_CardDAV_Card extends Sabre_DAV_File implements Sabre_CardDAV_ICard, } /** - * Returns the VCard-formatted object - * - * @return string + * Returns the VCard-formatted object + * + * @return string */ public function get() { - $cardData = $this->cardData['carddata']; - $s = fopen('php://temp','r+'); - fwrite($s, $cardData); - rewind($s); - return $s; + // Pre-populating 'carddata' is optional. If we don't yet have it + // already, we fetch it from the backend. + if (!isset($this->cardData['carddata'])) { + $this->cardData = $this->carddavBackend->getCard($this->addressBookInfo['id'], $this->cardData['uri']); + } + return $this->cardData['carddata']; } /** - * Updates the VCard-formatted object - * - * @param string $cardData - * @return void + * Updates the VCard-formatted object + * + * @param string $cardData + * @return void */ public function put($cardData) { @@ -87,14 +88,17 @@ class Sabre_CardDAV_Card extends Sabre_DAV_File implements Sabre_CardDAV_ICard, // Converting to UTF-8, if needed $cardData = Sabre_DAV_StringUtil::ensureUTF8($cardData); - $this->carddavBackend->updateCard($this->addressBookInfo['id'],$this->cardData['uri'],$cardData); + $etag = $this->carddavBackend->updateCard($this->addressBookInfo['id'],$this->cardData['uri'],$cardData); $this->cardData['carddata'] = $cardData; + $this->cardData['etag'] = $etag; + + return $etag; } /** * Deletes the card - * + * * @return void */ public function delete() { @@ -104,9 +108,9 @@ class Sabre_CardDAV_Card extends Sabre_DAV_File implements Sabre_CardDAV_ICard, } /** - * Returns the mime content-type - * - * @return string + * Returns the mime content-type + * + * @return string */ public function getContentType() { @@ -115,20 +119,24 @@ class Sabre_CardDAV_Card extends Sabre_DAV_File implements Sabre_CardDAV_ICard, } /** - * Returns an ETag for this object - * - * @return string + * Returns an ETag for this object + * + * @return string */ public function getETag() { - return '"' . md5($this->cardData['carddata']) . '"'; + if (isset($this->cardData['etag'])) { + return $this->cardData['etag']; + } else { + return '"' . md5($this->get()) . '"'; + } } /** * Returns the last modification date as a unix timestamp - * - * @return time + * + * @return time */ public function getLastModified() { @@ -137,21 +145,25 @@ class Sabre_CardDAV_Card extends Sabre_DAV_File implements Sabre_CardDAV_ICard, } /** - * Returns the size of this object in bytes - * + * Returns the size of this object in bytes + * * @return int */ public function getSize() { - return strlen($this->cardData['carddata']); + if (array_key_exists('size', $this->cardData)) { + return $this->cardData['size']; + } else { + return strlen($this->get()); + } } /** * Returns the owner principal * - * This must be a url to a principal, or null if there's no owner - * + * This must be a url to a principal, or null if there's no owner + * * @return string|null */ public function getOwner() { @@ -164,8 +176,8 @@ class Sabre_CardDAV_Card extends Sabre_DAV_File implements Sabre_CardDAV_ICard, * Returns a group principal * * This must be a url to a principal, or null if there's no owner - * - * @return string|null + * + * @return string|null */ public function getGroup() { @@ -177,13 +189,13 @@ class Sabre_CardDAV_Card extends Sabre_DAV_File implements Sabre_CardDAV_ICard, * 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 + * * '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 + * * 'protected' (optional), indicating that this ACE is not allowed to + * be updated. + * + * @return array */ public function getACL() { @@ -205,9 +217,9 @@ class Sabre_CardDAV_Card extends Sabre_DAV_File implements Sabre_CardDAV_ICard, /** * Updates the ACL * - * This method will receive a list of new ACE's. - * - * @param array $acl + * This method will receive a list of new ACE's. + * + * @param array $acl * @return void */ public function setACL(array $acl) { @@ -216,5 +228,23 @@ class Sabre_CardDAV_Card extends Sabre_DAV_File implements Sabre_CardDAV_ICard, } + /** + * Returns the list of supported privileges for this node. + * + * The returned data structure is a list of nested privileges. + * See Sabre_DAVACL_Plugin::getDefaultSupportedPrivilegeSet for a simple + * standard structure. + * + * If null is returned from this method, the default privilege set is used, + * which is fine for most common usecases. + * + * @return array|null + */ + public function getSupportedPrivilegeSet() { + + return null; + + } + } diff --git a/3rdparty/Sabre/CardDAV/IAddressBook.php b/3rdparty/Sabre/CardDAV/IAddressBook.php index a0dffb30ae..2bc275bcf7 100644 --- a/3rdparty/Sabre/CardDAV/IAddressBook.php +++ b/3rdparty/Sabre/CardDAV/IAddressBook.php @@ -4,15 +4,15 @@ * AddressBook interface * * Implement this interface to allow a node to be recognized as an addressbook. - * + * * @package Sabre * @subpackage CardDAV - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ interface Sabre_CardDAV_IAddressBook extends Sabre_DAV_ICollection { - + } diff --git a/3rdparty/Sabre/CardDAV/ICard.php b/3rdparty/Sabre/CardDAV/ICard.php index 25bcc551b7..a17299316c 100644 --- a/3rdparty/Sabre/CardDAV/ICard.php +++ b/3rdparty/Sabre/CardDAV/ICard.php @@ -1,18 +1,18 @@ subscribeEvent('beforeGetProperties', array($this, 'beforeGetProperties')); + $server->subscribeEvent('updateProperties', array($this, 'updateProperties')); $server->subscribeEvent('report', array($this,'report')); + $server->subscribeEvent('onHTMLActionsPanel', array($this,'htmlActionsPanel')); + $server->subscribeEvent('onBrowserPostAction', array($this,'browserPostAction')); /* Namespaces */ $server->xmlNamespaces[self::NS_CARDDAV] = 'card'; @@ -56,11 +59,14 @@ class Sabre_CardDAV_Plugin extends Sabre_DAV_ServerPlugin { /* Mapping Interfaces to {DAV:}resourcetype values */ $server->resourceTypeMapping['Sabre_CardDAV_IAddressBook'] = '{' . self::NS_CARDDAV . '}addressbook'; $server->resourceTypeMapping['Sabre_CardDAV_IDirectory'] = '{' . self::NS_CARDDAV . '}directory'; - + /* Adding properties that may never be changed */ $server->protectedProperties[] = '{' . self::NS_CARDDAV . '}supported-address-data'; $server->protectedProperties[] = '{' . self::NS_CARDDAV . '}max-resource-size'; + $server->protectedProperties[] = '{' . self::NS_CARDDAV . '}addressbook-home-set'; + $server->protectedProperties[] = '{' . self::NS_CARDDAV . '}supported-collation-set'; + $server->propertyMap['{http://calendarserver.org/ns/}me-card'] = 'Sabre_DAV_Property_Href'; $this->server = $server; @@ -69,7 +75,7 @@ class Sabre_CardDAV_Plugin extends Sabre_DAV_ServerPlugin { /** * Returns a list of supported features. * - * This is used in the DAV: header in the OPTIONS and PROPFIND requests. + * This is used in the DAV: header in the OPTIONS and PROPFIND requests. * * @return array */ @@ -83,11 +89,11 @@ class Sabre_CardDAV_Plugin extends Sabre_DAV_ServerPlugin { * Returns a list of reports this plugin supports. * * This will be used in the {DAV:}supported-report-set property. - * Note that you still need to subscribe to the 'report' event to actually - * implement them + * Note that you still need to subscribe to the 'report' event to actually + * implement them * * @param string $uri - * @return array + * @return array */ public function getSupportedReportSet($uri) { @@ -104,22 +110,22 @@ class Sabre_CardDAV_Plugin extends Sabre_DAV_ServerPlugin { /** - * Adds all CardDAV-specific properties + * Adds all CardDAV-specific properties * * @param string $path - * @param Sabre_DAV_INode $node + * @param Sabre_DAV_INode $node * @param array $requestedProperties - * @param array $returnedProperties + * @param array $returnedProperties * @return void */ - public function beforeGetProperties($path, Sabre_DAV_INode $node, array &$requestedProperties, array &$returnedProperties) { + public function beforeGetProperties($path, Sabre_DAV_INode $node, array &$requestedProperties, array &$returnedProperties) { if ($node instanceof Sabre_DAVACL_IPrincipal) { // calendar-home-set property $addHome = '{' . self::NS_CARDDAV . '}addressbook-home-set'; if (in_array($addHome,$requestedProperties)) { - $principalId = $node->getName(); + $principalId = $node->getName(); $addressbookHomePath = self::ADDRESSBOOK_ROOT . '/' . $principalId . '/'; unset($requestedProperties[array_search($addHome, $requestedProperties)]); $returnedProperties[200][$addHome] = new Sabre_DAV_Property_Href($addressbookHomePath); @@ -135,8 +141,8 @@ class Sabre_CardDAV_Plugin extends Sabre_DAV_ServerPlugin { if ($node instanceof Sabre_CardDAV_ICard) { - // The address-data property is not supposed to be a 'real' - // property, but in large chunks of the spec it does act as such. + // The address-data property is not supposed to be a 'real' + // property, but in large chunks of the spec it does act as such. // Therefore we simply expose it as a property. $addressDataProp = '{' . self::NS_CARDDAV . '}address-data'; if (in_array($addressDataProp, $requestedProperties)) { @@ -151,24 +157,95 @@ class Sabre_CardDAV_Plugin extends Sabre_DAV_ServerPlugin { } } + if ($node instanceof Sabre_CardDAV_UserAddressBooks) { + + $meCardProp = '{http://calendarserver.org/ns/}me-card'; + if (in_array($meCardProp, $requestedProperties)) { + + $props = $this->server->getProperties($node->getOwner(), array('{http://sabredav.org/ns}vcard-url')); + if (isset($props['{http://sabredav.org/ns}vcard-url'])) { + + $returnedProperties[200][$meCardProp] = new Sabre_DAV_Property_Href( + $props['{http://sabredav.org/ns}vcard-url'] + ); + $pos = array_search($meCardProp, $requestedProperties); + unset($requestedProperties[$pos]); + + } + + } + + } + } /** - * This functions handles REPORT requests specific to CardDAV + * This event is triggered when a PROPPATCH method is executed * - * @param string $reportName + * @param array $mutations + * @param array $result + * @param Sabre_DAV_INode $node + * @return void + */ + public function updateProperties(&$mutations, &$result, $node) { + + if (!$node instanceof Sabre_CardDAV_UserAddressBooks) { + return true; + } + + $meCard = '{http://calendarserver.org/ns/}me-card'; + + // The only property we care about + if (!isset($mutations[$meCard])) + return true; + + $value = $mutations[$meCard]; + unset($mutations[$meCard]); + + if ($value instanceof Sabre_DAV_Property_IHref) { + $value = $value->getHref(); + $value = $this->server->calculateUri($value); + } elseif (!is_null($value)) { + $result[400][$meCard] = null; + return false; + } + + $innerResult = $this->server->updateProperties( + $node->getOwner(), + array( + '{http://sabredav.org/ns}vcard-url' => $value, + ) + ); + + $closureResult = false; + foreach($innerResult as $status => $props) { + if (is_array($props) && array_key_exists('{http://sabredav.org/ns}vcard-url', $props)) { + $result[$status][$meCard] = null; + $closureResult = ($status>=200 && $status<300); + } + + } + + return $result; + + } + + /** + * This functions handles REPORT requests specific to CardDAV + * + * @param string $reportName * @param DOMNode $dom - * @return bool + * @return bool */ public function report($reportName,$dom) { - switch($reportName) { + switch($reportName) { case '{'.self::NS_CARDDAV.'}addressbook-multiget' : $this->addressbookMultiGetReport($dom); return false; case '{'.self::NS_CARDDAV.'}addressbook-query' : $this->addressBookQueryReport($dom); - return false; + return false; default : return; @@ -256,7 +333,8 @@ class Sabre_CardDAV_Plugin extends Sabre_DAV_ServerPlugin { $result = array(); foreach($validNodes as $validNode) { - if ($depth==0) { + + if ($depth==0) { $href = $this->server->getRequestUri(); } else { $href = $this->server->getRequestUri() . '/' . $validNode->getName(); @@ -265,7 +343,7 @@ class Sabre_CardDAV_Plugin extends Sabre_DAV_ServerPlugin { list($result[]) = $this->server->getPropertiesForPath($href, $query->requestedProperties, 0); } - + $this->server->httpResponse->sendStatus(207); $this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8'); $this->server->httpResponse->sendBody($this->server->generateMultiStatus($result)); @@ -274,17 +352,15 @@ class Sabre_CardDAV_Plugin extends Sabre_DAV_ServerPlugin { /** * Validates if a vcard makes it throught a list of filters. - * - * @param string $vcardData - * @param array $filters - * @param string $test anyof or allof (which means OR or AND) - * @return bool + * + * @param string $vcardData + * @param array $filters + * @param string $test anyof or allof (which means OR or AND) + * @return bool */ public function validateFilters($vcardData, array $filters, $test) { $vcard = Sabre_VObject_Reader::read($vcardData); - - $success = true; foreach($filters as $filter) { @@ -299,10 +375,10 @@ class Sabre_CardDAV_Plugin extends Sabre_DAV_ServerPlugin { // We only need to check for existence $success = $isDefined; - + } else { - $vProperties = $vcard->select($filter['name']); + $vProperties = $vcard->select($filter['name']); $results = array(); if ($filter['param-filters']) { @@ -328,7 +404,7 @@ class Sabre_CardDAV_Plugin extends Sabre_DAV_ServerPlugin { } // else - // There are two conditions where we can already determine wether + // There are two conditions where we can already determine whether // or not this filter succeeds. if ($test==='anyof' && $success) { return true; @@ -339,29 +415,28 @@ class Sabre_CardDAV_Plugin extends Sabre_DAV_ServerPlugin { } // foreach - // If we got all the way here, it means we haven't been able to + // If we got all the way here, it means we haven't been able to // determine early if the test failed or not. // - // This implies for 'anyof' that the test failed, and for 'allof' that + // This implies for 'anyof' that the test failed, and for 'allof' that // we succeeded. Sounds weird, but makes sense. return $test==='allof'; } /** - * Validates if a param-filter can be applied to a specific property. - * - * @todo currently we're only validating the first parameter of the passed + * Validates if a param-filter can be applied to a specific property. + * + * @todo currently we're only validating the first parameter of the passed * property. Any subsequence parameters with the same name are * ignored. - * @param Sabre_VObject_Property $vProperty - * @param array $filters - * @param string $test - * @return bool + * @param array $vProperties + * @param array $filters + * @param string $test + * @return bool */ protected function validateParamFilters(array $vProperties, array $filters, $test) { - $success = false; foreach($filters as $filter) { $isDefined = false; @@ -377,17 +452,16 @@ class Sabre_CardDAV_Plugin extends Sabre_DAV_ServerPlugin { $success = true; } - // If there's no text-match, we can just check for existence + // If there's no text-match, we can just check for existence } elseif (!$filter['text-match'] || !$isDefined) { $success = $isDefined; - + } else { - $texts = array(); $success = false; foreach($vProperties as $vProperty) { - // If we got all the way here, we'll need to validate the + // If we got all the way here, we'll need to validate the // text-match filter. $success = Sabre_DAV_StringUtil::textMatch($vProperty[$filter['name']]->value, $filter['text-match']['value'], $filter['text-match']['collation'], $filter['text-match']['match-type']); if ($success) break; @@ -398,7 +472,7 @@ class Sabre_CardDAV_Plugin extends Sabre_DAV_ServerPlugin { } // else - // There are two conditions where we can already determine wether + // There are two conditions where we can already determine whether // or not this filter succeeds. if ($test==='anyof' && $success) { return true; @@ -407,24 +481,24 @@ class Sabre_CardDAV_Plugin extends Sabre_DAV_ServerPlugin { return false; } - } + } - // If we got all the way here, it means we haven't been able to + // If we got all the way here, it means we haven't been able to // determine early if the test failed or not. // - // This implies for 'anyof' that the test failed, and for 'allof' that + // This implies for 'anyof' that the test failed, and for 'allof' that // we succeeded. Sounds weird, but makes sense. return $test==='allof'; } /** - * Validates if a text-filter can be applied to a specific property. - * + * Validates if a text-filter can be applied to a specific property. + * * @param array $texts - * @param array $filters - * @param string $test - * @return bool + * @param array $filters + * @param string $test + * @return bool */ protected function validateTextMatches(array $texts, array $filters, $test) { @@ -440,7 +514,7 @@ class Sabre_CardDAV_Plugin extends Sabre_DAV_ServerPlugin { if ($filter['negate-condition']) { $success = !$success; } - + if ($success && $test==='anyof') return true; @@ -450,14 +524,64 @@ class Sabre_CardDAV_Plugin extends Sabre_DAV_ServerPlugin { } - // If we got all the way here, it means we haven't been able to + // If we got all the way here, it means we haven't been able to // determine early if the test failed or not. // - // This implies for 'anyof' that the test failed, and for 'allof' that + // This implies for 'anyof' that the test failed, and for 'allof' that // we succeeded. Sounds weird, but makes sense. return $test==='allof'; } + /** + * This method is used to generate HTML output for the + * Sabre_DAV_Browser_Plugin. This allows us to generate an interface users + * can use to create new calendars. + * + * @param Sabre_DAV_INode $node + * @param string $output + * @return bool + */ + public function htmlActionsPanel(Sabre_DAV_INode $node, &$output) { + + if (!$node instanceof Sabre_CardDAV_UserAddressBooks) + return; + + $output.= '
+

Create new address book

+ +
+
+ +
+ '; + + return false; + + } + + /** + * This method allows us to intercept the 'mkcalendar' sabreAction. This + * action enables the user to create new calendars from the browser plugin. + * + * @param string $uri + * @param string $action + * @param array $postVars + * @return bool + */ + public function browserPostAction($uri, $action, array $postVars) { + + if ($action!=='mkaddressbook') + return; + + $resourceType = array('{DAV:}collection','{urn:ietf:params:xml:ns:carddav}addressbook'); + $properties = array(); + if (isset($postVars['{DAV:}displayname'])) { + $properties['{DAV:}displayname'] = $postVars['{DAV:}displayname']; + } + $this->server->createCollection($uri . '/' . $postVars['name'],$resourceType,$properties); + return false; + + } } diff --git a/3rdparty/Sabre/CardDAV/Property/SupportedAddressData.php b/3rdparty/Sabre/CardDAV/Property/SupportedAddressData.php index d57d3a6e7b..36d9306e7a 100644 --- a/3rdparty/Sabre/CardDAV/Property/SupportedAddressData.php +++ b/3rdparty/Sabre/CardDAV/Property/SupportedAddressData.php @@ -4,11 +4,11 @@ * Supported-address-data property * * This property is a representation of the supported-address-data property - * in the CardDAV namespace. + * in the CardDAV namespace. * * @package Sabre * @subpackage CardDAV - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. + * @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 */ @@ -16,15 +16,15 @@ class Sabre_CardDAV_Property_SupportedAddressData extends Sabre_DAV_Property { /** * supported versions - * - * @var array + * + * @var array */ protected $supportedData = array(); - + /** - * Creates the property - * - * @param array $components + * Creates the property + * + * @param array|null $supportedData */ public function __construct(array $supportedData = null) { @@ -35,22 +35,22 @@ class Sabre_CardDAV_Property_SupportedAddressData extends Sabre_DAV_Property { ); } - $this->supportedData = $supportedData; + $this->supportedData = $supportedData; } - + /** - * Serializes the property in a DOMDocument - * - * @param Sabre_DAV_Server $server - * @param DOMElement $node + * Serializes the property in a DOMDocument + * + * @param Sabre_DAV_Server $server + * @param DOMElement $node * @return void */ public function serialize(Sabre_DAV_Server $server,DOMElement $node) { $doc = $node->ownerDocument; - $prefix = + $prefix = isset($server->xmlNamespaces[Sabre_CardDAV_Plugin::NS_CARDDAV]) ? $server->xmlNamespaces[Sabre_CardDAV_Plugin::NS_CARDDAV] : 'card'; diff --git a/3rdparty/Sabre/CardDAV/UserAddressBooks.php b/3rdparty/Sabre/CardDAV/UserAddressBooks.php index e9f2de7f74..3f11fb1123 100644 --- a/3rdparty/Sabre/CardDAV/UserAddressBooks.php +++ b/3rdparty/Sabre/CardDAV/UserAddressBooks.php @@ -7,7 +7,7 @@ * * @package Sabre * @subpackage CardDAV - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. + * @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 */ @@ -15,47 +15,47 @@ class Sabre_CardDAV_UserAddressBooks extends Sabre_DAV_Collection implements Sab /** * Principal uri - * - * @var array + * + * @var array */ protected $principalUri; /** - * carddavBackend - * - * @var Sabre_CardDAV_Backend_Abstract + * carddavBackend + * + * @var Sabre_CardDAV_Backend_Abstract */ protected $carddavBackend; /** - * Constructor - * - * @param Sabre_CardDAV_Backend_Abstract $carddavBackend - * @param string $principalUri + * Constructor + * + * @param Sabre_CardDAV_Backend_Abstract $carddavBackend + * @param string $principalUri */ public function __construct(Sabre_CardDAV_Backend_Abstract $carddavBackend, $principalUri) { $this->carddavBackend = $carddavBackend; $this->principalUri = $principalUri; - + } /** - * Returns the name of this object - * + * Returns the name of this object + * * @return string */ public function getName() { - + list(,$name) = Sabre_DAV_URLUtil::splitPath($this->principalUri); - return $name; + return $name; } /** - * Updates the name of this object - * - * @param string $name + * Updates the name of this object + * + * @param string $name * @return void */ public function setName($name) { @@ -65,8 +65,8 @@ class Sabre_CardDAV_UserAddressBooks extends Sabre_DAV_Collection implements Sab } /** - * Deletes this object - * + * Deletes this object + * * @return void */ public function delete() { @@ -76,13 +76,13 @@ class Sabre_CardDAV_UserAddressBooks extends Sabre_DAV_Collection implements Sab } /** - * Returns the last modification date - * - * @return int + * Returns the last modification date + * + * @return int */ public function getLastModified() { - return null; + return null; } @@ -90,9 +90,9 @@ class Sabre_CardDAV_UserAddressBooks extends Sabre_DAV_Collection implements Sab * Creates a new file under this object. * * This is currently not allowed - * - * @param string $filename - * @param resource $data + * + * @param string $filename + * @param resource $data * @return void */ public function createFile($filename, $data=null) { @@ -105,8 +105,8 @@ class Sabre_CardDAV_UserAddressBooks extends Sabre_DAV_Collection implements Sab * Creates a new directory under this object. * * This is currently not allowed. - * - * @param string $filename + * + * @param string $filename * @return void */ public function createDirectory($filename) { @@ -116,8 +116,8 @@ class Sabre_CardDAV_UserAddressBooks extends Sabre_DAV_Collection implements Sab } /** - * Returns a single calendar, by name - * + * Returns a single calendar, by name + * * @param string $name * @todo needs optimizing * @return Sabre_CardDAV_AddressBook @@ -129,14 +129,14 @@ class Sabre_CardDAV_UserAddressBooks extends Sabre_DAV_Collection implements Sab return $child; } - throw new Sabre_DAV_Exception_FileNotFound('Addressbook with name \'' . $name . '\' could not be found'); + throw new Sabre_DAV_Exception_NotFound('Addressbook with name \'' . $name . '\' could not be found'); } /** - * Returns a list of addressbooks - * - * @return array + * Returns a list of addressbooks + * + * @return array */ public function getChildren() { @@ -150,11 +150,11 @@ class Sabre_CardDAV_UserAddressBooks extends Sabre_DAV_Collection implements Sab } /** - * Creates a new addressbook - * + * Creates a new addressbook + * * @param string $name - * @param array $resourceType - * @param array $properties + * @param array $resourceType + * @param array $properties * @return void */ public function createExtendedCollection($name, array $resourceType, array $properties) { @@ -169,8 +169,8 @@ class Sabre_CardDAV_UserAddressBooks extends Sabre_DAV_Collection implements Sab /** * Returns the owner principal * - * This must be a url to a principal, or null if there's no owner - * + * This must be a url to a principal, or null if there's no owner + * * @return string|null */ public function getOwner() { @@ -183,8 +183,8 @@ class Sabre_CardDAV_UserAddressBooks extends Sabre_DAV_Collection implements Sab * Returns a group principal * * This must be a url to a principal, or null if there's no owner - * - * @return string|null + * + * @return string|null */ public function getGroup() { @@ -196,13 +196,13 @@ class Sabre_CardDAV_UserAddressBooks extends Sabre_DAV_Collection implements Sab * 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 + * * '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 + * * 'protected' (optional), indicating that this ACE is not allowed to + * be updated. + * + * @return array */ public function getACL() { @@ -225,9 +225,9 @@ class Sabre_CardDAV_UserAddressBooks extends Sabre_DAV_Collection implements Sab /** * Updates the ACL * - * This method will receive a list of new ACE's. - * - * @param array $acl + * This method will receive a list of new ACE's. + * + * @param array $acl * @return void */ public function setACL(array $acl) { @@ -236,5 +236,22 @@ class Sabre_CardDAV_UserAddressBooks extends Sabre_DAV_Collection implements Sab } + /** + * Returns the list of supported privileges for this node. + * + * The returned data structure is a list of nested privileges. + * See Sabre_DAVACL_Plugin::getDefaultSupportedPrivilegeSet for a simple + * standard structure. + * + * If null is returned from this method, the default privilege set is used, + * which is fine for most common usecases. + * + * @return array|null + */ + public function getSupportedPrivilegeSet() { + + return null; + + } } diff --git a/3rdparty/Sabre/CardDAV/Version.php b/3rdparty/Sabre/CardDAV/Version.php index 900fbf5e75..811b929e39 100644 --- a/3rdparty/Sabre/CardDAV/Version.php +++ b/3rdparty/Sabre/CardDAV/Version.php @@ -4,10 +4,10 @@ * Version Class * * This class contains the Sabre_CardDAV version information - * + * * @package Sabre * @subpackage CardDAV - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. + * @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 */ @@ -16,7 +16,7 @@ class Sabre_CardDAV_Version { /** * Full version number */ - const VERSION = '1.5.3'; + const VERSION = '1.6.1'; /** * Stability : alpha, beta, stable diff --git a/3rdparty/Sabre/CardDAV/includes.php b/3rdparty/Sabre/CardDAV/includes.php new file mode 100644 index 0000000000..c3b8c04b07 --- /dev/null +++ b/3rdparty/Sabre/CardDAV/includes.php @@ -0,0 +1,32 @@ +setHTTPRequest($server->httpRequest); @@ -75,5 +79,5 @@ abstract class Sabre_DAV_Auth_Backend_AbstractBasic implements Sabre_DAV_Auth_IB } -} +} diff --git a/3rdparty/Sabre/DAV/Auth/Backend/AbstractDigest.php b/3rdparty/Sabre/DAV/Auth/Backend/AbstractDigest.php index 5bdc72753e..9833928b97 100644 --- a/3rdparty/Sabre/DAV/Auth/Backend/AbstractDigest.php +++ b/3rdparty/Sabre/DAV/Auth/Backend/AbstractDigest.php @@ -4,12 +4,12 @@ * HTTP Digest authentication backend class * * This class can be used by authentication objects wishing to use HTTP Digest - * Most of the digest logic is handled, implementors just need to worry about - * the getDigestHash method + * Most of the digest logic is handled, implementors just need to worry about + * the getDigestHash method * * @package Sabre * @subpackage DAV - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. + * @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 */ @@ -17,7 +17,7 @@ abstract class Sabre_DAV_Auth_Backend_AbstractDigest implements Sabre_DAV_Auth_I /** * This variable holds the currently logged in username. - * + * * @var array|null */ protected $currentUser; @@ -25,24 +25,26 @@ abstract class Sabre_DAV_Auth_Backend_AbstractDigest implements Sabre_DAV_Auth_I /** * Returns a users digest hash based on the username and realm. * - * If the user was not known, null must be returned. - * + * If the user was not known, null must be returned. + * * @param string $realm - * @param string $username - * @return string|null + * @param string $username + * @return string|null */ - abstract public function getDigestHash($realm,$username); + abstract public function getDigestHash($realm, $username); /** * Authenticates the user based on the current request. * - * If authentication is succesful, true must be returned. + * If authentication is successful, true must be returned. * If authentication fails, an exception must be thrown. * + * @param Sabre_DAV_Server $server + * @param string $realm * @throws Sabre_DAV_Exception_NotAuthenticated - * @return bool + * @return bool */ - public function authenticate(Sabre_DAV_Server $server,$realm) { + public function authenticate(Sabre_DAV_Server $server, $realm) { $digest = new Sabre_HTTP_DigestAuth(); @@ -83,9 +85,9 @@ abstract class Sabre_DAV_Auth_Backend_AbstractDigest implements Sabre_DAV_Auth_I } /** - * Returns the currently logged in username. - * - * @return string|null + * Returns the currently logged in username. + * + * @return string|null */ public function getCurrentUser() { diff --git a/3rdparty/Sabre/DAV/Auth/Backend/Apache.php b/3rdparty/Sabre/DAV/Auth/Backend/Apache.php index 6bcd76bdcb..d4294ea4d8 100644 --- a/3rdparty/Sabre/DAV/Auth/Backend/Apache.php +++ b/3rdparty/Sabre/DAV/Auth/Backend/Apache.php @@ -4,34 +4,36 @@ * Apache authenticator * * This authentication backend assumes that authentication has been - * conifgured in apache, rather than within SabreDAV. + * configured in apache, rather than within SabreDAV. * * Make sure apache is properly configured for this to work. * * @package Sabre * @subpackage DAV - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ class Sabre_DAV_Auth_Backend_Apache implements Sabre_DAV_Auth_IBackend { /** - * Current apache user - * - * @var string + * Current apache user + * + * @var string */ protected $remoteUser; - + /** * Authenticates the user based on the current request. * - * If authentication is succesful, true must be returned. + * If authentication is successful, true must be returned. * If authentication fails, an exception must be thrown. * - * @return bool + * @param Sabre_DAV_Server $server + * @param string $realm + * @return bool */ - public function authenticate(Sabre_DAV_Server $server,$realm) { + public function authenticate(Sabre_DAV_Server $server, $realm) { $remoteUser = $server->httpRequest->getRawServerValue('REMOTE_USER'); if (is_null($remoteUser)) { @@ -47,7 +49,7 @@ class Sabre_DAV_Auth_Backend_Apache implements Sabre_DAV_Auth_IBackend { * Returns information about the currently logged in user. * * If nobody is currently logged in, this method should return null. - * + * * @return array|null */ public function getCurrentUser() { diff --git a/3rdparty/Sabre/DAV/Auth/Backend/File.php b/3rdparty/Sabre/DAV/Auth/Backend/File.php index db1f04c477..de308d64a6 100644 --- a/3rdparty/Sabre/DAV/Auth/Backend/File.php +++ b/3rdparty/Sabre/DAV/Auth/Backend/File.php @@ -4,29 +4,28 @@ * This is an authentication backend that uses a file to manage passwords. * * The backend file must conform to Apache's htdigest format - * + * * @package Sabre * @subpackage DAV - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ class Sabre_DAV_Auth_Backend_File extends Sabre_DAV_Auth_Backend_AbstractDigest { /** - * List of users - * + * List of users + * * @var array */ protected $users = array(); /** - * Creates the backend object. + * Creates the backend object. * * If the filename argument is passed in, it will parse out the specified file fist. - * - * @param string $filename - * @return void + * + * @param string|null $filename */ public function __construct($filename=null) { @@ -38,22 +37,22 @@ class Sabre_DAV_Auth_Backend_File extends Sabre_DAV_Auth_Backend_AbstractDigest /** * Loads an htdigest-formatted file. This method can be called multiple times if * more than 1 file is used. - * - * @param string $filename + * + * @param string $filename * @return void */ public function loadFile($filename) { foreach(file($filename,FILE_IGNORE_NEW_LINES) as $line) { - if (substr_count($line, ":") !== 2) + if (substr_count($line, ":") !== 2) throw new Sabre_DAV_Exception('Malformed htdigest file. Every line should contain 2 colons'); - + list($username,$realm,$A1) = explode(':',$line); if (!preg_match('/^[a-zA-Z0-9]{32}$/', $A1)) throw new Sabre_DAV_Exception('Malformed htdigest file. Invalid md5 hash'); - + $this->users[$realm . ':' . $username] = $A1; } @@ -62,10 +61,10 @@ class Sabre_DAV_Auth_Backend_File extends Sabre_DAV_Auth_Backend_AbstractDigest /** * Returns a users' information - * - * @param string $realm - * @param string $username - * @return string + * + * @param string $realm + * @param string $username + * @return string */ public function getDigestHash($realm, $username) { diff --git a/3rdparty/Sabre/DAV/Auth/Backend/PDO.php b/3rdparty/Sabre/DAV/Auth/Backend/PDO.php index 0301503601..eac18a23fb 100644 --- a/3rdparty/Sabre/DAV/Auth/Backend/PDO.php +++ b/3rdparty/Sabre/DAV/Auth/Backend/PDO.php @@ -4,38 +4,37 @@ * This is an authentication backend that uses a file to manage passwords. * * The backend file must conform to Apache's htdigest format - * + * * @package Sabre * @subpackage DAV - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ class Sabre_DAV_Auth_Backend_PDO extends Sabre_DAV_Auth_Backend_AbstractDigest { /** - * Reference to PDO connection - * - * @var PDO + * Reference to PDO connection + * + * @var PDO */ protected $pdo; /** - * PDO table name we'll be using - * + * PDO table name we'll be using + * * @var string */ protected $tableName; /** - * Creates the backend object. + * Creates the backend object. * * If the filename argument is passed in, it will parse out the specified file fist. - * - * @param string $filename - * @param string $tableName The PDO table name to use - * @return void + * + * @param PDO $pdo + * @param string $tableName The PDO table name to use */ public function __construct(PDO $pdo, $tableName = 'users') { @@ -45,15 +44,15 @@ class Sabre_DAV_Auth_Backend_PDO extends Sabre_DAV_Auth_Backend_AbstractDigest { } /** - * Returns the digest hash for a user. - * - * @param string $realm - * @param string $username - * @return string|null + * Returns the digest hash for a user. + * + * @param string $realm + * @param string $username + * @return string|null */ public function getDigestHash($realm,$username) { - $stmt = $this->pdo->prepare('SELECT username, digesta1 FROM `'.$this->tableName.'` WHERE username = ?'); + $stmt = $this->pdo->prepare('SELECT username, digesta1 FROM '.$this->tableName.' WHERE username = ?'); $stmt->execute(array($username)); $result = $stmt->fetchAll(); diff --git a/3rdparty/Sabre/DAV/Auth/IBackend.php b/3rdparty/Sabre/DAV/Auth/IBackend.php index 1f67af4c2d..5be5d1bc93 100644 --- a/3rdparty/Sabre/DAV/Auth/IBackend.php +++ b/3rdparty/Sabre/DAV/Auth/IBackend.php @@ -5,7 +5,7 @@ * * @package Sabre * @subpackage DAV - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. + * @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 */ @@ -14,18 +14,20 @@ interface Sabre_DAV_Auth_IBackend { /** * Authenticates the user based on the current request. * - * If authentication is succesful, true must be returned. + * If authentication is successful, true must be returned. * If authentication fails, an exception must be thrown. * - * @return bool + * @param Sabre_DAV_Server $server + * @param string $realm + * @return bool */ - function authenticate(Sabre_DAV_Server $server,$realm); + function authenticate(Sabre_DAV_Server $server,$realm); /** * Returns information about the currently logged in username. * * If nobody is currently logged in, this method should return null. - * + * * @return string|null */ function getCurrentUser(); diff --git a/3rdparty/Sabre/DAV/Auth/Plugin.php b/3rdparty/Sabre/DAV/Auth/Plugin.php index f3718fcf46..55a4e39167 100644 --- a/3rdparty/Sabre/DAV/Auth/Plugin.php +++ b/3rdparty/Sabre/DAV/Auth/Plugin.php @@ -2,48 +2,47 @@ /** * This plugin provides Authentication for a WebDAV server. - * + * * It relies on a Backend object, which provides user information. * * Additionally, it provides support for: * * {DAV:}current-user-principal property from RFC5397 * * {DAV:}principal-collection-set property from RFC3744 - * + * * @package Sabre * @subpackage DAV - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. - * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. + * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ class Sabre_DAV_Auth_Plugin extends Sabre_DAV_ServerPlugin { /** - * Reference to main server object - * - * @var Sabre_DAV_Server + * Reference to main server object + * + * @var Sabre_DAV_Server */ private $server; /** * Authentication backend - * - * @var Sabre_DAV_Auth_Backend_Abstract + * + * @var Sabre_DAV_Auth_IBackend */ private $authBackend; /** - * The authentication realm. - * - * @var string + * The authentication realm. + * + * @var string */ private $realm; /** - * __construct - * - * @param Sabre_DAV_Auth_Backend_Abstract $authBackend - * @param string $realm - * @return void + * __construct + * + * @param Sabre_DAV_Auth_IBackend $authBackend + * @param string $realm */ public function __construct(Sabre_DAV_Auth_IBackend $authBackend, $realm) { @@ -53,9 +52,9 @@ class Sabre_DAV_Auth_Plugin extends Sabre_DAV_ServerPlugin { } /** - * Initializes the plugin. This function is automatically called by the server - * - * @param Sabre_DAV_Server $server + * Initializes the plugin. This function is automatically called by the server + * + * @param Sabre_DAV_Server $server * @return void */ public function initialize(Sabre_DAV_Server $server) { @@ -67,11 +66,11 @@ class Sabre_DAV_Auth_Plugin extends Sabre_DAV_ServerPlugin { /** * Returns a plugin name. - * + * * Using this name other plugins will be able to access other plugins - * using Sabre_DAV_Server::getPlugin - * - * @return string + * using Sabre_DAV_Server::getPlugin + * + * @return string */ public function getPluginName() { @@ -81,10 +80,10 @@ class Sabre_DAV_Auth_Plugin extends Sabre_DAV_ServerPlugin { /** * Returns the current users' principal uri. - * - * If nobody is logged in, this will return null. - * - * @return string|null + * + * If nobody is logged in, this will return null. + * + * @return string|null */ public function getCurrentUser() { @@ -97,10 +96,11 @@ class Sabre_DAV_Auth_Plugin extends Sabre_DAV_ServerPlugin { /** * This method is called before any HTTP method and forces users to be authenticated - * + * * @param string $method + * @param string $uri * @throws Sabre_DAV_Exception_NotAuthenticated - * @return bool + * @return bool */ public function beforeMethod($method, $uri) { diff --git a/3rdparty/Sabre/DAV/Browser/GuessContentType.php b/3rdparty/Sabre/DAV/Browser/GuessContentType.php index ee8c698d78..b6c00d461c 100644 --- a/3rdparty/Sabre/DAV/Browser/GuessContentType.php +++ b/3rdparty/Sabre/DAV/Browser/GuessContentType.php @@ -10,10 +10,10 @@ * There's really no accurate, fast and portable way to determine the contenttype * so this extension does what the rest of the world does, and guesses it based * on the file extension. - * + * * @package Sabre * @subpackage DAV - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. + * @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 */ @@ -43,9 +43,9 @@ class Sabre_DAV_Browser_GuessContentType extends Sabre_DAV_ServerPlugin { ); /** - * Initializes the plugin - * - * @param Sabre_DAV_Server $server + * Initializes the plugin + * + * @param Sabre_DAV_Server $server * @return void */ public function initialize(Sabre_DAV_Server $server) { @@ -57,16 +57,16 @@ class Sabre_DAV_Browser_GuessContentType extends Sabre_DAV_ServerPlugin { } /** - * Handler for teh afterGetProperties event - * - * @param string $path - * @param array $properties + * Handler for teh afterGetProperties event + * + * @param string $path + * @param array $properties * @return void */ public function afterGetProperties($path, &$properties) { if (array_key_exists('{DAV:}getcontenttype', $properties[404])) { - + list(, $fileName) = Sabre_DAV_URLUtil::splitPath($path); $contentType = $this->getContentType($fileName); @@ -81,9 +81,9 @@ class Sabre_DAV_Browser_GuessContentType extends Sabre_DAV_ServerPlugin { /** * Simple method to return the contenttype - * - * @param string $fileName - * @return string + * + * @param string $fileName + * @return string */ protected function getContentType($fileName) { diff --git a/3rdparty/Sabre/DAV/Browser/MapGetToPropFind.php b/3rdparty/Sabre/DAV/Browser/MapGetToPropFind.php index a66b57a3a9..1588488764 100644 --- a/3rdparty/Sabre/DAV/Browser/MapGetToPropFind.php +++ b/3rdparty/Sabre/DAV/Browser/MapGetToPropFind.php @@ -1,30 +1,30 @@ server->tree->getNodeForPath($uri); if ($node instanceof Sabre_DAV_IFile) return; $this->server->invokeMethod('PROPFIND',$uri); return false; - + } } diff --git a/3rdparty/Sabre/DAV/Browser/Plugin.php b/3rdparty/Sabre/DAV/Browser/Plugin.php index cd5617babb..09bbdd2ae0 100644 --- a/3rdparty/Sabre/DAV/Browser/Plugin.php +++ b/3rdparty/Sabre/DAV/Browser/Plugin.php @@ -6,77 +6,126 @@ * This plugin provides a html representation, so that a WebDAV server may be accessed * using a browser. * - * The class intercepts GET requests to collection resources and generates a simple - * html index. - * + * The class intercepts GET requests to collection resources and generates a simple + * html index. + * * @package Sabre * @subpackage DAV - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ class Sabre_DAV_Browser_Plugin extends Sabre_DAV_ServerPlugin { /** - * reference to server class - * - * @var Sabre_DAV_Server + * List of default icons for nodes. + * + * This is an array with class / interface names as keys, and asset names + * as values. + * + * The evaluation order is reversed. The last item in the list gets + * precendence. + * + * @var array + */ + public $iconMap = array( + 'Sabre_DAV_IFile' => 'icons/file', + 'Sabre_DAV_ICollection' => 'icons/collection', + 'Sabre_DAVACL_IPrincipal' => 'icons/principal', + 'Sabre_CalDAV_ICalendar' => 'icons/calendar', + 'Sabre_CardDAV_IAddressBook' => 'icons/addressbook', + 'Sabre_CardDAV_ICard' => 'icons/card', + ); + + /** + * The file extension used for all icons + * + * @var string + */ + public $iconExtension = '.png'; + + /** + * reference to server class + * + * @var Sabre_DAV_Server */ protected $server; /** - * enableEditing - * - * @var bool + * enablePost turns on the 'actions' panel, which allows people to create + * folders and upload files straight from a browser. + * + * @var bool */ protected $enablePost = true; + /** + * By default the browser plugin will generate a favicon and other images. + * To turn this off, set this property to false. + * + * @var bool + */ + protected $enableAssets = true; + /** * Creates the object. * * By default it will allow file creation and uploads. * Specify the first argument as false to disable this - * - * @param bool $enablePost - * @return void + * + * @param bool $enablePost + * @param bool $enableAssets */ - public function __construct($enablePost=true) { + public function __construct($enablePost=true, $enableAssets = true) { - $this->enablePost = $enablePost; + $this->enablePost = $enablePost; + $this->enableAssets = $enableAssets; } /** - * Initializes the plugin and subscribes to events - * - * @param Sabre_DAV_Server $server + * Initializes the plugin and subscribes to events + * + * @param Sabre_DAV_Server $server * @return void */ public function initialize(Sabre_DAV_Server $server) { $this->server = $server; $this->server->subscribeEvent('beforeMethod',array($this,'httpGetInterceptor')); + $this->server->subscribeEvent('onHTMLActionsPanel', array($this, 'htmlActionsPanel'),200); if ($this->enablePost) $this->server->subscribeEvent('unknownMethod',array($this,'httpPOSTHandler')); } /** - * This method intercepts GET requests to collections and returns the html - * - * @param string $method - * @return bool + * This method intercepts GET requests to collections and returns the html + * + * @param string $method + * @param string $uri + * @return bool */ public function httpGetInterceptor($method, $uri) { - if ($method!='GET') return true; + if ($method !== 'GET') return true; - try { + // We're not using straight-up $_GET, because we want everything to be + // unit testable. + $getVars = array(); + parse_str($this->server->httpRequest->getQueryString(), $getVars); + + if (isset($getVars['sabreAction']) && $getVars['sabreAction'] === 'asset' && isset($getVars['assetName'])) { + $this->serveAsset($getVars['assetName']); + return false; + } + + try { $node = $this->server->tree->getNodeForPath($uri); - } catch (Sabre_DAV_Exception_FileNotFound $e) { - // We're simply stopping when the file isn't found to not interfere + } catch (Sabre_DAV_Exception_NotFound $e) { + // We're simply stopping when the file isn't found to not interfere // with other plugins. return; } - if ($node instanceof Sabre_DAV_IFile) + if ($node instanceof Sabre_DAV_IFile) return; $this->server->httpResponse->sendStatus(200); @@ -87,57 +136,71 @@ class Sabre_DAV_Browser_Plugin extends Sabre_DAV_ServerPlugin { ); return false; - + } /** - * Handles POST requests for tree operations - * - * This method is not yet used. - * - * @param string $method + * Handles POST requests for tree operations. + * + * @param string $method + * @param string $uri * @return bool */ public function httpPOSTHandler($method, $uri) { - if ($method!='POST') return true; - if (isset($_POST['sabreAction'])) switch($_POST['sabreAction']) { + if ($method!='POST') return; + $contentType = $this->server->httpRequest->getHeader('Content-Type'); + list($contentType) = explode(';', $contentType); + if ($contentType !== 'application/x-www-form-urlencoded' && + $contentType !== 'multipart/form-data') { + return; + } + $postVars = $this->server->httpRequest->getPostVars(); - case 'mkcol' : - if (isset($_POST['name']) && trim($_POST['name'])) { - // Using basename() because we won't allow slashes - list(, $folderName) = Sabre_DAV_URLUtil::splitPath(trim($_POST['name'])); - $this->server->createDirectory($uri . '/' . $folderName); - } - break; - case 'put' : - if ($_FILES) $file = current($_FILES); - else break; - $newName = trim($file['name']); - list(, $newName) = Sabre_DAV_URLUtil::splitPath(trim($file['name'])); - if (isset($_POST['name']) && trim($_POST['name'])) - $newName = trim($_POST['name']); + if (!isset($postVars['sabreAction'])) + return; - // Making sure we only have a 'basename' component - list(, $newName) = Sabre_DAV_URLUtil::splitPath($newName); - - - if (is_uploaded_file($file['tmp_name'])) { - $parent = $this->server->tree->getNodeForPath(trim($uri,'/')); - $parent->createFile($newName,fopen($file['tmp_name'],'r')); - } + if ($this->server->broadcastEvent('onBrowserPostAction', array($uri, $postVars['sabreAction'], $postVars))) { + + switch($postVars['sabreAction']) { + + case 'mkcol' : + if (isset($postVars['name']) && trim($postVars['name'])) { + // Using basename() because we won't allow slashes + list(, $folderName) = Sabre_DAV_URLUtil::splitPath(trim($postVars['name'])); + $this->server->createDirectory($uri . '/' . $folderName); + } + break; + case 'put' : + if ($_FILES) $file = current($_FILES); + else break; + + list(, $newName) = Sabre_DAV_URLUtil::splitPath(trim($file['name'])); + if (isset($postVars['name']) && trim($postVars['name'])) + $newName = trim($postVars['name']); + + // Making sure we only have a 'basename' component + list(, $newName) = Sabre_DAV_URLUtil::splitPath($newName); + + if (is_uploaded_file($file['tmp_name'])) { + $this->server->createFile($uri . '/' . $newName, fopen($file['tmp_name'],'r')); + } + break; + + } } $this->server->httpResponse->setHeader('Location',$this->server->httpRequest->getUri()); + $this->server->httpResponse->sendStatus(302); return false; } /** - * Escapes a string for html. - * - * @param string $value - * @return void + * Escapes a string for html. + * + * @param string $value + * @return string */ public function escapeHTML($value) { @@ -146,118 +209,199 @@ class Sabre_DAV_Browser_Plugin extends Sabre_DAV_ServerPlugin { } /** - * Generates the html directory index for a given url + * Generates the html directory index for a given url * - * @param string $path - * @return string + * @param string $path + * @return string */ public function generateDirectoryIndex($path) { + $version = ''; + if (Sabre_DAV_Server::$exposeVersion) { + $version = Sabre_DAV_Version::VERSION ."-". Sabre_DAV_Version::STABILITY; + } + $html = " - Index for " . $this->escapeHTML($path) . "/ - SabreDAV " . Sabre_DAV_Version::VERSION . " - - + Index for " . $this->escapeHTML($path) . "/ - SabreDAV " . $version . " + + "; + + if ($this->enableAssets) { + $html.=''; + } + + $html .= "

Index for " . $this->escapeHTML($path) . "/

- - "; - - $files = $this->server->getPropertiesForPath($path,array( - '{DAV:}displayname', - '{DAV:}resourcetype', - '{DAV:}getcontenttype', - '{DAV:}getcontentlength', - '{DAV:}getlastmodified', - ),1); + + "; - $parent = $this->server->tree->getNodeForPath($path); + $files = $this->server->getPropertiesForPath($path,array( + '{DAV:}displayname', + '{DAV:}resourcetype', + '{DAV:}getcontenttype', + '{DAV:}getcontentlength', + '{DAV:}getlastmodified', + ),1); + + $parent = $this->server->tree->getNodeForPath($path); - if ($path) { + if ($path) { - list($parentUri) = Sabre_DAV_URLUtil::splitPath($path); - $fullPath = Sabre_DAV_URLUtil::encodePath($this->server->getBaseUri() . $parentUri); + list($parentUri) = Sabre_DAV_URLUtil::splitPath($path); + $fullPath = Sabre_DAV_URLUtil::encodePath($this->server->getBaseUri() . $parentUri); - $html.= " - - - - -"; + $icon = $this->enableAssets?'Parent':''; + $html.= " + + + + + + "; - } + } - foreach($files as $k=>$file) { + foreach($files as $file) { - // This is the current directory, we can skip it - if (rtrim($file['href'],'/')==$path) continue; + // This is the current directory, we can skip it + if (rtrim($file['href'],'/')==$path) continue; - list(, $name) = Sabre_DAV_URLUtil::splitPath($file['href']); + list(, $name) = Sabre_DAV_URLUtil::splitPath($file['href']); - $type = null; + $type = null; - if (isset($file[200]['{DAV:}resourcetype'])) { - $type = $file[200]['{DAV:}resourcetype']->getValue(); + if (isset($file[200]['{DAV:}resourcetype'])) { + $type = $file[200]['{DAV:}resourcetype']->getValue(); - // resourcetype can have multiple values - if (!is_array($type)) $type = array($type); + // resourcetype can have multiple values + if (!is_array($type)) $type = array($type); - foreach($type as $k=>$v) { + foreach($type as $k=>$v) { - // Some name mapping is preferred - switch($v) { - case '{DAV:}collection' : - $type[$k] = 'Collection'; - break; - case '{DAV:}principal' : - $type[$k] = 'Principal'; - break; - case '{urn:ietf:params:xml:ns:carddav}addressbook' : - $type[$k] = 'Addressbook'; - break; - case '{urn:ietf:params:xml:ns:caldav}calendar' : - $type[$k] = 'Calendar'; + // Some name mapping is preferred + switch($v) { + case '{DAV:}collection' : + $type[$k] = 'Collection'; + break; + case '{DAV:}principal' : + $type[$k] = 'Principal'; + break; + case '{urn:ietf:params:xml:ns:carddav}addressbook' : + $type[$k] = 'Addressbook'; + break; + case '{urn:ietf:params:xml:ns:caldav}calendar' : + $type[$k] = 'Calendar'; + break; + case '{urn:ietf:params:xml:ns:caldav}schedule-inbox' : + $type[$k] = 'Schedule Inbox'; + break; + case '{urn:ietf:params:xml:ns:caldav}schedule-outbox' : + $type[$k] = 'Schedule Outbox'; + break; + case '{http://calendarserver.org/ns/}calendar-proxy-read' : + $type[$k] = 'Proxy-Read'; + break; + case '{http://calendarserver.org/ns/}calendar-proxy-write' : + $type[$k] = 'Proxy-Write'; + break; + } + + } + $type = implode(', ', $type); + } + + // If no resourcetype was found, we attempt to use + // the contenttype property + if (!$type && isset($file[200]['{DAV:}getcontenttype'])) { + $type = $file[200]['{DAV:}getcontenttype']; + } + if (!$type) $type = 'Unknown'; + + $size = isset($file[200]['{DAV:}getcontentlength'])?(int)$file[200]['{DAV:}getcontentlength']:''; + $lastmodified = isset($file[200]['{DAV:}getlastmodified'])?$file[200]['{DAV:}getlastmodified']->getTime()->format(DateTime::ATOM):''; + + $fullPath = Sabre_DAV_URLUtil::encodePath('/' . trim($this->server->getBaseUri() . ($path?$path . '/':'') . $name,'/')); + + $displayName = isset($file[200]['{DAV:}displayname'])?$file[200]['{DAV:}displayname']:$name; + + $displayName = $this->escapeHTML($displayName); + $type = $this->escapeHTML($type); + + $icon = ''; + + if ($this->enableAssets) { + $node = $parent->getChild($name); + foreach(array_reverse($this->iconMap) as $class=>$iconName) { + + if ($node instanceof $class) { + $icon = ''; break; + } + + } } - $type = implode(', ', $type); + + $html.= " + + + + + + "; + } - // If no resourcetype was found, we attempt to use - // the contenttype property - if (!$type && isset($file[200]['{DAV:}getcontenttype'])) { - $type = $file[200]['{DAV:}getcontenttype']; + $html.= ""; + + $output = ''; + + if ($this->enablePost) { + $this->server->broadcastEvent('onHTMLActionsPanel',array($parent, &$output)); } - if (!$type) $type = 'Unknown'; - $size = isset($file[200]['{DAV:}getcontentlength'])?(int)$file[200]['{DAV:}getcontentlength']:''; - $lastmodified = isset($file[200]['{DAV:}getlastmodified'])?$file[200]['{DAV:}getlastmodified']->getTime()->format(DateTime::ATOM):''; + $html.=$output; - $fullPath = Sabre_DAV_URLUtil::encodePath('/' . trim($this->server->getBaseUri() . ($path?$path . '/':'') . $name,'/')); + $html.= "
NameTypeSizeLast modified

NameTypeSizeLast modified

..[parent]
$icon..[parent]
$icon{$displayName}{$type}{$size}{$lastmodified}

+
Generated by SabreDAV " . $version . " (c)2007-2012 http://code.google.com/p/sabredav/
+ + "; - $displayName = isset($file[200]['{DAV:}displayname'])?$file[200]['{DAV:}displayname']:$name; - - $name = $this->escapeHTML($name); - $displayName = $this->escapeHTML($displayName); - $type = $this->escapeHTML($type); - - $html.= " -{$displayName} -{$type} -{$size} -{$lastmodified} -"; + return $html; } - $html.= "
"; + /** + * This method is used to generate the 'actions panel' output for + * collections. + * + * This specifically generates the interfaces for creating new files, and + * creating new directories. + * + * @param Sabre_DAV_INode $node + * @param mixed $output + * @return void + */ + public function htmlActionsPanel(Sabre_DAV_INode $node, &$output) { - if ($this->enablePost && $parent instanceof Sabre_DAV_ICollection) { - $html.= '
+ if (!$node instanceof Sabre_DAV_ICollection) + return; + + // We also know fairly certain that if an object is a non-extended + // SimpleCollection, we won't need to show the panel either. + if (get_class($node)==='Sabre_DAV_SimpleCollection') + return; + + $output.= '

Create new folder

Name:
@@ -270,15 +414,75 @@ class Sabre_DAV_Browser_Plugin extends Sabre_DAV_ServerPlugin { File:
- '; - } + '; - $html.= " -
Generated by SabreDAV " . Sabre_DAV_Version::VERSION ."-". Sabre_DAV_Version::STABILITY . " (c)2007-2011 http://code.google.com/p/sabredav/
- -"; + } - return $html; + /** + * This method takes a path/name of an asset and turns it into url + * suiteable for http access. + * + * @param string $assetName + * @return string + */ + protected function getAssetUrl($assetName) { + + return $this->server->getBaseUri() . '?sabreAction=asset&assetName=' . urlencode($assetName); + + } + + /** + * This method returns a local pathname to an asset. + * + * @param string $assetName + * @return string + */ + protected function getLocalAssetPath($assetName) { + + // Making sure people aren't trying to escape from the base path. + $assetSplit = explode('/', $assetName); + if (in_array('..',$assetSplit)) { + throw new Sabre_DAV_Exception('Incorrect asset path'); + } + $path = __DIR__ . '/assets/' . $assetName; + return $path; + + } + + /** + * This method reads an asset from disk and generates a full http response. + * + * @param string $assetName + * @return void + */ + protected function serveAsset($assetName) { + + $assetPath = $this->getLocalAssetPath($assetName); + if (!file_exists($assetPath)) { + throw new Sabre_DAV_Exception_NotFound('Could not find an asset with this name'); + } + // Rudimentary mime type detection + switch(strtolower(substr($assetPath,strpos($assetPath,'.')+1))) { + + case 'ico' : + $mime = 'image/vnd.microsoft.icon'; + break; + + case 'png' : + $mime = 'image/png'; + break; + + default: + $mime = 'application/octet-stream'; + break; + + } + + $this->server->httpResponse->setHeader('Content-Type', $mime); + $this->server->httpResponse->setHeader('Content-Length', filesize($assetPath)); + $this->server->httpResponse->setHeader('Cache-Control', 'public, max-age=1209600'); + $this->server->httpResponse->sendStatus(200); + $this->server->httpResponse->sendBody(fopen($assetPath,'r')); } diff --git a/3rdparty/Sabre/DAV/Browser/assets/favicon.ico b/3rdparty/Sabre/DAV/Browser/assets/favicon.ico new file mode 100644 index 0000000000..2b2c10a22c Binary files /dev/null and b/3rdparty/Sabre/DAV/Browser/assets/favicon.ico differ diff --git a/3rdparty/Sabre/DAV/Browser/assets/icons/addressbook.png b/3rdparty/Sabre/DAV/Browser/assets/icons/addressbook.png new file mode 100644 index 0000000000..c9acc84172 Binary files /dev/null and b/3rdparty/Sabre/DAV/Browser/assets/icons/addressbook.png differ diff --git a/3rdparty/Sabre/DAV/Browser/assets/icons/calendar.png b/3rdparty/Sabre/DAV/Browser/assets/icons/calendar.png new file mode 100644 index 0000000000..3ecd6a800a Binary files /dev/null and b/3rdparty/Sabre/DAV/Browser/assets/icons/calendar.png differ diff --git a/3rdparty/Sabre/DAV/Browser/assets/icons/card.png b/3rdparty/Sabre/DAV/Browser/assets/icons/card.png new file mode 100644 index 0000000000..2ce954866d Binary files /dev/null and b/3rdparty/Sabre/DAV/Browser/assets/icons/card.png differ diff --git a/3rdparty/Sabre/DAV/Browser/assets/icons/collection.png b/3rdparty/Sabre/DAV/Browser/assets/icons/collection.png new file mode 100644 index 0000000000..156fa64fd5 Binary files /dev/null and b/3rdparty/Sabre/DAV/Browser/assets/icons/collection.png differ diff --git a/3rdparty/Sabre/DAV/Browser/assets/icons/file.png b/3rdparty/Sabre/DAV/Browser/assets/icons/file.png new file mode 100644 index 0000000000..3b98551cec Binary files /dev/null and b/3rdparty/Sabre/DAV/Browser/assets/icons/file.png differ diff --git a/3rdparty/Sabre/DAV/Browser/assets/icons/parent.png b/3rdparty/Sabre/DAV/Browser/assets/icons/parent.png new file mode 100644 index 0000000000..156fa64fd5 Binary files /dev/null and b/3rdparty/Sabre/DAV/Browser/assets/icons/parent.png differ diff --git a/3rdparty/Sabre/DAV/Browser/assets/icons/principal.png b/3rdparty/Sabre/DAV/Browser/assets/icons/principal.png new file mode 100644 index 0000000000..f8988f828e Binary files /dev/null and b/3rdparty/Sabre/DAV/Browser/assets/icons/principal.png differ diff --git a/3rdparty/Sabre/DAV/Client.php b/3rdparty/Sabre/DAV/Client.php index fc6a6fff08..a8320dd978 100644 --- a/3rdparty/Sabre/DAV/Client.php +++ b/3rdparty/Sabre/DAV/Client.php @@ -3,14 +3,14 @@ /** * SabreDAV DAV client * - * This client wraps around Curl to provide a convenient API to a WebDAV + * This client wraps around Curl to provide a convenient API to a WebDAV * server. * * NOTE: This class is experimental, it's api will likely change in the future. - * + * * @package Sabre * @subpackage DAVClient - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. + * @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 */ @@ -26,22 +26,22 @@ class Sabre_DAV_Client { /** * Constructor * - * Settings are provided through the 'settings' argument. The following + * Settings are provided through the 'settings' argument. The following * settings are supported: * * * baseUri * * userName (optional) * * password (optional) * * proxy (optional) - * - * @param array $settings + * + * @param array $settings */ public function __construct(array $settings) { if (!isset($settings['baseUri'])) { throw new InvalidArgumentException('A baseUri must be provided'); } - + $validSettings = array( 'baseUri', 'userName', @@ -62,23 +62,23 @@ class Sabre_DAV_Client { /** * Does a PROPFIND request * - * The list of requested properties must be specified as an array, in clark - * notation. + * The list of requested properties must be specified as an array, in clark + * notation. * - * The returned array will contain a list of filenames as keys, and + * The returned array will contain a list of filenames as keys, and * properties as values. * - * The properties array will contain the list of properties. Only properties - * that are actually returned from the server (without error) will be + * The properties array will contain the list of properties. Only properties + * that are actually returned from the server (without error) will be * returned, anything else is discarded. * - * Depth should be either 0 or 1. A depth of 1 will cause a request to be + * Depth should be either 0 or 1. A depth of 1 will cause a request to be * made to the server to also return all child resources. * - * @param string $url - * @param array $properties - * @param int $depth - * @return array + * @param string $url + * @param array $properties + * @param int $depth + * @return array */ public function propFind($url, array $properties, $depth = 0) { @@ -132,14 +132,14 @@ class Sabre_DAV_Client { /** * Updates a list of properties on the server * - * The list of properties must have clark-notation properties for the keys, - * and the actual (string) value for the value. If the value is null, an - * attempt is made to delete the property. + * The list of properties must have clark-notation properties for the keys, + * and the actual (string) value for the value. If the value is null, an + * attempt is made to delete the property. * - * @todo Must be building the request using the DOM, and does not yet - * support complex properties. - * @param string $url - * @param array $properties + * @todo Must be building the request using the DOM, and does not yet + * support complex properties. + * @param string $url + * @param array $properties * @return void */ public function propPatch($url, array $properties) { @@ -175,7 +175,7 @@ class Sabre_DAV_Client { $body.=" "; } // Shitty.. i know - $body.=htmlspecialchars($propValue, ENT_NOQUOTES, 'UTF-8'); + $body.=htmlspecialchars($propValue, ENT_NOQUOTES, 'UTF-8'); if ($namespace === 'DAV:') { $body.='' . "\n"; } else { @@ -189,7 +189,7 @@ class Sabre_DAV_Client { $body.= ''; - $response = $this->request('PROPPATCH', $url, $body, array( + $this->request('PROPPATCH', $url, $body, array( 'Content-Type' => 'application/xml' )); @@ -198,11 +198,11 @@ class Sabre_DAV_Client { /** * Performs an HTTP options request * - * This method returns all the features from the 'DAV:' header as an array. - * If there was no DAV header, or no contents this method will return an - * empty array. - * - * @return array + * This method returns all the features from the 'DAV:' header as an array. + * If there was no DAV header, or no contents this method will return an + * empty array. + * + * @return array */ public function options() { @@ -222,20 +222,20 @@ class Sabre_DAV_Client { /** * Performs an actual HTTP request, and returns the result. * - * If the specified url is relative, it will be expanded based on the base + * If the specified url is relative, it will be expanded based on the base * url. * * The returned array contains 3 keys: * * body - the response body * * httpCode - a HTTP code (200, 404, etc) - * * headers - a list of response http headers. The header names have + * * headers - a list of response http headers. The header names have * been lowercased. * - * @param string $method - * @param string $url - * @param string $body - * @param array $headers - * @return array + * @param string $method + * @param string $url + * @param string $body + * @param array $headers + * @return array */ public function request($method, $url = '', $body = null, $headers = array()) { @@ -243,14 +243,37 @@ class Sabre_DAV_Client { $curlSettings = array( CURLOPT_RETURNTRANSFER => true, - CURLOPT_CUSTOMREQUEST => $method, - CURLOPT_POSTFIELDS => $body, // Return headers as part of the response - CURLOPT_HEADER => true + CURLOPT_HEADER => true, + CURLOPT_POSTFIELDS => $body, + // Automatically follow redirects + CURLOPT_FOLLOWLOCATION => true, + CURLOPT_MAXREDIRS => 5, ); + switch ($method) { + case 'PUT': + $curlSettings[CURLOPT_PUT] = true; + break; + case 'HEAD' : + + // do not read body with HEAD requests (this is neccessary because cURL does not ignore the body with HEAD + // requests when the Content-Length header is given - which in turn is perfectly valid according to HTTP + // specs...) cURL does unfortunately return an error in this case ("transfer closed transfer closed with + // ... bytes remaining to read") this can be circumvented by explicitly telling cURL to ignore the + // response body + $curlSettings[CURLOPT_NOBODY] = true; + $curlSettings[CURLOPT_CUSTOMREQUEST] = 'HEAD'; + break; + + default: + $curlSettings[CURLOPT_CUSTOMREQUEST] = $method; + break; + + } + // Adding HTTP headers - $nHeaders = array(); + $nHeaders = array(); foreach($headers as $key=>$value) { $nHeaders[] = $key . ': ' . $value; @@ -277,17 +300,17 @@ class Sabre_DAV_Client { $headerBlob = substr($response, 0, $curlInfo['header_size']); $response = substr($response, $curlInfo['header_size']); - // In the case of 100 Continue, or redirects we'll have multiple lists - // of headers for each separate HTTP response. We can easily split this + // In the case of 100 Continue, or redirects we'll have multiple lists + // of headers for each separate HTTP response. We can easily split this // because they are separated by \r\n\r\n $headerBlob = explode("\r\n\r\n", trim($headerBlob, "\r\n")); - + // We only care about the last set of headers $headerBlob = $headerBlob[count($headerBlob)-1]; // Splitting headers $headerBlob = explode("\r\n", $headerBlob); - + $headers = array(); foreach($headerBlob as $header) { $parts = explode(':', $header, 2); @@ -304,10 +327,17 @@ class Sabre_DAV_Client { if ($curlErrNo) { throw new Sabre_DAV_Exception('[CURL] Error while making request: ' . $curlError . ' (error code: ' . $curlErrNo . ')'); - } + } if ($response['statusCode']>=400) { - throw new Sabre_DAV_Exception('HTTP error response. (errorcode ' . $response['statusCode'] . ')'); + switch ($response['statusCode']) { + case 404: + throw new Sabre_DAV_Exception_NotFound('Resource ' . $url . ' not found.'); + break; + + default: + throw new Sabre_DAV_Exception('HTTP error response. (errorcode ' . $response['statusCode'] . ')'); + } } return $response; @@ -317,12 +347,12 @@ class Sabre_DAV_Client { /** * Wrapper for all curl functions. * - * The only reason this was split out in a separate method, is so it - * becomes easier to unittest. + * The only reason this was split out in a separate method, is so it + * becomes easier to unittest. * * @param string $url - * @param array $settings - * @return + * @param array $settings + * @return array */ protected function curlRequest($url, $settings) { @@ -339,20 +369,20 @@ class Sabre_DAV_Client { } /** - * Returns the full url based on the given url (which may be relative). All - * urls are expanded based on the base url as given by the server. - * - * @param string $url - * @return string + * Returns the full url based on the given url (which may be relative). All + * urls are expanded based on the base url as given by the server. + * + * @param string $url + * @return string */ protected function getAbsoluteUrl($url) { - // If the url starts with http:// or https://, the url is already absolute. + // If the url starts with http:// or https://, the url is already absolute. if (preg_match('/^http(s?):\/\//', $url)) { return $url; } - // If the url starts with a slash, we must calculate the url based off + // If the url starts with a slash, we must calculate the url based off // the root of the base url. if (strpos($url,'/') === 0) { $parts = parse_url($this->baseUri); @@ -366,7 +396,7 @@ class Sabre_DAV_Client { /** * Parses a WebDAV multistatus response body - * + * * This method returns an array with the following structure * * array( @@ -387,7 +417,7 @@ class Sabre_DAV_Client { * * * @param string $body xml body - * @return array + * @return array */ public function parseMultiStatus($body) { @@ -397,14 +427,13 @@ class Sabre_DAV_Client { if ($responseXML===false) { throw new InvalidArgumentException('The passed data is not valid XML'); } - - $responseXML->registerXPathNamespace('d','DAV:'); + + $responseXML->registerXPathNamespace('d', 'urn:DAV'); $propResult = array(); foreach($responseXML->xpath('d:response') as $response) { - - $response->registerXPathNamespace('d','DAV:'); + $response->registerXPathNamespace('d', 'urn:DAV'); $href = $response->xpath('d:href'); $href = (string)$href[0]; @@ -412,11 +441,11 @@ class Sabre_DAV_Client { foreach($response->xpath('d:propstat') as $propStat) { - $propStat->registerXPathNamespace('d','DAV:'); + $propStat->registerXPathNamespace('d', 'urn:DAV'); $status = $propStat->xpath('d:status'); list($httpVersion, $statusCode, $message) = explode(' ', (string)$status[0],3); - $properties[$statusCode] = Sabre_DAV_XMLUtil::parseProperties(dom_import_simplexml($propStat), $this->propertyMap); + $properties[$statusCode] = Sabre_DAV_XMLUtil::parseProperties(dom_import_simplexml($propStat), $this->propertyMap); } diff --git a/3rdparty/Sabre/DAV/Collection.php b/3rdparty/Sabre/DAV/Collection.php index 9da04c1279..776c22531b 100644 --- a/3rdparty/Sabre/DAV/Collection.php +++ b/3rdparty/Sabre/DAV/Collection.php @@ -4,12 +4,12 @@ * Collection class * * This is a helper class, that should aid in getting collections classes setup. - * Most of its methods are implemented, and throw permission denied exceptions - * + * Most of its methods are implemented, and throw permission denied exceptions + * * @package Sabre * @subpackage DAV - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. - * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. + * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ abstract class Sabre_DAV_Collection extends Sabre_DAV_Node implements Sabre_DAV_ICollection { @@ -17,12 +17,12 @@ 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 - * + * * @param string $name - * @throws Sabre_DAV_Exception_FileNotFound - * @return Sabre_DAV_INode + * @throws Sabre_DAV_Exception_NotFound + * @return Sabre_DAV_INode */ public function getChild($name) { @@ -31,7 +31,7 @@ abstract class Sabre_DAV_Collection extends Sabre_DAV_Node implements Sabre_DAV_ if ($child->getName()==$name) return $child; } - throw new Sabre_DAV_Exception_FileNotFound('File not found: ' . $name); + throw new Sabre_DAV_Exception_NotFound('File not found: ' . $name); } @@ -39,9 +39,9 @@ abstract class Sabre_DAV_Collection extends Sabre_DAV_Node implements Sabre_DAV_ * Checks is a child-node exists. * * It is generally a good idea to try and override this. Usually it can be optimized. - * - * @param string $name - * @return bool + * + * @param string $name + * @return bool */ public function childExists($name) { @@ -50,7 +50,7 @@ abstract class Sabre_DAV_Collection extends Sabre_DAV_Node implements Sabre_DAV_ $this->getChild($name); return true; - } catch(Sabre_DAV_Exception_FileNotFound $e) { + } catch(Sabre_DAV_Exception_NotFound $e) { return false; @@ -59,12 +59,28 @@ abstract class Sabre_DAV_Collection extends Sabre_DAV_Node implements Sabre_DAV_ } /** - * Creates a new file in the directory - * - * @param string $name Name of the file - * @param resource $data Initial payload, passed as a readable stream resource. - * @throws Sabre_DAV_Exception_Forbidden - * @return void + * Creates a new file in the directory + * + * Data will either be supplied as a stream resource, or in certain cases + * as a string. Keep in mind that you may have to support either. + * + * After succesful creation of the file, you may choose to return the ETag + * of the new file here. + * + * The returned ETag must be surrounded by double-quotes (The quotes should + * be part of the actual string). + * + * If you cannot accurately determine the ETag, you should not return it. + * If you don't store the file exactly as-is (you're transforming it + * somehow) you should also not return an ETag. + * + * This means that if a subsequent GET to this new file does not exactly + * return the same contents of what was submitted here, you are strongly + * recommended to omit the ETag. + * + * @param string $name Name of the file + * @param resource|string $data Initial payload + * @return null|string */ public function createFile($name, $data = null) { @@ -73,9 +89,9 @@ abstract class Sabre_DAV_Collection extends Sabre_DAV_Node implements Sabre_DAV_ } /** - * Creates a new subdirectory - * - * @param string $name + * Creates a new subdirectory + * + * @param string $name * @throws Sabre_DAV_Exception_Forbidden * @return void */ diff --git a/3rdparty/Sabre/DAV/Directory.php b/3rdparty/Sabre/DAV/Directory.php index 86af4827b3..6db8febc02 100644 --- a/3rdparty/Sabre/DAV/Directory.php +++ b/3rdparty/Sabre/DAV/Directory.php @@ -8,7 +8,7 @@ * @package Sabre * @subpackage DAV * @deprecated Use Sabre_DAV_Collection instead - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. + * @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 */ diff --git a/3rdparty/Sabre/DAV/Exception.php b/3rdparty/Sabre/DAV/Exception.php index 61f8b87c0a..a2cd6cf582 100644 --- a/3rdparty/Sabre/DAV/Exception.php +++ b/3rdparty/Sabre/DAV/Exception.php @@ -7,42 +7,42 @@ * * @package Sabre * @subpackage DAV - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ /** - * Main Exception class. + * Main Exception class. * - * This class defines a getHTTPCode method, which should return the appropriate HTTP code for the Exception occured. + * This class defines a getHTTPCode method, which should return the appropriate HTTP code for the Exception occurred. * The default for this is 500. * * This class also allows you to generate custom xml data for your exceptions. This will be displayed * in the 'error' element in the failing response. */ -class Sabre_DAV_Exception extends Exception { +class Sabre_DAV_Exception extends Exception { /** - * Returns the HTTP statuscode for this exception + * Returns the HTTP statuscode for this exception * * @return int */ - public function getHTTPCode() { + public function getHTTPCode() { return 500; } /** - * This method allows the exception to include additonal information into the WebDAV error response + * This method allows the exception to include additional information into the WebDAV error response * * @param Sabre_DAV_Server $server - * @param DOMElement $errorNode + * @param DOMElement $errorNode * @return void */ public function serialize(Sabre_DAV_Server $server,DOMElement $errorNode) { - + } @@ -50,14 +50,15 @@ class Sabre_DAV_Exception extends Exception { * This method allows the exception to return any extra HTTP response headers. * * The headers must be returned as an array. - * - * @return array + * + * @param Sabre_DAV_Server $server + * @return array */ public function getHTTPHeaders(Sabre_DAV_Server $server) { return array(); - } + } } diff --git a/3rdparty/Sabre/DAV/Exception/BadRequest.php b/3rdparty/Sabre/DAV/Exception/BadRequest.php index 7025bb1031..b198648a75 100644 --- a/3rdparty/Sabre/DAV/Exception/BadRequest.php +++ b/3rdparty/Sabre/DAV/Exception/BadRequest.php @@ -4,24 +4,24 @@ * BadRequest * * The BadRequest is thrown when the user submitted an invalid HTTP request - * BadRequest - * + * BadRequest + * * @package Sabre * @subpackage DAV - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ class Sabre_DAV_Exception_BadRequest extends Sabre_DAV_Exception { /** - * Returns the HTTP statuscode for this exception + * Returns the HTTP statuscode for this exception * * @return int */ public function getHTTPCode() { - return 400; + return 400; } diff --git a/3rdparty/Sabre/DAV/Exception/Conflict.php b/3rdparty/Sabre/DAV/Exception/Conflict.php index 7eaa08178a..6b0bd1fad7 100644 --- a/3rdparty/Sabre/DAV/Exception/Conflict.php +++ b/3rdparty/Sabre/DAV/Exception/Conflict.php @@ -8,14 +8,14 @@ * * @package Sabre * @subpackage DAV - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ class Sabre_DAV_Exception_Conflict extends Sabre_DAV_Exception { /** - * Returns the HTTP statuscode for this exception + * Returns the HTTP statuscode for this exception * * @return int */ diff --git a/3rdparty/Sabre/DAV/Exception/ConflictingLock.php b/3rdparty/Sabre/DAV/Exception/ConflictingLock.php index 279f63dfde..6121868e69 100644 --- a/3rdparty/Sabre/DAV/Exception/ConflictingLock.php +++ b/3rdparty/Sabre/DAV/Exception/ConflictingLock.php @@ -1,28 +1,28 @@ lock) { $error = $errorNode->ownerDocument->createElementNS('DAV:','d:no-conflicting-lock'); $errorNode->appendChild($error); diff --git a/3rdparty/Sabre/DAV/Exception/FileNotFound.php b/3rdparty/Sabre/DAV/Exception/FileNotFound.php index b20e4a2fb3..d76e400c93 100644 --- a/3rdparty/Sabre/DAV/Exception/FileNotFound.php +++ b/3rdparty/Sabre/DAV/Exception/FileNotFound.php @@ -3,26 +3,17 @@ /** * FileNotFound * - * This Exception is thrown when a Node couldn't be found. It returns HTTP error code 404 + * Deprecated: Warning, this class is deprecated and will be removed in a + * future version of SabreDAV. Please use Sabre_DAV_Exception_NotFound instead. * * @package Sabre * @subpackage DAV - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @deprecated Use Sabre_DAV_Exception_NotFound instead * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ -class Sabre_DAV_Exception_FileNotFound extends Sabre_DAV_Exception { - - /** - * Returns the HTTP statuscode for this exception - * - * @return int - */ - public function getHTTPCode() { - - return 404; - - } +class Sabre_DAV_Exception_FileNotFound extends Sabre_DAV_Exception_NotFound { } diff --git a/3rdparty/Sabre/DAV/Exception/Forbidden.php b/3rdparty/Sabre/DAV/Exception/Forbidden.php index 167f3c2760..20b1056e31 100644 --- a/3rdparty/Sabre/DAV/Exception/Forbidden.php +++ b/3rdparty/Sabre/DAV/Exception/Forbidden.php @@ -4,17 +4,17 @@ * Forbidden * * This exception is thrown whenever a user tries to do an operation he's not allowed to - * + * * @package Sabre * @subpackage DAV - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ class Sabre_DAV_Exception_Forbidden extends Sabre_DAV_Exception { /** - * Returns the HTTP statuscode for this exception + * Returns the HTTP statuscode for this exception * * @return int */ diff --git a/3rdparty/Sabre/DAV/Exception/InsufficientStorage.php b/3rdparty/Sabre/DAV/Exception/InsufficientStorage.php index 15007cdd35..1a15089b0a 100644 --- a/3rdparty/Sabre/DAV/Exception/InsufficientStorage.php +++ b/3rdparty/Sabre/DAV/Exception/InsufficientStorage.php @@ -1,20 +1,20 @@ appendChild($error); } - + } diff --git a/3rdparty/Sabre/DAV/Exception/LockTokenMatchesRequestUri.php b/3rdparty/Sabre/DAV/Exception/LockTokenMatchesRequestUri.php index 47032cffc7..80ab7aff65 100644 --- a/3rdparty/Sabre/DAV/Exception/LockTokenMatchesRequestUri.php +++ b/3rdparty/Sabre/DAV/Exception/LockTokenMatchesRequestUri.php @@ -1,13 +1,13 @@ lock) { $error = $errorNode->ownerDocument->createElementNS('DAV:','d:lock-token-submitted'); $errorNode->appendChild($error); diff --git a/3rdparty/Sabre/DAV/Exception/MethodNotAllowed.php b/3rdparty/Sabre/DAV/Exception/MethodNotAllowed.php index 02c145ffeb..3187575150 100644 --- a/3rdparty/Sabre/DAV/Exception/MethodNotAllowed.php +++ b/3rdparty/Sabre/DAV/Exception/MethodNotAllowed.php @@ -4,17 +4,17 @@ * MethodNotAllowed * * The 405 is thrown when a client tried to create a directory on an already existing directory - * + * * @package Sabre * @subpackage DAV - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ class Sabre_DAV_Exception_MethodNotAllowed extends Sabre_DAV_Exception { /** - * Returns the HTTP statuscode for this exception + * Returns the HTTP statuscode for this exception * * @return int */ @@ -28,8 +28,9 @@ class Sabre_DAV_Exception_MethodNotAllowed extends Sabre_DAV_Exception { * This method allows the exception to return any extra HTTP response headers. * * The headers must be returned as an array. - * - * @return array + * + * @param Sabre_DAV_Server $server + * @return array */ public function getHTTPHeaders(Sabre_DAV_Server $server) { diff --git a/3rdparty/Sabre/DAV/Exception/NotAuthenticated.php b/3rdparty/Sabre/DAV/Exception/NotAuthenticated.php index 1faffddfa0..87ca624429 100644 --- a/3rdparty/Sabre/DAV/Exception/NotAuthenticated.php +++ b/3rdparty/Sabre/DAV/Exception/NotAuthenticated.php @@ -5,22 +5,22 @@ * * This exception is thrown when the client did not provide valid * authentication credentials. - * + * * @package Sabre * @subpackage DAV - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ class Sabre_DAV_Exception_NotAuthenticated extends Sabre_DAV_Exception { /** - * Returns the HTTP statuscode for this exception + * Returns the HTTP statuscode for this exception * * @return int */ public function getHTTPCode() { - + return 401; } diff --git a/3rdparty/Sabre/DAV/Exception/NotFound.php b/3rdparty/Sabre/DAV/Exception/NotFound.php new file mode 100644 index 0000000000..2b9da560d2 --- /dev/null +++ b/3rdparty/Sabre/DAV/Exception/NotFound.php @@ -0,0 +1,28 @@ +header = $header; - } + } /** - * Returns the HTTP statuscode for this exception + * Returns the HTTP statuscode for this exception * * @return int */ public function getHTTPCode() { - return 412; + return 412; } /** - * This method allows the exception to include additonal information into the WebDAV error response + * This method allows the exception to include additional information into the WebDAV error response * * @param Sabre_DAV_Server $server - * @param DOMElement $errorNode + * @param DOMElement $errorNode * @return void */ public function serialize(Sabre_DAV_Server $server,DOMElement $errorNode) { - + if ($this->header) { $prop = $errorNode->ownerDocument->createElement('s:header'); $prop->nodeValue = $this->header; diff --git a/3rdparty/Sabre/DAV/Exception/ReportNotImplemented.php b/3rdparty/Sabre/DAV/Exception/ReportNotImplemented.php index e4ed601b16..e86800f303 100644 --- a/3rdparty/Sabre/DAV/Exception/ReportNotImplemented.php +++ b/3rdparty/Sabre/DAV/Exception/ReportNotImplemented.php @@ -7,17 +7,17 @@ * * @package Sabre * @subpackage DAV - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ class Sabre_DAV_Exception_ReportNotImplemented extends Sabre_DAV_Exception_NotImplemented { /** - * This method allows the exception to include additonal information into the WebDAV error response + * This method allows the exception to include additional information into the WebDAV error response * * @param Sabre_DAV_Server $server - * @param DOMElement $errorNode + * @param DOMElement $errorNode * @return void */ public function serialize(Sabre_DAV_Server $server,DOMElement $errorNode) { diff --git a/3rdparty/Sabre/DAV/Exception/RequestedRangeNotSatisfiable.php b/3rdparty/Sabre/DAV/Exception/RequestedRangeNotSatisfiable.php index 37abbd729d..29ee3654a7 100644 --- a/3rdparty/Sabre/DAV/Exception/RequestedRangeNotSatisfiable.php +++ b/3rdparty/Sabre/DAV/Exception/RequestedRangeNotSatisfiable.php @@ -1,21 +1,21 @@ path . '/' . $name; - if (!file_exists($path)) throw new Sabre_DAV_Exception_FileNotFound('File with name ' . $path . ' could not be located'); + if (!file_exists($path)) throw new Sabre_DAV_Exception_NotFound('File with name ' . $path . ' could not be located'); if (is_dir($path)) { @@ -66,9 +81,9 @@ class Sabre_DAV_FS_Directory extends Sabre_DAV_FS_Node implements Sabre_DAV_ICol } /** - * Returns an array with all the child nodes - * - * @return Sabre_DAV_INode[] + * Returns an array with all the child nodes + * + * @return Sabre_DAV_INode[] */ public function getChildren() { @@ -79,10 +94,10 @@ class Sabre_DAV_FS_Directory extends Sabre_DAV_FS_Node implements Sabre_DAV_ICol } /** - * Checks if a child exists. - * - * @param string $name - * @return bool + * Checks if a child exists. + * + * @param string $name + * @return bool */ public function childExists($name) { @@ -92,8 +107,8 @@ class Sabre_DAV_FS_Directory extends Sabre_DAV_FS_Node implements Sabre_DAV_ICol } /** - * Deletes all files in this directory, and then itself - * + * Deletes all files in this directory, and then itself + * * @return void */ public function delete() { @@ -104,16 +119,16 @@ class Sabre_DAV_FS_Directory extends Sabre_DAV_FS_Node implements Sabre_DAV_ICol } /** - * Returns available diskspace information - * - * @return array + * Returns available diskspace information + * + * @return array */ public function getQuotaInfo() { return array( disk_total_space($this->path)-disk_free_space($this->path), disk_free_space($this->path) - ); + ); } diff --git a/3rdparty/Sabre/DAV/FS/File.php b/3rdparty/Sabre/DAV/FS/File.php index 262187d7e8..6a8039fe30 100644 --- a/3rdparty/Sabre/DAV/FS/File.php +++ b/3rdparty/Sabre/DAV/FS/File.php @@ -1,21 +1,21 @@ path); } @@ -60,10 +60,10 @@ class Sabre_DAV_FS_File extends Sabre_DAV_FS_Node implements Sabre_DAV_IFile { * Returns the ETag for a file * * An ETag is a unique identifier representing the current version of the file. If the file changes, the ETag MUST change. - * The ETag is an arbritrary string, but MUST be surrounded by double-quotes. + * The ETag is an arbitrary string, but MUST be surrounded by double-quotes. * * Return null if the ETag can not effectively be determined - * + * * @return mixed */ public function getETag() { @@ -77,8 +77,8 @@ class Sabre_DAV_FS_File extends Sabre_DAV_FS_Node implements Sabre_DAV_IFile { * * If null is returned, we'll assume application/octet-stream * - * @return mixed - */ + * @return mixed + */ public function getContentType() { return null; diff --git a/3rdparty/Sabre/DAV/FS/Node.php b/3rdparty/Sabre/DAV/FS/Node.php index b8d7bcfe84..1283e9d0fd 100644 --- a/3rdparty/Sabre/DAV/FS/Node.php +++ b/3rdparty/Sabre/DAV/FS/Node.php @@ -1,30 +1,29 @@ path,$newPath); - + $this->path = $newPath; } @@ -67,9 +66,9 @@ abstract class Sabre_DAV_FS_Node implements Sabre_DAV_INode { /** - * Returns the last modification time, as a unix timestamp - * - * @return int + * Returns the last modification time, as a unix timestamp + * + * @return int */ public function getLastModified() { diff --git a/3rdparty/Sabre/DAV/FSExt/Directory.php b/3rdparty/Sabre/DAV/FSExt/Directory.php index c43d4385ac..540057183b 100644 --- a/3rdparty/Sabre/DAV/FSExt/Directory.php +++ b/3rdparty/Sabre/DAV/FSExt/Directory.php @@ -1,22 +1,39 @@ path . '/' . $name; file_put_contents($newPath,$data); + return '"' . md5_file($newPath) . '"'; + } /** - * Creates a new subdirectory - * - * @param string $name + * Creates a new subdirectory + * + * @param string $name * @return void */ public function createDirectory($name) { @@ -43,18 +62,18 @@ class Sabre_DAV_FSExt_Directory extends Sabre_DAV_FSExt_Node implements Sabre_DA } /** - * Returns a specific child node, referenced by its name - * - * @param string $name - * @throws Sabre_DAV_Exception_FileNotFound - * @return Sabre_DAV_INode + * Returns a specific child node, referenced by its name + * + * @param string $name + * @throws Sabre_DAV_Exception_NotFound + * @return Sabre_DAV_INode */ public function getChild($name) { $path = $this->path . '/' . $name; - if (!file_exists($path)) throw new Sabre_DAV_Exception_FileNotFound('File could not be located'); - if ($name=='.' || $name=='..') throw new Sabre_DAV_Exception_Forbidden('Permission denied to . and ..'); + if (!file_exists($path)) throw new Sabre_DAV_Exception_NotFound('File could not be located'); + if ($name=='.' || $name=='..') throw new Sabre_DAV_Exception_Forbidden('Permission denied to . and ..'); if (is_dir($path)) { @@ -69,10 +88,10 @@ class Sabre_DAV_FSExt_Directory extends Sabre_DAV_FSExt_Node implements Sabre_DA } /** - * Checks if a child exists. - * - * @param string $name - * @return bool + * Checks if a child exists. + * + * @param string $name + * @return bool */ public function childExists($name) { @@ -85,9 +104,9 @@ class Sabre_DAV_FSExt_Directory extends Sabre_DAV_FSExt_Node implements Sabre_DA } /** - * Returns an array with all the child nodes - * - * @return Sabre_DAV_INode[] + * Returns an array with all the child nodes + * + * @return Sabre_DAV_INode[] */ public function getChildren() { @@ -98,9 +117,9 @@ class Sabre_DAV_FSExt_Directory extends Sabre_DAV_FSExt_Node implements Sabre_DA } /** - * Deletes all files in this directory, and then itself - * - * @return void + * Deletes all files in this directory, and then itself + * + * @return bool */ public function delete() { @@ -118,16 +137,16 @@ class Sabre_DAV_FSExt_Directory extends Sabre_DAV_FSExt_Node implements Sabre_DA } /** - * Returns available diskspace information - * - * @return array + * Returns available diskspace information + * + * @return array */ public function getQuotaInfo() { return array( disk_total_space($this->path)-disk_free_space($this->path), disk_free_space($this->path) - ); + ); } diff --git a/3rdparty/Sabre/DAV/FSExt/File.php b/3rdparty/Sabre/DAV/FSExt/File.php index 7a8e7a11f2..b93ce5aee2 100644 --- a/3rdparty/Sabre/DAV/FSExt/File.php +++ b/3rdparty/Sabre/DAV/FSExt/File.php @@ -1,34 +1,35 @@ path,$data); + return '"' . md5_file($this->path) . '"'; } /** * Returns the data * - * @return string + * @return string */ public function get() { @@ -39,7 +40,7 @@ class Sabre_DAV_FSExt_File extends Sabre_DAV_FSExt_Node implements Sabre_DAV_IFi /** * Delete the current file * - * @return void + * @return bool */ public function delete() { @@ -52,9 +53,11 @@ class Sabre_DAV_FSExt_File extends Sabre_DAV_FSExt_Node implements Sabre_DAV_IFi * Returns the ETag for a file * * An ETag is a unique identifier representing the current version of the file. If the file changes, the ETag MUST change. - * The ETag is an arbritrary string, but MUST be surrounded by double-quotes. + * The ETag is an arbitrary string, but MUST be surrounded by double-quotes. * * Return null if the ETag can not effectively be determined + * + * @return string|null */ public function getETag() { @@ -66,7 +69,9 @@ class Sabre_DAV_FSExt_File extends Sabre_DAV_FSExt_Node implements Sabre_DAV_IFi * Returns the mime-type for a file * * If null is returned, we'll assume application/octet-stream - */ + * + * @return string|null + */ public function getContentType() { return null; @@ -74,9 +79,9 @@ class Sabre_DAV_FSExt_File extends Sabre_DAV_FSExt_Node implements Sabre_DAV_IFi } /** - * Returns the size of the file, in bytes - * - * @return int + * Returns the size of the file, in bytes + * + * @return int */ public function getSize() { diff --git a/3rdparty/Sabre/DAV/FSExt/Node.php b/3rdparty/Sabre/DAV/FSExt/Node.php index 9e36222bfd..68ca06beb7 100644 --- a/3rdparty/Sabre/DAV/FSExt/Node.php +++ b/3rdparty/Sabre/DAV/FSExt/Node.php @@ -1,95 +1,28 @@ getResourceData(); - $locks = $resourceData['locks']; - foreach($locks as $k=>$lock) { - if (time() > $lock->timeout + $lock->created) unset($locks[$k]); - } - return $locks; - - } - - /** - * Locks this node - * - * @param Sabre_DAV_Locks_LockInfo $lockInfo - * @return void - */ - function lock(Sabre_DAV_Locks_LockInfo $lockInfo) { - - // We're making the lock timeout 30 minutes - $lockInfo->timeout = 1800; - $lockInfo->created = time(); - - $resourceData = $this->getResourceData(); - if (!isset($resourceData['locks'])) $resourceData['locks'] = array(); - $current = null; - foreach($resourceData['locks'] as $k=>$lock) { - if ($lock->token === $lockInfo->token) $current = $k; - } - if (!is_null($current)) $resourceData['locks'][$current] = $lockInfo; - else $resourceData['locks'][] = $lockInfo; - - $this->putResourceData($resourceData); - - } - - /** - * Removes a lock from this node - * - * @param Sabre_DAV_Locks_LockInfo $lockInfo - * @return bool - */ - function unlock(Sabre_DAV_Locks_LockInfo $lockInfo) { - - //throw new Sabre_DAV_Exception('bla'); - $resourceData = $this->getResourceData(); - foreach($resourceData['locks'] as $k=>$lock) { - - if ($lock->token === $lockInfo->token) { - - unset($resourceData['locks'][$k]); - $this->putResourceData($resourceData); - return true; - - } - } - return false; - - } +abstract class Sabre_DAV_FSExt_Node extends Sabre_DAV_FS_Node implements Sabre_DAV_IProperties { /** * Updates properties on this node, * - * @param array $mutations + * @param array $properties * @see Sabre_DAV_IProperties::updateProperties - * @return bool|array + * @return bool|array */ public function updateProperties($properties) { $resourceData = $this->getResourceData(); - - $result = array(); foreach($properties as $propertyName=>$propertyValue) { @@ -101,11 +34,11 @@ abstract class Sabre_DAV_FSExt_Node extends Sabre_DAV_FS_Node implements Sabre_D } else { $resourceData['properties'][$propertyName] = $propertyValue; } - + } $this->putResourceData($resourceData); - return true; + return true; } /** @@ -114,8 +47,8 @@ abstract class Sabre_DAV_FSExt_Node extends Sabre_DAV_FS_Node implements Sabre_D * The properties list is a list of propertynames the client requested, encoded as xmlnamespace#tagName, for example: http://www.example.org/namespace#author * If the array is empty, all properties should be returned * - * @param array $properties - * @return void + * @param array $properties + * @return array */ function getProperties($properties) { @@ -134,9 +67,9 @@ abstract class Sabre_DAV_FSExt_Node extends Sabre_DAV_FS_Node implements Sabre_D } /** - * Returns the path to the resource file - * - * @return string + * Returns the path to the resource file + * + * @return string */ protected function getResourceInfoPath() { @@ -146,14 +79,14 @@ abstract class Sabre_DAV_FSExt_Node extends Sabre_DAV_FS_Node implements Sabre_D } /** - * Returns all the stored resource information - * - * @return array + * Returns all the stored resource information + * + * @return array */ protected function getResourceData() { $path = $this->getResourceInfoPath(); - if (!file_exists($path)) return array('locks'=>array(), 'properties' => array()); + if (!file_exists($path)) return array('properties' => array()); // opening up the file, and creating a shared lock $handle = fopen($path,'r'); @@ -171,20 +104,19 @@ abstract class Sabre_DAV_FSExt_Node extends Sabre_DAV_FS_Node implements Sabre_D // Unserializing and checking if the resource file contains data for this file $data = unserialize($data); if (!isset($data[$this->getName()])) { - return array('locks'=>array(), 'properties' => array()); + return array('properties' => array()); } $data = $data[$this->getName()]; - if (!isset($data['locks'])) $data['locks'] = array(); if (!isset($data['properties'])) $data['properties'] = array(); return $data; } /** - * Updates the resource information - * - * @param array $newData + * Updates the resource information + * + * @param array $newData * @return void */ protected function putResourceData(array $newData) { @@ -238,6 +170,9 @@ abstract class Sabre_DAV_FSExt_Node extends Sabre_DAV_FS_Node implements Sabre_D } + /** + * @return bool + */ public function deleteResourceData() { // When we're deleting this node, we also need to delete any resource information @@ -264,6 +199,7 @@ abstract class Sabre_DAV_FSExt_Node extends Sabre_DAV_FS_Node implements Sabre_D fwrite($handle,serialize($data)); fclose($handle); + return true; } public function delete() { diff --git a/3rdparty/Sabre/DAV/File.php b/3rdparty/Sabre/DAV/File.php index b74bd9525b..3126bd8d36 100644 --- a/3rdparty/Sabre/DAV/File.php +++ b/3rdparty/Sabre/DAV/File.php @@ -4,12 +4,12 @@ * File class * * This is a helper class, that should aid in getting file classes setup. - * Most of its methods are implemented, and throw permission denied exceptions - * + * Most of its methods are implemented, and throw permission denied exceptions + * * @package Sabre * @subpackage DAV - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. - * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. + * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ abstract class Sabre_DAV_File extends Sabre_DAV_Node implements Sabre_DAV_IFile { @@ -18,33 +18,33 @@ abstract class Sabre_DAV_File extends Sabre_DAV_Node implements Sabre_DAV_IFile * Updates the data * * data is a readable stream resource. - * - * @param resource $data - * @return void + * + * @param resource $data + * @return void */ - public function put($data) { + public function put($data) { throw new Sabre_DAV_Exception_Forbidden('Permission denied to change data'); } /** - * Returns the data + * Returns the data * * This method may either return a string or a readable stream resource * - * @return mixed + * @return mixed */ - public function get() { + public function get() { throw new Sabre_DAV_Exception_Forbidden('Permission denied to read this file'); } - + /** - * Returns the size of the file, in bytes. - * - * @return int + * Returns the size of the file, in bytes. + * + * @return int */ public function getSize() { @@ -56,9 +56,11 @@ abstract class Sabre_DAV_File extends Sabre_DAV_Node implements Sabre_DAV_IFile * Returns the ETag for a file * * An ETag is a unique identifier representing the current version of the file. If the file changes, the ETag MUST change. - * The ETag is an arbritrary string, but MUST be surrounded by double-quotes. + * The ETag is an arbitrary string, but MUST be surrounded by double-quotes. * * Return null if the ETag can not effectively be determined + * + * @return string|null */ public function getETag() { @@ -70,7 +72,9 @@ abstract class Sabre_DAV_File extends Sabre_DAV_Node implements Sabre_DAV_IFile * Returns the mime-type for a file * * If null is returned, we'll assume application/octet-stream - */ + * + * @return string|null + */ public function getContentType() { return null; diff --git a/3rdparty/Sabre/DAV/ICollection.php b/3rdparty/Sabre/DAV/ICollection.php index 0667d88899..4626038a66 100644 --- a/3rdparty/Sabre/DAV/ICollection.php +++ b/3rdparty/Sabre/DAV/ICollection.php @@ -3,54 +3,70 @@ /** * The ICollection Interface * - * This interface should be implemented by each class that represents a collection - * + * This interface should be implemented by each class that represents a collection + * * @package Sabre * @subpackage DAV - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. - * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. + * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ interface Sabre_DAV_ICollection extends Sabre_DAV_INode { /** - * Creates a new file in the directory - * - * data is a readable stream resource + * Creates a new file in the directory * - * @param string $name Name of the file - * @param resource $data Initial payload - * @return void + * Data will either be supplied as a stream resource, or in certain cases + * as a string. Keep in mind that you may have to support either. + * + * After succesful creation of the file, you may choose to return the ETag + * of the new file here. + * + * The returned ETag must be surrounded by double-quotes (The quotes should + * be part of the actual string). + * + * If you cannot accurately determine the ETag, you should not return it. + * If you don't store the file exactly as-is (you're transforming it + * somehow) you should also not return an ETag. + * + * This means that if a subsequent GET to this new file does not exactly + * return the same contents of what was submitted here, you are strongly + * recommended to omit the ETag. + * + * @param string $name Name of the file + * @param resource|string $data Initial payload + * @return null|string */ function createFile($name, $data = null); /** - * Creates a new subdirectory - * - * @param string $name + * Creates a new subdirectory + * + * @param string $name * @return void */ function createDirectory($name); /** - * Returns a specific child node, referenced by its name - * - * @param string $name - * @return Sabre_DAV_INode + * Returns a specific child node, referenced by its name + * + * @param string $name + * @return Sabre_DAV_INode */ function getChild($name); /** - * Returns an array with all the child nodes - * - * @return Sabre_DAV_INode[] + * Returns an array with all the child nodes + * + * @return Sabre_DAV_INode[] */ function getChildren(); /** - * Checks if a child-node with the specified name exists - * - * @return bool + * Checks if a child-node with the specified name exists + * + * @param string $name + * @return bool */ function childExists($name); diff --git a/3rdparty/Sabre/DAV/IExtendedCollection.php b/3rdparty/Sabre/DAV/IExtendedCollection.php index b8db1ab2f2..6ec345f9a6 100644 --- a/3rdparty/Sabre/DAV/IExtendedCollection.php +++ b/3rdparty/Sabre/DAV/IExtendedCollection.php @@ -5,11 +5,11 @@ * * This interface can be used to create special-type of collection-resources * as defined by RFC 5689. - * + * * @package Sabre * @subpackage DAV - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. - * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. + * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ interface Sabre_DAV_IExtendedCollection extends Sabre_DAV_ICollection { @@ -17,7 +17,7 @@ interface Sabre_DAV_IExtendedCollection extends Sabre_DAV_ICollection { /** * Creates a new collection * - * @param string $name + * @param string $name * @param array $resourceType * @param array $properties * @return void diff --git a/3rdparty/Sabre/DAV/IFile.php b/3rdparty/Sabre/DAV/IFile.php index 446ec86187..478f822ae7 100644 --- a/3rdparty/Sabre/DAV/IFile.php +++ b/3rdparty/Sabre/DAV/IFile.php @@ -1,34 +1,48 @@ getData($currentPath); foreach($uriLocks as $uriLock) { - // Unless we're on the leaf of the uri-tree we should ingore locks with depth 0 + // Unless we're on the leaf of the uri-tree we should ignore locks with depth 0 if($uri==$currentPath || $uriLock->depth!=0) { $uriLock->uri = $currentPath; $lockList[] = $uriLock; @@ -79,18 +81,18 @@ class Sabre_DAV_Locks_Backend_FS extends Sabre_DAV_Locks_Backend_Abstract { // Checking if we can remove any of these locks foreach($lockList as $k=>$lock) { - if (time() > $lock->timeout + $lock->created) unset($lockList[$k]); + if (time() > $lock->timeout + $lock->created) unset($lockList[$k]); } return $lockList; } /** - * Locks a uri - * - * @param string $uri - * @param Sabre_DAV_Locks_LockInfo $lockInfo - * @return bool + * Locks a uri + * + * @param string $uri + * @param Sabre_DAV_Locks_LockInfo $lockInfo + * @return bool */ public function lock($uri,Sabre_DAV_Locks_LockInfo $lockInfo) { @@ -109,11 +111,11 @@ class Sabre_DAV_Locks_Backend_FS extends Sabre_DAV_Locks_Backend_Abstract { } /** - * Removes a lock from a uri - * - * @param string $uri - * @param Sabre_DAV_Locks_LockInfo $lockInfo - * @return bool + * Removes a lock from a uri + * + * @param string $uri + * @param Sabre_DAV_Locks_LockInfo $lockInfo + * @return bool */ public function unlock($uri,Sabre_DAV_Locks_LockInfo $lockInfo) { @@ -136,7 +138,7 @@ class Sabre_DAV_Locks_Backend_FS extends Sabre_DAV_Locks_Backend_Abstract { * Returns the stored data for a uri * * @param string $uri - * @return array + * @return array */ protected function getData($uri) { @@ -167,7 +169,7 @@ class Sabre_DAV_Locks_Backend_FS extends Sabre_DAV_Locks_Backend_Abstract { * Updates the lock information * * @param string $uri - * @param array $newData + * @param array $newData * @return void */ protected function putData($uri,array $newData) { diff --git a/3rdparty/Sabre/DAV/Locks/Backend/File.php b/3rdparty/Sabre/DAV/Locks/Backend/File.php index f65b20c430..c33f963514 100644 --- a/3rdparty/Sabre/DAV/Locks/Backend/File.php +++ b/3rdparty/Sabre/DAV/Locks/Backend/File.php @@ -3,30 +3,30 @@ /** * The Lock manager allows you to handle all file-locks centrally. * - * This Lock Manager stores all its data in a single file. + * This Lock Manager stores all its data in a single file. * * Note that this is not nearly as robust as a database, you are encouraged * to use the PDO backend instead. * * @package Sabre * @subpackage DAV - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. - * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. + * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ class Sabre_DAV_Locks_Backend_File extends Sabre_DAV_Locks_Backend_Abstract { /** * The storage file - * - * @var string + * + * @var string */ private $locksFile; /** * Constructor * - * @param string $locksFile path to file + * @param string $locksFile path to file */ public function __construct($locksFile) { @@ -35,24 +35,24 @@ class Sabre_DAV_Locks_Backend_File extends Sabre_DAV_Locks_Backend_Abstract { } /** - * Returns a list of Sabre_DAV_Locks_LockInfo objects - * + * Returns a list of Sabre_DAV_Locks_LockInfo objects + * * This method should return all the locks for a particular uri, including * locks that might be set on a parent uri. * * If returnChildLocks is set to true, this method should also look for * any locks in the subtree of the uri for locks. * - * @param string $uri + * @param string $uri * @param bool $returnChildLocks - * @return array + * @return array */ public function getLocks($uri, $returnChildLocks) { $newLocks = array(); - $currentPath = ''; $locks = $this->getData(); + foreach($locks as $lock) { if ($lock->uri === $uri || @@ -70,29 +70,35 @@ class Sabre_DAV_Locks_Backend_File extends Sabre_DAV_Locks_Backend_Abstract { // Checking if we can remove any of these locks foreach($newLocks as $k=>$lock) { - if (time() > $lock->timeout + $lock->created) unset($newLocks[$k]); + if (time() > $lock->timeout + $lock->created) unset($newLocks[$k]); } return $newLocks; } /** - * Locks a uri - * - * @param string $uri - * @param Sabre_DAV_Locks_LockInfo $lockInfo - * @return bool + * Locks a uri + * + * @param string $uri + * @param Sabre_DAV_Locks_LockInfo $lockInfo + * @return bool */ - public function lock($uri,Sabre_DAV_Locks_LockInfo $lockInfo) { + public function lock($uri, Sabre_DAV_Locks_LockInfo $lockInfo) { // We're making the lock timeout 30 minutes $lockInfo->timeout = 1800; $lockInfo->created = time(); $lockInfo->uri = $uri; - $locks = $this->getLocks($uri,false); + $locks = $this->getData(); + foreach($locks as $k=>$lock) { - if ($lock->token == $lockInfo->token) unset($locks[$k]); + if ( + ($lock->token == $lockInfo->token) || + (time() > $lock->timeout + $lock->created) + ) { + unset($locks[$k]); + } } $locks[] = $lockInfo; $this->putData($locks); @@ -101,15 +107,15 @@ class Sabre_DAV_Locks_Backend_File extends Sabre_DAV_Locks_Backend_Abstract { } /** - * Removes a lock from a uri - * - * @param string $uri - * @param Sabre_DAV_Locks_LockInfo $lockInfo - * @return bool + * Removes a lock from a uri + * + * @param string $uri + * @param Sabre_DAV_Locks_LockInfo $lockInfo + * @return bool */ - public function unlock($uri,Sabre_DAV_Locks_LockInfo $lockInfo) { + public function unlock($uri, Sabre_DAV_Locks_LockInfo $lockInfo) { - $locks = $this->getLocks($uri,false); + $locks = $this->getData(); foreach($locks as $k=>$lock) { if ($lock->token == $lockInfo->token) { @@ -127,7 +133,7 @@ class Sabre_DAV_Locks_Backend_File extends Sabre_DAV_Locks_Backend_Abstract { /** * Loads the lockdata from the filesystem. * - * @return array + * @return array */ protected function getData() { @@ -153,7 +159,7 @@ class Sabre_DAV_Locks_Backend_File extends Sabre_DAV_Locks_Backend_Abstract { /** * Saves the lockdata * - * @param array $newData + * @param array $newData * @return void */ protected function putData(array $newData) { diff --git a/3rdparty/Sabre/DAV/Locks/Backend/PDO.php b/3rdparty/Sabre/DAV/Locks/Backend/PDO.php index c3923af19d..acce80638e 100644 --- a/3rdparty/Sabre/DAV/Locks/Backend/PDO.php +++ b/3rdparty/Sabre/DAV/Locks/Backend/PDO.php @@ -5,34 +5,34 @@ * * This Lock Manager stores all its data in a database. You must pass a PDO * connection object in the constructor. - * + * * @package Sabre * @subpackage DAV - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. - * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. + * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ class Sabre_DAV_Locks_Backend_PDO extends Sabre_DAV_Locks_Backend_Abstract { /** - * The PDO connection object - * - * @var pdo + * The PDO connection object + * + * @var pdo */ private $pdo; /** - * The PDO tablename this backend uses. - * + * The PDO tablename this backend uses. + * * @var string */ protected $tableName; /** - * Constructor - * + * Constructor + * * @param PDO $pdo - * @param string $tableName + * @param string $tableName */ public function __construct(PDO $pdo, $tableName = 'locks') { @@ -42,24 +42,24 @@ class Sabre_DAV_Locks_Backend_PDO extends Sabre_DAV_Locks_Backend_Abstract { } /** - * Returns a list of Sabre_DAV_Locks_LockInfo objects - * + * Returns a list of Sabre_DAV_Locks_LockInfo objects + * * This method should return all the locks for a particular uri, including * locks that might be set on a parent uri. * * If returnChildLocks is set to true, this method should also look for * any locks in the subtree of the uri for locks. * - * @param string $uri + * @param string $uri * @param bool $returnChildLocks - * @return array + * @return array */ public function getLocks($uri, $returnChildLocks) { - // NOTE: the following 10 lines or so could be easily replaced by - // pure sql. MySQL's non-standard string concatination prevents us + // NOTE: the following 10 lines or so could be easily replaced by + // pure sql. MySQL's non-standard string concatenation prevents us // from doing this though. - $query = 'SELECT owner, token, timeout, created, scope, depth, uri FROM `'.$this->tableName.'` WHERE ((created + timeout) > CAST(? AS UNSIGNED INTEGER)) AND ((uri = ?)'; + $query = 'SELECT owner, token, timeout, created, scope, depth, uri FROM '.$this->tableName.' WHERE ((created + timeout) > CAST(? AS UNSIGNED INTEGER)) AND ((uri = ?)'; $params = array(time(),$uri); // We need to check locks for every part in the uri. @@ -112,11 +112,11 @@ class Sabre_DAV_Locks_Backend_PDO extends Sabre_DAV_Locks_Backend_Abstract { } /** - * Locks a uri - * - * @param string $uri - * @param Sabre_DAV_Locks_LockInfo $lockInfo - * @return bool + * Locks a uri + * + * @param string $uri + * @param Sabre_DAV_Locks_LockInfo $lockInfo + * @return bool */ public function lock($uri,Sabre_DAV_Locks_LockInfo $lockInfo) { @@ -127,15 +127,15 @@ class Sabre_DAV_Locks_Backend_PDO extends Sabre_DAV_Locks_Backend_Abstract { $locks = $this->getLocks($uri,false); $exists = false; - foreach($locks as $k=>$lock) { + foreach($locks as $lock) { if ($lock->token == $lockInfo->token) $exists = true; } - + if ($exists) { - $stmt = $this->pdo->prepare('UPDATE `'.$this->tableName.'` SET owner = ?, timeout = ?, scope = ?, depth = ?, uri = ?, created = ? WHERE token = ?'); + $stmt = $this->pdo->prepare('UPDATE '.$this->tableName.' SET owner = ?, timeout = ?, scope = ?, depth = ?, uri = ?, created = ? WHERE token = ?'); $stmt->execute(array($lockInfo->owner,$lockInfo->timeout,$lockInfo->scope,$lockInfo->depth,$uri,$lockInfo->created,$lockInfo->token)); } else { - $stmt = $this->pdo->prepare('INSERT INTO `'.$this->tableName.'` (owner,timeout,scope,depth,uri,created,token) VALUES (?,?,?,?,?,?,?)'); + $stmt = $this->pdo->prepare('INSERT INTO '.$this->tableName.' (owner,timeout,scope,depth,uri,created,token) VALUES (?,?,?,?,?,?,?)'); $stmt->execute(array($lockInfo->owner,$lockInfo->timeout,$lockInfo->scope,$lockInfo->depth,$uri,$lockInfo->created,$lockInfo->token)); } @@ -146,15 +146,15 @@ class Sabre_DAV_Locks_Backend_PDO extends Sabre_DAV_Locks_Backend_Abstract { /** - * Removes a lock from a uri - * - * @param string $uri - * @param Sabre_DAV_Locks_LockInfo $lockInfo - * @return bool + * Removes a lock from a uri + * + * @param string $uri + * @param Sabre_DAV_Locks_LockInfo $lockInfo + * @return bool */ public function unlock($uri,Sabre_DAV_Locks_LockInfo $lockInfo) { - $stmt = $this->pdo->prepare('DELETE FROM `'.$this->tableName.'` WHERE uri = ? AND token = ?'); + $stmt = $this->pdo->prepare('DELETE FROM '.$this->tableName.' WHERE uri = ? AND token = ?'); $stmt->execute(array($uri,$lockInfo->token)); return $stmt->rowCount()===1; diff --git a/3rdparty/Sabre/DAV/Locks/LockInfo.php b/3rdparty/Sabre/DAV/Locks/LockInfo.php index 6a064466f4..9df014a428 100644 --- a/3rdparty/Sabre/DAV/Locks/LockInfo.php +++ b/3rdparty/Sabre/DAV/Locks/LockInfo.php @@ -5,11 +5,11 @@ * * An object of the LockInfo class holds all the information relevant to a * single lock. - * + * * @package Sabre * @subpackage DAV - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. - * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. + * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ class Sabre_DAV_Locks_LockInfo { @@ -30,37 +30,37 @@ class Sabre_DAV_Locks_LockInfo { const TIMEOUT_INFINITE = -1; /** - * The owner of the lock - * - * @var string + * The owner of the lock + * + * @var string */ public $owner; /** - * The locktoken - * - * @var string + * The locktoken + * + * @var string */ public $token; /** - * How long till the lock is expiring - * - * @var int + * How long till the lock is expiring + * + * @var int */ public $timeout; /** - * UNIX Timestamp of when this lock was created - * - * @var int + * UNIX Timestamp of when this lock was created + * + * @var int */ public $created; /** - * Exclusive or shared lock - * - * @var int + * Exclusive or shared lock + * + * @var int */ public $scope = self::EXCLUSIVE; @@ -72,7 +72,7 @@ class Sabre_DAV_Locks_LockInfo { /** * The uri this lock locks * - * TODO: This value is not always set + * TODO: This value is not always set * @var mixed */ public $uri; diff --git a/3rdparty/Sabre/DAV/Locks/Plugin.php b/3rdparty/Sabre/DAV/Locks/Plugin.php index 461e2847e0..fd956950b8 100644 --- a/3rdparty/Sabre/DAV/Locks/Plugin.php +++ b/3rdparty/Sabre/DAV/Locks/Plugin.php @@ -9,38 +9,37 @@ * $lockBackend = new Sabre_DAV_Locks_Backend_File('./mylockdb'); * $lockPlugin = new Sabre_DAV_Locks_Plugin($lockBackend); * $server->addPlugin($lockPlugin); - * + * * @package Sabre * @subpackage DAV - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. - * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. + * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ class Sabre_DAV_Locks_Plugin extends Sabre_DAV_ServerPlugin { /** - * locksBackend - * - * @var Sabre_DAV_Locks_Backend_Abstract + * locksBackend + * + * @var Sabre_DAV_Locks_Backend_Abstract */ private $locksBackend; /** * server - * - * @var Sabre_DAV_Server + * + * @var Sabre_DAV_Server */ private $server; /** - * __construct - * - * @param Sabre_DAV_Locks_Backend_Abstract $locksBackend - * @return void + * __construct + * + * @param Sabre_DAV_Locks_Backend_Abstract $locksBackend */ public function __construct(Sabre_DAV_Locks_Backend_Abstract $locksBackend = null) { - $this->locksBackend = $locksBackend; + $this->locksBackend = $locksBackend; } @@ -48,8 +47,8 @@ class Sabre_DAV_Locks_Plugin extends Sabre_DAV_ServerPlugin { * Initializes the plugin * * This method is automatically called by the Server class after addPlugin. - * - * @param Sabre_DAV_Server $server + * + * @param Sabre_DAV_Server $server * @return void */ public function initialize(Sabre_DAV_Server $server) { @@ -63,11 +62,11 @@ class Sabre_DAV_Locks_Plugin extends Sabre_DAV_ServerPlugin { /** * Returns a plugin name. - * + * * Using this name other plugins will be able to access other plugins - * using Sabre_DAV_Server::getPlugin - * - * @return string + * using Sabre_DAV_Server::getPlugin + * + * @return string */ public function getPluginName() { @@ -76,20 +75,21 @@ class Sabre_DAV_Locks_Plugin extends Sabre_DAV_ServerPlugin { } /** - * This method is called by the Server if the user used an HTTP method + * This method is called by the Server if the user used an HTTP method * the server didn't recognize. * * This plugin intercepts the LOCK and UNLOCK methods. - * - * @param string $method - * @return bool + * + * @param string $method + * @param string $uri + * @return bool */ public function unknownMethod($method, $uri) { - switch($method) { + switch($method) { - case 'LOCK' : $this->httpLock($uri); return false; - case 'UNLOCK' : $this->httpUnlock($uri); return false; + case 'LOCK' : $this->httpLock($uri); return false; + case 'UNLOCK' : $this->httpUnlock($uri); return false; } @@ -98,26 +98,20 @@ class Sabre_DAV_Locks_Plugin extends Sabre_DAV_ServerPlugin { /** * This method is called after most properties have been found * it allows us to add in any Lock-related properties - * - * @param string $path - * @param array $properties - * @return bool + * + * @param string $path + * @param array $newProperties + * @return bool */ - public function afterGetProperties($path,&$newProperties) { + public function afterGetProperties($path, &$newProperties) { foreach($newProperties[404] as $propName=>$discard) { - $node = null; - switch($propName) { case '{DAV:}supportedlock' : $val = false; if ($this->locksBackend) $val = true; - else { - if (!$node) $node = $this->server->tree->getNodeForPath($path); - if ($node instanceof Sabre_DAV_ILockable) $val = true; - } $newProperties[200][$propName] = new Sabre_DAV_Property_SupportedLock($val); unset($newProperties[404][$propName]); break; @@ -141,10 +135,10 @@ class Sabre_DAV_Locks_Plugin extends Sabre_DAV_ServerPlugin { * handled. * * This plugin uses that feature to intercept access to locked resources. - * + * * @param string $method * @param string $uri - * @return bool + * @return bool */ public function beforeMethod($method, $uri) { @@ -187,18 +181,17 @@ class Sabre_DAV_Locks_Plugin extends Sabre_DAV_ServerPlugin { * 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 + * This method is passed a uri. It should only return HTTP methods that are * available for the specified uri. * * @param string $uri - * @return array + * @return array */ public function getHTTPMethods($uri) { - if ($this->locksBackend || - $this->server->tree->getNodeForPath($uri) instanceof Sabre_DAV_ILocks) { + if ($this->locksBackend) return array('LOCK','UNLOCK'); - } + return array(); } @@ -208,8 +201,8 @@ class Sabre_DAV_Locks_Plugin extends Sabre_DAV_ServerPlugin { * * In this case this is only the number 2. The 2 in the Dav: header * indicates the server supports locks. - * - * @return array + * + * @return array */ public function getFeatures() { @@ -218,49 +211,23 @@ class Sabre_DAV_Locks_Plugin extends Sabre_DAV_ServerPlugin { } /** - * Returns all lock information on a particular uri - * + * Returns all lock information on a particular uri + * * This function should return an array with Sabre_DAV_Locks_LockInfo objects. If there are no locks on a file, return an empty array. * - * Additionally there is also the possibility of locks on parent nodes, so we'll need to traverse every part of the tree + * Additionally there is also the possibility of locks on parent nodes, so we'll need to traverse every part of the tree * If the $returnChildLocks argument is set to true, we'll also traverse all the children of the object * for any possible locks and return those as well. * - * @param string $uri + * @param string $uri * @param bool $returnChildLocks - * @return array + * @return array */ public function getLocks($uri, $returnChildLocks = false) { $lockList = array(); - $currentPath = ''; - foreach(explode('/',$uri) as $uriPart) { - $uriLocks = array(); - if ($currentPath) $currentPath.='/'; - $currentPath.=$uriPart; - - try { - - $node = $this->server->tree->getNodeForPath($currentPath); - if ($node instanceof Sabre_DAV_ILockable) $uriLocks = $node->getLocks(); - - } catch (Sabre_DAV_Exception_FileNotFound $e){ - // In case the node didn't exist, this could be a lock-null request - } - - foreach($uriLocks as $uriLock) { - - // Unless we're on the leaf of the uri-tree we should ignore locks with depth 0 - if($uri==$currentPath || $uriLock->depth!=0) { - $uriLock->uri = $currentPath; - $lockList[] = $uriLock; - } - - } - - } - if ($this->locksBackend) + if ($this->locksBackend) $lockList = array_merge($lockList,$this->locksBackend->getLocks($uri, $returnChildLocks)); return $lockList; @@ -271,13 +238,13 @@ class Sabre_DAV_Locks_Plugin extends Sabre_DAV_ServerPlugin { * Locks an uri * * The WebDAV lock request can be operated to either create a new lock on a file, or to refresh an existing lock - * If a new lock is created, a full XML body should be supplied, containing information about the lock such as the type + * If a new lock is created, a full XML body should be supplied, containing information about the lock such as the type * of lock (shared or exclusive) and the owner of the lock * * If a lock is to be refreshed, no body should be supplied and there should be a valid If header containing the lock * - * Additionally, a lock can be requested for a non-existant file. In these case we're obligated to create an empty file as per RFC4918:S7.3 - * + * Additionally, a lock can be requested for a non-existent file. In these case we're obligated to create an empty file as per RFC4918:S7.3 + * * @param string $uri * @return void */ @@ -297,7 +264,7 @@ class Sabre_DAV_Locks_Plugin extends Sabre_DAV_ServerPlugin { if ($body = $this->server->httpRequest->getBody(true)) { // This is a new lock request $lockInfo = $this->parseLockRequest($body); - $lockInfo->depth = $this->server->getHTTPDepth(); + $lockInfo->depth = $this->server->getHTTPDepth(); $lockInfo->uri = $uri; if($lastLock && $lockInfo->scope != Sabre_DAV_Locks_LockInfo::SHARED) throw new Sabre_DAV_Exception_ConflictingLock($lastLock); @@ -306,11 +273,11 @@ class Sabre_DAV_Locks_Plugin extends Sabre_DAV_ServerPlugin { // This must have been a lock refresh $lockInfo = $lastLock; - // The resource could have been locked through another uri. + // The resource could have been locked through another uri. if ($uri!=$lockInfo->uri) $uri = $lockInfo->uri; } else { - + // There was neither a lock refresh nor a new lock request throw new Sabre_DAV_Exception_BadRequest('An xml body is required for lock requests'); @@ -322,16 +289,16 @@ class Sabre_DAV_Locks_Plugin extends Sabre_DAV_ServerPlugin { // If we got this far.. we should go check if this node actually exists. If this is not the case, we need to create it first try { - $node = $this->server->tree->getNodeForPath($uri); - + $this->server->tree->getNodeForPath($uri); + // We need to call the beforeWriteContent event for RFC3744 $this->server->broadcastEvent('beforeWriteContent',array($uri)); - } catch (Sabre_DAV_Exception_FileNotFound $e) { - + } catch (Sabre_DAV_Exception_NotFound $e) { + // It didn't, lets create it - $this->server->createFile($uri,fopen('php://memory','r')); - $newFile = true; + $this->server->createFile($uri,fopen('php://memory','r')); + $newFile = true; } @@ -362,7 +329,7 @@ class Sabre_DAV_Locks_Plugin extends Sabre_DAV_ServerPlugin { $locks = $this->getLocks($uri); - // Windows sometimes forgets to include < and > in the Lock-Token + // Windows sometimes forgets to include < and > in the Lock-Token // header if ($lockToken[0]!=='<') $lockToken = '<' . $lockToken . '>'; @@ -370,7 +337,6 @@ class Sabre_DAV_Locks_Plugin extends Sabre_DAV_ServerPlugin { if ('token . '>' == $lockToken) { - $this->server->broadcastEvent('beforeUnlock',array($uri, $lock)); $this->unlockNode($uri,$lock); $this->server->httpResponse->setHeader('Content-Length','0'); $this->server->httpResponse->sendStatus(204); @@ -390,21 +356,15 @@ class Sabre_DAV_Locks_Plugin extends Sabre_DAV_ServerPlugin { * * All the locking information is supplied in the lockInfo object. The object has a suggested timeout, but this can be safely ignored * It is important that if the existing timeout is ignored, the property is overwritten, as this needs to be sent back to the client - * - * @param string $uri - * @param Sabre_DAV_Locks_LockInfo $lockInfo - * @return void + * + * @param string $uri + * @param Sabre_DAV_Locks_LockInfo $lockInfo + * @return bool */ public function lockNode($uri,Sabre_DAV_Locks_LockInfo $lockInfo) { if (!$this->server->broadcastEvent('beforeLock',array($uri,$lockInfo))) return; - try { - $node = $this->server->tree->getNodeForPath($uri); - if ($node instanceof Sabre_DAV_ILockable) return $node->lock($lockInfo); - } catch (Sabre_DAV_Exception_FileNotFound $e) { - // In case the node didn't exist, this could be a lock-null request - } if ($this->locksBackend) return $this->locksBackend->lock($uri,$lockInfo); throw new Sabre_DAV_Exception_MethodNotAllowed('Locking support is not enabled for this resource. No Locking backend was found so if you didn\'t expect this error, please check your configuration.'); @@ -414,29 +374,22 @@ class Sabre_DAV_Locks_Plugin extends Sabre_DAV_ServerPlugin { * Unlocks a uri * * This method removes a lock from a uri. It is assumed all the supplied information is correct and verified - * - * @param string $uri - * @param Sabre_DAV_Locks_LockInfo $lockInfo - * @return void + * + * @param string $uri + * @param Sabre_DAV_Locks_LockInfo $lockInfo + * @return bool */ public function unlockNode($uri,Sabre_DAV_Locks_LockInfo $lockInfo) { if (!$this->server->broadcastEvent('beforeUnlock',array($uri,$lockInfo))) return; - try { - $node = $this->server->tree->getNodeForPath($uri); - if ($node instanceof Sabre_DAV_ILockable) return $node->unlock($lockInfo); - } catch (Sabre_DAV_Exception_FileNotFound $e) { - // In case the node didn't exist, this could be a lock-null request - } - if ($this->locksBackend) return $this->locksBackend->unlock($uri,$lockInfo); } /** - * Returns the contents of the HTTP Timeout header. - * + * Returns the contents of the HTTP Timeout header. + * * The method formats the header into an integer. * * @return int @@ -444,7 +397,7 @@ class Sabre_DAV_Locks_Plugin extends Sabre_DAV_ServerPlugin { public function getTimeoutHeader() { $header = $this->server->httpRequest->getHeader('Timeout'); - + if ($header) { if (stripos($header,'second-')===0) $header = (int)(substr($header,7)); @@ -462,16 +415,16 @@ class Sabre_DAV_Locks_Plugin extends Sabre_DAV_ServerPlugin { } /** - * Generates the response for successfull LOCK requests - * - * @param Sabre_DAV_Locks_LockInfo $lockInfo - * @return string + * Generates the response for successful LOCK requests + * + * @param Sabre_DAV_Locks_LockInfo $lockInfo + * @return string */ protected function generateLockResponse(Sabre_DAV_Locks_LockInfo $lockInfo) { $dom = new DOMDocument('1.0','utf-8'); $dom->formatOutput = true; - + $prop = $dom->createElementNS('DAV:','d:prop'); $dom->appendChild($prop); @@ -484,10 +437,10 @@ class Sabre_DAV_Locks_Plugin extends Sabre_DAV_ServerPlugin { return $dom->saveXML(); } - + /** * validateLock should be called when a write operation is about to happen - * It will check if the requested url is locked, and see if the correct lock tokens are passed + * It will check if the requested url is locked, and see if the correct lock tokens are passed * * @param mixed $urls List of relevant urls. Can be an array, a string or nothing at all for the current request uri * @param mixed $lastLock This variable will be populated with the last checked lock object (Sabre_DAV_Locks_LockInfo) @@ -511,13 +464,13 @@ class Sabre_DAV_Locks_Plugin extends Sabre_DAV_ServerPlugin { $locks = $this->getLocks($url, $checkChildLocks); - // If there were no conditions, but there were locks, we fail + // If there were no conditions, but there were locks, we fail if (!$conditions && $locks) { reset($locks); $lastLock = current($locks); return false; } - + // If there were no locks or conditions, we go to the next url if (!$locks && !$conditions) continue; @@ -542,7 +495,7 @@ class Sabre_DAV_Locks_Plugin extends Sabre_DAV_ServerPlugin { // key 2 can contain an etag if ($conditionToken[2]) { - $uri = $conditionUri?$conditionUri:$this->server->getRequestUri(); + $uri = $conditionUri?$conditionUri:$this->server->getRequestUri(); $node = $this->server->tree->getNodeForPath($uri); $etagValid = $node->getETag()==$conditionToken[2]; @@ -609,23 +562,23 @@ class Sabre_DAV_Locks_Plugin extends Sabre_DAV_ServerPlugin { * This method is created to extract information from the WebDAV HTTP 'If:' header * * The If header can be quite complex, and has a bunch of features. We're using a regex to extract all relevant information - * The function will return an array, containg structs with the following keys + * The function will return an array, containing structs with the following keys * - * * uri - the uri the condition applies to. If this is returned as an + * * uri - the uri the condition applies to. If this is returned as an * empty string, this implies it's referring to the request url. - * * tokens - The lock token. another 2 dimensional array containg 2 elements (0 = true/false.. If this is a negative condition its set to false, 1 = the actual token) + * * tokens - The lock token. another 2 dimensional array containing 2 elements (0 = true/false.. If this is a negative condition its set to false, 1 = the actual token) * * etag - an etag, if supplied - * - * @return void + * + * @return array */ public function getIfConditions() { - $header = $this->server->httpRequest->getHeader('If'); + $header = $this->server->httpRequest->getHeader('If'); if (!$header) return array(); $matches = array(); - $regex = '/(?:\<(?P.*?)\>\s)?\((?PNot\s)?(?:\<(?P[^\>]*)\>)?(?:\s?)(?:\[(?P[^\]]*)\])?\)/im'; + $regex = '/(?:\<(?P.*?)\>\s)?\((?PNot\s)?(?:\<(?P[^\>]*)\>)?(?:\s?)(?:\[(?P[^\]]*)\])?\)/im'; preg_match_all($regex,$header,$matches,PREG_SET_ORDER); $conditions = array(); @@ -636,7 +589,7 @@ class Sabre_DAV_Locks_Plugin extends Sabre_DAV_ServerPlugin { 'uri' => $match['uri'], 'tokens' => array( array($match['not']?0:1,$match['token'],isset($match['etag'])?$match['etag']:'') - ), + ), ); if (!$condition['uri'] && count($conditions)) $conditions[count($conditions)-1]['tokens'][] = array( @@ -655,9 +608,9 @@ class Sabre_DAV_Locks_Plugin extends Sabre_DAV_ServerPlugin { } /** - * Parses a webdav lock xml body, and returns a new Sabre_DAV_Locks_LockInfo object - * - * @param string $body + * Parses a webdav lock xml body, and returns a new Sabre_DAV_Locks_LockInfo object + * + * @param string $body * @return Sabre_DAV_Locks_LockInfo */ protected function parseLockRequest($body) { diff --git a/3rdparty/Sabre/DAV/Mount/Plugin.php b/3rdparty/Sabre/DAV/Mount/Plugin.php index f93a1aa25a..b37a90ae99 100644 --- a/3rdparty/Sabre/DAV/Mount/Plugin.php +++ b/3rdparty/Sabre/DAV/Mount/Plugin.php @@ -4,25 +4,25 @@ * This plugin provides support for RFC4709: Mounting WebDAV servers * * Simply append ?mount to any collection to generate the davmount response. - * + * * @package Sabre * @subpackage DAV - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. - * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. + * @author Evert Pot (http://www.rooftopsolutions.nl/) */ class Sabre_DAV_Mount_Plugin extends Sabre_DAV_ServerPlugin { /** - * Reference to Server class - * - * @var Sabre_DAV_Server + * Reference to Server class + * + * @var Sabre_DAV_Server */ private $server; /** - * Initializes the plugin and registers event handles - * - * @param Sabre_DAV_Server $server + * Initializes the plugin and registers event handles + * + * @param Sabre_DAV_Server $server * @return void */ public function initialize(Sabre_DAV_Server $server) { @@ -35,9 +35,10 @@ class Sabre_DAV_Mount_Plugin extends Sabre_DAV_ServerPlugin { /** * 'beforeMethod' event handles. This event handles intercepts GET requests ending * with ?mount - * - * @param string $method - * @return void + * + * @param string $method + * @param string $uri + * @return bool */ public function beforeMethod($method, $uri) { @@ -57,13 +58,13 @@ class Sabre_DAV_Mount_Plugin extends Sabre_DAV_ServerPlugin { } /** - * Generates the davmount response - * - * @param string $uri absolute uri + * Generates the davmount response + * + * @param string $uri absolute uri * @return void */ public function davMount($uri) { - + $this->server->httpResponse->sendStatus(200); $this->server->httpResponse->setHeader('Content-Type','application/davmount+xml'); ob_start(); diff --git a/3rdparty/Sabre/DAV/Node.php b/3rdparty/Sabre/DAV/Node.php index 0510df5fdf..070b7176af 100644 --- a/3rdparty/Sabre/DAV/Node.php +++ b/3rdparty/Sabre/DAV/Node.php @@ -3,22 +3,22 @@ /** * Node class * - * This is a helper class, that should aid in getting nodes setup. - * + * This is a helper class, that should aid in getting nodes setup. + * * @package Sabre * @subpackage DAV - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. - * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. + * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ abstract class Sabre_DAV_Node implements Sabre_DAV_INode { /** - * Returns the last modification time + * Returns the last modification time * * In this case, it will simply return the current time * - * @return int + * @return int */ public function getLastModified() { @@ -30,7 +30,7 @@ abstract class Sabre_DAV_Node implements Sabre_DAV_INode { * Deleted the current node * * @throws Sabre_DAV_Exception_Forbidden - * @return void + * @return void */ public function delete() { @@ -40,7 +40,7 @@ abstract class Sabre_DAV_Node implements Sabre_DAV_INode { /** * Renames the node - * + * * @throws Sabre_DAV_Exception_Forbidden * @param string $name The new name * @return void diff --git a/3rdparty/Sabre/DAV/ObjectTree.php b/3rdparty/Sabre/DAV/ObjectTree.php index f12a368370..bce5146390 100644 --- a/3rdparty/Sabre/DAV/ObjectTree.php +++ b/3rdparty/Sabre/DAV/ObjectTree.php @@ -3,27 +3,27 @@ /** * ObjectTree class * - * This implementation of the Tree class makes use of the INode, IFile and ICollection API's - * + * This implementation of the Tree class makes use of the INode, IFile and ICollection API's + * * @package Sabre * @subpackage DAV - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. - * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. + * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ class Sabre_DAV_ObjectTree extends Sabre_DAV_Tree { /** - * The root node - * + * The root node + * * @var Sabre_DAV_ICollection */ protected $rootNode; /** - * This is the node cache. Accessed nodes are stored here - * - * @var array + * This is the node cache. Accessed nodes are stored here + * + * @var array */ protected $cache = array(); @@ -31,9 +31,8 @@ class Sabre_DAV_ObjectTree extends Sabre_DAV_Tree { * Creates the object * * This method expects the rootObject to be passed as a parameter - * - * @param Sabre_DAV_ICollection $rootNode - * @return void + * + * @param Sabre_DAV_ICollection $rootNode */ public function __construct(Sabre_DAV_ICollection $rootNode) { @@ -42,10 +41,10 @@ class Sabre_DAV_ObjectTree extends Sabre_DAV_Tree { } /** - * Returns the INode object for the requested path - * - * @param string $path - * @return Sabre_DAV_INode + * Returns the INode object for the requested path + * + * @param string $path + * @return Sabre_DAV_INode */ public function getNodeForPath($path) { @@ -54,17 +53,17 @@ class Sabre_DAV_ObjectTree extends Sabre_DAV_Tree { //if (!$path || $path=='.') return $this->rootNode; $currentNode = $this->rootNode; - $i=0; - // We're splitting up the path variable into folder/subfolder components and traverse to the correct node.. + + // We're splitting up the path variable into folder/subfolder components and traverse to the correct node.. foreach(explode('/',$path) as $pathPart) { // If this part of the path is just a dot, it actually means we can skip it if ($pathPart=='.' || $pathPart=='') continue; if (!($currentNode instanceof Sabre_DAV_ICollection)) - throw new Sabre_DAV_Exception_FileNotFound('Could not find node at path: ' . $path); + throw new Sabre_DAV_Exception_NotFound('Could not find node at path: ' . $path); - $currentNode = $currentNode->getChild($pathPart); + $currentNode = $currentNode->getChild($pathPart); } @@ -76,8 +75,8 @@ class Sabre_DAV_ObjectTree extends Sabre_DAV_Tree { /** * This function allows you to check if a node exists. * - * @param string $path - * @return bool + * @param string $path + * @return bool */ public function nodeExists($path) { @@ -92,7 +91,7 @@ class Sabre_DAV_ObjectTree extends Sabre_DAV_Tree { if (!$parentNode instanceof Sabre_DAV_ICollection) return false; return $parentNode->childExists($base); - } catch (Sabre_DAV_Exception_FileNotFound $e) { + } catch (Sabre_DAV_Exception_NotFound $e) { return false; @@ -101,10 +100,10 @@ class Sabre_DAV_ObjectTree extends Sabre_DAV_Tree { } /** - * Returns a list of childnodes for a given path. - * - * @param string $path - * @return array + * Returns a list of childnodes for a given path. + * + * @param string $path + * @return array */ public function getChildren($path) { @@ -127,14 +126,14 @@ class Sabre_DAV_ObjectTree extends Sabre_DAV_Tree { * * node creations * * copy * * move - * * renaming nodes - * + * * renaming nodes + * * If Tree classes implement a form of caching, this will allow * them to make sure caches will be expired. - * + * * If a path is passed, it is assumed that the entire subtree is dirty * - * @param string $path + * @param string $path * @return void */ public function markDirty($path) { @@ -145,7 +144,7 @@ class Sabre_DAV_ObjectTree extends Sabre_DAV_Tree { foreach($this->cache as $nodePath=>$node) { if ($nodePath == $path || strpos($nodePath,$path.'/')===0) unset($this->cache[$nodePath]); - + } } diff --git a/3rdparty/Sabre/DAV/Property.php b/3rdparty/Sabre/DAV/Property.php index 577535b012..1cfada3236 100644 --- a/3rdparty/Sabre/DAV/Property.php +++ b/3rdparty/Sabre/DAV/Property.php @@ -4,16 +4,16 @@ * Abstract property class * * Extend this class to create custom complex properties - * + * * @package Sabre * @subpackage DAV - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ abstract class Sabre_DAV_Property { - abstract function serialize(Sabre_DAV_Server $server, DOMElement $prop); + abstract function serialize(Sabre_DAV_Server $server, DOMElement $prop); static function unserialize(DOMElement $prop) { diff --git a/3rdparty/Sabre/DAV/Property/GetLastModified.php b/3rdparty/Sabre/DAV/Property/GetLastModified.php index 4a81262997..bd63f57314 100644 --- a/3rdparty/Sabre/DAV/Property/GetLastModified.php +++ b/3rdparty/Sabre/DAV/Property/GetLastModified.php @@ -2,33 +2,32 @@ /** * This property represents the {DAV:}getlastmodified property. - * + * * Although this is normally a simple property, windows requires us to add * some new attributes. * - * This class uses unix timestamps internally, and converts them to RFC 1123 times for + * This class uses unix timestamps internally, and converts them to RFC 1123 times for * serialization * * @package Sabre * @subpackage DAV - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. - * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. + * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ class Sabre_DAV_Property_GetLastModified extends Sabre_DAV_Property { /** - * time - * - * @var int + * time + * + * @var int */ public $time; /** - * __construct - * - * @param int|DateTime $time - * @return void + * __construct + * + * @param int|DateTime $time */ public function __construct($time) { @@ -46,9 +45,10 @@ class Sabre_DAV_Property_GetLastModified extends Sabre_DAV_Property { } /** - * serialize - * - * @param DOMElement $prop + * serialize + * + * @param Sabre_DAV_Server $server + * @param DOMElement $prop * @return void */ public function serialize(Sabre_DAV_Server $server, DOMElement $prop) { @@ -56,14 +56,14 @@ class Sabre_DAV_Property_GetLastModified extends Sabre_DAV_Property { $doc = $prop->ownerDocument; $prop->setAttribute('xmlns:b','urn:uuid:c2f41010-65b3-11d1-a29f-00aa00c14882/'); $prop->setAttribute('b:dt','dateTime.rfc1123'); - $prop->nodeValue = $this->time->format(DateTime::RFC1123); + $prop->nodeValue = Sabre_HTTP_Util::toHTTPDate($this->time); } /** - * getTime - * - * @return DateTime + * getTime + * + * @return DateTime */ public function getTime() { diff --git a/3rdparty/Sabre/DAV/Property/Href.php b/3rdparty/Sabre/DAV/Property/Href.php index 3294ff2ac6..dac564f24d 100644 --- a/3rdparty/Sabre/DAV/Property/Href.php +++ b/3rdparty/Sabre/DAV/Property/Href.php @@ -3,36 +3,36 @@ /** * Href property * - * The href property represpents a url within a {DAV:}href element. + * The href property represents a url within a {DAV:}href element. * This is used by many WebDAV extensions, but not really within the WebDAV core spec - * + * * @package Sabre * @subpackage DAV - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. - * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. + * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ class Sabre_DAV_Property_Href extends Sabre_DAV_Property implements Sabre_DAV_Property_IHref { /** - * href - * - * @var string + * href + * + * @var string */ private $href; /** - * Automatically prefix the url with the server base directory - * - * @var bool + * Automatically prefix the url with the server base directory + * + * @var bool */ private $autoPrefix = true; /** - * __construct - * - * @param string $href - * @return void + * __construct + * + * @param string $href + * @param bool $autoPrefix */ public function __construct($href, $autoPrefix = true) { @@ -42,9 +42,9 @@ class Sabre_DAV_Property_Href extends Sabre_DAV_Property implements Sabre_DAV_Pr } /** - * Returns the uri - * - * @return string + * Returns the uri + * + * @return string */ public function getHref() { @@ -56,12 +56,12 @@ class Sabre_DAV_Property_Href extends Sabre_DAV_Property implements Sabre_DAV_Pr * Serializes this property. * * It will additionally prepend the href property with the server's base uri. - * - * @param Sabre_DAV_Server $server - * @param DOMElement $dom + * + * @param Sabre_DAV_Server $server + * @param DOMElement $dom * @return void */ - public function serialize(Sabre_DAV_Server $server,DOMElement $dom) { + public function serialize(Sabre_DAV_Server $server, DOMElement $dom) { $prefix = $server->xmlNamespaces['DAV:']; @@ -72,13 +72,13 @@ class Sabre_DAV_Property_Href extends Sabre_DAV_Property implements Sabre_DAV_Pr } /** - * Unserializes this property from a DOM Element + * Unserializes this property from a DOM Element * * This method returns an instance of this class. * It will only decode {DAV:}href values. For non-compatible elements null will be returned. * - * @param DOMElement $dom - * @return Sabre_DAV_Property_Href + * @param DOMElement $dom + * @return Sabre_DAV_Property_Href */ static function unserialize(DOMElement $dom) { @@ -86,6 +86,6 @@ class Sabre_DAV_Property_Href extends Sabre_DAV_Property implements Sabre_DAV_Pr return new self($dom->firstChild->textContent,false); } - } + } } diff --git a/3rdparty/Sabre/DAV/Property/HrefList.php b/3rdparty/Sabre/DAV/Property/HrefList.php index 76a5512901..7a52272e88 100644 --- a/3rdparty/Sabre/DAV/Property/HrefList.php +++ b/3rdparty/Sabre/DAV/Property/HrefList.php @@ -3,35 +3,35 @@ /** * HrefList property * - * This property contains multiple {DAV:}href elements, each containing a url. - * + * This property contains multiple {DAV:}href elements, each containing a url. + * * @package Sabre * @subpackage DAV - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. - * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. + * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ class Sabre_DAV_Property_HrefList extends Sabre_DAV_Property { /** - * hrefs - * - * @var array + * hrefs + * + * @var array */ private $hrefs; /** - * Automatically prefix the url with the server base directory - * - * @var bool + * Automatically prefix the url with the server base directory + * + * @var bool */ private $autoPrefix = true; /** - * __construct - * + * __construct + * * @param array $hrefs - * @param bool $autoPrefix + * @param bool $autoPrefix */ public function __construct(array $hrefs, $autoPrefix = true) { @@ -41,9 +41,9 @@ class Sabre_DAV_Property_HrefList extends Sabre_DAV_Property { } /** - * Returns the uris - * - * @return array + * Returns the uris + * + * @return array */ public function getHrefs() { @@ -55,9 +55,9 @@ class Sabre_DAV_Property_HrefList extends Sabre_DAV_Property { * Serializes this property. * * It will additionally prepend the href property with the server's base uri. - * - * @param Sabre_DAV_Server $server - * @param DOMElement $dom + * + * @param Sabre_DAV_Server $server + * @param DOMElement $dom * @return void */ public function serialize(Sabre_DAV_Server $server,DOMElement $dom) { @@ -73,13 +73,13 @@ class Sabre_DAV_Property_HrefList extends Sabre_DAV_Property { } /** - * Unserializes this property from a DOM Element + * Unserializes this property from a DOM Element * * This method returns an instance of this class. * It will only decode {DAV:}href values. * - * @param DOMElement $dom - * @return Sabre_DAV_Property_Href + * @param DOMElement $dom + * @return Sabre_DAV_Property_Href */ static function unserialize(DOMElement $dom) { @@ -91,6 +91,6 @@ class Sabre_DAV_Property_HrefList extends Sabre_DAV_Property { } return new self($hrefs, false); - } + } } diff --git a/3rdparty/Sabre/DAV/Property/IHref.php b/3rdparty/Sabre/DAV/Property/IHref.php index 29d76a44fc..5c0409064c 100644 --- a/3rdparty/Sabre/DAV/Property/IHref.php +++ b/3rdparty/Sabre/DAV/Property/IHref.php @@ -9,16 +9,16 @@ * * @package Sabre * @subpackage DAV - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. - * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. + * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ interface Sabre_DAV_Property_IHref { /** - * getHref - * - * @return string + * getHref + * + * @return string */ function getHref(); diff --git a/3rdparty/Sabre/DAV/Property/LockDiscovery.php b/3rdparty/Sabre/DAV/Property/LockDiscovery.php index 05c7470b4e..2ded5649a4 100644 --- a/3rdparty/Sabre/DAV/Property/LockDiscovery.php +++ b/3rdparty/Sabre/DAV/Property/LockDiscovery.php @@ -4,26 +4,26 @@ * Represents {DAV:}lockdiscovery property * * This property contains all the open locks on a given resource - * + * * @package Sabre * @subpackage DAV - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. - * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. + * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ class Sabre_DAV_Property_LockDiscovery extends Sabre_DAV_Property { /** - * locks - * - * @var array + * locks + * + * @var array */ public $locks; - + /** - * Should we show the locktoken as well? - * - * @var bool + * Should we show the locktoken as well? + * + * @var bool */ public $revealLockToken; @@ -36,13 +36,12 @@ class Sabre_DAV_Property_LockDiscovery extends Sabre_DAV_Property { static public $hideLockRoot = false; /** - * __construct - * - * @param array $locks - * @param bool $revealLockToken - * @return void + * __construct + * + * @param array $locks + * @param bool $revealLockToken */ - public function __construct($locks,$revealLockToken = false) { + public function __construct($locks, $revealLockToken = false) { $this->locks = $locks; $this->revealLockToken = $revealLockToken; @@ -50,12 +49,13 @@ class Sabre_DAV_Property_LockDiscovery extends Sabre_DAV_Property { } /** - * serialize - * - * @param DOMElement $prop + * serialize + * + * @param Sabre_DAV_Server $server + * @param DOMElement $prop * @return void */ - public function serialize(Sabre_DAV_Server $server,DOMElement $prop) { + public function serialize(Sabre_DAV_Server $server, DOMElement $prop) { $doc = $prop->ownerDocument; @@ -74,7 +74,7 @@ class Sabre_DAV_Property_LockDiscovery extends Sabre_DAV_Property { $lockType->appendChild($doc->createElementNS('DAV:','d:write')); - /* {DAV:}lockroot */ + /* {DAV:}lockroot */ if (!self::$hideLockRoot) { $lockRoot = $doc->createElementNS('DAV:','d:lockroot'); $activeLock->appendChild($lockRoot); @@ -91,7 +91,7 @@ class Sabre_DAV_Property_LockDiscovery extends Sabre_DAV_Property { $activeLock->appendChild($lockToken); $lockToken->appendChild($doc->createElementNS('DAV:','d:href','opaquelocktoken:' . $lock->token)); } - + $activeLock->appendChild($doc->createElementNS('DAV:','d:owner',$lock->owner)); } diff --git a/3rdparty/Sabre/DAV/Property/ResourceType.php b/3rdparty/Sabre/DAV/Property/ResourceType.php index 2c606c22d6..f6269611e5 100644 --- a/3rdparty/Sabre/DAV/Property/ResourceType.php +++ b/3rdparty/Sabre/DAV/Property/ResourceType.php @@ -4,28 +4,27 @@ * This class represents the {DAV:}resourcetype property * * Normally for files this is empty, and for collection {DAV:}collection. - * However, other specs define different values for this. - * + * However, other specs define different values for this. + * * @package Sabre * @subpackage DAV - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ class Sabre_DAV_Property_ResourceType extends Sabre_DAV_Property { /** - * resourceType - * + * resourceType + * * @var array */ public $resourceType = array(); /** - * __construct - * - * @param mixed $resourceType - * @return void + * __construct + * + * @param mixed $resourceType */ public function __construct($resourceType = array()) { @@ -33,7 +32,7 @@ class Sabre_DAV_Property_ResourceType extends Sabre_DAV_Property { $this->resourceType = array(); elseif ($resourceType === Sabre_DAV_Server::NODE_DIRECTORY) $this->resourceType = array('{DAV:}collection'); - elseif (is_array($resourceType)) + elseif (is_array($resourceType)) $this->resourceType = $resourceType; else $this->resourceType = array($resourceType); @@ -41,25 +40,26 @@ class Sabre_DAV_Property_ResourceType extends Sabre_DAV_Property { } /** - * serialize - * - * @param DOMElement $prop + * serialize + * + * @param Sabre_DAV_Server $server + * @param DOMElement $prop * @return void */ - public function serialize(Sabre_DAV_Server $server,DOMElement $prop) { + public function serialize(Sabre_DAV_Server $server, DOMElement $prop) { $propName = null; $rt = $this->resourceType; - + foreach($rt as $resourceType) { - if (preg_match('/^{([^}]*)}(.*)$/',$resourceType,$propName)) { - + if (preg_match('/^{([^}]*)}(.*)$/',$resourceType,$propName)) { + if (isset($server->xmlNamespaces[$propName[1]])) { $prop->appendChild($prop->ownerDocument->createElement($server->xmlNamespaces[$propName[1]] . ':' . $propName[2])); } else { $prop->appendChild($prop->ownerDocument->createElementNS($propName[1],'custom:' . $propName[2])); } - + } } @@ -69,8 +69,8 @@ class Sabre_DAV_Property_ResourceType extends Sabre_DAV_Property { * Returns the values in clark-notation * * For example array('{DAV:}collection') - * - * @return array + * + * @return array */ public function getValue() { @@ -79,10 +79,10 @@ class Sabre_DAV_Property_ResourceType extends Sabre_DAV_Property { } /** - * Checks if the principal contains a certain value - * - * @param string $type - * @return bool + * Checks if the principal contains a certain value + * + * @param string $type + * @return bool */ public function is($type) { @@ -104,10 +104,10 @@ class Sabre_DAV_Property_ResourceType extends Sabre_DAV_Property { } /** - * Unserializes a DOM element into a ResourceType property. - * - * @param DOMElement $dom - * @return void + * Unserializes a DOM element into a ResourceType property. + * + * @param DOMElement $dom + * @return Sabre_DAV_Property_ResourceType */ static public function unserialize(DOMElement $dom) { diff --git a/3rdparty/Sabre/DAV/Property/Response.php b/3rdparty/Sabre/DAV/Property/Response.php index 7d3a2db038..88afbcfb26 100644 --- a/3rdparty/Sabre/DAV/Property/Response.php +++ b/3rdparty/Sabre/DAV/Property/Response.php @@ -1,53 +1,52 @@ href = $href; - $this->responseProperties = $responseProperties; + $this->responseProperties = $responseProperties; } /** - * Returns the url - * - * @return string + * Returns the url + * + * @return string */ public function getHref() { @@ -56,9 +55,9 @@ class Sabre_DAV_Property_Response extends Sabre_DAV_Property implements Sabre_DA } /** - * Returns the property list - * - * @return array + * Returns the property list + * + * @return array */ public function getResponseProperties() { @@ -67,19 +66,19 @@ class Sabre_DAV_Property_Response extends Sabre_DAV_Property implements Sabre_DA } /** - * serialize - * - * @param Sabre_DAV_Server $server - * @param DOMElement $dom + * serialize + * + * @param Sabre_DAV_Server $server + * @param DOMElement $dom * @return void */ - public function serialize(Sabre_DAV_Server $server,DOMElement $dom) { + public function serialize(Sabre_DAV_Server $server, DOMElement $dom) { $document = $dom->ownerDocument; $properties = $this->responseProperties; - + $xresponse = $document->createElement('d:response'); - $dom->appendChild($xresponse); + $dom->appendChild($xresponse); $uri = Sabre_DAV_URLUtil::encodePath($this->href); @@ -87,7 +86,7 @@ class Sabre_DAV_Property_Response extends Sabre_DAV_Property implements Sabre_DA $uri = $server->getBaseUri() . $uri; $xresponse->appendChild($document->createElement('d:href',$uri)); - + // The properties variable is an array containing properties, grouped by // HTTP status foreach($properties as $httpStatus=>$propertyGroup) { @@ -111,7 +110,7 @@ class Sabre_DAV_Property_Response extends Sabre_DAV_Property implements Sabre_DA $propName = null; preg_match('/^{([^}]*)}(.*)$/',$propertyName,$propName); - + // special case for empty namespaces if ($propName[1]=='') { @@ -125,7 +124,7 @@ class Sabre_DAV_Property_Response extends Sabre_DAV_Property implements Sabre_DA $nsList[$propName[1]] = 'x' . count($nsList); } - // If the namespace was defined in the top-level xml namespaces, it means + // If the namespace was defined in the top-level xml namespaces, it means // there was already a namespace declaration, and we don't have to worry about it. if (isset($server->xmlNamespaces[$propName[1]])) { $currentProperty = $document->createElement($nsList[$propName[1]] . ':' . $propName[2]); diff --git a/3rdparty/Sabre/DAV/Property/ResponseList.php b/3rdparty/Sabre/DAV/Property/ResponseList.php index cd70b12861..cae923afbf 100644 --- a/3rdparty/Sabre/DAV/Property/ResponseList.php +++ b/3rdparty/Sabre/DAV/Property/ResponseList.php @@ -1,33 +1,32 @@ addReport($reports); } @@ -44,8 +43,8 @@ class Sabre_DAV_Property_SupportedReportSet extends Sabre_DAV_Property { * * The report must be a string in clark-notation. * Multiple reports can be specified as an array. - * - * @param mixed $report + * + * @param mixed $report * @return void */ public function addReport($report) { @@ -54,7 +53,7 @@ class Sabre_DAV_Property_SupportedReportSet extends Sabre_DAV_Property { foreach($report as $r) { - if (!preg_match('/^{([^}]*)}(.*)$/',$r)) + if (!preg_match('/^{([^}]*)}(.*)$/',$r)) throw new Sabre_DAV_Exception('Reportname must be in clark-notation'); $this->reports[] = $r; @@ -65,8 +64,8 @@ class Sabre_DAV_Property_SupportedReportSet extends Sabre_DAV_Property { /** * Returns the list of supported reports - * - * @return array + * + * @return array */ public function getValue() { @@ -75,16 +74,16 @@ class Sabre_DAV_Property_SupportedReportSet extends Sabre_DAV_Property { } /** - * Serializes the node + * Serializes the node * * @param Sabre_DAV_Server $server - * @param DOMElement $prop + * @param DOMElement $prop * @return void */ - public function serialize(Sabre_DAV_Server $server,DOMElement $prop) { + public function serialize(Sabre_DAV_Server $server, DOMElement $prop) { foreach($this->reports as $reportName) { - + $supportedReport = $prop->ownerDocument->createElement('d:supported-report'); $prop->appendChild($supportedReport); @@ -92,9 +91,9 @@ class Sabre_DAV_Property_SupportedReportSet extends Sabre_DAV_Property { $supportedReport->appendChild($report); preg_match('/^{([^}]*)}(.*)$/',$reportName,$matches); - + list(, $namespace, $element) = $matches; - + $prefix = isset($server->xmlNamespaces[$namespace])?$server->xmlNamespaces[$namespace]:null; if ($prefix) { diff --git a/3rdparty/Sabre/DAV/Server.php b/3rdparty/Sabre/DAV/Server.php index 3d76d4f191..4284c127b6 100644 --- a/3rdparty/Sabre/DAV/Server.php +++ b/3rdparty/Sabre/DAV/Server.php @@ -2,11 +2,11 @@ /** * Main DAV server class - * + * * @package Sabre * @subpackage DAV - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. - * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. + * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ class Sabre_DAV_Server { @@ -26,9 +26,6 @@ class Sabre_DAV_Server { */ const NODE_DIRECTORY = 2; - const PROP_SET = 1; - const PROP_REMOVE = 2; - /** * XML namespace for all SabreDAV related elements */ @@ -36,42 +33,42 @@ class Sabre_DAV_Server { /** * The tree object - * - * @var Sabre_DAV_Tree + * + * @var Sabre_DAV_Tree */ public $tree; /** - * The base uri - * - * @var string + * The base uri + * + * @var string */ - protected $baseUri = null; + protected $baseUri = null; /** - * httpResponse - * - * @var Sabre_HTTP_Response + * httpResponse + * + * @var Sabre_HTTP_Response */ public $httpResponse; /** * httpRequest - * - * @var Sabre_HTTP_Request + * + * @var Sabre_HTTP_Request */ public $httpRequest; /** - * The list of plugins - * - * @var array + * The list of plugins + * + * @var array */ protected $plugins = array(); /** - * This array contains a list of callbacks we should call when certain events are triggered - * + * This array contains a list of callbacks we should call when certain events are triggered + * * @var array */ protected $eventSubscriptions = array(); @@ -81,7 +78,7 @@ class Sabre_DAV_Server { * * If you are defining your own custom namespace, add it here to reduce * bandwidth and improve legibility of xml bodies. - * + * * @var array */ public $xmlNamespaces = array( @@ -90,9 +87,9 @@ class Sabre_DAV_Server { ); /** - * The propertymap can be used to map properties from + * The propertymap can be used to map properties from * requests to property classes. - * + * * @var array */ public $propertyMap = array( @@ -125,23 +122,32 @@ class Sabre_DAV_Server { * This is a flag that allow or not showing file, line and code * of the exception in the returned XML * - * @var bool + * @var bool */ public $debugExceptions = false; /** - * This property allows you to automatically add the 'resourcetype' value + * This property allows you to automatically add the 'resourcetype' value * based on a node's classname or interface. * - * The preset ensures that {DAV:}collection is automaticlly added for nodes + * The preset ensures that {DAV:}collection is automaticlly added for nodes * implementing Sabre_DAV_ICollection. - * + * * @var array */ public $resourceTypeMapping = array( 'Sabre_DAV_ICollection' => '{DAV:}collection', ); + /** + * If this setting is turned off, SabreDAV's version number will be hidden + * from various places. + * + * Some people feel this is a good security measure. + * + * @var bool + */ + static public $exposeVersion = true; /** * Sets up the server @@ -150,14 +156,13 @@ class Sabre_DAV_Server { * use it as the directory tree. If a Sabre_DAV_INode is passed, it * will create a Sabre_DAV_ObjectTree and use the node as the root. * - * If nothing is passed, a Sabre_DAV_SimpleCollection is created in + * If nothing is passed, a Sabre_DAV_SimpleCollection is created in * a Sabre_DAV_ObjectTree. * * If an array is passed, we automatically create a root node, and use - * the nodes in the array as top-level children. - * - * @param Sabre_DAV_Tree $tree The tree object - * @return void + * the nodes in the array as top-level children. + * + * @param Sabre_DAV_Tree|Sabre_DAV_INode|null $treeOrNode The tree object */ public function __construct($treeOrNode = null) { @@ -190,7 +195,7 @@ class Sabre_DAV_Server { } /** - * Starts the DAV Server + * Starts the DAV Server * * @return void */ @@ -218,7 +223,9 @@ class Sabre_DAV_Server { $error->appendChild($DOM->createElement('s:stacktrace',$e->getTraceAsString())); } - $error->appendChild($DOM->createElement('s:sabredav-version',Sabre_DAV_Version::VERSION)); + if (self::$exposeVersion) { + $error->appendChild($DOM->createElement('s:sabredav-version',Sabre_DAV_Version::VERSION)); + } if($e instanceof Sabre_DAV_Exception) { @@ -233,7 +240,7 @@ class Sabre_DAV_Server { } $headers['Content-Type'] = 'application/xml; charset=utf-8'; - + $this->httpResponse->sendStatus($httpCode); $this->httpResponse->setHeaders($headers); $this->httpResponse->sendBody($DOM->saveXML()); @@ -244,24 +251,24 @@ class Sabre_DAV_Server { /** * Sets the base server uri - * + * * @param string $uri * @return void */ public function setBaseUri($uri) { // If the baseUri does not end with a slash, we must add it - if ($uri[strlen($uri)-1]!=='/') + if ($uri[strlen($uri)-1]!=='/') $uri.='/'; - $this->baseUri = $uri; + $this->baseUri = $uri; } /** * Returns the base responding uri - * - * @return string + * + * @return string */ public function getBaseUri() { @@ -272,11 +279,11 @@ class Sabre_DAV_Server { /** * This method attempts to detect the base uri. - * Only the PATH_INFO variable is considered. - * - * If this variable is not set, the root (/) is assumed. + * Only the PATH_INFO variable is considered. * - * @return void + * If this variable is not set, the root (/) is assumed. + * + * @return string */ public function guessBaseUri() { @@ -303,21 +310,21 @@ class Sabre_DAV_Server { return rtrim($baseUri,'/') . '/'; } - throw new Sabre_DAV_Exception('The REQUEST_URI ('. $uri . ') did not end with the contents of PATH_INFO (' . $pathInfo . '). This server might be misconfigured.'); + throw new Sabre_DAV_Exception('The REQUEST_URI ('. $uri . ') did not end with the contents of PATH_INFO (' . $pathInfo . '). This server might be misconfigured.'); - } + } - // The last fallback is that we're just going to assume the server root. + // The last fallback is that we're just going to assume the server root. return '/'; } /** * Adds a plugin to the server - * + * * For more information, console the documentation of Sabre_DAV_ServerPlugin * - * @param Sabre_DAV_ServerPlugin $plugin + * @param Sabre_DAV_ServerPlugin $plugin * @return void */ public function addPlugin(Sabre_DAV_ServerPlugin $plugin) { @@ -333,11 +340,11 @@ class Sabre_DAV_Server { * This function returns null if the plugin was not found. * * @param string $name - * @return Sabre_DAV_ServerPlugin + * @return Sabre_DAV_ServerPlugin */ public function getPlugin($name) { - if (isset($this->plugins[$name])) + if (isset($this->plugins[$name])) return $this->plugins[$name]; // This is a fallback and deprecated. @@ -350,9 +357,9 @@ class Sabre_DAV_Server { } /** - * Returns all plugins - * - * @return array + * Returns all plugins + * + * @return array */ public function getPlugins() { @@ -361,7 +368,6 @@ class Sabre_DAV_Server { } - /** * Subscribe to an event. * @@ -371,9 +377,9 @@ class Sabre_DAV_Server { * * This is for example used to make sure that the authentication plugin * is triggered before anything else. If it's not needed to change this - * number, it is recommended to ommit. - * - * @param string $event + * number, it is recommended to ommit. + * + * @param string $event * @param callback $callback * @param int $priority * @return void @@ -398,7 +404,7 @@ class Sabre_DAV_Server { * * @param string $eventName * @param array $arguments - * @return bool + * @return bool */ public function broadcastEvent($eventName,$arguments = array()) { @@ -418,7 +424,7 @@ class Sabre_DAV_Server { } /** - * Handles a http request, and execute a method based on its name + * Handles a http request, and execute a method based on its name * * @param string $method * @param string $uri @@ -426,7 +432,7 @@ class Sabre_DAV_Server { */ public function invokeMethod($method, $uri) { - $method = strtoupper($method); + $method = strtoupper($method); if (!$this->broadcastEvent('beforeMethod',array($method, $uri))) return; @@ -453,7 +459,7 @@ class Sabre_DAV_Server { if ($this->broadcastEvent('unknownMethod',array($method, $uri))) { // Unsupported method - throw new Sabre_DAV_Exception_NotImplemented(); + throw new Sabre_DAV_Exception_NotImplemented('There was no handler found for this "' . $method . '" method'); } } @@ -461,9 +467,9 @@ class Sabre_DAV_Server { } // {{{ HTTP Method implementations - + /** - * HTTP OPTIONS + * HTTP OPTIONS * * @param string $uri * @return void @@ -476,11 +482,13 @@ class Sabre_DAV_Server { $features = array('1','3', 'extended-mkcol'); foreach($this->plugins as $plugin) $features = array_merge($features,$plugin->getFeatures()); - + $this->httpResponse->setHeader('DAV',implode(', ',$features)); $this->httpResponse->setHeader('MS-Author-Via','DAV'); $this->httpResponse->setHeader('Accept-Ranges','bytes'); - $this->httpResponse->setHeader('X-Sabre-Version',Sabre_DAV_Version::VERSION); + if (self::$exposeVersion) { + $this->httpResponse->setHeader('X-Sabre-Version',Sabre_DAV_Version::VERSION); + } $this->httpResponse->setHeader('Content-Length',0); $this->httpResponse->sendStatus(200); @@ -492,13 +500,13 @@ class Sabre_DAV_Server { * This method simply fetches the contents of a uri, like normal * * @param string $uri - * @return void + * @return bool */ protected function httpGet($uri) { $node = $this->tree->getNodeForPath($uri,0); - if (!$this->checkPreconditions(true)) return false; + if (!$this->checkPreconditions(true)) return false; if (!($node instanceof Sabre_DAV_IFile)) throw new Sabre_DAV_Exception_NotImplemented('GET is only implemented on File objects'); $body = $node->get(); @@ -535,7 +543,7 @@ class Sabre_DAV_Server { } else { $nodeSize = null; } - + $this->httpResponse->setHeaders($httpHeaders); $range = $this->getHTTPRange(); @@ -545,12 +553,12 @@ class Sabre_DAV_Server { // If ifRange is set, and range is specified, we first need to check // the precondition. if ($nodeSize && $range && $ifRange) { - + // if IfRange is parsable as a date we'll treat it as a DateTime // otherwise, we must treat it as an etag. try { $ifRangeDate = new DateTime($ifRange); - + // It's a date. We must check if the entity is modified since // the specified date. if (!isset($httpHeaders['Last-Modified'])) $ignoreRangeHeader = true; @@ -560,8 +568,8 @@ class Sabre_DAV_Server { } } catch (Exception $e) { - - // It's an entity. We can do a simple comparison. + + // It's an entity. We can do a simple comparison. if (!isset($httpHeaders['ETag'])) $ignoreRangeHeader = true; elseif ($httpHeaders['ETag']!==$ifRange) $ignoreRangeHeader = true; } @@ -575,7 +583,7 @@ class Sabre_DAV_Server { $start = $range[0]; $end = $range[1]?$range[1]:$nodeSize-1; - if($start >= $nodeSize) + if($start >= $nodeSize) throw new Sabre_DAV_Exception_RequestedRangeNotSatisfiable('The start offset (' . $range[0] . ') exceeded the size of the entity (' . $nodeSize . ')'); if($end < $start) throw new Sabre_DAV_Exception_RequestedRangeNotSatisfiable('The end offset (' . $range[1] . ') is lower than the start offset (' . $range[0] . ')'); @@ -625,7 +633,7 @@ class Sabre_DAV_Server { $node = $this->tree->getNodeForPath($uri); /* This information is only collection for File objects. - * Ideally we want to throw 405 Method Not Allowed for every + * Ideally we want to throw 405 Method Not Allowed for every * non-file, but MS Office does not like this */ if ($node instanceof Sabre_DAV_IFile) { @@ -640,7 +648,7 @@ class Sabre_DAV_Server { } /** - * HTTP Delete + * HTTP Delete * * The HTTP delete method, deletes a given uri * @@ -651,6 +659,7 @@ class Sabre_DAV_Server { if (!$this->broadcastEvent('beforeUnbind',array($uri))) return; $this->tree->delete($uri); + $this->broadcastEvent('afterUnbind',array($uri)); $this->httpResponse->sendStatus(204); $this->httpResponse->setHeader('Content-Length','0'); @@ -659,13 +668,13 @@ class Sabre_DAV_Server { /** - * WebDAV PROPFIND + * WebDAV PROPFIND * * This WebDAV method requests information about an uri resource, or a list of resources * If a client wants to receive the properties for a single resource it will add an HTTP Depth: header with a 0 value * If the value is 1, it means that it also expects a list of sub-resources (e.g.: files in a directory) * - * The request body contains an XML data structure that has a list of properties the client understands + * The request body contains an XML data structure that has a list of properties the client understands * The response body is also an xml document, containing information about every uri resource and the requested properties * * It has to return a HTTP 207 Multi-status status code @@ -679,7 +688,7 @@ class Sabre_DAV_Server { $requestedProperties = $this->parsePropfindRequest($this->httpRequest->getBody(true)); $depth = $this->getHTTPDepth(1); - // The only two options for the depth of a propfind is 0 or 1 + // The only two options for the depth of a propfind is 0 or 1 if ($depth!=0) $depth = 1; $newProperties = $this->getPropertiesForPath($uri,$requestedProperties,$depth); @@ -688,8 +697,8 @@ class Sabre_DAV_Server { $this->httpResponse->sendStatus(207); $this->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8'); - // Normally this header is only needed for OPTIONS responses, however.. - // iCal seems to also depend on these being set for PROPFIND. Since + // Normally this header is only needed for OPTIONS responses, however.. + // iCal seems to also depend on these being set for PROPFIND. Since // this is not harmful, we'll add it. $features = array('1','3', 'extended-mkcol'); foreach($this->plugins as $plugin) $features = array_merge($features,$plugin->getFeatures()); @@ -712,7 +721,7 @@ class Sabre_DAV_Server { protected function httpPropPatch($uri) { $newProperties = $this->parsePropPatchRequest($this->httpRequest->getBody(true)); - + $result = $this->updateProperties($uri, $newProperties); $this->httpResponse->sendStatus(207); @@ -725,14 +734,14 @@ class Sabre_DAV_Server { } /** - * HTTP PUT method - * + * HTTP PUT method + * * This HTTP method updates a file, or creates a new one. * - * If a new resource was created, a 201 Created status code should be returned. If an existing resource is updated, it's a 200 Ok + * If a new resource was created, a 201 Created status code should be returned. If an existing resource is updated, it's a 204 No Content * * @param string $uri - * @return void + * @return bool */ protected function httpPut($uri) { @@ -768,13 +777,13 @@ class Sabre_DAV_Server { // Intercepting the Finder problem if (($expected = $this->httpRequest->getHeader('X-Expected-Entity-Length')) && $expected > 0) { - + /** - Many webservers will not cooperate well with Finder PUT requests, + Many webservers will not cooperate well with Finder PUT requests, because it uses 'Chunked' transfer encoding for the request body. - The symptom of this problem is that Finder sends files to the - server, but they arrive as 0-lenght files in PHP. + The symptom of this problem is that Finder sends files to the + server, but they arrive as 0-length files in PHP. If we don't do anything, the user might think they are uploading files successfully, but they end up empty on the server. Instead, @@ -808,29 +817,36 @@ class Sabre_DAV_Server { } - if ($this->tree->nodeExists($uri)) { + if ($this->tree->nodeExists($uri)) { $node = $this->tree->getNodeForPath($uri); - + // Checking If-None-Match and related headers. if (!$this->checkPreconditions()) return; - + // If the node is a collection, we'll deny it if (!($node instanceof Sabre_DAV_IFile)) throw new Sabre_DAV_Exception_Conflict('PUT is not allowed on non-files.'); - if (!$this->broadcastEvent('beforeWriteContent',array($this->getRequestUri()))) return false; + if (!$this->broadcastEvent('beforeWriteContent',array($uri, $node, &$body))) return false; + + $etag = $node->put($body); + + $this->broadcastEvent('afterWriteContent',array($uri, $node)); - $node->put($body); $this->httpResponse->setHeader('Content-Length','0'); + if ($etag) $this->httpResponse->setHeader('ETag',$etag); $this->httpResponse->sendStatus(204); } else { + $etag = null; // If we got here, the resource didn't exist yet. - if (!$this->createFile($this->getRequestUri(),$body)) { + if (!$this->createFile($this->getRequestUri(),$body,$etag)) { // For one reason or another the file was not created. return; } + $this->httpResponse->setHeader('Content-Length','0'); + if ($etag) $this->httpResponse->setHeader('ETag', $etag); $this->httpResponse->sendStatus(201); } @@ -855,7 +871,7 @@ class Sabre_DAV_Server { $contentType = $this->httpRequest->getHeader('Content-Type'); if (strpos($contentType,'application/xml')!==0 && strpos($contentType,'text/xml')!==0) { - // We must throw 415 for unsupport mkcol bodies + // We must throw 415 for unsupported mkcol bodies throw new Sabre_DAV_Exception_UnsupportedMediaType('The request body for the MKCOL request must have an xml Content-Type'); } @@ -863,7 +879,7 @@ class Sabre_DAV_Server { $dom = Sabre_DAV_XMLUtil::loadDOMDocument($requestBody); if (Sabre_DAV_XMLUtil::toClarkNotation($dom->firstChild)!=='{DAV:}mkcol') { - // We must throw 415 for unsupport mkcol bodies + // We must throw 415 for unsupported mkcol bodies throw new Sabre_DAV_Exception_UnsupportedMediaType('The request body for the MKCOL request must be a {DAV:}mkcol request construct.'); } @@ -875,7 +891,7 @@ class Sabre_DAV_Server { $properties = array_merge($properties, Sabre_DAV_XMLUtil::parseProperties($childNode, $this->propertyMap)); } - if (!isset($properties['{DAV:}resourcetype'])) + if (!isset($properties['{DAV:}resourcetype'])) throw new Sabre_DAV_Exception_BadRequest('The mkcol request must include a {DAV:}resourcetype property'); $resourceType = $properties['{DAV:}resourcetype']->getValue(); @@ -918,19 +934,21 @@ class Sabre_DAV_Server { $moveInfo = $this->getCopyAndMoveInfo(); // If the destination is part of the source tree, we must fail - if ($moveInfo['destination']==$uri) + if ($moveInfo['destination']==$uri) throw new Sabre_DAV_Exception_Forbidden('Source and destination uri are identical.'); if ($moveInfo['destinationExists']) { if (!$this->broadcastEvent('beforeUnbind',array($moveInfo['destination']))) return false; $this->tree->delete($moveInfo['destination']); + $this->broadcastEvent('afterUnbind',array($moveInfo['destination'])); } if (!$this->broadcastEvent('beforeUnbind',array($uri))) return false; if (!$this->broadcastEvent('beforeBind',array($moveInfo['destination']))) return false; $this->tree->move($uri,$moveInfo['destination']); + $this->broadcastEvent('afterUnbind',array($uri)); $this->broadcastEvent('afterBind',array($moveInfo['destination'])); // If a resource was overwritten we should send a 204, otherwise a 201 @@ -946,13 +964,13 @@ class Sabre_DAV_Server { * A lot of the actual request processing is done in getCopyMoveInfo * * @param string $uri - * @return void + * @return bool */ protected function httpCopy($uri) { $copyInfo = $this->getCopyAndMoveInfo(); // If the destination is part of the source tree, we must fail - if ($copyInfo['destination']==$uri) + if ($copyInfo['destination']==$uri) throw new Sabre_DAV_Exception_Forbidden('Source and destination uri are identical.'); if ($copyInfo['destinationExists']) { @@ -998,13 +1016,13 @@ class Sabre_DAV_Server { } // }}} - // {{{ HTTP/WebDAV protocol helpers + // {{{ HTTP/WebDAV protocol helpers /** - * Returns an array with all the supported HTTP methods for a specific uri. + * Returns an array with all the supported HTTP methods for a specific uri. * - * @param string $uri - * @return array + * @param string $uri + * @return array */ public function getAllowedMethods($uri) { @@ -1023,13 +1041,13 @@ class Sabre_DAV_Server { // The MKCOL is only allowed on an unmapped uri try { - $node = $this->tree->getNodeForPath($uri); - } catch (Sabre_DAV_Exception_FileNotFound $e) { + $this->tree->getNodeForPath($uri); + } catch (Sabre_DAV_Exception_NotFound $e) { $methods[] = 'MKCOL'; } // We're also checking if any of the plugins register any new methods - foreach($this->plugins as $plugin) $methods = array_merge($methods,$plugin->getHTTPMethods($uri)); + foreach($this->plugins as $plugin) $methods = array_merge($methods, $plugin->getHTTPMethods($uri)); array_unique($methods); return $methods; @@ -1037,8 +1055,8 @@ class Sabre_DAV_Server { } /** - * Gets the uri for the request, keeping the base uri into consideration - * + * Gets the uri for the request, keeping the base uri into consideration + * * @return string */ public function getRequestUri() { @@ -1048,9 +1066,9 @@ class Sabre_DAV_Server { } /** - * Calculates the uri for a request, making sure that the base uri is stripped out - * - * @param string $uri + * Calculates the uri for a request, making sure that the base uri is stripped out + * + * @param string $uri * @throws Sabre_DAV_Exception_Forbidden A permission denied exception is thrown whenever there was an attempt to supply a uri outside of the base uri * @return string */ @@ -1068,9 +1086,9 @@ class Sabre_DAV_Server { return trim(Sabre_DAV_URLUtil::decodePath(substr($uri,strlen($this->getBaseUri()))),'/'); - // A special case, if the baseUri was accessed without a trailing - // slash, we'll accept it as well. - } elseif ($uri.'/' === $this->getBaseUri()) { + // A special case, if the baseUri was accessed without a trailing + // slash, we'll accept it as well. + } elseif ($uri.'/' === $this->getBaseUri()) { return ''; @@ -1086,10 +1104,10 @@ class Sabre_DAV_Server { * Returns the HTTP depth header * * This method returns the contents of the HTTP depth request header. If the depth header was 'infinity' it will return the Sabre_DAV_Server::DEPTH_INFINITY object - * It is possible to supply a default depth value, which is used when the depth header has invalid content, or is completely non-existant - * - * @param mixed $default - * @return int + * It is possible to supply a default depth value, which is used when the depth header has invalid content, or is completely non-existent + * + * @param mixed $default + * @return int */ public function getHTTPDepth($default = self::DEPTH_INFINITY) { @@ -1100,7 +1118,7 @@ class Sabre_DAV_Server { if ($depth == 'infinity') return self::DEPTH_INFINITY; - + // If its an unknown value. we'll grab the default if (!ctype_digit($depth)) return $default; @@ -1118,14 +1136,14 @@ class Sabre_DAV_Server { * 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 + * If the first offset is null, the second offset should be used to retrieve the last x bytes of the entity * - * return $mixed + * @return array|null */ public function getHTTPRange() { $range = $this->httpRequest->getHeader('range'); - if (is_null($range)) return null; + if (is_null($range)) return null; // Matching "Range: bytes=1234-5678: both numbers are optional @@ -1143,15 +1161,15 @@ class Sabre_DAV_Server { /** * Returns information about Copy and Move requests - * - * This function is created to help getting information about the source and the destination for the - * WebDAV MOVE and COPY HTTP request. It also validates a lot of information and throws proper exceptions - * + * + * This function is created to help getting information about the source and the destination for the + * WebDAV MOVE and COPY HTTP request. It also validates a lot of information and throws proper exceptions + * * The returned value is an array with the following keys: * * destination - Destination path - * * destinationExists - Wether or not the destination is an existing url (and should therefore be overwritten) + * * destinationExists - Whether or not the destination is an existing url (and should therefore be overwritten) * - * @return array + * @return array */ public function getCopyAndMoveInfo() { @@ -1170,7 +1188,7 @@ class Sabre_DAV_Server { try { $destinationParent = $this->tree->getNodeForPath($destinationDir); if (!($destinationParent instanceof Sabre_DAV_ICollection)) throw new Sabre_DAV_Exception_UnsupportedMediaType('The destination node is not a collection'); - } catch (Sabre_DAV_Exception_FileNotFound $e) { + } catch (Sabre_DAV_Exception_NotFound $e) { // If the destination parent node is not found, we throw a 409 throw new Sabre_DAV_Exception_Conflict('The destination node is not found'); @@ -1179,12 +1197,12 @@ class Sabre_DAV_Server { try { $destinationNode = $this->tree->getNodeForPath($destination); - + // If this succeeded, it means the destination already exists // we'll need to throw precondition failed in case overwrite is false if (!$overwrite) throw new Sabre_DAV_Exception_PreconditionFailed('The destination node already exists, and the overwrite header is set to false','Overwrite'); - } catch (Sabre_DAV_Exception_FileNotFound $e) { + } catch (Sabre_DAV_Exception_NotFound $e) { // Destination didn't exist, we're all good $destinationNode = false; @@ -1219,23 +1237,51 @@ class Sabre_DAV_Server { } + /** + * A kid-friendly way to fetch properties for a node's children. + * + * The returned array will be indexed by the path of the of child node. + * Only properties that are actually found will be returned. + * + * The parent node will not be returned. + * + * @param string $path + * @param array $propertyNames + * @return array + */ + public function getPropertiesForChildren($path, $propertyNames) { + + $result = array(); + foreach($this->getPropertiesForPath($path,$propertyNames,1) as $k=>$row) { + + // Skipping the parent path + if ($k === 0) continue; + + $result[$row['href']] = $row[200]; + + } + return $result; + + } + /** * Returns a list of HTTP headers for a particular resource * - * The generated http headers are based on properties provided by the + * The generated http headers are based on properties provided by the * resource. The method basically provides a simple mapping between * DAV property and HTTP header. * * The headers are intended to be used for HEAD and GET requests. - * + * * @param string $path + * @return array */ public function getHTTPHeaders($path) { $propertyMap = array( '{DAV:}getcontenttype' => 'Content-Type', '{DAV:}getcontentlength' => 'Content-Length', - '{DAV:}getlastmodified' => 'Last-Modified', + '{DAV:}getlastmodified' => 'Last-Modified', '{DAV:}getetag' => 'ETag', ); @@ -1245,40 +1291,40 @@ class Sabre_DAV_Server { foreach($propertyMap as $property=>$header) { if (!isset($properties[$property])) continue; - if (is_scalar($properties[$property])) { + if (is_scalar($properties[$property])) { $headers[$header] = $properties[$property]; - // GetLastModified gets special cased + // GetLastModified gets special cased } elseif ($properties[$property] instanceof Sabre_DAV_Property_GetLastModified) { - $headers[$header] = $properties[$property]->getTime()->format(DateTime::RFC1123); + $headers[$header] = Sabre_HTTP_Util::toHTTPDate($properties[$property]->getTime()); } } return $headers; - + } /** * Returns a list of properties for a given path - * + * * The path that should be supplied should have the baseUrl stripped out * The list of properties should be supplied in Clark notation. If the list is empty * 'allprops' is assumed. * * If a depth of 1 is requested child elements will also be returned. * - * @param string $path + * @param string $path * @param array $propertyNames - * @param int $depth + * @param int $depth * @return array */ - public function getPropertiesForPath($path,$propertyNames = array(),$depth = 0) { + public function getPropertiesForPath($path, $propertyNames = array(), $depth = 0) { if ($depth!=0) $depth = 1; $returnPropertyList = array(); - + $parentNode = $this->tree->getNodeForPath($path); $nodes = array( $path => $parentNode @@ -1286,11 +1332,11 @@ class Sabre_DAV_Server { if ($depth==1 && $parentNode instanceof Sabre_DAV_ICollection) { foreach($this->tree->getChildren($path) as $childNode) $nodes[$path . '/' . $childNode->getName()] = $childNode; - } - + } + // If the propertyNames array is empty, it means all properties are requested. // We shouldn't actually return everything we know though, and only return a - // sensible list. + // sensible list. $allProperties = count($propertyNames)==0; foreach($nodes as $myPath=>$node) { @@ -1315,8 +1361,8 @@ class Sabre_DAV_Server { ); } - // If the resourceType was not part of the list, we manually add it - // and mark it for removal. We need to know the resourcetype in order + // If the resourceType was not part of the list, we manually add it + // and mark it for removal. We need to know the resourcetype in order // to make certain decisions about the entry. // WebDAV dictates we should add a / and the end of href's for collections $removeRT = false; @@ -1326,32 +1372,39 @@ class Sabre_DAV_Server { } $result = $this->broadcastEvent('beforeGetProperties',array($myPath, $node, &$currentPropertyNames, &$newProperties)); - // If this method explicitly returned false, we must ignore this - // node as it is inacessible. + // If this method explicitly returned false, we must ignore this + // node as it is inaccessible. if ($result===false) continue; if (count($currentPropertyNames) > 0) { - if ($node instanceof Sabre_DAV_IProperties) + if ($node instanceof Sabre_DAV_IProperties) $newProperties['200'] = $newProperties[200] + $node->getProperties($currentPropertyNames); } foreach($currentPropertyNames as $prop) { - + if (isset($newProperties[200][$prop])) continue; switch($prop) { case '{DAV:}getlastmodified' : if ($node->getLastModified()) $newProperties[200][$prop] = new Sabre_DAV_Property_GetLastModified($node->getLastModified()); break; - case '{DAV:}getcontentlength' : if ($node instanceof Sabre_DAV_IFile) $newProperties[200][$prop] = (int)$node->getSize(); break; - case '{DAV:}quota-used-bytes' : + case '{DAV:}getcontentlength' : + if ($node instanceof Sabre_DAV_IFile) { + $size = $node->getSize(); + if (!is_null($size)) { + $newProperties[200][$prop] = (int)$node->getSize(); + } + } + break; + case '{DAV:}quota-used-bytes' : if ($node instanceof Sabre_DAV_IQuota) { $quotaInfo = $node->getQuotaInfo(); $newProperties[200][$prop] = $quotaInfo[0]; } break; - case '{DAV:}quota-available-bytes' : + case '{DAV:}quota-available-bytes' : if ($node instanceof Sabre_DAV_IQuota) { $quotaInfo = $node->getQuotaInfo(); $newProperties[200][$prop] = $quotaInfo[1]; @@ -1364,7 +1417,7 @@ class Sabre_DAV_Server { foreach($this->plugins as $plugin) { $reports = array_merge($reports, $plugin->getSupportedReportSet($myPath)); } - $newProperties[200][$prop] = new Sabre_DAV_Property_SupportedReportSet($reports); + $newProperties[200][$prop] = new Sabre_DAV_Property_SupportedReportSet($reports); break; case '{DAV:}resourcetype' : $newProperties[200]['{DAV:}resourcetype'] = new Sabre_DAV_Property_ResourceType(); @@ -1379,14 +1432,14 @@ class Sabre_DAV_Server { if (!$allProperties && !isset($newProperties[200][$prop])) $newProperties[404][$prop] = null; } - + $this->broadcastEvent('afterGetProperties',array(trim($myPath,'/'),&$newProperties)); - $newProperties['href'] = trim($myPath,'/'); + $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 + // 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'] .='/'; @@ -1397,7 +1450,7 @@ class Sabre_DAV_Server { $returnPropertyList[] = $newProperties; } - + return $returnPropertyList; } @@ -1406,27 +1459,31 @@ class Sabre_DAV_Server { * This method is invoked by sub-systems creating a new file. * * Currently this is done by HTTP PUT and HTTP LOCK (in the Locks_Plugin). - * It was important to get this done through a centralized function, + * It was important to get this done through a centralized function, * allowing plugins to intercept this using the beforeCreateFile event. * * This method will return true if the file was actually created - * - * @param string $uri - * @param resource $data - * @return bool + * + * @param string $uri + * @param resource $data + * @param string $etag + * @return bool */ - public function createFile($uri,$data) { + public function createFile($uri,$data, &$etag = null) { list($dir,$name) = Sabre_DAV_URLUtil::splitPath($uri); if (!$this->broadcastEvent('beforeBind',array($uri))) return false; - if (!$this->broadcastEvent('beforeCreateFile',array($uri,$data))) return false; $parent = $this->tree->getNodeForPath($dir); - $parent->createFile($name,$data); + + if (!$this->broadcastEvent('beforeCreateFile',array($uri, &$data, $parent))) return false; + + $etag = $parent->createFile($name,$data); $this->tree->markDirty($dir); $this->broadcastEvent('afterBind',array($uri)); + $this->broadcastEvent('afterCreateFile',array($uri, $parent)); return true; } @@ -1434,7 +1491,7 @@ class Sabre_DAV_Server { /** * This method is invoked by sub-systems creating a new directory. * - * @param string $uri + * @param string $uri * @return void */ public function createDirectory($uri) { @@ -1447,14 +1504,14 @@ class Sabre_DAV_Server { * Use this method to create a new collection * * The {DAV:}resourcetype is specified using the resourceType array. - * At the very least it must contain {DAV:}collection. + * At the very least it must contain {DAV:}collection. * * The properties array can contain a list of additional properties. - * - * @param string $uri The new uri - * @param array $resourceType The resourceType(s) + * + * @param string $uri The new uri + * @param array $resourceType The resourceType(s) * @param array $properties A list of properties - * @return void + * @return array|null */ public function createCollection($uri, array $resourceType, array $properties) { @@ -1471,7 +1528,7 @@ class Sabre_DAV_Server { $parent = $this->tree->getNodeForPath($parentUri); - } catch (Sabre_DAV_Exception_FileNotFound $e) { + } catch (Sabre_DAV_Exception_NotFound $e) { throw new Sabre_DAV_Exception_Conflict('Parent node does not exist'); @@ -1491,14 +1548,14 @@ class Sabre_DAV_Server { // If we got here.. it means there's already a node on that url, and we need to throw a 405 throw new Sabre_DAV_Exception_MethodNotAllowed('The resource you tried to create already exists'); - } catch (Sabre_DAV_Exception_FileNotFound $e) { + } catch (Sabre_DAV_Exception_NotFound $e) { // This is correct } - + if (!$this->broadcastEvent('beforeBind',array($uri))) return; - // There are 2 modes of operation. The standard collection + // There are 2 modes of operation. The standard collection // creates the directory, and then updates properties // the extended collection can create it directly. if ($parent instanceof Sabre_DAV_IExtendedCollection) { @@ -1513,7 +1570,7 @@ class Sabre_DAV_Server { } $parent->createDirectory($newName); - $rollBack = false; + $rollBack = false; $exception = null; $errorResult = null; @@ -1544,7 +1601,7 @@ class Sabre_DAV_Server { return $errorResult; } - + } $this->tree->markDirty($parentUri); $this->broadcastEvent('afterBind',array($uri)); @@ -1557,29 +1614,29 @@ class Sabre_DAV_Server { * The properties array must be a list of properties. Array-keys are * property names in clarknotation, array-values are it's values. * If a property must be deleted, the value should be null. - * - * Note that this request should either completely succeed, or + * + * Note that this request should either completely succeed, or * completely fail. * * The response is an array with statuscodes for keys, which in turn * contain arrays with propertynames. This response can be used * to generate a multistatus body. - * - * @param string $uri - * @param array $properties - * @return array + * + * @param string $uri + * @param array $properties + * @return array */ public function updateProperties($uri, array $properties) { // we'll start by grabbing the node, this will throw the appropriate - // exceptions if it doesn't. + // exceptions if it doesn't. $node = $this->tree->getNodeForPath($uri); - + $result = array( 200 => array(), 403 => array(), 424 => array(), - ); + ); $remainingProperties = $properties; $hasError = false; @@ -1684,14 +1741,15 @@ class Sabre_DAV_Server { * the appropriate HTTP response headers are already set. * * Normally this method will throw 412 Precondition Failed for failures - * related to If-None-Match, If-Match and If-Unmodified Since. It will + * related to If-None-Match, If-Match and If-Unmodified Since. It will * set the status to 304 Not Modified for If-Modified_since. * - * If the $handleAsGET argument is set to true, it will also return 304 + * If the $handleAsGET argument is set to true, it will also return 304 * Not Modified for failure of the If-None-Match precondition. This is the * desired behaviour for HTTP GET and HTTP HEAD requests. * - * @return bool + * @param bool $handleAsGET + * @return bool */ public function checkPreconditions($handleAsGET = false) { @@ -1708,7 +1766,7 @@ class Sabre_DAV_Server { // request succeed if a resource exists at that url. try { $node = $this->tree->getNodeForPath($uri); - } catch (Sabre_DAV_Exception_FileNotFound $e) { + } catch (Sabre_DAV_Exception_NotFound $e) { throw new Sabre_DAV_Exception_PreconditionFailed('An If-Match header was specified and the resource did not exist','If-Match'); } @@ -1722,7 +1780,7 @@ class Sabre_DAV_Server { // Stripping any extra spaces $ifMatchItem = trim($ifMatchItem,' '); - + $etag = $node->getETag(); if ($etag===$ifMatchItem) { $haveMatch = true; @@ -1744,7 +1802,7 @@ class Sabre_DAV_Server { if (!$node) { try { $node = $this->tree->getNodeForPath($uri); - } catch (Sabre_DAV_Exception_FileNotFound $e) { + } catch (Sabre_DAV_Exception_NotFound $e) { $nodeExists = false; } } @@ -1758,10 +1816,10 @@ class Sabre_DAV_Server { $etag = $node->getETag(); foreach($ifNoneMatch as $ifNoneMatchItem) { - + // Stripping any extra spaces $ifNoneMatchItem = trim($ifNoneMatchItem,' '); - + if ($etag===$ifNoneMatchItem) $haveMatch = true; } @@ -1781,7 +1839,7 @@ class Sabre_DAV_Server { } if (!$ifNoneMatch && ($ifModifiedSince = $this->httpRequest->getHeader('If-Modified-Since'))) { - + // The If-Modified-Since header contains a date. We // will only return the entity if it has been changed since // that date. If it hasn't been changed, we return a 304 @@ -1799,23 +1857,24 @@ class Sabre_DAV_Server { $lastMod = new DateTime('@' . $lastMod); if ($lastMod <= $date) { $this->httpResponse->sendStatus(304); + $this->httpResponse->setHeader('Last-Modified', Sabre_HTTP_Util::toHTTPDate($lastMod)); return false; - } + } } } } if ($ifUnmodifiedSince = $this->httpRequest->getHeader('If-Unmodified-Since')) { - + // The If-Unmodified-Since will allow allow the request if the // entity has not changed since the specified date. $date = Sabre_HTTP_Util::parseHTTPDate($ifUnmodifiedSince); - + // We must only check the date if it's valid if ($date) { if (is_null($node)) { $node = $this->tree->getNodeForPath($uri); - } + } $lastMod = $node->getLastModified(); if ($lastMod) { $lastMod = new DateTime('@' . $lastMod); @@ -1830,16 +1889,15 @@ class Sabre_DAV_Server { } - // }}} - // {{{ XML Readers & Writers - - + // }}} + // {{{ XML Readers & Writers + + /** - * Generates a WebDAV propfind response body based on a list of nodes - * + * Generates a WebDAV propfind response body based on a list of nodes + * * @param array $fileProperties The list with nodes - * @param array $requestedProperties The properties that should be returned - * @return string + * @return string */ public function generateMultiStatus(array $fileProperties) { @@ -1859,7 +1917,7 @@ class Sabre_DAV_Server { $href = $entry['href']; unset($entry['href']); - + $response = new Sabre_DAV_Property_Response($href,$entry); $response->serialize($this,$multiStatus); @@ -1878,7 +1936,7 @@ class Sabre_DAV_Server { * The keys in the returned array contain the property name (e.g.: {DAV:}displayname, * and the value contains the property value. If a property is to be removed the value * will be null. - * + * * @param string $body xml body * @return array list of properties in need of updating or deletion */ @@ -1886,17 +1944,17 @@ class Sabre_DAV_Server { //We'll need to change the DAV namespace declaration to something else in order to make it parsable $dom = Sabre_DAV_XMLUtil::loadDOMDocument($body); - + $newProperties = array(); foreach($dom->firstChild->childNodes as $child) { - if ($child->nodeType !== XML_ELEMENT_NODE) continue; + if ($child->nodeType !== XML_ELEMENT_NODE) continue; $operation = Sabre_DAV_XMLUtil::toClarkNotation($child); if ($operation!=='{DAV:}set' && $operation!=='{DAV:}remove') continue; - + $innerProperties = Sabre_DAV_XMLUtil::parseProperties($child, $this->propertyMap); foreach($innerProperties as $propertyName=>$propertyValue) { @@ -1920,9 +1978,9 @@ class Sabre_DAV_Server { * * This will either be a list of properties, or an empty array; in which case * an {DAV:}allprop was requested. - * - * @param string $body - * @return array + * + * @param string $body + * @return array */ public function parsePropFindRequest($body) { @@ -1931,7 +1989,7 @@ class Sabre_DAV_Server { $dom = Sabre_DAV_XMLUtil::loadDOMDocument($body); $elem = $dom->getElementsByTagNameNS('urn:DAV','propfind')->item(0); - return array_keys(Sabre_DAV_XMLUtil::parseProperties($elem)); + return array_keys(Sabre_DAV_XMLUtil::parseProperties($elem)); } diff --git a/3rdparty/Sabre/DAV/ServerPlugin.php b/3rdparty/Sabre/DAV/ServerPlugin.php index 6909f600c2..131863d13f 100644 --- a/3rdparty/Sabre/DAV/ServerPlugin.php +++ b/3rdparty/Sabre/DAV/ServerPlugin.php @@ -3,12 +3,12 @@ /** * The baseclass for all server plugins. * - * Plugins can modify or extend the servers behaviour. - * + * Plugins can modify or extend the servers behaviour. + * * @package Sabre * @subpackage DAV - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. - * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. + * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ abstract class Sabre_DAV_ServerPlugin { @@ -20,18 +20,18 @@ abstract class Sabre_DAV_ServerPlugin { * addPlugin is called. * * This method should set up the requires event subscriptions. - * - * @param Sabre_DAV_Server $server + * + * @param Sabre_DAV_Server $server * @return void */ abstract public function initialize(Sabre_DAV_Server $server); - + /** - * This method should return a list of server-features. + * This method should return a list of server-features. * * This is for example 'versioning' and is added to the DAV: header * in an OPTIONS response. - * + * * @return array */ public function getFeatures() { @@ -44,11 +44,11 @@ abstract class Sabre_DAV_ServerPlugin { * 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 + * This method is passed a uri. It should only return HTTP methods that are * available for the specified uri. * * @param string $uri - * @return array + * @return array */ public function getHTTPMethods($uri) { @@ -58,11 +58,11 @@ abstract class Sabre_DAV_ServerPlugin { /** * Returns a plugin name. - * + * * Using this name other plugins will be able to access other plugins - * using Sabre_DAV_Server::getPlugin - * - * @return string + * using Sabre_DAV_Server::getPlugin + * + * @return string */ public function getPluginName() { @@ -74,11 +74,11 @@ abstract class Sabre_DAV_ServerPlugin { * Returns a list of reports this plugin supports. * * This will be used in the {DAV:}supported-report-set property. - * Note that you still need to subscribe to the 'report' event to actually - * implement them - * + * Note that you still need to subscribe to the 'report' event to actually + * implement them + * * @param string $uri - * @return array + * @return array */ public function getSupportedReportSet($uri) { diff --git a/3rdparty/Sabre/DAV/SimpleCollection.php b/3rdparty/Sabre/DAV/SimpleCollection.php index 223d05fed5..4acf971caa 100644 --- a/3rdparty/Sabre/DAV/SimpleCollection.php +++ b/3rdparty/Sabre/DAV/SimpleCollection.php @@ -8,23 +8,23 @@ * * @package Sabre * @subpackage DAV - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. - * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. + * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ class Sabre_DAV_SimpleCollection extends Sabre_DAV_Collection { /** - * List of childnodes + * List of childnodes * * @var array */ protected $children = array(); /** - * Name of this resource - * - * @var string + * Name of this resource + * + * @var string */ protected $name; @@ -33,10 +33,9 @@ class Sabre_DAV_SimpleCollection extends Sabre_DAV_Collection { * * The name of the node must be passed, child nodes can also be bassed. * This nodes must be instances of Sabre_DAV_INode - * - * @param string $name - * @param array $children - * @return void + * + * @param string $name + * @param array $children */ public function __construct($name,array $children = array()) { @@ -51,9 +50,9 @@ class Sabre_DAV_SimpleCollection extends Sabre_DAV_Collection { } /** - * Adds a new childnode to this collection - * - * @param Sabre_DAV_INode $child + * Adds a new childnode to this collection + * + * @param Sabre_DAV_INode $child * @return void */ public function addChild(Sabre_DAV_INode $child) { @@ -63,9 +62,9 @@ class Sabre_DAV_SimpleCollection extends Sabre_DAV_Collection { } /** - * Returns the name of the collection - * - * @return string + * Returns the name of the collection + * + * @return string */ public function getName() { @@ -76,24 +75,24 @@ class Sabre_DAV_SimpleCollection extends Sabre_DAV_Collection { /** * 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 - * + * * @param string $name - * @throws Sabre_DAV_Exception_FileNotFound - * @return Sabre_DAV_INode + * @throws Sabre_DAV_Exception_NotFound + * @return Sabre_DAV_INode */ public function getChild($name) { if (isset($this->children[$name])) return $this->children[$name]; - throw new Sabre_DAV_Exception_FileNotFound('File not found: ' . $name . ' in \'' . $this->getName() . '\''); + throw new Sabre_DAV_Exception_NotFound('File not found: ' . $name . ' in \'' . $this->getName() . '\''); } /** - * Returns a list of children for this collection - * - * @return array + * Returns a list of children for this collection + * + * @return array */ public function getChildren() { diff --git a/3rdparty/Sabre/DAV/SimpleDirectory.php b/3rdparty/Sabre/DAV/SimpleDirectory.php index 516a3aa907..621222ebc5 100644 --- a/3rdparty/Sabre/DAV/SimpleDirectory.php +++ b/3rdparty/Sabre/DAV/SimpleDirectory.php @@ -11,8 +11,8 @@ * @package Sabre * @subpackage DAV * @deprecated Use Sabre_DAV_SimpleCollection instead. - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. - * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. + * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ class Sabre_DAV_SimpleDirectory extends Sabre_DAV_SimpleCollection { diff --git a/3rdparty/Sabre/DAV/SimpleFile.php b/3rdparty/Sabre/DAV/SimpleFile.php index 304dff1c5e..58330d6861 100644 --- a/3rdparty/Sabre/DAV/SimpleFile.php +++ b/3rdparty/Sabre/DAV/SimpleFile.php @@ -3,47 +3,48 @@ /** * SimpleFile * - * The 'SimpleFile' class is used to easily add read-only immutable files to - * the directory structure. One usecase would be to add a 'readme.txt' to a + * The 'SimpleFile' class is used to easily add read-only immutable files to + * the directory structure. One usecase would be to add a 'readme.txt' to a * root of a webserver with some standard content. * * @package Sabre * @subpackage DAV - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. - * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. + * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ class Sabre_DAV_SimpleFile extends Sabre_DAV_File { /** - * File contents + * File contents * - * @var string + * @var string */ protected $contents = array(); /** - * Name of this resource - * - * @var string + * Name of this resource + * + * @var string */ protected $name; /** * A mimetype, such as 'text/plain' or 'text/html' - * - * @var string + * + * @var string */ protected $mimeType; /** * Creates this node * - * The name of the node must be passed, as well as the contents of the + * The name of the node must be passed, as well as the contents of the * file. - * - * @param string $name - * @param string $contents + * + * @param string $name + * @param string $contents + * @param string|null $mimeType */ public function __construct($name, $contents, $mimeType = null) { @@ -57,8 +58,8 @@ class Sabre_DAV_SimpleFile extends Sabre_DAV_File { * Returns the node name for this file. * * This name is used to construct the url. - * - * @return string + * + * @return string */ public function getName() { @@ -67,7 +68,7 @@ class Sabre_DAV_SimpleFile extends Sabre_DAV_File { } /** - * Returns the data + * Returns the data * * This method may either return a string or a readable stream resource * @@ -75,14 +76,14 @@ class Sabre_DAV_SimpleFile extends Sabre_DAV_File { */ public function get() { - return $this->contents; + return $this->contents; } /** - * Returns the size of the file, in bytes. - * - * @return int + * Returns the size of the file, in bytes. + * + * @return int */ public function getSize() { @@ -94,13 +95,14 @@ class Sabre_DAV_SimpleFile extends Sabre_DAV_File { * Returns the ETag for a file * * An ETag is a unique identifier representing the current version of the file. If the file changes, the ETag MUST change. - * The ETag is an arbritrary string, but MUST be surrounded by double-quotes. + * The ETag is an arbitrary string, but MUST be surrounded by double-quotes. * * Return null if the ETag can not effectively be determined + * @return string */ public function getETag() { - return '"' . md5($this->contents) . '"'; + return '"' . md5($this->contents) . '"'; } @@ -108,13 +110,12 @@ class Sabre_DAV_SimpleFile extends Sabre_DAV_File { * Returns the mime-type for a file * * If null is returned, we'll assume application/octet-stream - */ + * @return string + */ public function getContentType() { - return $this->mimeType; + return $this->mimeType; } } - -?> diff --git a/3rdparty/Sabre/DAV/StringUtil.php b/3rdparty/Sabre/DAV/StringUtil.php index 440cf6866c..b126a94c82 100644 --- a/3rdparty/Sabre/DAV/StringUtil.php +++ b/3rdparty/Sabre/DAV/StringUtil.php @@ -3,14 +3,14 @@ /** * String utility * - * This class is mainly used to implement the 'text-match' filter, used by both - * the CalDAV calendar-query REPORT, and CardDAV addressbook-query REPORT. + * This class is mainly used to implement the 'text-match' filter, used by both + * the CalDAV calendar-query REPORT, and CardDAV addressbook-query REPORT. * Because they both need it, it was decided to put it in Sabre_DAV instead. - * + * * @package Sabre * @subpackage DAV - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. - * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. + * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ class Sabre_DAV_StringUtil { @@ -18,11 +18,11 @@ class Sabre_DAV_StringUtil { /** * Checks if a needle occurs in a haystack ;) * - * @param string $haystack - * @param string $needle - * @param string $collation - * @param string $matchType - * @return bool + * @param string $haystack + * @param string $needle + * @param string $collation + * @param string $matchType + * @return bool */ static public function textMatch($haystack, $needle, $collation, $matchType = 'contains') { @@ -37,7 +37,7 @@ class Sabre_DAV_StringUtil { case 'i;octet' : // Do nothing - break; + break; case 'i;unicode-casemap' : $haystack = mb_strtoupper($haystack, 'UTF-8'); @@ -63,18 +63,18 @@ class Sabre_DAV_StringUtil { throw new Sabre_DAV_Exception_BadRequest('Match-type: ' . $matchType . ' is not supported'); } - + } /** - * This method takes an input string, checks if it's not valid UTF-8 and + * This method takes an input string, checks if it's not valid UTF-8 and * attempts to convert it to UTF-8 if it's not. * - * Note that currently this can only convert ISO-8559-1 to UTF-8 (latin-1), + * Note that currently this can only convert ISO-8559-1 to UTF-8 (latin-1), * anything else will likely fail. * - * @param string $input - * @return string + * @param string $input + * @return string */ static public function ensureUTF8($input) { @@ -84,7 +84,7 @@ class Sabre_DAV_StringUtil { return utf8_encode($input); } else { return $input; - } + } } diff --git a/3rdparty/Sabre/DAV/TemporaryFileFilterPlugin.php b/3rdparty/Sabre/DAV/TemporaryFileFilterPlugin.php index e8276af561..36096e6777 100644 --- a/3rdparty/Sabre/DAV/TemporaryFileFilterPlugin.php +++ b/3rdparty/Sabre/DAV/TemporaryFileFilterPlugin.php @@ -22,8 +22,8 @@ * * @package Sabre * @subpackage DAV - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. - * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. + * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ class Sabre_DAV_TemporaryFileFilterPlugin extends Sabre_DAV_ServerPlugin { @@ -31,7 +31,7 @@ class Sabre_DAV_TemporaryFileFilterPlugin extends Sabre_DAV_ServerPlugin { /** * This is the list of patterns we intercept. * If new patterns are added, they must be valid patterns for preg_match. - * + * * @var array */ public $temporaryFilePatterns = array( @@ -43,19 +43,19 @@ class Sabre_DAV_TemporaryFileFilterPlugin extends Sabre_DAV_ServerPlugin { '/^\.dat(.*)$/', // Smultron seems to create these '/^~lock.(.*)#$/', // Windows 7 lockfiles ); - + /** * This is the directory where this plugin * will store it's files. - * - * @var string + * + * @var string */ private $dataDir; /** * A reference to the main Server class - * - * @var Sabre_DAV_Server + * + * @var Sabre_DAV_Server */ private $server; @@ -65,9 +65,8 @@ class Sabre_DAV_TemporaryFileFilterPlugin extends Sabre_DAV_ServerPlugin { * Make sure you specify a directory for your files. If you don't, we * will use PHP's directory for session-storage instead, and you might * not want that. - * - * @param string $dataDir - * @return void + * + * @param string|null $dataDir */ public function __construct($dataDir = null) { @@ -75,15 +74,15 @@ class Sabre_DAV_TemporaryFileFilterPlugin extends Sabre_DAV_ServerPlugin { if (!is_dir($dataDir)) mkdir($dataDir); $this->dataDir = $dataDir; - } + } /** * Initialize the plugin * * This is called automatically be the Server class after this plugin is * added with Sabre_DAV_Server::addPlugin() - * - * @param Sabre_DAV_Server $server + * + * @param Sabre_DAV_Server $server * @return void */ public function initialize(Sabre_DAV_Server $server) { @@ -97,11 +96,12 @@ class Sabre_DAV_TemporaryFileFilterPlugin extends Sabre_DAV_ServerPlugin { /** * This method is called before any HTTP method handler * - * This method intercepts any GET, DELETE, PUT and PROPFIND calls to + * This method intercepts any GET, DELETE, PUT and PROPFIND calls to * filenames that are known to match the 'temporary file' regex. - * - * @param string $method - * @return bool + * + * @param string $method + * @param string $uri + * @return bool */ public function beforeMethod($method, $uri) { @@ -125,33 +125,33 @@ class Sabre_DAV_TemporaryFileFilterPlugin extends Sabre_DAV_ServerPlugin { /** * This method is invoked if some subsystem creates a new file. * - * This is used to deal with HTTP LOCK requests which create a new + * This is used to deal with HTTP LOCK requests which create a new * file. - * - * @param string $uri - * @param resource $data - * @return bool + * + * @param string $uri + * @param resource $data + * @return bool */ public function beforeCreateFile($uri,$data) { if ($tempPath = $this->isTempFile($uri)) { - + $hR = $this->server->httpResponse; $hR->setHeader('X-Sabre-Temp','true'); file_put_contents($tempPath,$data); return false; } - return true; + return true; } /** * This method will check if the url matches the temporary file pattern - * if it does, it will return an path based on $this->dataDir for the + * if it does, it will return an path based on $this->dataDir for the * temporary file storage. - * - * @param string $path - * @return boolean|string + * + * @param string $path + * @return boolean|string */ protected function isTempFile($path) { @@ -161,7 +161,7 @@ class Sabre_DAV_TemporaryFileFilterPlugin extends Sabre_DAV_ServerPlugin { foreach($this->temporaryFilePatterns as $tempFile) { if (preg_match($tempFile,$tempPath)) { - return $this->dataDir . '/sabredav_' . md5($path) . '.tempfile'; + return $this->getDataDir() . '/sabredav_' . md5($path) . '.tempfile'; } } @@ -175,9 +175,9 @@ class Sabre_DAV_TemporaryFileFilterPlugin extends Sabre_DAV_ServerPlugin { * This method handles the GET method for temporary files. * If the file doesn't exist, it will return false which will kick in * the regular system for the GET method. - * - * @param string $tempLocation - * @return bool + * + * @param string $tempLocation + * @return bool */ public function httpGet($tempLocation) { @@ -195,9 +195,9 @@ class Sabre_DAV_TemporaryFileFilterPlugin extends Sabre_DAV_ServerPlugin { /** * This method handles the PUT method. - * - * @param string $tempLocation - * @return bool + * + * @param string $tempLocation + * @return bool */ public function httpPut($tempLocation) { @@ -205,7 +205,7 @@ class Sabre_DAV_TemporaryFileFilterPlugin extends Sabre_DAV_ServerPlugin { $hR->setHeader('X-Sabre-Temp','true'); $newFile = !file_exists($tempLocation); - + if (!$newFile && ($this->server->httpRequest->getHeader('If-None-Match'))) { throw new Sabre_DAV_Exception_PreconditionFailed('The resource already exists, and an If-None-Match header was supplied'); } @@ -219,11 +219,11 @@ class Sabre_DAV_TemporaryFileFilterPlugin extends Sabre_DAV_ServerPlugin { /** * This method handles the DELETE method. * - * If the file didn't exist, it will return false, which will make the + * If the file didn't exist, it will return false, which will make the * standard HTTP DELETE handler kick in. - * - * @param string $tempLocation - * @return bool + * + * @param string $tempLocation + * @return bool */ public function httpDelete($tempLocation) { @@ -238,25 +238,26 @@ class Sabre_DAV_TemporaryFileFilterPlugin extends Sabre_DAV_ServerPlugin { } /** - * This method handles the PROPFIND method. + * This method handles the PROPFIND method. * * It's a very lazy method, it won't bother checking the request body * for which properties were requested, and just sends back a default * set of properties. * - * @param string $tempLocation - * @return void + * @param string $tempLocation + * @param string $uri + * @return bool */ public function httpPropfind($tempLocation, $uri) { if (!file_exists($tempLocation)) return true; - + $hR = $this->server->httpResponse; $hR->setHeader('X-Sabre-Temp','true'); $hR->sendStatus(207); $hR->setHeader('Content-Type','application/xml; charset=utf-8'); - $requestedProps = $this->server->parsePropFindRequest($this->server->httpRequest->getBody(true)); + $this->server->parsePropFindRequest($this->server->httpRequest->getBody(true)); $properties = array( 'href' => $uri, @@ -264,7 +265,7 @@ class Sabre_DAV_TemporaryFileFilterPlugin extends Sabre_DAV_ServerPlugin { '{DAV:}getlastmodified' => new Sabre_DAV_Property_GetLastModified(filemtime($tempLocation)), '{DAV:}getcontentlength' => filesize($tempLocation), '{DAV:}resourcetype' => new Sabre_DAV_Property_ResourceType(null), - '{'.Sabre_DAV_Server::NS_SABREDAV.'}tempFile' => true, + '{'.Sabre_DAV_Server::NS_SABREDAV.'}tempFile' => true, ), ); @@ -276,4 +277,13 @@ class Sabre_DAV_TemporaryFileFilterPlugin extends Sabre_DAV_ServerPlugin { } + /** + * This method returns the directory where the temporary files should be stored. + * + * @return string + */ + protected function getDataDir() + { + return $this->dataDir; + } } diff --git a/3rdparty/Sabre/DAV/Tree.php b/3rdparty/Sabre/DAV/Tree.php index 98e6f62c9e..5021639415 100644 --- a/3rdparty/Sabre/DAV/Tree.php +++ b/3rdparty/Sabre/DAV/Tree.php @@ -1,34 +1,34 @@ getNodeForPath($path); return true; - } catch (Sabre_DAV_Exception_FileNotFound $e) { + } catch (Sabre_DAV_Exception_NotFound $e) { return false; @@ -50,12 +50,12 @@ abstract class Sabre_DAV_Tree { * * @param string $sourcePath The source location * @param string $destinationPath The full destination path - * @return void + * @return void */ public function copy($sourcePath, $destinationPath) { $sourceNode = $this->getNodeForPath($sourcePath); - + // grab the dirname and basename components list($destinationDir, $destinationName) = Sabre_DAV_URLUtil::splitPath($destinationPath); @@ -67,9 +67,9 @@ abstract class Sabre_DAV_Tree { } /** - * Moves a file from one location to another - * - * @param string $sourcePath The path to the file which should be moved + * Moves a file from one location to another + * + * @param string $sourcePath The path to the file which should be moved * @param string $destinationPath The full destination path, so not just the destination parent node * @return int */ @@ -91,26 +91,26 @@ abstract class Sabre_DAV_Tree { } /** - * Deletes a node from the tree - * - * @param string $path + * Deletes a node from the tree + * + * @param string $path * @return void */ public function delete($path) { $node = $this->getNodeForPath($path); $node->delete(); - + list($parent) = Sabre_DAV_URLUtil::splitPath($path); $this->markDirty($parent); } /** - * Returns a list of childnodes for a given path. - * - * @param string $path - * @return array + * Returns a list of childnodes for a given path. + * + * @param string $path + * @return array */ public function getChildren($path) { @@ -127,14 +127,14 @@ abstract class Sabre_DAV_Tree { * * node creations * * copy * * move - * * renaming nodes - * + * * renaming nodes + * * If Tree classes implement a form of caching, this will allow * them to make sure caches will be expired. - * + * * If a path is passed, it is assumed that the entire subtree is dirty * - * @param string $path + * @param string $path * @return void */ public function markDirty($path) { @@ -143,10 +143,11 @@ abstract class Sabre_DAV_Tree { } /** - * copyNode - * - * @param Sabre_DAV_INode $source - * @param Sabre_DAV_ICollection $destination + * copyNode + * + * @param Sabre_DAV_INode $source + * @param Sabre_DAV_ICollection $destinationParent + * @param string $destinationName * @return void */ protected function copyNode(Sabre_DAV_INode $source,Sabre_DAV_ICollection $destinationParent,$destinationName = null) { @@ -163,14 +164,14 @@ abstract class Sabre_DAV_Tree { fwrite($stream,$data); rewind($stream); $data = $stream; - } + } $destinationParent->createFile($destinationName,$data); $destination = $destinationParent->getChild($destinationName); } elseif ($source instanceof Sabre_DAV_ICollection) { $destinationParent->createDirectory($destinationName); - + $destination = $destinationParent->getChild($destinationName); foreach($source->getChildren() as $child) { diff --git a/3rdparty/Sabre/DAV/Tree/Filesystem.php b/3rdparty/Sabre/DAV/Tree/Filesystem.php index 5c611047e0..85a9ee317b 100644 --- a/3rdparty/Sabre/DAV/Tree/Filesystem.php +++ b/3rdparty/Sabre/DAV/Tree/Filesystem.php @@ -1,12 +1,12 @@ getRealPath($path); - if (!file_exists($realPath)) throw new Sabre_DAV_Exception_FileNotFound('File at location ' . $realPath . ' not found'); - if (is_dir($realPath)) { + if (!file_exists($realPath)) throw new Sabre_DAV_Exception_NotFound('File at location ' . $realPath . ' not found'); + if (is_dir($realPath)) { return new Sabre_DAV_FS_Directory($path); } else { return new Sabre_DAV_FS_File($path); @@ -51,10 +50,10 @@ class Sabre_DAV_Tree_Filesystem extends Sabre_DAV_Tree { } /** - * Returns the real filesystem path for a webdav url. - * - * @param string $publicPath - * @return string + * Returns the real filesystem path for a webdav url. + * + * @param string $publicPath + * @return string */ protected function getRealPath($publicPath) { @@ -67,24 +66,24 @@ class Sabre_DAV_Tree_Filesystem extends Sabre_DAV_Tree { * * This method must work recursively and delete the destination * if it exists - * - * @param string $source - * @param string $destination + * + * @param string $source + * @param string $destination * @return void */ public function copy($source,$destination) { $source = $this->getRealPath($source); $destination = $this->getRealPath($destination); - $this->realCopy($source,$destination); + $this->realCopy($source,$destination); } /** - * Used by self::copy - * - * @param string $source - * @param string $destination + * Used by self::copy + * + * @param string $source + * @param string $destination * @return void */ protected function realCopy($source,$destination) { @@ -107,9 +106,9 @@ class Sabre_DAV_Tree_Filesystem extends Sabre_DAV_Tree { * Moves a file or directory recursively. * * If the destination exists, delete it first. - * - * @param string $source - * @param string $destination + * + * @param string $source + * @param string $destination * @return void */ public function move($source,$destination) { diff --git a/3rdparty/Sabre/DAV/URLUtil.php b/3rdparty/Sabre/DAV/URLUtil.php index 8f38749264..794665a44f 100644 --- a/3rdparty/Sabre/DAV/URLUtil.php +++ b/3rdparty/Sabre/DAV/URLUtil.php @@ -11,11 +11,11 @@ * Specifically, it was found that GVFS (gnome's webdav client) does not like encoding of ( and * ). Since these are reserved, but don't have a reserved meaning in url, these characters are * kept as-is. - * + * * @package Sabre * @subpackage DAV - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. - * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. + * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ class Sabre_DAV_URLUtil { @@ -24,46 +24,42 @@ class Sabre_DAV_URLUtil { * Encodes the path of a url. * * slashes (/) are treated as path-separators. - * - * @param string $path - * @return string + * + * @param string $path + * @return string */ static function encodePath($path) { - $valid_chars = '/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-.~()'; - $newStr = ''; - for( $i=0; isset($path[$i]); ++$i ) { - if( strpos($valid_chars,($c=$path[$i]))===false ) $newStr .= '%'.sprintf('%02x',ord($c)); - else $newStr .= $c; - } - return $newStr; - + return preg_replace_callback('/([^A-Za-z0-9_\-\.~\(\)\/])/',function($match) { + + return '%'.sprintf('%02x',ord($match[0])); + + }, $path); + } /** * Encodes a 1 segment of a path * * Slashes are considered part of the name, and are encoded as %2f - * - * @param string $pathSegment - * @return string + * + * @param string $pathSegment + * @return string */ static function encodePathSegment($pathSegment) { - $valid_chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-.~()'; - $newStr = ''; - for( $i=0; isset($pathSegment[$i]); ++$i ) { - if( strpos($valid_chars,($c=$pathSegment[$i]))===false ) $newStr .= '%'.sprintf('%02x',ord($c)); - else $newStr .= $c; - } - return $newStr; + return preg_replace_callback('/([^A-Za-z0-9_\-\.~\(\)])/',function($match) { + + return '%'.sprintf('%02x',ord($match[0])); + + }, $pathSegment); } /** * Decodes a url-encoded path * - * @param string $path - * @return string + * @param string $path + * @return string */ static function decodePath($path) { @@ -74,17 +70,17 @@ class Sabre_DAV_URLUtil { /** * Decodes a url-encoded path segment * - * @param string $path - * @return string + * @param string $path + * @return string */ static function decodePathSegment($path) { - $path = urldecode($path); + $path = rawurldecode($path); $encoding = mb_detect_encoding($path, array('UTF-8','ISO-8859-1')); switch($encoding) { - case 'ISO-8859-1' : + case 'ISO-8859-1' : $path = utf8_encode($path); } @@ -94,7 +90,7 @@ class Sabre_DAV_URLUtil { } /** - * Returns the 'dirname' and 'basename' for a path. + * Returns the 'dirname' and 'basename' for a path. * * The reason there is a custom function for this purpose, is because * basename() is locale aware (behaviour changes if C locale or a UTF-8 locale is used) @@ -108,8 +104,8 @@ class Sabre_DAV_URLUtil { * If there is no dirname, it will return an empty string. Any / appearing at the end of the * string is stripped off. * - * @param string $path - * @return array + * @param string $path + * @return array */ static function splitPath($path) { diff --git a/3rdparty/Sabre/DAV/UUIDUtil.php b/3rdparty/Sabre/DAV/UUIDUtil.php index e42a536ad8..f0eebe598e 100644 --- a/3rdparty/Sabre/DAV/UUIDUtil.php +++ b/3rdparty/Sabre/DAV/UUIDUtil.php @@ -4,13 +4,13 @@ * UUID Utility * * This class has static methods to generate and validate UUID's. - * UUIDs are used a decent amount within various *DAV standards, so it made + * UUIDs are used a decent amount within various *DAV standards, so it made * sense to include it. - * + * * @package Sabre * @subpackage DAV - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. - * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. + * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ class Sabre_DAV_UUIDUtil { @@ -18,10 +18,10 @@ class Sabre_DAV_UUIDUtil { /** * Returns a pseudo-random v4 UUID * - * This function is based on a comment by Andrew Moore on php.net - * + * This function is based on a comment by Andrew Moore on php.net + * * @see http://www.php.net/manual/en/function.uniqid.php#94959 - * @return void + * @return string */ static function getUUID() { @@ -48,9 +48,9 @@ class Sabre_DAV_UUIDUtil { /** * Checks if a string is a valid UUID. - * - * @param string $uuid - * @return bool + * + * @param string $uuid + * @return bool */ static function validateUUID($uuid) { diff --git a/3rdparty/Sabre/DAV/Version.php b/3rdparty/Sabre/DAV/Version.php index 6bece1985e..5e5d15e403 100644 --- a/3rdparty/Sabre/DAV/Version.php +++ b/3rdparty/Sabre/DAV/Version.php @@ -2,10 +2,10 @@ /** * This class contains the SabreDAV version constants. - * + * * @package Sabre * @subpackage DAV - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. + * @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 */ @@ -14,7 +14,7 @@ class Sabre_DAV_Version { /** * Full version number */ - const VERSION = '1.5.4'; + const VERSION = '1.6.2'; /** * Stability : alpha, beta, stable diff --git a/3rdparty/Sabre/DAV/XMLUtil.php b/3rdparty/Sabre/DAV/XMLUtil.php index bd05be4b22..60eff3b159 100644 --- a/3rdparty/Sabre/DAV/XMLUtil.php +++ b/3rdparty/Sabre/DAV/XMLUtil.php @@ -2,32 +2,32 @@ /** * XML utilities for WebDAV - * + * * @package Sabre * @subpackage DAV - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. - * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. + * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ class Sabre_DAV_XMLUtil { /** * Returns the 'clark notation' for an element. - * + * * For example, and element encoded as: * * will be returned as: * {http://www.example.org}myelem * * This format is used throughout the SabreDAV sourcecode. - * Elements encoded with the urn:DAV namespace will + * Elements encoded with the urn:DAV namespace will * be returned as if they were in the DAV: namespace. This is to avoid * compatibility problems. * * This function will return null if a nodetype other than an Element is passed. * - * @param DOMElement $dom - * @return string + * @param DOMNode $dom + * @return string */ static function toClarkNotation(DOMNode $dom) { @@ -35,21 +35,21 @@ class Sabre_DAV_XMLUtil { // Mapping back to the real namespace, in case it was dav if ($dom->namespaceURI=='urn:DAV') $ns = 'DAV:'; else $ns = $dom->namespaceURI; - + // Mapping to clark notation return '{' . $ns . '}' . $dom->localName; } /** - * Parses a clark-notation string, and returns the namespace and element + * Parses a clark-notation string, and returns the namespace and element * name components. * * If the string was invalid, it will throw an InvalidArgumentException. - * + * * @param string $str - * @throws InvalidArgumentException - * @return array + * @throws InvalidArgumentException + * @return array */ static function parseClarkNotation($str) { @@ -70,6 +70,9 @@ class Sabre_DAV_XMLUtil { * * 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) { @@ -83,17 +86,17 @@ class Sabre_DAV_XMLUtil { * 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, and it converts the DAV: namespace to urn:DAV. + * * @param string $xml - * @throws Sabre_DAV_Exception_BadRequest - * @return DOMDocument + * @throws Sabre_DAV_Exception_BadRequest + * @return DOMDocument */ static function loadDOMDocument($xml) { if (empty($xml)) throw new Sabre_DAV_Exception_BadRequest('Empty XML document sent'); - + // The BitKinex client sends xml documents as UTF-16. PHP 5.3.1 (and presumably lower) // does not support this, so we must intercept this and convert to UTF-8. if (substr($xml,0,12) === "\x3c\x00\x3f\x00\x78\x00\x6d\x00\x6c\x00\x20\x00") { @@ -111,7 +114,7 @@ class Sabre_DAV_XMLUtil { // Retaining old error setting $oldErrorSetting = libxml_use_internal_errors(true); - // Clearing any previous errors + // Clearing any previous errors libxml_clear_errors(); $dom = new DOMDocument(); @@ -135,7 +138,7 @@ class Sabre_DAV_XMLUtil { /** * Parses all WebDAV properties out of a DOM Element * - * Generally WebDAV properties are encloded in {DAV:}prop elements. This + * Generally WebDAV properties are enclosed in {DAV:}prop elements. This * method helps by going through all these and pulling out the actual * propertynames, making them array keys and making the property values, * well.. the array values. @@ -145,7 +148,7 @@ class Sabre_DAV_XMLUtil { * * Complex values are supported through the propertyMap argument. The * propertyMap should have the clark-notation properties as it's keys, and - * classnames as values. + * classnames as values. * * When any of these properties are found, the unserialize() method will be * (statically) called. The result of this method is used as the value. @@ -168,7 +171,7 @@ class Sabre_DAV_XMLUtil { $propertyName = Sabre_DAV_XMLUtil::toClarkNotation($propNodeData); if (isset($propertyMap[$propertyName])) { - $propList[$propertyName] = call_user_func(array($propertyMap[$propertyName],'unserialize'),$propNodeData); + $propList[$propertyName] = call_user_func(array($propertyMap[$propertyName],'unserialize'),$propNodeData); } else { $propList[$propertyName] = $propNodeData->textContent; } diff --git a/3rdparty/Sabre/DAV/includes.php b/3rdparty/Sabre/DAV/includes.php new file mode 100644 index 0000000000..6a4890677e --- /dev/null +++ b/3rdparty/Sabre/DAV/includes.php @@ -0,0 +1,97 @@ +principalPrefix); - return $name; + return $name; } /** - * Return the list of users - * - * @return void + * Return the list of users + * + * @return array */ public function getChildren() { @@ -99,23 +98,57 @@ abstract class Sabre_DAVACL_AbstractPrincipalCollection extends Sabre_DAV_Collec } - return $children; + return $children; } /** * Returns a child object, by its name. - * + * * @param string $name - * @throws Sabre_DAV_Exception_FileNotFound + * @throws Sabre_DAV_Exception_NotFound * @return Sabre_DAV_IPrincipal */ public function getChild($name) { $principalInfo = $this->principalBackend->getPrincipalByPath($this->principalPrefix . '/' . $name); - if (!$principalInfo) throw new Sabre_DAV_Exception_FileNotFound('Principal with name ' . $name . ' not found'); + if (!$principalInfo) throw new Sabre_DAV_Exception_NotFound('Principal with name ' . $name . ' not found'); return $this->getChildForPrincipal($principalInfo); } + /** + * This method is used to search for principals matching a set of + * properties. + * + * This search is specifically used by RFC3744's principal-property-search + * REPORT. You should at least allow searching on + * http://sabredav.org/ns}email-address. + * + * The actual search should be a unicode-non-case-sensitive search. The + * keys in searchProperties are the WebDAV property names, while the values + * are the property values to search on. + * + * If multiple properties are being searched on, the search should be + * AND'ed. + * + * This method should simply return a list of 'child names', which may be + * used to call $this->getChild in the future. + * + * @param array $searchProperties + * @return array + */ + public function searchPrincipals(array $searchProperties) { + + $result = $this->principalBackend->searchPrincipals($this->principalPrefix, $searchProperties); + $r = array(); + + foreach($result as $row) { + list(, $r[]) = Sabre_DAV_URLUtil::splitPath($row); + } + + return $r; + + } + } diff --git a/3rdparty/Sabre/DAVACL/Exception/AceConflict.php b/3rdparty/Sabre/DAVACL/Exception/AceConflict.php index d10aeb4345..4b9f93b003 100644 --- a/3rdparty/Sabre/DAVACL/Exception/AceConflict.php +++ b/3rdparty/Sabre/DAVACL/Exception/AceConflict.php @@ -1,12 +1,12 @@ ownerDocument; - + $np = $doc->createElementNS('DAV:','d:no-ace-conflict'); $errorNode->appendChild($np); } } - -?> diff --git a/3rdparty/Sabre/DAVACL/Exception/NeedPrivileges.php b/3rdparty/Sabre/DAVACL/Exception/NeedPrivileges.php index 024ab6641f..9b055dd970 100644 --- a/3rdparty/Sabre/DAVACL/Exception/NeedPrivileges.php +++ b/3rdparty/Sabre/DAVACL/Exception/NeedPrivileges.php @@ -1,60 +1,61 @@ uri = $uri; $this->privileges = $privileges; + parent::__construct('User did not have the required privileges (' . implode(',', $privileges) . ') for path "' . $uri . '"'); + } /** * Adds in extra information in the xml response. * * This method adds the {DAV:}need-privileges element as defined in rfc3744 - * - * @param Sabre_DAV_Server $server - * @param DOMElement $errorNode + * + * @param Sabre_DAV_Server $server + * @param DOMElement $errorNode * @return void */ public function serialize(Sabre_DAV_Server $server,DOMElement $errorNode) { - + $doc = $errorNode->ownerDocument; - + $np = $doc->createElementNS('DAV:','d:need-privileges'); $errorNode->appendChild($np); diff --git a/3rdparty/Sabre/DAVACL/Exception/NoAbstract.php b/3rdparty/Sabre/DAVACL/Exception/NoAbstract.php index 60f49ebff4..f44e3e3228 100644 --- a/3rdparty/Sabre/DAVACL/Exception/NoAbstract.php +++ b/3rdparty/Sabre/DAVACL/Exception/NoAbstract.php @@ -2,11 +2,11 @@ /** * Sabre_DAVACL_Exception_NoAbstract - * + * * @package Sabre * @subpackage DAVACL - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. - * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. + * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ class Sabre_DAVACL_Exception_NoAbstract extends Sabre_DAV_Exception_PreconditionFailed { @@ -15,20 +15,18 @@ class Sabre_DAVACL_Exception_NoAbstract extends Sabre_DAV_Exception_Precondition * Adds in extra information in the xml response. * * This method adds the {DAV:}no-abstract element as defined in rfc3744 - * - * @param Sabre_DAV_Server $server - * @param DOMElement $errorNode + * + * @param Sabre_DAV_Server $server + * @param DOMElement $errorNode * @return void */ public function serialize(Sabre_DAV_Server $server,DOMElement $errorNode) { - + $doc = $errorNode->ownerDocument; - + $np = $doc->createElementNS('DAV:','d:no-abstract'); $errorNode->appendChild($np); } } - -?> diff --git a/3rdparty/Sabre/DAVACL/Exception/NotRecognizedPrincipal.php b/3rdparty/Sabre/DAVACL/Exception/NotRecognizedPrincipal.php index e056dc9e4f..8d1e38ca1b 100644 --- a/3rdparty/Sabre/DAVACL/Exception/NotRecognizedPrincipal.php +++ b/3rdparty/Sabre/DAVACL/Exception/NotRecognizedPrincipal.php @@ -2,11 +2,11 @@ /** * Sabre_DAVACL_Exception_NotRecognizedPrincipal - * + * * @package Sabre * @subpackage DAVACL - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. - * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. + * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ class Sabre_DAVACL_Exception_NotRecognizedPrincipal extends Sabre_DAV_Exception_PreconditionFailed { @@ -15,20 +15,18 @@ class Sabre_DAVACL_Exception_NotRecognizedPrincipal extends Sabre_DAV_Exception_ * Adds in extra information in the xml response. * * This method adds the {DAV:}recognized-principal element as defined in rfc3744 - * - * @param Sabre_DAV_Server $server - * @param DOMElement $errorNode + * + * @param Sabre_DAV_Server $server + * @param DOMElement $errorNode * @return void */ public function serialize(Sabre_DAV_Server $server,DOMElement $errorNode) { - + $doc = $errorNode->ownerDocument; - + $np = $doc->createElementNS('DAV:','d:recognized-principal'); $errorNode->appendChild($np); } } - -?> diff --git a/3rdparty/Sabre/DAVACL/Exception/NotSupportedPrivilege.php b/3rdparty/Sabre/DAVACL/Exception/NotSupportedPrivilege.php index 27db7cdd7d..3b5d012d7f 100644 --- a/3rdparty/Sabre/DAVACL/Exception/NotSupportedPrivilege.php +++ b/3rdparty/Sabre/DAVACL/Exception/NotSupportedPrivilege.php @@ -2,11 +2,11 @@ /** * Sabre_DAVACL_Exception_NotSupportedPrivilege - * + * * @package Sabre * @subpackage DAVACL - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. - * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. + * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ class Sabre_DAVACL_Exception_NotSupportedPrivilege extends Sabre_DAV_Exception_PreconditionFailed { @@ -15,20 +15,18 @@ class Sabre_DAVACL_Exception_NotSupportedPrivilege extends Sabre_DAV_Exception_P * Adds in extra information in the xml response. * * This method adds the {DAV:}not-supported-privilege element as defined in rfc3744 - * - * @param Sabre_DAV_Server $server - * @param DOMElement $errorNode + * + * @param Sabre_DAV_Server $server + * @param DOMElement $errorNode * @return void */ public function serialize(Sabre_DAV_Server $server,DOMElement $errorNode) { - + $doc = $errorNode->ownerDocument; - + $np = $doc->createElementNS('DAV:','d:not-supported-privilege'); $errorNode->appendChild($np); } } - -?> diff --git a/3rdparty/Sabre/DAVACL/IACL.php b/3rdparty/Sabre/DAVACL/IACL.php index 506be4248d..003e699348 100644 --- a/3rdparty/Sabre/DAVACL/IACL.php +++ b/3rdparty/Sabre/DAVACL/IACL.php @@ -4,11 +4,11 @@ * ACL-enabled node * * If you want to add WebDAV ACL to a node, you must implement this class - * + * * @package Sabre * @subpackage DAVACL - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. - * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. + * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ interface Sabre_DAVACL_IACL extends Sabre_DAV_INode { @@ -16,8 +16,8 @@ interface Sabre_DAVACL_IACL extends Sabre_DAV_INode { /** * Returns the owner principal * - * This must be a url to a principal, or null if there's no owner - * + * This must be a url to a principal, or null if there's no owner + * * @return string|null */ function getOwner(); @@ -26,8 +26,8 @@ interface Sabre_DAVACL_IACL extends Sabre_DAV_INode { * Returns a group principal * * This must be a url to a principal, or null if there's no owner - * - * @return string|null + * + * @return string|null */ function getGroup(); @@ -35,24 +35,39 @@ interface Sabre_DAVACL_IACL extends Sabre_DAV_INode { * 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 + * * '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 + * * 'protected' (optional), indicating that this ACE is not allowed to + * be updated. + * + * @return array */ function getACL(); /** * Updates the ACL * - * This method will receive a list of new ACE's. - * - * @param array $acl + * This method will receive a list of new ACE's. + * + * @param array $acl * @return void */ function setACL(array $acl); + /** + * Returns the list of supported privileges for this node. + * + * The returned data structure is a list of nested privileges. + * See Sabre_DAVACL_Plugin::getDefaultSupportedPrivilegeSet for a simple + * standard structure. + * + * If null is returned from this method, the default privilege set is used, + * which is fine for most common usecases. + * + * @return array|null + */ + function getSupportedPrivilegeSet(); + + } diff --git a/3rdparty/Sabre/DAVACL/IPrincipal.php b/3rdparty/Sabre/DAVACL/IPrincipal.php index 7868811db7..fc7605bf62 100644 --- a/3rdparty/Sabre/DAVACL/IPrincipal.php +++ b/3rdparty/Sabre/DAVACL/IPrincipal.php @@ -2,50 +2,50 @@ /** * IPrincipal interface - * + * * Implement this interface to define your own principals - * + * * @package Sabre * @subpackage DAVACL - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. - * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. + * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ interface Sabre_DAVACL_IPrincipal extends Sabre_DAV_INode { /** - * Returns a list of altenative urls for a principal - * + * Returns a list of alternative urls for a principal + * * This can for example be an email address, or ldap url. - * - * @return array + * + * @return array */ function getAlternateUriSet(); /** - * Returns the full principal url - * - * @return string + * Returns the full principal url + * + * @return string */ function getPrincipalUrl(); /** * Returns the list of group members - * + * * If this principal is a group, this function should return - * all member principal uri's for the group. - * + * all member principal uri's for the group. + * * @return array */ function getGroupMemberSet(); /** * Returns the list of groups this principal is member of - * + * * If this principal is a member of a (list of) groups, this function - * should return a list of principal uri's for it's members. - * - * @return array + * should return a list of principal uri's for it's members. + * + * @return array */ function getGroupMembership(); @@ -54,11 +54,11 @@ interface Sabre_DAVACL_IPrincipal extends Sabre_DAV_INode { * * If this principal is a group, this method sets all the group members. * The list of members is always overwritten, never appended to. - * - * This method should throw an exception if the members could not be set. - * - * @param array $principals - * @return void + * + * This method should throw an exception if the members could not be set. + * + * @param array $principals + * @return void */ function setGroupMemberSet(array $principals); @@ -66,9 +66,9 @@ interface Sabre_DAVACL_IPrincipal extends Sabre_DAV_INode { * Returns the displayname * * This should be a human readable name for the principal. - * If none is available, return the nodename. - * - * @return string + * If none is available, return the nodename. + * + * @return string */ function getDisplayName(); diff --git a/3rdparty/Sabre/DAVACL/IPrincipalBackend.php b/3rdparty/Sabre/DAVACL/IPrincipalBackend.php index 8899f6f80d..e798bf890c 100644 --- a/3rdparty/Sabre/DAVACL/IPrincipalBackend.php +++ b/3rdparty/Sabre/DAVACL/IPrincipalBackend.php @@ -3,14 +3,14 @@ /** * Implement this interface to create your own principal backends. * - * Creating backends for principals is entirely optional. You can also - * implement Sabre_DAVACL_IPrincipal directly. This interface is used solely by + * Creating backends for principals is entirely optional. You can also + * implement Sabre_DAVACL_IPrincipal directly. This interface is used solely by * Sabre_DAVACL_AbstractPrincipalCollection. * * @package Sabre * @subpackage DAVACL - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. - * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. + * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ interface Sabre_DAVACL_IPrincipalBackend { @@ -18,56 +18,136 @@ interface Sabre_DAVACL_IPrincipalBackend { /** * Returns a list of principals based on a prefix. * - * This prefix will often contain something like 'principals'. You are only + * This prefix will often contain something like 'principals'. You are only * expected to return principals that are in this base path. * - * You are expected to return at least a 'uri' for every user, you can + * You are expected to return at least a 'uri' for every user, you can * return any additional properties if you wish so. Common properties are: - * {DAV:}displayname - * {http://sabredav.org/ns}email-address - This is a custom SabreDAV - * field that's actualy injected in a number of other properties. If + * {DAV:}displayname + * {http://sabredav.org/ns}email-address - This is a custom SabreDAV + * field that's actually injected in a number of other properties. If * you have an email address, use this property. - * - * @param string $prefixPath - * @return array + * + * @param string $prefixPath + * @return array */ function getPrincipalsByPrefix($prefixPath); /** * Returns a specific principal, specified by it's path. - * The returned structure should be the exact same as from - * getPrincipalsByPrefix. - * - * @param string $path - * @return array + * The returned structure should be the exact same as from + * getPrincipalsByPrefix. + * + * @param string $path + * @return array */ function getPrincipalByPath($path); /** - * Returns the list of members for a group-principal - * - * @param string $principal - * @return array + * Updates one ore more webdav properties on a principal. + * + * The list of mutations is supplied as an array. Each key in the array is + * a propertyname, such as {DAV:}displayname. + * + * Each value is the actual value to be updated. If a value is null, it + * must be deleted. + * + * This method should be atomic. It must either completely succeed, or + * completely fail. Success and failure can simply be returned as 'true' or + * 'false'. + * + * It is also possible to return detailed failure information. In that case + * an array such as this should be returned: + * + * array( + * 200 => array( + * '{DAV:}prop1' => null, + * ), + * 201 => array( + * '{DAV:}prop2' => null, + * ), + * 403 => array( + * '{DAV:}prop3' => null, + * ), + * 424 => array( + * '{DAV:}prop4' => null, + * ), + * ); + * + * In this previous example prop1 was successfully updated or deleted, and + * prop2 was succesfully created. + * + * prop3 failed to update due to '403 Forbidden' and because of this prop4 + * also could not be updated with '424 Failed dependency'. + * + * This last example was actually incorrect. While 200 and 201 could appear + * in 1 response, if there's any error (403) the other properties should + * always fail with 423 (failed dependency). + * + * But anyway, if you don't want to scratch your head over this, just + * return true or false. + * + * @param string $path + * @param array $mutations + * @return array|bool + */ + function updatePrincipal($path, $mutations); + + /** + * This method is used to search for principals matching a set of + * properties. + * + * This search is specifically used by RFC3744's principal-property-search + * REPORT. You should at least allow searching on + * http://sabredav.org/ns}email-address. + * + * The actual search should be a unicode-non-case-sensitive search. The + * keys in searchProperties are the WebDAV property names, while the values + * are the property values to search on. + * + * If multiple properties are being searched on, the search should be + * AND'ed. + * + * This method should simply return an array with full principal uri's. + * + * If somebody attempted to search on a property the backend does not + * support, you should simply return 0 results. + * + * You can also just return 0 results if you choose to not support + * searching at all, but keep in mind that this may stop certain features + * from working. + * + * @param string $prefixPath + * @param array $searchProperties + * @return array + */ + function searchPrincipals($prefixPath, array $searchProperties); + + /** + * Returns the list of members for a group-principal + * + * @param string $principal + * @return array */ function getGroupMemberSet($principal); /** - * Returns the list of groups a principal is a member of - * - * @param string $principal - * @return array + * Returns the list of groups a principal is a member of + * + * @param string $principal + * @return array */ function getGroupMembership($principal); /** * Updates the list of group members for a group principal. * - * The principals should be passed as a list of uri's. - * - * @param string $principal - * @param array $members + * The principals should be passed as a list of uri's. + * + * @param string $principal + * @param array $members * @return void */ - function setGroupMemberSet($principal, array $members); + function setGroupMemberSet($principal, array $members); } diff --git a/3rdparty/Sabre/DAVACL/Plugin.php b/3rdparty/Sabre/DAVACL/Plugin.php index b964bdb5de..5c828c6d97 100644 --- a/3rdparty/Sabre/DAVACL/Plugin.php +++ b/3rdparty/Sabre/DAVACL/Plugin.php @@ -6,14 +6,14 @@ * This plugin provides funcitonality to enforce ACL permissions. * ACL is defined in RFC3744. * - * In addition it also provides support for the {DAV:}current-user-principal - * property, defined in RFC5397 and the {DAV:}expand-property report, as - * defined in RFC3253. - * + * In addition it also provides support for the {DAV:}current-user-principal + * property, defined in RFC5397 and the {DAV:}expand-property report, as + * defined in RFC3253. + * * @package Sabre * @subpackage DAVACL - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. - * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. + * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ class Sabre_DAVACL_Plugin extends Sabre_DAV_ServerPlugin { @@ -40,16 +40,16 @@ class Sabre_DAVACL_Plugin extends Sabre_DAV_ServerPlugin { const R_RECURSIVEPARENTS = 3; /** - * Reference to server object. - * - * @var Sabre_DAV_Server + * Reference to server object. + * + * @var Sabre_DAV_Server */ protected $server; /** * List of urls containing principal collections. - * Modify this if your principals are located elsewhere. - * + * Modify this if your principals are located elsewhere. + * * @var array */ public $principalCollectionSet = array( @@ -57,14 +57,14 @@ class Sabre_DAVACL_Plugin extends Sabre_DAV_ServerPlugin { ); /** - * By default ACL is only enforced for nodes that have ACL support (the - * ones that implement Sabre_DAVACL_IACL). For any other node, access is + * By default ACL is only enforced for nodes that have ACL support (the + * ones that implement Sabre_DAVACL_IACL). For any other node, access is * always granted. * - * To override this behaviour you can turn this setting off. This is useful + * To override this behaviour you can turn this setting off. This is useful * if you plan to fully support ACL in the entire tree. * - * @var bool + * @var bool */ public $allowAccessToNodesWithoutACL = true; @@ -72,28 +72,50 @@ class Sabre_DAVACL_Plugin extends Sabre_DAV_ServerPlugin { * By default nodes that are inaccessible by the user, can still be seen * in directory listings (PROPFIND on parent with Depth: 1) * - * In certain cases it's desirable to hide inaccessible nodes. Setting this + * In certain cases it's desirable to hide inaccessible nodes. Setting this * to true will cause these nodes to be hidden from directory listings. - * - * @var bool + * + * @var bool */ public $hideNodesFromListings = false; /** - * This string is prepended to the username of the currently logged in - * user. This allows the plugin to determine the principal path based on + * This string is prepended to the username of the currently logged in + * user. This allows the plugin to determine the principal path based on * the username. - * + * * @var string */ public $defaultUsernamePath = 'principals'; + /** + * This list of properties are the properties a client can search on using + * the {DAV:}principal-property-search report. + * + * The keys are the property names, values are descriptions. + * + * @var array + */ + public $principalSearchPropertySet = array( + '{DAV:}displayname' => 'Display name', + '{http://sabredav.org/ns}email-address' => 'Email address', + ); + + /** + * Any principal uri's added here, will automatically be added to the list + * of ACL's. They will effectively receive {DAV:}all privileges, as a + * protected privilege. + * + * @var array + */ + public $adminPrincipals = array(); + /** * Returns a list of features added by this plugin. * * This list is used in the response of a HTTP OPTIONS request. - * - * @return array + * + * @return array */ public function getFeatures() { @@ -102,10 +124,10 @@ class Sabre_DAVACL_Plugin extends Sabre_DAV_ServerPlugin { } /** - * Returns a list of available methods for a given url - * - * @param string $uri - * @return array + * Returns a list of available methods for a given url + * + * @param string $uri + * @return array */ public function getMethods($uri) { @@ -115,11 +137,11 @@ class Sabre_DAVACL_Plugin extends Sabre_DAV_ServerPlugin { /** * Returns a plugin name. - * + * * Using this name other plugins will be able to access other plugins - * using Sabre_DAV_Server::getPlugin - * - * @return string + * using Sabre_DAV_Server::getPlugin + * + * @return string */ public function getPluginName() { @@ -131,37 +153,38 @@ class Sabre_DAVACL_Plugin extends Sabre_DAV_ServerPlugin { * Returns a list of reports this plugin supports. * * This will be used in the {DAV:}supported-report-set property. - * Note that you still need to subscribe to the 'report' event to actually - * implement them - * + * Note that you still need to subscribe to the 'report' event to actually + * implement them + * * @param string $uri - * @return array + * @return array */ public function getSupportedReportSet($uri) { return array( '{DAV:}expand-property', '{DAV:}principal-property-search', - '{DAV:}principal-search-property-set', + '{DAV:}principal-search-property-set', ); } /** - * Checks if the current user has the specified privilege(s). - * + * Checks if the current user has the specified privilege(s). + * * You can specify a single privilege, or a list of privileges. * This method will throw an exception if the privilege is not available * and return true otherwise. * * @param string $uri * @param array|string $privileges - * @param bool $throwExceptions if set to false, this method won't through exceptions. + * @param int $recursion + * @param bool $throwExceptions if set to false, this method won't through exceptions. * @throws Sabre_DAVACL_Exception_NeedPrivileges - * @return bool + * @return bool */ - public function checkPrivileges($uri,$privileges,$recursion = self::R_PARENT, $throwExceptions = true) { + public function checkPrivileges($uri, $privileges, $recursion = self::R_PARENT, $throwExceptions = true) { if (!is_array($privileges)) $privileges = array($privileges); @@ -171,7 +194,7 @@ class Sabre_DAVACL_Plugin extends Sabre_DAV_ServerPlugin { if ($this->allowAccessToNodesWithoutACL) { return true; } else { - if ($throwExceptions) + if ($throwExceptions) throw new Sabre_DAVACL_Exception_NeedPrivileges($uri,$privileges); else return false; @@ -189,7 +212,7 @@ class Sabre_DAVACL_Plugin extends Sabre_DAV_ServerPlugin { } if ($failed) { - if ($throwExceptions) + if ($throwExceptions) throw new Sabre_DAVACL_Exception_NeedPrivileges($uri,$failed); else return false; @@ -202,9 +225,9 @@ class Sabre_DAVACL_Plugin extends Sabre_DAV_ServerPlugin { * Returns the standard users' principal. * * This is one authorative principal url for the current user. - * This method will return null if the user wasn't logged in. - * - * @return string|null + * This method will return null if the user wasn't logged in. + * + * @return string|null */ public function getCurrentUserPrincipal() { @@ -220,9 +243,9 @@ class Sabre_DAVACL_Plugin extends Sabre_DAV_ServerPlugin { /** * Returns a list of principals that's associated to the current - * user, either directly or through group membership. - * - * @return array + * user, either directly or through group membership. + * + * @return array */ public function getCurrentUserPrincipals() { @@ -236,7 +259,7 @@ class Sabre_DAVACL_Plugin extends Sabre_DAV_ServerPlugin { while(count($check)) { $principal = array_shift($check); - + $node = $this->server->tree->getNodeForPath($principal); if ($node instanceof Sabre_DAVACL_IPrincipal) { foreach($node->getGroupMembership() as $groupMember) { @@ -262,11 +285,38 @@ class Sabre_DAVACL_Plugin extends Sabre_DAV_ServerPlugin { * Returns the supported privilege structure for this ACL plugin. * * See RFC3744 for more details. Currently we default on a simple, - * standard structure. - * - * @return array + * standard structure. + * + * You can either get the list of privileges by a uri (path) or by + * specifying a Node. + * + * @param string|Sabre_DAV_INode $node + * @return array */ - public function getSupportedPrivilegeSet() { + public function getSupportedPrivilegeSet($node) { + + if (is_string($node)) { + $node = $this->server->tree->getNodeForPath($node); + } + + if ($node instanceof Sabre_DAVACL_IACL) { + $result = $node->getSupportedPrivilegeSet(); + + if ($result) + return $result; + } + + return self::getDefaultSupportedPrivilegeSet(); + + } + + /** + * Returns a fairly standard set of privileges, which may be useful for + * other systems to use as a basis. + * + * @return array + */ + static function getDefaultSupportedPrivilegeSet() { return array( 'privilege' => '{DAV:}all', @@ -314,7 +364,7 @@ class Sabre_DAVACL_Plugin extends Sabre_DAV_ServerPlugin { ), ), ), // {DAV:}write - ), + ), ); // {DAV:}all } @@ -329,12 +379,13 @@ class Sabre_DAVACL_Plugin extends Sabre_DAV_ServerPlugin { * - aggregates * - abstract * - concrete - * - * @return array + * + * @param string|Sabre_DAV_INode $node + * @return array */ - final public function getFlatPrivilegeSet() { + final public function getFlatPrivilegeSet($node) { - $privs = $this->getSupportedPrivilegeSet(); + $privs = $this->getSupportedPrivilegeSet($node); $flat = array(); $this->getFPSTraverse($privs, null, $flat); @@ -346,9 +397,12 @@ class Sabre_DAVACL_Plugin extends Sabre_DAV_ServerPlugin { /** * Traverses the privilege set tree for reordering * - * This function is solely used by getFlatPrivilegeSet, and would have been + * This function is solely used by getFlatPrivilegeSet, and would have been * a closure if it wasn't for the fact I need to support PHP 5.2. - * + * + * @param array $priv + * @param $concrete + * @param array $flat * @return void */ final private function getFPSTraverse($priv, $concrete, &$flat) { @@ -368,7 +422,7 @@ class Sabre_DAVACL_Plugin extends Sabre_DAV_ServerPlugin { if (isset($priv['aggregates'])) { foreach($priv['aggregates'] as $subPriv) { - + $this->getFPSTraverse($subPriv, $myPriv['concrete'], $flat); } @@ -382,8 +436,8 @@ class Sabre_DAVACL_Plugin extends Sabre_DAV_ServerPlugin { * * Either a uri or a Sabre_DAV_INode may be passed. * - * null will be returned if the node doesn't support ACLs. - * + * null will be returned if the node doesn't support ACLs. + * * @param string|Sabre_DAV_INode $node * @return array */ @@ -392,10 +446,18 @@ class Sabre_DAVACL_Plugin extends Sabre_DAV_ServerPlugin { if (is_string($node)) { $node = $this->server->tree->getNodeForPath($node); } - if ($node instanceof Sabre_DAVACL_IACL) { - return $node->getACL(); + if (!$node instanceof Sabre_DAVACL_IACL) { + return null; } - return null; + $acl = $node->getACL(); + foreach($this->adminPrincipals as $adminPrincipal) { + $acl[] = array( + 'principal' => $adminPrincipal, + 'privilege' => '{DAV:}all', + 'protected' => true, + ); + } + return $acl; } @@ -405,10 +467,10 @@ class Sabre_DAVACL_Plugin extends Sabre_DAV_ServerPlugin { * * Either a uri or a Sabre_DAV_INode may be passed. * - * null will be returned if the node doesn't support ACLs. - * - * @param string|Sabre_DAV_INode $node - * @return array + * null will be returned if the node doesn't support ACLs. + * + * @param string|Sabre_DAV_INode $node + * @return array */ public function getCurrentUserPrivilegeSet($node) { @@ -417,6 +479,7 @@ class Sabre_DAVACL_Plugin extends Sabre_DAV_ServerPlugin { } $acl = $this->getACL($node); + if (is_null($acl)) return null; $principals = $this->getCurrentUserPrincipals(); @@ -425,27 +488,121 @@ class Sabre_DAVACL_Plugin extends Sabre_DAV_ServerPlugin { foreach($acl as $ace) { - if (in_array($ace['principal'], $principals)) { - $collected[] = $ace; + $principal = $ace['principal']; + + switch($principal) { + + case '{DAV:}owner' : + $owner = $node->getOwner(); + if ($owner && in_array($owner, $principals)) { + $collected[] = $ace; + } + break; + + + // 'all' matches for every user + case '{DAV:}all' : + + // 'authenticated' matched for every user that's logged in. + // Since it's not possible to use ACL while not being logged + // in, this is also always true. + case '{DAV:}authenticated' : + $collected[] = $ace; + break; + + // 'unauthenticated' can never occur either, so we simply + // ignore these. + case '{DAV:}unauthenticated' : + break; + + default : + if (in_array($ace['principal'], $principals)) { + $collected[] = $ace; + } + break; + } + + } // Now we deduct all aggregated privileges. - $flat = $this->getFlatPrivilegeSet(); + $flat = $this->getFlatPrivilegeSet($node); $collected2 = array(); - foreach($collected as $privilege) { + while(count($collected)) { - $collected2[] = $privilege['privilege']; - foreach($flat[$privilege['privilege']]['aggregates'] as $subPriv) { - if (!in_array($subPriv, $collected2)) - $collected2[] = $subPriv; + $current = array_pop($collected); + $collected2[] = $current['privilege']; + + foreach($flat[$current['privilege']]['aggregates'] as $subPriv) { + $collected2[] = $subPriv; + $collected[] = $flat[$subPriv]; } } - return $collected2; + return array_values(array_unique($collected2)); + + } + + /** + * Principal property search + * + * This method can search for principals matching certain values in + * properties. + * + * This method will return a list of properties for the matched properties. + * + * @param array $searchProperties The properties to search on. This is a + * key-value list. The keys are property + * names, and the values the strings to + * match them on. + * @param array $requestedProperties This is the list of properties to + * return for every match. + * @param string $collectionUri The principal collection to search on. + * If this is ommitted, the standard + * principal collection-set will be used. + * @return array This method returns an array structure similar to + * Sabre_DAV_Server::getPropertiesForPath. Returned + * properties are index by a HTTP status code. + * + */ + public function principalSearch(array $searchProperties, array $requestedProperties, $collectionUri = null) { + + if (!is_null($collectionUri)) { + $uris = array($collectionUri); + } else { + $uris = $this->principalCollectionSet; + } + + $lookupResults = array(); + foreach($uris as $uri) { + + $principalCollection = $this->server->tree->getNodeForPath($uri); + if (!$principalCollection instanceof Sabre_DAVACL_AbstractPrincipalCollection) { + // Not a principal collection, we're simply going to ignore + // this. + continue; + } + + $results = $principalCollection->searchPrincipals($searchProperties); + foreach($results as $result) { + $lookupResults[] = rtrim($uri,'/') . '/' . $result; + } + + } + + $matches = array(); + + foreach($lookupResults as $lookupResult) { + + list($matches[]) = $this->server->getPropertiesForPath($lookupResult, $requestedProperties, 0); + + } + + return $matches; } @@ -453,8 +610,8 @@ class Sabre_DAVACL_Plugin extends Sabre_DAV_ServerPlugin { * Sets up the plugin * * This method is automatically called by the server class. - * - * @param Sabre_DAV_Server $server + * + * @param Sabre_DAV_Server $server * @return void */ public function initialize(Sabre_DAV_Server $server) { @@ -485,11 +642,11 @@ class Sabre_DAVACL_Plugin extends Sabre_DAV_ServerPlugin { '{DAV:}group' ); - // Automatically mapping nodes implementing IPrincipal to the + // Automatically mapping nodes implementing IPrincipal to the // {DAV:}principal resourcetype. $server->resourceTypeMapping['Sabre_DAVACL_IPrincipal'] = '{DAV:}principal'; - // Mapping the group-member-set property to the HrefList property + // Mapping the group-member-set property to the HrefList property // class. $server->propertyMap['{DAV:}group-member-set'] = 'Sabre_DAV_Property_HrefList'; @@ -499,10 +656,10 @@ class Sabre_DAVACL_Plugin extends Sabre_DAV_ServerPlugin { /* {{{ Event handlers */ /** - * Triggered before any method is handled - * - * @param string $method - * @param string $uri + * Triggered before any method is handled + * + * @param string $method + * @param string $uri * @return void */ public function beforeMethod($method, $uri) { @@ -523,14 +680,14 @@ class Sabre_DAVACL_Plugin extends Sabre_DAV_ServerPlugin { case 'PUT' : case 'LOCK' : - case 'UNLOCK' : - // This method requires the write-content priv if the node - // already exists, and bind on the parent if the node is being - // created. - // The bind privilege is handled in the beforeBind event. + case 'UNLOCK' : + // This method requires the write-content priv if the node + // already exists, and bind on the parent if the node is being + // created. + // The bind privilege is handled in the beforeBind event. $this->checkPrivileges($uri,'{DAV:}write-content'); break; - + case 'PROPPATCH' : $this->checkPrivileges($uri,'{DAV:}write-properties'); @@ -543,16 +700,16 @@ class Sabre_DAVACL_Plugin extends Sabre_DAV_ServerPlugin { case 'COPY' : case 'MOVE' : // Copy requires read privileges on the entire source tree. - // If the target exists write-content normally needs to be - // checked, however, we're deleting the node beforehand and - // creating a new one after, so this is handled by the + // If the target exists write-content normally needs to be + // checked, however, we're deleting the node beforehand and + // creating a new one after, so this is handled by the // beforeUnbind event. - // - // The creation of the new node is handled by the beforeBind + // + // The creation of the new node is handled by the beforeBind // event. // - // If MOVE is used beforeUnbind will also be used to check if - // the sourcenode can be deleted. + // If MOVE is used beforeUnbind will also be used to check if + // the sourcenode can be deleted. $this->checkPrivileges($uri,'{DAV:}read',self::R_RECURSIVE); break; @@ -563,11 +720,11 @@ class Sabre_DAVACL_Plugin extends Sabre_DAV_ServerPlugin { /** * Triggered before a new node is created. - * + * * This allows us to check permissions for any operation that creates a * new node, such as PUT, MKCOL, MKCALENDAR, LOCK, COPY and MOVE. - * - * @param string $uri + * + * @param string $uri * @return void */ public function beforeBind($uri) { @@ -578,12 +735,12 @@ class Sabre_DAVACL_Plugin extends Sabre_DAV_ServerPlugin { } /** - * Triggered before a node is deleted - * - * This allows us to check permissions for any operation that will delete - * an existing node. - * - * @param string $uri + * Triggered before a node is deleted + * + * This allows us to check permissions for any operation that will delete + * an existing node. + * + * @param string $uri * @return void */ public function beforeUnbind($uri) { @@ -594,26 +751,26 @@ class Sabre_DAVACL_Plugin extends Sabre_DAV_ServerPlugin { } /** - * Triggered before a node is unlocked. - * - * @param string $uri + * Triggered before a node is unlocked. + * + * @param string $uri * @param Sabre_DAV_Locks_LockInfo $lock - * @TODO: not yet implemented + * @TODO: not yet implemented * @return void */ public function beforeUnlock($uri, Sabre_DAV_Locks_LockInfo $lock) { - + } /** - * Triggered before properties are looked up in specific nodes. - * - * @param string $uri - * @param Sabre_DAV_INode $node - * @param array $requestedProperties + * Triggered before properties are looked up in specific nodes. + * + * @param string $uri + * @param Sabre_DAV_INode $node + * @param array $requestedProperties * @param array $returnedProperties - * @TODO really should be broken into multiple methods, or even a class. + * @TODO really should be broken into multiple methods, or even a class. * @return void */ public function beforeGetProperties($uri, Sabre_DAV_INode $node, &$requestedProperties, &$returnedProperties) { @@ -633,7 +790,7 @@ class Sabre_DAVACL_Plugin extends Sabre_DAV_ServerPlugin { } return; - } + } /* Adding principal properties */ if ($node instanceof Sabre_DAVACL_IPrincipal) { @@ -692,7 +849,7 @@ class Sabre_DAVACL_Plugin extends Sabre_DAV_ServerPlugin { if (false !== ($index = array_search('{DAV:}supported-privilege-set', $requestedProperties))) { unset($requestedProperties[$index]); - $returnedProperties[200]['{DAV:}supported-privilege-set'] = new Sabre_DAVACL_Property_SupportedPrivilegeSet($this->getSupportedPrivilegeSet()); + $returnedProperties[200]['{DAV:}supported-privilege-set'] = new Sabre_DAVACL_Property_SupportedPrivilegeSet($this->getSupportedPrivilegeSet($node)); } if (false !== ($index = array_search('{DAV:}current-user-privilege-set', $requestedProperties))) { @@ -730,16 +887,24 @@ class Sabre_DAVACL_Plugin extends Sabre_DAV_ServerPlugin { } + /* The acl-restrictions property contains information on how privileges + * must behave. + */ + if (false !== ($index = array_search('{DAV:}acl-restrictions', $requestedProperties))) { + unset($requestedProperties[$index]); + $returnedProperties[200]['{DAV:}acl-restrictions'] = new Sabre_DAVACL_Property_AclRestrictions(); + } + } /** - * This method intercepts PROPPATCH methods and make sure the - * group-member-set is updated correctly. - * - * @param array $propertyDelta - * @param array $result - * @param Sabre_DAV_INode $node - * @return void + * This method intercepts PROPPATCH methods and make sure the + * group-member-set is updated correctly. + * + * @param array $propertyDelta + * @param array $result + * @param Sabre_DAV_INode $node + * @return bool */ public function updateProperties(&$propertyDelta, &$result, Sabre_DAV_INode $node) { @@ -763,18 +928,18 @@ class Sabre_DAVACL_Plugin extends Sabre_DAV_ServerPlugin { } $node->setGroupMemberSet($memberSet); - + $result[200]['{DAV:}group-member-set'] = null; unset($propertyDelta['{DAV:}group-member-set']); } /** - * This method handels HTTP REPORT requests - * - * @param string $reportName - * @param DOMNode $dom - * @return void + * This method handels HTTP REPORT requests + * + * @param string $reportName + * @param DOMNode $dom + * @return bool */ public function report($reportName, $dom) { @@ -785,7 +950,7 @@ class Sabre_DAVACL_Plugin extends Sabre_DAV_ServerPlugin { return false; case '{DAV:}principal-search-property-set' : $this->principalSearchPropertySetReport($dom); - return false; + return false; case '{DAV:}expand-property' : $this->expandPropertyReport($dom); return false; @@ -795,12 +960,12 @@ class Sabre_DAVACL_Plugin extends Sabre_DAV_ServerPlugin { } /** - * This event is triggered for any HTTP method that is not known by the - * webserver. + * This event is triggered for any HTTP method that is not known by the + * webserver. * - * @param string $method - * @param string $uri - * @return void + * @param string $method + * @param string $uri + * @return bool */ public function unknownMethod($method, $uri) { @@ -817,12 +982,12 @@ class Sabre_DAVACL_Plugin extends Sabre_DAV_ServerPlugin { * @param string $uri * @return void */ - public function httpACL($uri) { + public function httpACL($uri) { $body = $this->server->httpRequest->getBody(true); $dom = Sabre_DAV_XMLUtil::loadDOMDocument($body); - $newAcl = + $newAcl = Sabre_DAVACL_Property_Acl::unserialize($dom->firstChild) ->getPrivileges(); @@ -839,13 +1004,13 @@ class Sabre_DAVACL_Plugin extends Sabre_DAV_ServerPlugin { $oldAcl = $this->getACL($node); - $supportedPrivileges = $this->getFlatPrivilegeSet(); + $supportedPrivileges = $this->getFlatPrivilegeSet($node); - /* Checking if protected principals from the existing principal set are + /* Checking if protected principals from the existing principal set are not overwritten. */ - foreach($oldAcl as $k=>$oldAce) { + foreach($oldAcl as $oldAce) { - if (!isset($oldAce['protected']) || !$oldAce['protected']) continue; + if (!isset($oldAce['protected']) || !$oldAce['protected']) continue; $found = false; foreach($newAcl as $newAce) { @@ -853,16 +1018,16 @@ class Sabre_DAVACL_Plugin extends Sabre_DAV_ServerPlugin { $newAce['privilege'] === $oldAce['privilege'] && $newAce['principal'] === $oldAce['principal'] && $newAce['protected'] - ) + ) $found = true; } - if (!$found) + if (!$found) throw new Sabre_DAVACL_Exception_AceConflict('This resource contained a protected {DAV:}ace, but this privilege did not occur in the ACL request'); } - foreach($newAcl as $k=>$newAce) { + foreach($newAcl as $newAce) { // Do we recognize the privilege if (!isset($supportedPrivileges[$newAce['privilege']])) { @@ -876,12 +1041,12 @@ class Sabre_DAVACL_Plugin extends Sabre_DAV_ServerPlugin { // Looking up the principal try { $principal = $this->server->tree->getNodeForPath($newAce['principal']); - } catch (Sabre_DAV_Exception_FileNotFound $e) { + } catch (Sabre_DAV_Exception_NotFound $e) { throw new Sabre_DAVACL_Exception_NotRecognizedPrincipal('The specified principal (' . $newAce['principal'] . ') does not exist'); } if (!($principal instanceof Sabre_DAVACL_IPrincipal)) { throw new Sabre_DAVACL_Exception_NotRecognizedPrincipal('The specified uri (' . $newAce['principal'] . ') is not a principal'); - } + } } $node->setACL($newAcl); @@ -893,7 +1058,7 @@ class Sabre_DAVACL_Plugin extends Sabre_DAV_ServerPlugin { /* Reports {{{ */ /** - * The expand-property report is defined in RFC3253 section 3-8. + * The expand-property report is defined in RFC3253 section 3-8. * * This report is very similar to a standard PROPFIND. The difference is * that it has the additional ability to look at properties containing a @@ -903,7 +1068,7 @@ class Sabre_DAVACL_Plugin extends Sabre_DAV_ServerPlugin { * Other rfc's, such as ACL rely on this report, so it made sense to put * it in this plugin. * - * @param DOMElement $dom + * @param DOMElement $dom * @return void */ protected function expandPropertyReport($dom) { @@ -940,9 +1105,9 @@ class Sabre_DAVACL_Plugin extends Sabre_DAV_ServerPlugin { /** * This method is used by expandPropertyReport to parse * out the entire HTTP request. - * - * @param DOMElement $node - * @return array + * + * @param DOMElement $node + * @return array */ protected function parseExpandPropertyReportRequest($node) { @@ -950,9 +1115,9 @@ class Sabre_DAVACL_Plugin extends Sabre_DAV_ServerPlugin { do { if (Sabre_DAV_XMLUtil::toClarkNotation($node)!=='{DAV:}property') continue; - + if ($node->firstChild) { - + $children = $this->parseExpandPropertyReportRequest($node->firstChild); } else { @@ -965,7 +1130,7 @@ class Sabre_DAVACL_Plugin extends Sabre_DAV_ServerPlugin { if (!$namespace) $namespace = 'DAV:'; $propName = '{'.$namespace.'}' . $node->getAttribute('name'); - $requestedProperties[$propName] = $children; + $requestedProperties[$propName] = $children; } while ($node = $node->nextSibling); @@ -979,11 +1144,12 @@ class Sabre_DAVACL_Plugin extends Sabre_DAV_ServerPlugin { * * @param array $path * @param array $requestedProperties the list of required properties - * @param array $depth + * @param int $depth + * @return array */ - protected function expandProperties($path,array $requestedProperties,$depth) { + protected function expandProperties($path, array $requestedProperties, $depth) { - $foundProperties = $this->server->getPropertiesForPath($path,array_keys($requestedProperties),$depth); + $foundProperties = $this->server->getPropertiesForPath($path, array_keys($requestedProperties), $depth); $result = array(); @@ -993,7 +1159,7 @@ class Sabre_DAVACL_Plugin extends Sabre_DAV_ServerPlugin { // We're only traversing if sub-properties were requested if(count($childRequestedProperties)===0) continue; - + // We only have to do the expansion if the property was found // and it contains an href element. if (!array_key_exists($propertyName,$node[200])) continue; @@ -1006,7 +1172,7 @@ class Sabre_DAVACL_Plugin extends Sabre_DAV_ServerPlugin { $childProps = array(); foreach($hrefs as $href) { - $childProps = array_merge($childProps, $this->expandProperties($href,$childRequestedProperties,0)); + $childProps = array_merge($childProps, $this->expandProperties($href, $childRequestedProperties, 0)); } $node[200][$propertyName] = new Sabre_DAV_Property_ResponseList($childProps); @@ -1022,27 +1188,23 @@ class Sabre_DAVACL_Plugin extends Sabre_DAV_ServerPlugin { /** * principalSearchPropertySetReport * - * This method responsible for handing the + * This method responsible for handing the * {DAV:}principal-search-property-set report. This report returns a list * of properties the client may search on, using the * {DAV:}principal-property-search report. - * - * @param DOMDocument $dom + * + * @param DOMDocument $dom * @return void */ protected function principalSearchPropertySetReport(DOMDocument $dom) { - $searchProperties = array( - '{DAV:}displayname' => 'display name' - ); - $httpDepth = $this->server->getHTTPDepth(0); if ($httpDepth!==0) { throw new Sabre_DAV_Exception_BadRequest('This report is only defined when Depth: 0'); } - - if ($dom->firstChild->hasChildNodes()) - throw new Sabre_DAV_Exception_BadRequest('The principal-search-property-set report element is not allowed to have child elements'); + + if ($dom->firstChild->hasChildNodes()) + throw new Sabre_DAV_Exception_BadRequest('The principal-search-property-set report element is not allowed to have child elements'); $dom = new DOMDocument('1.0','utf-8'); $dom->formatOutput = true; @@ -1055,16 +1217,16 @@ class Sabre_DAVACL_Plugin extends Sabre_DAV_ServerPlugin { } - $nsList = $this->server->xmlNamespaces; + $nsList = $this->server->xmlNamespaces; - foreach($searchProperties as $propertyName=>$description) { + foreach($this->principalSearchPropertySet as $propertyName=>$description) { $psp = $dom->createElement('d:principal-search-property'); $root->appendChild($psp); $prop = $dom->createElement('d:prop'); $psp->appendChild($prop); - + $propName = null; preg_match('/^{([^}]*)}(.*)$/',$propertyName,$propName); @@ -1088,78 +1250,25 @@ class Sabre_DAVACL_Plugin extends Sabre_DAV_ServerPlugin { /** * principalPropertySearchReport * - * This method is reponsible for handing the - * {DAV:}principal-property-search report. This report can be used for + * This method is responsible for handing the + * {DAV:}principal-property-search report. This report can be used for * clients to search for groups of principals, based on the value of one * or more properties. - * - * @param DOMDocument $dom + * + * @param DOMDocument $dom * @return void */ protected function principalPropertySearchReport(DOMDocument $dom) { - $searchableProperties = array( - '{DAV:}displayname' => 'display name' - - ); - list($searchProperties, $requestedProperties, $applyToPrincipalCollectionSet) = $this->parsePrincipalPropertySearchReportRequest($dom); - $result = array(); - - if ($applyToPrincipalCollectionSet) { - $uris = array(); - } else { - $uris = array($this->server->getRequestUri()); + $uri = null; + if (!$applyToPrincipalCollectionSet) { + $uri = $this->server->getRequestUri(); } + $result = $this->principalSearch($searchProperties, $requestedProperties, $uri); - $lookupResults = array(); - foreach($uris as $uri) { - - $p = array_keys($searchProperties); - $p[] = '{DAV:}resourcetype'; - $r = $this->server->getPropertiesForPath($uri, $p, 1); - - // The first item in the results is the parent, so we get rid of it. - array_shift($r); - $lookupResults = array_merge($lookupResults, $r); - } - - $matches = array(); - - foreach($lookupResults as $lookupResult) { - - // We're only looking for principals - if (!isset($lookupResult[200]['{DAV:}resourcetype']) || - (!($lookupResult[200]['{DAV:}resourcetype'] instanceof Sabre_DAV_Property_ResourceType)) || - !$lookupResult[200]['{DAV:}resourcetype']->is('{DAV:}principal')) continue; - - foreach($searchProperties as $searchProperty=>$searchValue) { - if (!isset($searchableProperties[$searchProperty])) { - // If a property is not 'searchable', the spec dictates - // this is not a match. - continue; - } - - if (isset($lookupResult[200][$searchProperty]) && - mb_stripos($lookupResult[200][$searchProperty], $searchValue, 0, 'UTF-8')!==false) { - $matches[] = $lookupResult['href']; - } - - } - - } - - $matchProperties = array(); - - foreach($matches as $match) { - - list($result) = $this->server->getPropertiesForPath($match, $requestedProperties, 0); - $matchProperties[] = $result; - - } - - $xml = $this->server->generateMultiStatus($matchProperties); + $xml = $this->server->generateMultiStatus($result); $this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8'); $this->server->httpResponse->sendStatus(207); $this->server->httpResponse->sendBody($xml); @@ -1175,9 +1284,9 @@ class Sabre_DAVACL_Plugin extends Sabre_DAV_ServerPlugin { * This method returns an array with two elements: * 1. an array with properties to search on, and their values * 2. a list of propertyvalues that should be returned for the request. - * - * @param DOMDocument $dom - * @return array + * + * @param DOMDocument $dom + * @return array */ protected function parsePrincipalPropertySearchReportRequest($dom) { @@ -1193,8 +1302,9 @@ class Sabre_DAVACL_Plugin extends Sabre_DAV_ServerPlugin { // Parsing the search request foreach($dom->firstChild->childNodes as $searchNode) { - if (Sabre_DAV_XMLUtil::toClarkNotation($searchNode) == '{DAV:}apply-to-principal-collection-set') + if (Sabre_DAV_XMLUtil::toClarkNotation($searchNode) == '{DAV:}apply-to-principal-collection-set') { $applyToPrincipalCollectionSet = true; + } if (Sabre_DAV_XMLUtil::toClarkNotation($searchNode)!=='{DAV:}property-search') continue; @@ -1208,7 +1318,7 @@ class Sabre_DAVACL_Plugin extends Sabre_DAV_ServerPlugin { case '{DAV:}prop' : $property = Sabre_DAV_XMLUtil::parseProperties($searchNode); - reset($property); + reset($property); $propertyName = key($property); break; diff --git a/3rdparty/Sabre/DAVACL/Principal.php b/3rdparty/Sabre/DAVACL/Principal.php index 790603c900..51c6658afd 100644 --- a/3rdparty/Sabre/DAVACL/Principal.php +++ b/3rdparty/Sabre/DAVACL/Principal.php @@ -4,17 +4,17 @@ * Principal class * * This class is a representation of a simple principal - * - * Many WebDAV specs require a user to show up in the directory - * structure. + * + * Many WebDAV specs require a user to show up in the directory + * structure. * * This principal also has basic ACL settings, only allowing the principal - * access it's own principal. - * + * access it's own principal. + * * @package Sabre * @subpackage DAVACL - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. - * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. + * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ class Sabre_DAVACL_Principal extends Sabre_DAV_Node implements Sabre_DAVACL_IPrincipal, Sabre_DAV_IProperties, Sabre_DAVACL_IACL { @@ -22,20 +22,21 @@ class Sabre_DAVACL_Principal extends Sabre_DAV_Node implements Sabre_DAVACL_IPri /** * Struct with principal information. * - * @var array + * @var array */ protected $principalProperties; /** - * Principal backend - * - * @var Sabre_DAVACL_IPrincipalBackend + * Principal backend + * + * @var Sabre_DAVACL_IPrincipalBackend */ protected $principalBackend; /** - * Creates the principal object + * Creates the principal object * + * @param Sabre_DAVACL_IPrincipalBackend $principalBackend * @param array $principalProperties */ public function __construct(Sabre_DAVACL_IPrincipalBackend $principalBackend, array $principalProperties = array()) { @@ -49,22 +50,22 @@ class Sabre_DAVACL_Principal extends Sabre_DAV_Node implements Sabre_DAVACL_IPri } /** - * Returns the full principal url - * - * @return string + * Returns the full principal url + * + * @return string */ public function getPrincipalUrl() { return $this->principalProperties['uri']; - } + } /** - * Returns a list of altenative urls for a principal - * + * Returns a list of alternative urls for a principal + * * This can for example be an email address, or ldap url. - * - * @return array + * + * @return array */ public function getAlternateUriSet() { @@ -79,16 +80,16 @@ class Sabre_DAVACL_Principal extends Sabre_DAV_Node implements Sabre_DAVACL_IPri $uris[] = 'mailto:' . $this->principalProperties['{http://sabredav.org/ns}email-address']; } - return array_unique($uris); + return array_unique($uris); } /** * Returns the list of group members - * + * * If this principal is a group, this function should return - * all member principal uri's for the group. - * + * all member principal uri's for the group. + * * @return array */ public function getGroupMemberSet() { @@ -99,11 +100,11 @@ class Sabre_DAVACL_Principal extends Sabre_DAV_Node implements Sabre_DAVACL_IPri /** * Returns the list of groups this principal is member of - * + * * If this principal is a member of a (list of) groups, this function - * should return a list of principal uri's for it's members. - * - * @return array + * should return a list of principal uri's for it's members. + * + * @return array */ public function getGroupMembership() { @@ -117,11 +118,11 @@ class Sabre_DAVACL_Principal extends Sabre_DAV_Node implements Sabre_DAVACL_IPri * * If this principal is a group, this method sets all the group members. * The list of members is always overwritten, never appended to. - * - * This method should throw an exception if the members could not be set. - * - * @param array $principals - * @return void + * + * This method should throw an exception if the members could not be set. + * + * @param array $groupMembers + * @return void */ public function setGroupMemberSet(array $groupMembers) { @@ -132,22 +133,21 @@ class Sabre_DAVACL_Principal extends Sabre_DAV_Node implements Sabre_DAVACL_IPri /** * Returns this principals name. - * - * @return string + * + * @return string */ public function getName() { $uri = $this->principalProperties['uri']; list(, $name) = Sabre_DAV_URLUtil::splitPath($uri); - return $name; } /** - * Returns the name of the user - * - * @return void + * Returns the name of the user + * + * @return string */ public function getDisplayName() { @@ -160,16 +160,16 @@ class Sabre_DAVACL_Principal extends Sabre_DAV_Node implements Sabre_DAVACL_IPri } /** - * Returns a list of properties - * - * @param array $requestedProperties - * @return void + * Returns a list of properties + * + * @param array $requestedProperties + * @return array */ public function getProperties($requestedProperties) { $newProperties = array(); foreach($requestedProperties as $propName) { - + if (isset($this->principalProperties[$propName])) { $newProperties[$propName] = $this->principalProperties[$propName]; } @@ -177,29 +177,27 @@ class Sabre_DAVACL_Principal extends Sabre_DAV_Node implements Sabre_DAVACL_IPri } return $newProperties; - + } /** * Updates this principals properties. - * - * Currently this is not supported * - * @param array $properties + * @param array $mutations * @see Sabre_DAV_IProperties::updateProperties - * @return bool|array + * @return bool|array */ - public function updateProperties($properties) { + public function updateProperties($mutations) { - return false; + return $this->principalBackend->updatePrincipal($this->principalProperties['uri'], $mutations); } /** * Returns the owner principal * - * This must be a url to a principal, or null if there's no owner - * + * This must be a url to a principal, or null if there's no owner + * * @return string|null */ public function getOwner() { @@ -213,8 +211,8 @@ class Sabre_DAVACL_Principal extends Sabre_DAV_Node implements Sabre_DAVACL_IPri * Returns a group principal * * This must be a url to a principal, or null if there's no owner - * - * @return string|null + * + * @return string|null */ public function getGroup() { @@ -226,20 +224,20 @@ class Sabre_DAVACL_Principal extends Sabre_DAV_Node implements Sabre_DAVACL_IPri * 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 + * * '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 + * * 'protected' (optional), indicating that this ACE is not allowed to + * be updated. + * + * @return array */ public function getACL() { return array( array( 'privilege' => '{DAV:}read', - 'principal' => $this->principalProperties['uri'], + 'principal' => $this->getPrincipalUrl(), 'protected' => true, ), ); @@ -249,9 +247,9 @@ class Sabre_DAVACL_Principal extends Sabre_DAV_Node implements Sabre_DAVACL_IPri /** * Updates the ACL * - * This method will receive a list of new ACE's. - * - * @param array $acl + * This method will receive a list of new ACE's. + * + * @param array $acl * @return void */ public function setACL(array $acl) { @@ -260,4 +258,22 @@ class Sabre_DAVACL_Principal extends Sabre_DAV_Node implements Sabre_DAVACL_IPri } + /** + * Returns the list of supported privileges for this node. + * + * The returned data structure is a list of nested privileges. + * See Sabre_DAVACL_Plugin::getDefaultSupportedPrivilegeSet for a simple + * standard structure. + * + * If null is returned from this method, the default privilege set is used, + * which is fine for most common usecases. + * + * @return array|null + */ + public function getSupportedPrivilegeSet() { + + return null; + + } + } diff --git a/3rdparty/Sabre/DAVACL/PrincipalBackend/PDO.php b/3rdparty/Sabre/DAVACL/PrincipalBackend/PDO.php index 55bd1903c9..a76b4a9d72 100644 --- a/3rdparty/Sabre/DAVACL/PrincipalBackend/PDO.php +++ b/3rdparty/Sabre/DAVACL/PrincipalBackend/PDO.php @@ -3,46 +3,80 @@ /** * PDO principal backend * - * This is a simple principal backend that maps exactly to the users table, as + * This is a simple principal backend that maps exactly to the users table, as * used by Sabre_DAV_Auth_Backend_PDO. * - * It assumes all principals are in a single collection. The default collection + * It assumes all principals are in a single collection. The default collection * is 'principals/', but this can be overriden. * * @package Sabre * @subpackage DAVACL - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. - * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. + * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ class Sabre_DAVACL_PrincipalBackend_PDO implements Sabre_DAVACL_IPrincipalBackend { /** - * pdo - * - * @var PDO + * pdo + * + * @var PDO */ protected $pdo; /** - * PDO table name for 'principals' - * - * @var string + * PDO table name for 'principals' + * + * @var string */ protected $tableName; /** - * PDO table name for 'group members' - * - * @var string + * PDO table name for 'group members' + * + * @var string */ protected $groupMembersTableName; + /** + * A list of additional fields to support + * + * @var array + */ + protected $fieldMap = array( + + /** + * This property can be used to display the users' real name. + */ + '{DAV:}displayname' => array( + 'dbField' => 'displayname', + ), + + /** + * This property is actually used by the CardDAV plugin, where it gets + * mapped to {http://calendarserver.orgi/ns/}me-card. + * + * The reason we don't straight-up use that property, is because + * me-card is defined as a property on the users' addressbook + * collection. + */ + '{http://sabredav.org/ns}vcard-url' => array( + 'dbField' => 'vcardurl', + ), + /** + * This is the users' primary email-address. + */ + '{http://sabredav.org/ns}email-address' => array( + 'dbField' => 'email', + ), + ); + /** * Sets up the backend. - * + * * @param PDO $pdo - * @param string $tableName + * @param string $tableName + * @param string $groupMembersTableName */ public function __construct(PDO $pdo, $tableName = 'principals', $groupMembersTableName = 'groupmembers') { @@ -50,27 +84,35 @@ class Sabre_DAVACL_PrincipalBackend_PDO implements Sabre_DAVACL_IPrincipalBacken $this->tableName = $tableName; $this->groupMembersTableName = $groupMembersTableName; - } + } /** * Returns a list of principals based on a prefix. * - * This prefix will often contain something like 'principals'. You are only + * This prefix will often contain something like 'principals'. You are only * expected to return principals that are in this base path. * - * You are expected to return at least a 'uri' for every user, you can + * You are expected to return at least a 'uri' for every user, you can * return any additional properties if you wish so. Common properties are: - * {DAV:}displayname - * {http://sabredav.org/ns}email-address - This is a custom SabreDAV + * {DAV:}displayname + * {http://sabredav.org/ns}email-address - This is a custom SabreDAV * field that's actualy injected in a number of other properties. If * you have an email address, use this property. - * - * @param string $prefixPath - * @return array + * + * @param string $prefixPath + * @return array */ public function getPrincipalsByPrefix($prefixPath) { - $result = $this->pdo->query('SELECT uri, email, displayname FROM `'. $this->tableName . '`'); + + $fields = array( + 'uri', + ); + + foreach($this->fieldMap as $key=>$value) { + $fields[] = $value['dbField']; + } + $result = $this->pdo->query('SELECT '.implode(',', $fields).' FROM '. $this->tableName); $principals = array(); @@ -80,11 +122,15 @@ class Sabre_DAVACL_PrincipalBackend_PDO implements Sabre_DAVACL_IPrincipalBacken list($rowPrefix) = Sabre_DAV_URLUtil::splitPath($row['uri']); if ($rowPrefix !== $prefixPath) continue; - $principals[] = array( + $principal = array( 'uri' => $row['uri'], - '{DAV:}displayname' => $row['displayname']?$row['displayname']:basename($row['uri']), - '{http://sabredav.org/ns}email-address' => $row['email'], ); + foreach($this->fieldMap as $key=>$value) { + if ($row[$value['dbField']]) { + $principal[$key] = $row[$value['dbField']]; + } + } + $principals[] = $principal; } @@ -94,43 +140,218 @@ class Sabre_DAVACL_PrincipalBackend_PDO implements Sabre_DAVACL_IPrincipalBacken /** * Returns a specific principal, specified by it's path. - * The returned structure should be the exact same as from - * getPrincipalsByPrefix. - * - * @param string $path - * @return array + * The returned structure should be the exact same as from + * getPrincipalsByPrefix. + * + * @param string $path + * @return array */ public function getPrincipalByPath($path) { - $stmt = $this->pdo->prepare('SELECT id, uri, email, displayname FROM `'.$this->tableName.'` WHERE uri = ?'); - $stmt->execute(array($path)); + $fields = array( + 'id', + 'uri', + ); - $users = array(); + foreach($this->fieldMap as $key=>$value) { + $fields[] = $value['dbField']; + } + $stmt = $this->pdo->prepare('SELECT '.implode(',', $fields).' FROM '. $this->tableName . ' WHERE uri = ?'); + $stmt->execute(array($path)); $row = $stmt->fetch(PDO::FETCH_ASSOC); if (!$row) return; - return array( + $principal = array( 'id' => $row['id'], 'uri' => $row['uri'], - '{DAV:}displayname' => $row['displayname']?$row['displayname']:basename($row['uri']), - '{http://sabredav.org/ns}email-address' => $row['email'], ); + foreach($this->fieldMap as $key=>$value) { + if ($row[$value['dbField']]) { + $principal[$key] = $row[$value['dbField']]; + } + } + return $principal; } /** - * Returns the list of members for a group-principal - * - * @param string $principal - * @return array + * Updates one ore more webdav properties on a principal. + * + * The list of mutations is supplied as an array. Each key in the array is + * a propertyname, such as {DAV:}displayname. + * + * Each value is the actual value to be updated. If a value is null, it + * must be deleted. + * + * This method should be atomic. It must either completely succeed, or + * completely fail. Success and failure can simply be returned as 'true' or + * 'false'. + * + * It is also possible to return detailed failure information. In that case + * an array such as this should be returned: + * + * array( + * 200 => array( + * '{DAV:}prop1' => null, + * ), + * 201 => array( + * '{DAV:}prop2' => null, + * ), + * 403 => array( + * '{DAV:}prop3' => null, + * ), + * 424 => array( + * '{DAV:}prop4' => null, + * ), + * ); + * + * In this previous example prop1 was successfully updated or deleted, and + * prop2 was succesfully created. + * + * prop3 failed to update due to '403 Forbidden' and because of this prop4 + * also could not be updated with '424 Failed dependency'. + * + * This last example was actually incorrect. While 200 and 201 could appear + * in 1 response, if there's any error (403) the other properties should + * always fail with 423 (failed dependency). + * + * But anyway, if you don't want to scratch your head over this, just + * return true or false. + * + * @param string $path + * @param array $mutations + * @return array|bool + */ + public function updatePrincipal($path, $mutations) { + + $updateAble = array(); + foreach($mutations as $key=>$value) { + + // We are not aware of this field, we must fail. + if (!isset($this->fieldMap[$key])) { + + $response = array( + 403 => array( + $key => null, + ), + 424 => array(), + ); + + // Adding the rest to the response as a 424 + foreach($mutations as $subKey=>$subValue) { + if ($subKey !== $key) { + $response[424][$subKey] = null; + } + } + return $response; + } + + $updateAble[$this->fieldMap[$key]['dbField']] = $value; + + } + + // No fields to update + $query = "UPDATE " . $this->tableName . " SET "; + + $first = true; + foreach($updateAble as $key => $value) { + if (!$first) { + $query.= ', '; + } + $first = false; + $query.= "$key = :$key "; + } + $query.='WHERE uri = :uri'; + $stmt = $this->pdo->prepare($query); + $updateAble['uri'] = $path; + $stmt->execute($updateAble); + + return true; + + } + + /** + * This method is used to search for principals matching a set of + * properties. + * + * This search is specifically used by RFC3744's principal-property-search + * REPORT. You should at least allow searching on + * http://sabredav.org/ns}email-address. + * + * The actual search should be a unicode-non-case-sensitive search. The + * keys in searchProperties are the WebDAV property names, while the values + * are the property values to search on. + * + * If multiple properties are being searched on, the search should be + * AND'ed. + * + * This method should simply return an array with full principal uri's. + * + * If somebody attempted to search on a property the backend does not + * support, you should simply return 0 results. + * + * You can also just return 0 results if you choose to not support + * searching at all, but keep in mind that this may stop certain features + * from working. + * + * @param string $prefixPath + * @param array $searchProperties + * @return array + */ + public function searchPrincipals($prefixPath, array $searchProperties) { + + $query = 'SELECT uri FROM ' . $this->tableName . ' WHERE 1=1 '; + $values = array(); + foreach($searchProperties as $property => $value) { + + switch($property) { + + case '{DAV:}displayname' : + $query.=' AND displayname LIKE ?'; + $values[] = '%' . $value . '%'; + break; + case '{http://sabredav.org/ns}email-address' : + $query.=' AND email LIKE ?'; + $values[] = '%' . $value . '%'; + break; + default : + // Unsupported property + return array(); + + } + + } + $stmt = $this->pdo->prepare($query); + $stmt->execute($values); + + $principals = array(); + while($row = $stmt->fetch(PDO::FETCH_ASSOC)) { + + // Checking if the principal is in the prefix + list($rowPrefix) = Sabre_DAV_URLUtil::splitPath($row['uri']); + if ($rowPrefix !== $prefixPath) continue; + + $principals[] = $row['uri']; + + } + + return $principals; + + } + + /** + * Returns the list of members for a group-principal + * + * @param string $principal + * @return array */ public function getGroupMemberSet($principal) { $principal = $this->getPrincipalByPath($principal); if (!$principal) throw new Sabre_DAV_Exception('Principal not found'); - $stmt = $this->pdo->prepare('SELECT principals.uri as uri FROM `'.$this->groupMembersTableName.'` AS groupmembers LEFT JOIN `'.$this->tableName.'` AS principals ON groupmembers.member_id = principals.id WHERE groupmembers.principal_id = ?'); + $stmt = $this->pdo->prepare('SELECT principals.uri as uri FROM '.$this->groupMembersTableName.' AS groupmembers LEFT JOIN '.$this->tableName.' AS principals ON groupmembers.member_id = principals.id WHERE groupmembers.principal_id = ?'); $stmt->execute(array($principal['id'])); $result = array(); @@ -138,21 +359,21 @@ class Sabre_DAVACL_PrincipalBackend_PDO implements Sabre_DAVACL_IPrincipalBacken $result[] = $row['uri']; } return $result; - + } /** - * Returns the list of groups a principal is a member of - * - * @param string $principal - * @return array + * Returns the list of groups a principal is a member of + * + * @param string $principal + * @return array */ public function getGroupMembership($principal) { $principal = $this->getPrincipalByPath($principal); if (!$principal) throw new Sabre_DAV_Exception('Principal not found'); - $stmt = $this->pdo->prepare('SELECT principals.uri as uri FROM `'.$this->groupMembersTableName.'` AS groupmembers LEFT JOIN `'.$this->tableName.'` AS principals ON groupmembers.principal_id = principals.id WHERE groupmembers.member_id = ?'); + $stmt = $this->pdo->prepare('SELECT principals.uri as uri FROM '.$this->groupMembersTableName.' AS groupmembers LEFT JOIN '.$this->tableName.' AS principals ON groupmembers.principal_id = principals.id WHERE groupmembers.member_id = ?'); $stmt->execute(array($principal['id'])); $result = array(); @@ -166,16 +387,16 @@ class Sabre_DAVACL_PrincipalBackend_PDO implements Sabre_DAVACL_IPrincipalBacken /** * Updates the list of group members for a group principal. * - * The principals should be passed as a list of uri's. - * - * @param string $principal - * @param array $members + * The principals should be passed as a list of uri's. + * + * @param string $principal + * @param array $members * @return void */ public function setGroupMemberSet($principal, array $members) { // Grabbing the list of principal id's. - $stmt = $this->pdo->prepare('SELECT id, uri FROM `'.$this->tableName.'` WHERE uri IN (? ' . str_repeat(', ? ', count($members)) . ');'); + $stmt = $this->pdo->prepare('SELECT id, uri FROM '.$this->tableName.' WHERE uri IN (? ' . str_repeat(', ? ', count($members)) . ');'); $stmt->execute(array_merge(array($principal), $members)); $memberIds = array(); @@ -191,12 +412,12 @@ class Sabre_DAVACL_PrincipalBackend_PDO implements Sabre_DAVACL_IPrincipalBacken if (!$principalId) throw new Sabre_DAV_Exception('Principal not found'); // Wiping out old members - $stmt = $this->pdo->prepare('DELETE FROM `'.$this->groupMembersTableName.'` WHERE principal_id = ?;'); + $stmt = $this->pdo->prepare('DELETE FROM '.$this->groupMembersTableName.' WHERE principal_id = ?;'); $stmt->execute(array($principalId)); foreach($memberIds as $memberId) { - $stmt = $this->pdo->prepare('INSERT INTO `'.$this->groupMembersTableName.'` (principal_id, member_id) VALUES (?, ?);'); + $stmt = $this->pdo->prepare('INSERT INTO '.$this->groupMembersTableName.' (principal_id, member_id) VALUES (?, ?);'); $stmt->execute(array($principalId, $memberId)); } diff --git a/3rdparty/Sabre/DAVACL/PrincipalCollection.php b/3rdparty/Sabre/DAVACL/PrincipalCollection.php index 4d22bf8aa7..c3e4cb83f2 100644 --- a/3rdparty/Sabre/DAVACL/PrincipalCollection.php +++ b/3rdparty/Sabre/DAVACL/PrincipalCollection.php @@ -7,11 +7,11 @@ * Sabre_DAV_Auth_Backend to determine which users are available on the list. * * The users are instances of Sabre_DAV_Auth_Principal - * + * * @package Sabre * @subpackage DAVACL - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. - * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. + * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ class Sabre_DAVACL_PrincipalCollection extends Sabre_DAVACL_AbstractPrincipalCollection { @@ -22,9 +22,9 @@ class Sabre_DAVACL_PrincipalCollection extends Sabre_DAVACL_AbstractPrincipalCol * The passed array contains principal information, and is guaranteed to * at least contain a uri item. Other properties may or may not be * supplied by the authentication backend. - * - * @param array $principal - * @return Sabre_DAV_INode + * + * @param array $principal + * @return Sabre_DAV_INode */ public function getChildForPrincipal(array $principal) { diff --git a/3rdparty/Sabre/DAVACL/Property/Acl.php b/3rdparty/Sabre/DAVACL/Property/Acl.php index e41e741131..05e1a690b3 100644 --- a/3rdparty/Sabre/DAVACL/Property/Acl.php +++ b/3rdparty/Sabre/DAVACL/Property/Acl.php @@ -1,47 +1,47 @@ item(0)); - if ($principal->getType()!==Sabre_DAVACL_Property_Principal::HREF) { - throw new Sabre_DAV_Exception_NotImplemented('Currently only uri based principals are support, {DAV:}all, {DAV:}unauthenticated and {DAV:}authenticated are not implemented yet'); + switch($principal->getType()) { + case Sabre_DAVACL_Property_Principal::HREF : + $principal = $principal->getHref(); + break; + case Sabre_DAVACL_Property_Principal::AUTHENTICATED : + $principal = '{DAV:}authenticated'; + break; + case Sabre_DAVACL_Property_Principal::UNAUTHENTICATED : + $principal = '{DAV:}unauthenticated'; + break; + case Sabre_DAVACL_Property_Principal::ALL : + $principal = '{DAV:}all'; + break; + } - $principal = $principal->getHref(); $protected = false; if ($xace->getElementsByTagNameNS('urn:DAV','protected')->length > 0) { @@ -140,7 +151,7 @@ class Sabre_DAVACL_Property_Acl extends Sabre_DAV_Property { 'privilege' => $privilegeName, ); - } + } } @@ -149,12 +160,12 @@ class Sabre_DAVACL_Property_Acl extends Sabre_DAV_Property { } /** - * Serializes a single access control entry. - * - * @param DOMDocument $doc - * @param DOMElement $node + * Serializes a single access control entry. + * + * @param DOMDocument $doc + * @param DOMElement $node * @param array $ace - * @param Sabre_DAV_Server $server + * @param Sabre_DAV_Server $server * @return void */ private function serializeAce($doc,$node,$ace, $server) { @@ -164,7 +175,19 @@ class Sabre_DAVACL_Property_Acl extends Sabre_DAV_Property { $principal = $doc->createElementNS('DAV:','d:principal'); $xace->appendChild($principal); - $principal->appendChild($doc->createElementNS('DAV:','d:href',($this->prefixBaseUrl?$server->getBaseUri():'') . $ace['principal'] . '/')); + switch($ace['principal']) { + case '{DAV:}authenticated' : + $principal->appendChild($doc->createElementNS('DAV:','d:authenticated')); + break; + case '{DAV:}unauthenticated' : + $principal->appendChild($doc->createElementNS('DAV:','d:unauthenticated')); + break; + case '{DAV:}all' : + $principal->appendChild($doc->createElementNS('DAV:','d:all')); + break; + default: + $principal->appendChild($doc->createElementNS('DAV:','d:href',($this->prefixBaseUrl?$server->getBaseUri():'') . $ace['principal'] . '/')); + } $grant = $doc->createElementNS('DAV:','d:grant'); $xace->appendChild($grant); diff --git a/3rdparty/Sabre/DAVACL/Property/AclRestrictions.php b/3rdparty/Sabre/DAVACL/Property/AclRestrictions.php new file mode 100644 index 0000000000..a8b054956d --- /dev/null +++ b/3rdparty/Sabre/DAVACL/Property/AclRestrictions.php @@ -0,0 +1,32 @@ +ownerDocument; + + $elem->appendChild($doc->createElementNS('DAV:','d:grant-only')); + $elem->appendChild($doc->createElementNS('DAV:','d:no-invert')); + + } + +} diff --git a/3rdparty/Sabre/DAVACL/Property/CurrentUserPrivilegeSet.php b/3rdparty/Sabre/DAVACL/Property/CurrentUserPrivilegeSet.php index 72274597b3..94a2964061 100644 --- a/3rdparty/Sabre/DAVACL/Property/CurrentUserPrivilegeSet.php +++ b/3rdparty/Sabre/DAVACL/Property/CurrentUserPrivilegeSet.php @@ -2,31 +2,31 @@ /** * CurrentUserPrivilegeSet - * - * This class represents the current-user-privilege-set property. When - * requested, it contain all the privileges a user has on a specific node. - * + * + * This class represents the current-user-privilege-set property. When + * requested, it contain all the privileges a user has on a specific node. + * * @package Sabre * @subpackage DAVACL - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. - * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. + * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ class Sabre_DAVACL_Property_CurrentUserPrivilegeSet extends Sabre_DAV_Property { /** - * List of privileges - * - * @var array + * List of privileges + * + * @var array */ private $privileges; /** * Creates the object * - * Pass the privileges in clark-notation - * - * @param array $privileges + * Pass the privileges in clark-notation + * + * @param array $privileges */ public function __construct(array $privileges) { @@ -35,10 +35,10 @@ class Sabre_DAVACL_Property_CurrentUserPrivilegeSet extends Sabre_DAV_Property { } /** - * Serializes the property in the DOM - * - * @param Sabre_DAV_Server $server - * @param DOMElement $node + * Serializes the property in the DOM + * + * @param Sabre_DAV_Server $server + * @param DOMElement $node * @return void */ public function serialize(Sabre_DAV_Server $server,DOMElement $node) { @@ -53,11 +53,11 @@ class Sabre_DAVACL_Property_CurrentUserPrivilegeSet extends Sabre_DAV_Property { } /** - * Serializes one privilege - * - * @param DOMDocument $doc - * @param DOMElement $node - * @param string $privName + * Serializes one privilege + * + * @param DOMDocument $doc + * @param DOMElement $node + * @param string $privName * @return void */ protected function serializePriv($doc,$node,$privName) { diff --git a/3rdparty/Sabre/DAVACL/Property/Principal.php b/3rdparty/Sabre/DAVACL/Property/Principal.php index dad9a3550f..c36328a58e 100644 --- a/3rdparty/Sabre/DAVACL/Property/Principal.php +++ b/3rdparty/Sabre/DAVACL/Property/Principal.php @@ -4,12 +4,12 @@ * Principal property * * The principal property represents a principal from RFC3744 (ACL). - * The property can be used to specify a principal or pseudo principals. + * The property can be used to specify a principal or pseudo principals. * * @package Sabre * @subpackage DAVACL - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. - * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. + * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ class Sabre_DAVACL_Property_Principal extends Sabre_DAV_Property implements Sabre_DAV_Property_IHref { @@ -25,16 +25,21 @@ class Sabre_DAVACL_Property_Principal extends Sabre_DAV_Property implements Sabr const AUTHENTICATED = 2; /** - * Specific princpals can be specified with the HREF + * Specific principals can be specified with the HREF */ const HREF = 3; + /** + * Everybody, basically + */ + const ALL = 4; + /** * Principal-type * * Must be one of the UNAUTHENTICATED, AUTHENTICATED or HREF constants. - * - * @var int + * + * @var int */ private $type; @@ -42,8 +47,8 @@ class Sabre_DAVACL_Property_Principal extends Sabre_DAV_Property implements Sabr * Url to principal * * This value is only used for the HREF principal type. - * - * @var string + * + * @var string */ private $href; @@ -53,10 +58,9 @@ class Sabre_DAVACL_Property_Principal extends Sabre_DAV_Property implements Sabr * The 'type' argument must be one of the type constants defined in this class. * * 'href' is only required for the HREF type. - * - * @param int $type - * @param string $href - * @return void + * + * @param int $type + * @param string|null $href */ public function __construct($type, $href = null) { @@ -70,9 +74,9 @@ class Sabre_DAVACL_Property_Principal extends Sabre_DAV_Property implements Sabr } /** - * Returns the principal type - * - * @return int + * Returns the principal type + * + * @return int */ public function getType() { @@ -81,8 +85,8 @@ class Sabre_DAVACL_Property_Principal extends Sabre_DAV_Property implements Sabr } /** - * Returns the principal uri. - * + * Returns the principal uri. + * * @return string */ public function getHref() { @@ -92,10 +96,10 @@ class Sabre_DAVACL_Property_Principal extends Sabre_DAV_Property implements Sabr } /** - * Serializes the property into a DOMElement. - * - * @param Sabre_DAV_Server $server - * @param DOMElement $node + * Serializes the property into a DOMElement. + * + * @param Sabre_DAV_Server $server + * @param DOMElement $node * @return void */ public function serialize(Sabre_DAV_Server $server, DOMElement $node) { @@ -124,10 +128,10 @@ 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 + * Deserializes a DOM element into a property object. + * + * @param DOMElement $dom + * @return Sabre_DAV_Property_Principal */ static public function unserialize(DOMElement $dom) { @@ -144,6 +148,8 @@ class Sabre_DAVACL_Property_Principal extends Sabre_DAV_Property implements Sabr return new self(self::AUTHENTICATED); case '{DAV:}href': return new self(self::HREF, $parent->textContent); + case '{DAV:}all': + return new self(self::ALL); default : throw new Sabre_DAV_Exception_BadRequest('Unexpected element (' . Sabre_DAV_XMLUtil::toClarkNotation($parent) . '). Could not deserialize'); diff --git a/3rdparty/Sabre/DAVACL/Property/SupportedPrivilegeSet.php b/3rdparty/Sabre/DAVACL/Property/SupportedPrivilegeSet.php index 93c3895035..276d57ae09 100644 --- a/3rdparty/Sabre/DAVACL/Property/SupportedPrivilegeSet.php +++ b/3rdparty/Sabre/DAVACL/Property/SupportedPrivilegeSet.php @@ -3,32 +3,32 @@ /** * SupportedPrivilegeSet property * - * This property encodes the {DAV:}supported-privilege-set property, as defined + * This property encodes the {DAV:}supported-privilege-set property, as defined * in rfc3744. Please consult the rfc for details about it's structure. * - * This class expects a structure like the one given from - * Sabre_DAVACL_Plugin::getSupportedPrivilegeSet as the argument in its + * This class expects a structure like the one given from + * Sabre_DAVACL_Plugin::getSupportedPrivilegeSet as the argument in its * constructor. - * + * * @package Sabre * @subpackage DAVACL - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. - * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. + * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ class Sabre_DAVACL_Property_SupportedPrivilegeSet extends Sabre_DAV_Property { /** - * privileges - * - * @var array + * privileges + * + * @var array */ private $privileges; /** - * Constructor - * - * @param array $privileges + * Constructor + * + * @param array $privileges */ public function __construct(array $privileges) { @@ -37,10 +37,10 @@ class Sabre_DAVACL_Property_SupportedPrivilegeSet extends Sabre_DAV_Property { } /** - * Serializes the property into a domdocument. - * - * @param Sabre_DAV_Server $server - * @param DOMElement $node + * Serializes the property into a domdocument. + * + * @param Sabre_DAV_Server $server + * @param DOMElement $node * @return void */ public function serialize(Sabre_DAV_Server $server,DOMElement $node) { @@ -53,11 +53,11 @@ class Sabre_DAVACL_Property_SupportedPrivilegeSet extends Sabre_DAV_Property { /** * Serializes a property * - * This is a recursive function. - * - * @param DOMDocument $doc - * @param DOMElement $node - * @param array $privilege + * This is a recursive function. + * + * @param DOMDocument $doc + * @param DOMElement $node + * @param array $privilege * @return void */ private function serializePriv($doc,$node,$privilege) { @@ -81,7 +81,7 @@ class Sabre_DAVACL_Property_SupportedPrivilegeSet extends Sabre_DAV_Property { $xsp->appendChild($doc->createElementNS('DAV:','d:description',$privilege['description'])); } - if (isset($privilege['aggregates'])) { + if (isset($privilege['aggregates'])) { foreach($privilege['aggregates'] as $subPrivilege) { $this->serializePriv($doc,$xsp,$subPrivilege); } diff --git a/3rdparty/Sabre/DAVACL/Version.php b/3rdparty/Sabre/DAVACL/Version.php index 124463e311..9950f74874 100644 --- a/3rdparty/Sabre/DAVACL/Version.php +++ b/3rdparty/Sabre/DAVACL/Version.php @@ -2,10 +2,10 @@ /** * This class contains the SabreDAV version constants. - * + * * @package Sabre * @subpackage DAVACL - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. + * @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 */ @@ -14,7 +14,7 @@ class Sabre_DAVACL_Version { /** * Full version number */ - const VERSION = '1.5.2'; + const VERSION = '1.6.0'; /** * Stability : alpha, beta, stable diff --git a/3rdparty/Sabre/DAVACL/includes.php b/3rdparty/Sabre/DAVACL/includes.php new file mode 100644 index 0000000000..28fa3eed22 --- /dev/null +++ b/3rdparty/Sabre/DAVACL/includes.php @@ -0,0 +1,38 @@ +httpRequest->getBody(true); $this->httpRequest->setBody($body,true); - + if ($contentMD5!=base64_encode(md5($body,true))) { // content-md5 header did not match md5 signature of body $this->errorCode = self::ERR_MD5CHECKSUMWRONG; @@ -98,10 +99,10 @@ class Sabre_HTTP_AWSAuth extends Sabre_HTTP_AbstractAuth { } - if (!$requestDate = $this->httpRequest->getHeader('x-amz-date')) + if (!$requestDate = $this->httpRequest->getHeader('x-amz-date')) $requestDate = $this->httpRequest->getHeader('Date'); - if (!$this->validateRFC2616Date($requestDate)) + if (!$this->validateRFC2616Date($requestDate)) return false; $amzHeaders = $this->getAmzHeaders(); @@ -109,10 +110,10 @@ class Sabre_HTTP_AWSAuth extends Sabre_HTTP_AbstractAuth { $signature = base64_encode( $this->hmacsha1($secretKey, $this->httpRequest->getMethod() . "\n" . - $contentMD5 . "\n" . + $contentMD5 . "\n" . $this->httpRequest->getHeader('Content-type') . "\n" . $requestDate . "\n" . - $amzHeaders . + $amzHeaders . $this->httpRequest->getURI() ) ); @@ -146,14 +147,14 @@ class Sabre_HTTP_AWSAuth extends Sabre_HTTP_AbstractAuth { /** * Makes sure the supplied value is a valid RFC2616 date. * - * If we would just use strtotime to get a valid timestamp, we have no way of checking if a + * If we would just use strtotime to get a valid timestamp, we have no way of checking if a * user just supplied the word 'now' for the date header. * - * This function also makes sure the Date header is within 15 minutes of the operating + * This function also makes sure the Date header is within 15 minutes of the operating * system date, to prevent replay attacks. - * - * @param string $dateHeader - * @return bool + * + * @param string $dateHeader + * @return bool */ protected function validateRFC2616Date($dateHeader) { @@ -177,11 +178,11 @@ class Sabre_HTTP_AWSAuth extends Sabre_HTTP_AbstractAuth { return $date; } - + /** - * Returns a list of AMZ headers - * - * @return void + * Returns a list of AMZ headers + * + * @return string */ protected function getAmzHeaders() { @@ -193,7 +194,7 @@ class Sabre_HTTP_AWSAuth extends Sabre_HTTP_AbstractAuth { } } ksort($amzHeaders); - + $headerStr = ''; foreach($amzHeaders as $h=>$v) { $headerStr.=$h.':'.$v; @@ -204,11 +205,11 @@ class Sabre_HTTP_AWSAuth extends Sabre_HTTP_AbstractAuth { } /** - * Generates an HMAC-SHA1 signature - * - * @param string $key - * @param string $message - * @return string + * Generates an HMAC-SHA1 signature + * + * @param string $key + * @param string $message + * @return string */ private function hmacsha1($key, $message) { diff --git a/3rdparty/Sabre/HTTP/AbstractAuth.php b/3rdparty/Sabre/HTTP/AbstractAuth.php index eb528f6fde..3bccabcd1c 100644 --- a/3rdparty/Sabre/HTTP/AbstractAuth.php +++ b/3rdparty/Sabre/HTTP/AbstractAuth.php @@ -4,10 +4,10 @@ * HTTP Authentication baseclass * * This class has the common functionality for BasicAuth and DigestAuth - * + * * @package Sabre - * @subpackage HTTP - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. + * @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 */ @@ -17,29 +17,29 @@ abstract class Sabre_HTTP_AbstractAuth { * The realm will be displayed in the dialog boxes * * This identifier can be changed through setRealm() - * + * * @var string */ protected $realm = 'SabreDAV'; /** - * HTTP response helper - * - * @var Sabre_HTTP_Response + * HTTP response helper + * + * @var Sabre_HTTP_Response */ protected $httpResponse; /** - * HTTP request helper - * - * @var Sabre_HTTP_Request + * HTTP request helper + * + * @var Sabre_HTTP_Request */ protected $httpRequest; /** - * __construct - * + * __construct + * */ public function __construct() { @@ -49,9 +49,9 @@ abstract class Sabre_HTTP_AbstractAuth { } /** - * Sets an alternative HTTP response object - * - * @param Sabre_HTTP_Response $response + * Sets an alternative HTTP response object + * + * @param Sabre_HTTP_Response $response * @return void */ public function setHTTPResponse(Sabre_HTTP_Response $response) { @@ -61,9 +61,9 @@ abstract class Sabre_HTTP_AbstractAuth { } /** - * Sets an alternative HTTP request object - * - * @param Sabre_HTTP_Request $request + * Sets an alternative HTTP request object + * + * @param Sabre_HTTP_Request $request * @return void */ public function setHTTPRequest(Sabre_HTTP_Request $request) { @@ -78,8 +78,8 @@ abstract class Sabre_HTTP_AbstractAuth { * * The realm is often displayed in authentication dialog boxes * Commonly an application name displayed here - * - * @param string $realm + * + * @param string $realm * @return void */ public function setRealm($realm) { @@ -91,7 +91,7 @@ abstract class Sabre_HTTP_AbstractAuth { /** * Returns the realm * - * @return string + * @return string */ public function getRealm() { @@ -106,6 +106,6 @@ abstract class Sabre_HTTP_AbstractAuth { * * @return void */ - abstract public function requireLogin(); + abstract public function requireLogin(); } diff --git a/3rdparty/Sabre/HTTP/BasicAuth.php b/3rdparty/Sabre/HTTP/BasicAuth.php index 35c22d22dc..a747cc6a31 100644 --- a/3rdparty/Sabre/HTTP/BasicAuth.php +++ b/3rdparty/Sabre/HTTP/BasicAuth.php @@ -4,11 +4,11 @@ * HTTP Basic Authentication handler * * Use this class for easy http authentication setup - * + * * @package Sabre - * @subpackage HTTP - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. - * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @subpackage HTTP + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. + * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ class Sabre_HTTP_BasicAuth extends Sabre_HTTP_AbstractAuth { @@ -22,7 +22,7 @@ class Sabre_HTTP_BasicAuth extends Sabre_HTTP_AbstractAuth { * * If nothing was supplied, 'false' will be returned * - * @return mixed + * @return mixed */ public function getUserPass() { @@ -33,12 +33,18 @@ class Sabre_HTTP_BasicAuth extends Sabre_HTTP_AbstractAuth { } - // Most other webservers + // Most other webservers $auth = $this->httpRequest->getHeader('Authorization'); + // Apache could prefix environment variables with REDIRECT_ when urls + // are passed through mod_rewrite + if (!$auth) { + $auth = $this->httpRequest->getRawServerValue('REDIRECT_HTTP_AUTHORIZATION'); + } + if (!$auth) return false; - if (strpos(strtolower($auth),'basic')!==0) return false; + if (strpos(strtolower($auth),'basic')!==0) return false; return explode(':', base64_decode(substr($auth, 6))); diff --git a/3rdparty/Sabre/HTTP/DigestAuth.php b/3rdparty/Sabre/HTTP/DigestAuth.php index 5e75592957..ee7f05c08e 100644 --- a/3rdparty/Sabre/HTTP/DigestAuth.php +++ b/3rdparty/Sabre/HTTP/DigestAuth.php @@ -10,18 +10,18 @@ * 2. Call the setRealm() method with the realm you plan to use * 3. Call the init method function. * 4. Call the getUserName() function. This function may return false if no - * authentication information was supplied. Based on the username you + * authentication information was supplied. Based on the username you * should check your internal database for either the associated password, * or the so-called A1 hash of the digest. * 5. Call either validatePassword() or validateA1(). This will return true - * or false. + * or false. * 6. To make sure an authentication prompt is displayed, call the * requireLogin() method. - * - * + * + * * @package Sabre - * @subpackage HTTP - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. + * @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 */ @@ -40,7 +40,7 @@ class Sabre_HTTP_DigestAuth extends Sabre_HTTP_AbstractAuth { protected $qop = self::QOP_AUTH; /** - * Initializes the object + * Initializes the object */ public function __construct() { @@ -54,7 +54,7 @@ class Sabre_HTTP_DigestAuth extends Sabre_HTTP_AbstractAuth { * Gathers all information from the headers * * This method needs to be called prior to anything else. - * + * * @return void */ public function init() { @@ -73,11 +73,11 @@ class Sabre_HTTP_DigestAuth extends Sabre_HTTP_AbstractAuth { * * Multiple values can be specified using logical OR. * - * QOP_AUTHINT ensures integrity of the request body, but this is not - * supported by most HTTP clients. QOP_AUTHINT also requires the entire + * QOP_AUTHINT ensures integrity of the request body, but this is not + * supported by most HTTP clients. QOP_AUTHINT also requires the entire * request body to be md5'ed, which can put strains on CPU and memory. * - * @param int $qop + * @param int $qop * @return void */ public function setQOP($qop) { @@ -91,8 +91,8 @@ class Sabre_HTTP_DigestAuth extends Sabre_HTTP_AbstractAuth { * * The A1 parameter should be md5($username . ':' . $realm . ':' . $password); * - * @param string $A1 - * @return bool + * @param string $A1 + * @return bool */ public function validateA1($A1) { @@ -104,9 +104,9 @@ class Sabre_HTTP_DigestAuth extends Sabre_HTTP_AbstractAuth { /** * Validates authentication through a password. The actual password must be provided here. * It is strongly recommended not store the password in plain-text and use validateA1 instead. - * - * @param string $password - * @return bool + * + * @param string $password + * @return bool */ public function validatePassword($password) { @@ -116,9 +116,9 @@ class Sabre_HTTP_DigestAuth extends Sabre_HTTP_AbstractAuth { } /** - * Returns the username for the request - * - * @return string + * Returns the username for the request + * + * @return string */ public function getUsername() { @@ -127,14 +127,14 @@ class Sabre_HTTP_DigestAuth extends Sabre_HTTP_AbstractAuth { } /** - * Validates the digest challenge - * - * @return bool + * Validates the digest challenge + * + * @return bool */ protected function validate() { $A2 = $this->httpRequest->getMethod() . ':' . $this->digestParts['uri']; - + if ($this->digestParts['qop']=='auth-int') { // Making sure we support this qop value if (!($this->qop & self::QOP_AUTHINT)) return false; @@ -144,16 +144,16 @@ class Sabre_HTTP_DigestAuth extends Sabre_HTTP_AbstractAuth { $A2 .= ':' . md5($body); } else { - // We need to make sure we support this qop value - if (!($this->qop & self::QOP_AUTH)) return false; + // We need to make sure we support this qop value + if (!($this->qop & self::QOP_AUTH)) return false; } $A2 = md5($A2); - $validResponse = md5("{$this->A1}:{$this->digestParts['nonce']}:{$this->digestParts['nc']}:{$this->digestParts['cnonce']}:{$this->digestParts['qop']}:{$A2}"); + $validResponse = md5("{$this->A1}:{$this->digestParts['nonce']}:{$this->digestParts['nc']}:{$this->digestParts['cnonce']}:{$this->digestParts['qop']}:{$A2}"); return $this->digestParts['response']==$validResponse; - + } @@ -186,7 +186,7 @@ class Sabre_HTTP_DigestAuth extends Sabre_HTTP_AbstractAuth { * * If the header could not be found, null will be returned * - * @return mixed + * @return mixed */ public function getDigest() { @@ -197,6 +197,12 @@ class Sabre_HTTP_DigestAuth extends Sabre_HTTP_AbstractAuth { // most other servers $digest = $this->httpRequest->getHeader('Authorization'); + // Apache could prefix environment variables with REDIRECT_ when urls + // are passed through mod_rewrite + if (!$digest) { + $digest = $this->httpRequest->getRawServerValue('REDIRECT_HTTP_AUTHORIZATION'); + } + if ($digest && strpos(strtolower($digest),'digest')===0) { return substr($digest,7); } else { @@ -208,11 +214,11 @@ class Sabre_HTTP_DigestAuth extends Sabre_HTTP_AbstractAuth { /** * Parses the different pieces of the digest string into an array. - * + * * This method returns false if an incomplete digest was supplied * - * @param string $digest - * @return mixed + * @param string $digest + * @return mixed */ protected function parseDigest($digest) { @@ -227,7 +233,7 @@ class Sabre_HTTP_DigestAuth extends Sabre_HTTP_AbstractAuth { unset($needed_parts[$m[1]]); } - return $needed_parts ? false : $data; + return $needed_parts ? false : $data; } diff --git a/3rdparty/Sabre/HTTP/Request.php b/3rdparty/Sabre/HTTP/Request.php index 95a64171aa..4746ef7770 100644 --- a/3rdparty/Sabre/HTTP/Request.php +++ b/3rdparty/Sabre/HTTP/Request.php @@ -6,74 +6,85 @@ * This object can be used to easily access information about an HTTP request. * It can additionally be used to create 'mock' requests. * - * This class mostly operates indepentend, but because of the nature of a single - * request per run it can operate as a singleton. For more information check out + * This class mostly operates independent, but because of the nature of a single + * request per run it can operate as a singleton. For more information check out * the behaviour around 'defaultInputStream'. * * @package Sabre - * @subpackage HTTP - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. - * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @subpackage HTTP + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. + * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ class Sabre_HTTP_Request { /** * PHP's $_SERVER data - * - * @var string + * + * @var array */ protected $_SERVER; + /** + * PHP's $_POST data + * + * @var array + */ + protected $_POST; + /** * The request body, if any. * * This is stored in the form of a stream resource. * - * @var resource + * @var resource */ protected $body = null; /** * This will be set as the 'default' inputStream for a specific HTTP request - * We sometimes need to retain, or rebuild this if we need multiple runs + * We sometimes need to retain, or rebuild this if we need multiple runs * of parsing the original HTTP request. - * - * @var resource + * + * @var resource */ static $defaultInputStream=null; /** * Sets up the object * - * The serverData array can be used to override usage of PHP's - * global _SERVER variable. - * - * @param array $serverData + * The serverData and postData array can be used to override usage of PHP's + * global _SERVER and _POST variable respectively. + * + * @param array $serverData + * @param array $postData */ - public function __construct($serverData = null) { + public function __construct(array $serverData = null, array $postData = null) { if ($serverData) $this->_SERVER = $serverData; else $this->_SERVER =& $_SERVER; + if ($postData) $this->_POST = $postData; + else $this->_POST =& $_POST; + } /** * Returns the value for a specific http header. * * This method returns null if the header did not exist. - * - * @param string $name - * @return string + * + * @param string $name + * @return string */ public function getHeader($name) { $name = strtoupper(str_replace(array('-'),array('_'),$name)); if (isset($this->_SERVER['HTTP_' . $name])) { return $this->_SERVER['HTTP_' . $name]; - } + } - // There's a few headers that seem to end up in the top-level + // There's a few headers that seem to end up in the top-level // server array. switch($name) { case 'CONTENT_TYPE' : @@ -92,9 +103,9 @@ class Sabre_HTTP_Request { * Returns all (known) HTTP headers. * * All headers are converted to lower-case, and additionally all underscores - * are automatically converted to dashes - * - * @return array + * are automatically converted to dashes + * + * @return array */ public function getHeaders() { @@ -122,9 +133,9 @@ class Sabre_HTTP_Request { /** * Returns the HTTP request method * - * This is for example POST or GET + * This is for example POST or GET * - * @return string + * @return string */ public function getMethod() { @@ -135,18 +146,18 @@ class Sabre_HTTP_Request { /** * Returns the requested uri * - * @return string + * @return string */ public function getUri() { - + return $this->_SERVER['REQUEST_URI']; } /** - * Will return protocol + the hostname + the uri - * - * @return void + * Will return protocol + the hostname + the uri + * + * @return string */ public function getAbsoluteUri() { @@ -157,9 +168,9 @@ class Sabre_HTTP_Request { } /** - * Returns everything after the ? from the current url - * - * @return string + * Returns everything after the ? from the current url + * + * @return string */ public function getQueryString() { @@ -168,13 +179,13 @@ class Sabre_HTTP_Request { } /** - * Returns the HTTP request body body + * Returns the HTTP request body body * * This method returns a readable stream resource. - * If the asString parameter is set to true, a string is sent instead. + * If the asString parameter is set to true, a string is sent instead. * * @param bool asString - * @return resource + * @return resource */ public function getBody($asString = false) { @@ -196,14 +207,14 @@ class Sabre_HTTP_Request { } /** - * Sets the contents of the HTTP request body - * + * Sets the contents of the HTTP request body + * * This method can either accept a string, or a readable stream resource. * - * If the setAsDefaultInputStream is set to true, it means for this run of the + * If the setAsDefaultInputStream is set to true, it means for this run of the * script the supplied body will be used instead of php://input. * - * @param mixed $body + * @param mixed $body * @param bool $setAsDefaultInputStream * @return void */ @@ -226,12 +237,26 @@ class Sabre_HTTP_Request { } /** - * Returns a specific item from the _SERVER array. + * Returns PHP's _POST variable. + * + * The reason this is in a method is so it can be subclassed and + * overridden. + * + * @return array + */ + public function getPostVars() { + + return $this->_POST; + + } + + /** + * Returns a specific item from the _SERVER array. * * Do not rely on this feature, it is for internal use only. * - * @param string $field - * @return string + * @param string $field + * @return string */ public function getRawServerValue($field) { diff --git a/3rdparty/Sabre/HTTP/Response.php b/3rdparty/Sabre/HTTP/Response.php index dce6feac55..ffe9bda208 100644 --- a/3rdparty/Sabre/HTTP/Response.php +++ b/3rdparty/Sabre/HTTP/Response.php @@ -1,20 +1,20 @@ 'Locked', // RFC 4918 424 => 'Failed Dependency', // RFC 4918 426 => 'Upgrade required', + 428 => 'Precondition required', // draft-nottingham-http-new-status + 429 => 'Too Many Requests', // draft-nottingham-http-new-status + 431 => 'Request Header Fields Too Large', // draft-nottingham-http-new-status 500 => 'Internal Server Error', 501 => 'Not Implemented', 502 => 'Bad Gateway', @@ -71,25 +74,26 @@ class Sabre_HTTP_Response { 504 => 'Gateway Timeout', 505 => 'HTTP Version not supported', 506 => 'Variant Also Negotiates', - 507 => 'Unsufficient Storage', // RFC 4918 + 507 => 'Insufficient Storage', // RFC 4918 508 => 'Loop Detected', // RFC 5842 509 => 'Bandwidth Limit Exceeded', // non-standard 510 => 'Not extended', - ); + 511 => 'Network Authentication Required', // draft-nottingham-http-new-status + ); return 'HTTP/1.1 ' . $code . ' ' . $msg[$code]; } /** - * Sends an HTTP status header to the client - * - * @param int $code HTTP status code - * @return void + * Sends an HTTP status header to the client + * + * @param int $code HTTP status code + * @return bool */ public function sendStatus($code) { - if (!headers_sent()) + if (!headers_sent()) return header($this->getStatusMessage($code)); else return false; @@ -97,15 +101,16 @@ class Sabre_HTTP_Response { /** * Sets an HTTP header for the response - * - * @param string $name - * @param string $value - * @return void + * + * @param string $name + * @param string $value + * @param bool $replace + * @return bool */ public function setHeader($name, $value, $replace = true) { $value = str_replace(array("\r","\n"),array('\r','\n'),$value); - if (!headers_sent()) + if (!headers_sent()) return header($name . ': ' . $value, $replace); else return false; @@ -115,8 +120,8 @@ class Sabre_HTTP_Response { * Sets a bunch of HTTP Headers * * headersnames are specified as keys, value in the array value - * - * @param array $headers + * + * @param array $headers * @return void */ public function setHeaders(array $headers) { @@ -130,14 +135,14 @@ class Sabre_HTTP_Response { * Sends the entire response body * * This method can accept either an open filestream, or a string. - * - * @param mixed $body + * + * @param mixed $body * @return void */ public function sendBody($body) { if (is_resource($body)) { - + fpassthru($body); } else { diff --git a/3rdparty/Sabre/HTTP/Util.php b/3rdparty/Sabre/HTTP/Util.php index 8a6bd7df48..67bdd489e1 100644 --- a/3rdparty/Sabre/HTTP/Util.php +++ b/3rdparty/Sabre/HTTP/Util.php @@ -1,11 +1,11 @@ = 0) return new DateTime('@' . $realDate, new DateTimeZone('UTC')); - return false; + } + + /** + * Transforms a DateTime object to HTTP's most common date format. + * + * We're serializing it as the RFC 1123 date, which, for HTTP must be + * specified as GMT. + * + * @param DateTime $dateTime + * @return string + */ + static function toHTTPDate(DateTime $dateTime) { + + // We need to clone it, as we don't want to affect the existing + // DateTime. + $dateTime = clone $dateTime; + $dateTime->setTimeZone(new DateTimeZone('GMT')); + return $dateTime->format('D, d M Y H:i:s \G\M\T'); } diff --git a/3rdparty/Sabre/HTTP/Version.php b/3rdparty/Sabre/HTTP/Version.php index 67be232fc2..23dc7f8a7a 100644 --- a/3rdparty/Sabre/HTTP/Version.php +++ b/3rdparty/Sabre/HTTP/Version.php @@ -2,10 +2,10 @@ /** * This class contains the Sabre_HTTP version constants. - * + * * @package Sabre - * @subpackage HTTP - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. + * @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 */ @@ -14,7 +14,7 @@ class Sabre_HTTP_Version { /** * Full version number */ - const VERSION = '1.5.3'; + const VERSION = '1.6.2'; /** * Stability : alpha, beta, stable diff --git a/3rdparty/Sabre/HTTP/includes.php b/3rdparty/Sabre/HTTP/includes.php new file mode 100644 index 0000000000..9d34bf3a8b --- /dev/null +++ b/3rdparty/Sabre/HTTP/includes.php @@ -0,0 +1,27 @@ + '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 + * By default this object will iterate over its own children, but this can * be overridden with the iterator argument - * - * @param string $name + * + * @param string $name * @param Sabre_VObject_ElementList $iterator */ public function __construct($name, Sabre_VObject_ElementList $iterator = null) { @@ -47,23 +81,65 @@ class Sabre_VObject_Component extends Sabre_VObject_Element { } /** - * Turns the object back into a serialized blob. - * - * @return string + * 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 componenten or element + * Adds a new component or element * * You can call this method with the following syntaxes: * @@ -71,10 +147,10 @@ class Sabre_VObject_Component extends Sabre_VObject_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 + * The second adds a property as a string. + * + * @param mixed $item + * @param mixed $itemValue * @return void */ public function add($item, $itemValue = null) { @@ -90,12 +166,12 @@ class Sabre_VObject_Component extends Sabre_VObject_Element { if (!is_scalar($itemValue)) { throw new InvalidArgumentException('The second argument must be scalar'); } - $item = new Sabre_VObject_Property($item,$itemValue); + $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'); } @@ -103,9 +179,9 @@ class Sabre_VObject_Component extends Sabre_VObject_Element { } /** - * Returns an iterable list of children - * - * @return Sabre_VObject_ElementList + * Returns an iterable list of children + * + * @return Sabre_VObject_ElementList */ public function children() { @@ -116,18 +192,18 @@ class Sabre_VObject_Component extends Sabre_VObject_Element { /** * 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 + * 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. + * Keys are retained from the 'children' array, which may be confusing in + * certain cases. * - * @param string $name - * @return array + * @param string $name + * @return array */ public function select($name) { @@ -144,7 +220,7 @@ class Sabre_VObject_Component extends Sabre_VObject_Element { strtoupper($child->name) === $name && (is_null($group) || ( $child instanceof Sabre_VObject_Property && strtoupper($child->group) === $group)) ) { - + $result[$key] = $child; } @@ -155,16 +231,35 @@ class Sabre_VObject_Component extends Sabre_VObject_Element { } + /** + * 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 propery or component, + * 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 void + * + * @param string $name + * @return Sabre_VObject_Property */ public function __get($name) { @@ -173,6 +268,7 @@ class Sabre_VObject_Component extends Sabre_VObject_Element { return null; } else { $firstMatch = current($matches); + /** @var $firstMatch Sabre_VObject_Property */ $firstMatch->setIterator(new Sabre_VObject_ElementList(array_values($matches))); return $firstMatch; } @@ -180,10 +276,10 @@ class Sabre_VObject_Component extends Sabre_VObject_Element { } /** - * This method checks if a sub-element with the specified name exists. - * - * @param string $name - * @return bool + * This method checks if a sub-element with the specified name exists. + * + * @param string $name + * @return bool */ public function __isset($name) { @@ -200,7 +296,7 @@ class Sabre_VObject_Component extends Sabre_VObject_Element { * * 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 @@ -218,7 +314,7 @@ class Sabre_VObject_Component extends Sabre_VObject_Element { $this->children[] = $value; } } elseif (is_scalar($value)) { - $property = new Sabre_VObject_Property($name,$value); + $property = Sabre_VObject_Property::create($name,$value); $property->parent = $this; if (!is_null($overWrite)) { $this->children[$overWrite] = $property; @@ -232,9 +328,9 @@ class Sabre_VObject_Component extends Sabre_VObject_Element { } /** - * Removes all properties and components within this component. - * - * @param string $name + * Removes all properties and components within this component. + * + * @param string $name * @return void */ public function __unset($name) { @@ -251,4 +347,19 @@ class Sabre_VObject_Component extends Sabre_VObject_Element { /* }}} */ + /** + * This method is automatically called when the object is cloned. + * Specifically, this will ensure all child elements are also cloned. + * + * @return void + */ + public function __clone() { + + foreach($this->children as $key=>$child) { + $this->children[$key] = clone $child; + $this->children[$key]->parent = $this; + } + + } + } diff --git a/3rdparty/Sabre/VObject/Component/VAlarm.php b/3rdparty/Sabre/VObject/Component/VAlarm.php new file mode 100644 index 0000000000..ebb4a9b18f --- /dev/null +++ b/3rdparty/Sabre/VObject/Component/VAlarm.php @@ -0,0 +1,102 @@ +TRIGGER; + if(!isset($trigger['VALUE']) || strtoupper($trigger['VALUE']) === 'DURATION') { + $triggerDuration = Sabre_VObject_DateTimeParser::parseDuration($this->TRIGGER); + $related = (isset($trigger['RELATED']) && strtoupper($trigger['RELATED']) == 'END') ? 'END' : 'START'; + + $parentComponent = $this->parent; + if ($related === 'START') { + $effectiveTrigger = clone $parentComponent->DTSTART->getDateTime(); + $effectiveTrigger->add($triggerDuration); + } else { + if ($parentComponent->name === 'VTODO') { + $endProp = 'DUE'; + } elseif ($parentComponent->name === 'VEVENT') { + $endProp = 'DTEND'; + } else { + throw new Sabre_DAV_Exception('time-range filters on VALARM components are only supported when they are a child of VTODO or VEVENT'); + } + + if (isset($parentComponent->$endProp)) { + $effectiveTrigger = clone $parentComponent->$endProp->getDateTime(); + $effectiveTrigger->add($triggerDuration); + } elseif (isset($parentComponent->DURATION)) { + $effectiveTrigger = clone $parentComponent->DTSTART->getDateTime(); + $duration = Sabre_VObject_DateTimeParser::parseDuration($parentComponent->DURATION); + $effectiveTrigger->add($duration); + $effectiveTrigger->add($triggerDuration); + } else { + $effectiveTrigger = clone $parentComponent->DTSTART->getDateTime(); + $effectiveTrigger->add($triggerDuration); + } + } + } else { + $effectiveTrigger = $trigger->getDateTime(); + } + return $effectiveTrigger; + + } + + /** + * Returns true or false depending on if the event falls in the specified + * time-range. This is used for filtering purposes. + * + * The rules used to determine if an event falls within the specified + * time-range is based on the CalDAV specification. + * + * @param DateTime $start + * @param DateTime $end + * @return bool + */ + public function isInTimeRange(DateTime $start, DateTime $end) { + + $effectiveTrigger = $this->getEffectiveTriggerTime(); + + if (isset($this->DURATION)) { + $duration = Sabre_VObject_DateTimeParser::parseDuration($this->DURATION); + $repeat = (string)$this->repeat; + if (!$repeat) { + $repeat = 1; + } + + $period = new DatePeriod($effectiveTrigger, $duration, (int)$repeat); + + foreach($period as $occurrence) { + + if ($start <= $occurrence && $end > $occurrence) { + return true; + } + } + return false; + } else { + return ($start <= $effectiveTrigger && $end > $effectiveTrigger); + } + + } + +} + +?> diff --git a/3rdparty/Sabre/VObject/Component/VCalendar.php b/3rdparty/Sabre/VObject/Component/VCalendar.php new file mode 100644 index 0000000000..f3be29afdb --- /dev/null +++ b/3rdparty/Sabre/VObject/Component/VCalendar.php @@ -0,0 +1,133 @@ +children as $component) { + + if (!$component instanceof Sabre_VObject_Component) + continue; + + if (isset($component->{'RECURRENCE-ID'})) + continue; + + if ($componentName && $component->name !== strtoupper($componentName)) + continue; + + if ($component->name === 'VTIMEZONE') + continue; + + $components[] = $component; + + } + + return $components; + + } + + /** + * If this calendar object, has events with recurrence rules, this method + * can be used to expand the event into multiple sub-events. + * + * Each event will be stripped from it's recurrence information, and only + * the instances of the event in the specified timerange will be left + * alone. + * + * In addition, this method will cause timezone information to be stripped, + * and normalized to UTC. + * + * This method will alter the VCalendar. This cannot be reversed. + * + * This functionality is specifically used by the CalDAV standard. It is + * possible for clients to request expand events, if they are rather simple + * clients and do not have the possibility to calculate recurrences. + * + * @param DateTime $start + * @param DateTime $end + * @return void + */ + public function expand(DateTime $start, DateTime $end) { + + $newEvents = array(); + + foreach($this->select('VEVENT') as $key=>$vevent) { + + if (isset($vevent->{'RECURRENCE-ID'})) { + unset($this->children[$key]); + continue; + } + + + if (!$vevent->rrule) { + unset($this->children[$key]); + if ($vevent->isInTimeRange($start, $end)) { + $newEvents[] = $vevent; + } + continue; + } + + $uid = (string)$vevent->uid; + if (!$uid) { + throw new LogicException('Event did not have a UID!'); + } + + $it = new Sabre_VObject_RecurrenceIterator($this, $vevent->uid); + $it->fastForward($start); + + while($it->valid() && $it->getDTStart() < $end) { + + if ($it->getDTEnd() > $start) { + + $newEvents[] = $it->getEventObject(); + + } + $it->next(); + + } + unset($this->children[$key]); + + } + + foreach($newEvents as $newEvent) { + + foreach($newEvent->children as $child) { + if ($child instanceof Sabre_VObject_Property_DateTime && + $child->getDateType() == Sabre_VObject_Property_DateTime::LOCALTZ) { + $child->setDateTime($child->getDateTime(),Sabre_VObject_Property_DateTime::UTC); + } + } + + $this->add($newEvent); + + } + + // Removing all VTIMEZONE components + unset($this->VTIMEZONE); + + } + +} + diff --git a/3rdparty/Sabre/VObject/Component/VEvent.php b/3rdparty/Sabre/VObject/Component/VEvent.php new file mode 100644 index 0000000000..4cc1e36d7d --- /dev/null +++ b/3rdparty/Sabre/VObject/Component/VEvent.php @@ -0,0 +1,70 @@ +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)) { + $effectiveEnd = $this->DTEND->getDateTime(); + // If this was an all-day event, we should just increase the + // end-date by 1. Otherwise the event will last until the second + // the date changed, by increasing this by 1 day the event lasts + // all of the last day as well. + if ($this->DTSTART->getDateType() == Sabre_VObject_Element_DateTime::DATE) { + $effectiveEnd->modify('+1 day'); + } + } elseif (isset($this->DURATION)) { + $effectiveEnd = clone $effectiveStart; + $effectiveEnd->add( Sabre_VObject_DateTimeParser::parseDuration($this->DURATION) ); + } elseif ($this->DTSTART->getDateType() == Sabre_VObject_Element_DateTime::DATE) { + $effectiveEnd = clone $effectiveStart; + $effectiveEnd->modify('+1 day'); + } else { + $effectiveEnd = clone $effectiveStart; + } + return ( + ($start <= $effectiveEnd) && ($end > $effectiveStart) + ); + + } + +} + +?> diff --git a/3rdparty/Sabre/VObject/Component/VJournal.php b/3rdparty/Sabre/VObject/Component/VJournal.php new file mode 100644 index 0000000000..22b3ec921e --- /dev/null +++ b/3rdparty/Sabre/VObject/Component/VJournal.php @@ -0,0 +1,46 @@ +DTSTART)?$this->DTSTART->getDateTime():null; + if ($dtstart) { + $effectiveEnd = clone $dtstart; + if ($this->DTSTART->getDateType() == Sabre_VObject_Element_DateTime::DATE) { + $effectiveEnd->modify('+1 day'); + } + + return ($start <= $effectiveEnd && $end > $dtstart); + + } + return false; + + + } + +} + +?> diff --git a/3rdparty/Sabre/VObject/Component/VTodo.php b/3rdparty/Sabre/VObject/Component/VTodo.php new file mode 100644 index 0000000000..79d06298d7 --- /dev/null +++ b/3rdparty/Sabre/VObject/Component/VTodo.php @@ -0,0 +1,68 @@ +DTSTART)?$this->DTSTART->getDateTime():null; + $duration = isset($this->DURATION)?Sabre_VObject_DateTimeParser::parseDuration($this->DURATION):null; + $due = isset($this->DUE)?$this->DUE->getDateTime():null; + $completed = isset($this->COMPLETED)?$this->COMPLETED->getDateTime():null; + $created = isset($this->CREATED)?$this->CREATED->getDateTime():null; + + if ($dtstart) { + if ($duration) { + $effectiveEnd = clone $dtstart; + $effectiveEnd->add($duration); + return $start <= $effectiveEnd && $end > $dtstart; + } elseif ($due) { + return + ($start < $due || $start <= $dtstart) && + ($end > $dtstart || $end >= $due); + } else { + return $start <= $dtstart && $end > $dtstart; + } + } + if ($due) { + return ($start < $due && $end >= $due); + } + if ($completed && $created) { + return + ($start <= $created || $start <= $completed) && + ($end >= $created || $end >= $completed); + } + if ($completed) { + return ($start <= $completed && $end >= $completed); + } + if ($created) { + return ($end > $created); + } + return true; + + } + +} + +?> diff --git a/3rdparty/Sabre/VObject/DateTimeParser.php b/3rdparty/Sabre/VObject/DateTimeParser.php new file mode 100644 index 0000000000..1e2d54ef3a --- /dev/null +++ b/3rdparty/Sabre/VObject/DateTimeParser.php @@ -0,0 +1,177 @@ +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'; + + } + + $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); + 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 index 8d2b0aaacd..e20ff0b353 100644 --- a/3rdparty/Sabre/VObject/Element.php +++ b/3rdparty/Sabre/VObject/Element.php @@ -2,14 +2,15 @@ /** * Base class for all elements - * + * * @package Sabre * @subpackage VObject - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. - * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. + * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ abstract class Sabre_VObject_Element extends Sabre_VObject_Node { + public $parent = null; } diff --git a/3rdparty/Sabre/VObject/Element/DateTime.php b/3rdparty/Sabre/VObject/Element/DateTime.php index 3350ec02c8..5e5eb7ab6f 100644 --- a/3rdparty/Sabre/VObject/Element/DateTime.php +++ b/3rdparty/Sabre/VObject/Element/DateTime.php @@ -1,25 +1,18 @@ 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 { - $tz = new DateTimeZone($tzid->value); - } catch (Exception $e) { - - // The id was invalid, 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'})) { - $tzid = (string)$vtimezone->{'X-LIC-LOCATION'}; - } - } - } - } - - $tz = new DateTimeZone($tzid); - - } - $dt = new DateTime($dateStr, $tz); - $dt->setTimeZone($tz); - - return array( - self::LOCALTZ, - $dt - ); - - } - } - -?> diff --git a/3rdparty/Sabre/VObject/Element/MultiDateTime.php b/3rdparty/Sabre/VObject/Element/MultiDateTime.php index dc6ca5abb8..8a12ced94a 100644 --- a/3rdparty/Sabre/VObject/Element/MultiDateTime.php +++ b/3rdparty/Sabre/VObject/Element/MultiDateTime.php @@ -1,168 +1,17 @@ offsetUnset('VALUE'); - $this->offsetUnset('TZID'); - switch($dateType) { - - case Sabre_VObject_Element_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_Element_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_Element_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_Element_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_Element_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_Element_DateTime::parseData($val, $this); - $dts[] = $dt; - $this->dateType = $type; - } - $this->dateTimes = $dts; - return $this->dateType; - - } +class Sabre_VObject_Element_MultiDateTime extends Sabre_VObject_Property_MultiDateTime { } - -?> diff --git a/3rdparty/Sabre/VObject/ElementList.php b/3rdparty/Sabre/VObject/ElementList.php index 9922cd587b..7e508db20f 100644 --- a/3rdparty/Sabre/VObject/ElementList.php +++ b/3rdparty/Sabre/VObject/ElementList.php @@ -8,15 +8,15 @@ * * @package Sabre * @subpackage VObject - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. - * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @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 - * + * Inner elements + * * @var array */ protected $elements = array(); @@ -24,37 +24,37 @@ class Sabre_VObject_ElementList implements Iterator, Countable, ArrayAccess { /** * Creates the element list. * - * @param array $elements + * @param array $elements */ public function __construct(array $elements) { $this->elements = $elements; - } + } /* {{{ Iterator interface */ /** - * Current position - * - * @var int + * Current position + * + * @var int */ private $key = 0; /** - * Returns current item in iteration - * - * @return Sabre_VObject_Element + * Returns current item in iteration + * + * @return Sabre_VObject_Element */ public function current() { return $this->elements[$this->key]; } - + /** - * To the next item in the iterator - * + * To the next item in the iterator + * * @return void */ public function next() { @@ -64,8 +64,8 @@ class Sabre_VObject_ElementList implements Iterator, Countable, ArrayAccess { } /** - * Returns the current iterator key - * + * Returns the current iterator key + * * @return int */ public function key() { @@ -75,9 +75,9 @@ class Sabre_VObject_ElementList implements Iterator, Countable, ArrayAccess { } /** - * Returns true if the current position in the iterator is a valid one - * - * @return bool + * Returns true if the current position in the iterator is a valid one + * + * @return bool */ public function valid() { @@ -86,9 +86,9 @@ class Sabre_VObject_ElementList implements Iterator, Countable, ArrayAccess { } /** - * Rewinds the iterator - * - * @return void + * Rewinds the iterator + * + * @return void */ public function rewind() { @@ -101,9 +101,9 @@ class Sabre_VObject_ElementList implements Iterator, Countable, ArrayAccess { /* {{{ Countable interface */ /** - * Returns the number of elements - * - * @return int + * Returns the number of elements + * + * @return int */ public function count() { @@ -115,12 +115,12 @@ class Sabre_VObject_ElementList implements Iterator, Countable, ArrayAccess { /* {{{ ArrayAccess Interface */ - + /** * Checks if an item exists through ArrayAccess. * - * @param int $offset - * @return bool + * @param int $offset + * @return bool */ public function offsetExists($offset) { @@ -131,8 +131,8 @@ class Sabre_VObject_ElementList implements Iterator, Countable, ArrayAccess { /** * Gets an item through ArrayAccess. * - * @param int $offset - * @return mixed + * @param int $offset + * @return mixed */ public function offsetGet($offset) { @@ -143,8 +143,8 @@ class Sabre_VObject_ElementList implements Iterator, Countable, ArrayAccess { /** * Sets an item through ArrayAccess. * - * @param int $offset - * @param mixed $value + * @param int $offset + * @param mixed $value * @return void */ public function offsetSet($offset,$value) { @@ -158,7 +158,7 @@ class Sabre_VObject_ElementList implements Iterator, Countable, ArrayAccess { * * This method just forwards the request to the inner iterator * - * @param int $offset + * @param int $offset * @return void */ public function offsetUnset($offset) { diff --git a/3rdparty/Sabre/VObject/FreeBusyGenerator.php b/3rdparty/Sabre/VObject/FreeBusyGenerator.php new file mode 100644 index 0000000000..1c96a64a00 --- /dev/null +++ b/3rdparty/Sabre/VObject/FreeBusyGenerator.php @@ -0,0 +1,297 @@ +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 index 7100b62f1c..d89e01b56c 100644 --- a/3rdparty/Sabre/VObject/Node.php +++ b/3rdparty/Sabre/VObject/Node.php @@ -1,47 +1,47 @@ iterator)) + if (!is_null($this->iterator)) return $this->iterator; return new Sabre_VObject_ElementList(array($this)); @@ -52,8 +52,8 @@ abstract class Sabre_VObject_Node implements IteratorAggregate, ArrayAccess, Cou * Sets the overridden iterator * * Note that this is not actually part of the iterator interface - * - * @param Sabre_VObject_ElementList $iterator + * + * @param Sabre_VObject_ElementList $iterator * @return void */ public function setIterator(Sabre_VObject_ElementList $iterator) { @@ -67,9 +67,9 @@ abstract class Sabre_VObject_Node implements IteratorAggregate, ArrayAccess, Cou /* {{{ Countable interface */ /** - * Returns the number of elements - * - * @return int + * Returns the number of elements + * + * @return int */ public function count() { @@ -82,14 +82,14 @@ abstract class Sabre_VObject_Node implements IteratorAggregate, ArrayAccess, Cou /* {{{ ArrayAccess Interface */ - + /** * Checks if an item exists through ArrayAccess. * * This method just forwards the request to the inner iterator - * - * @param int $offset - * @return bool + * + * @param int $offset + * @return bool */ public function offsetExists($offset) { @@ -103,8 +103,8 @@ abstract class Sabre_VObject_Node implements IteratorAggregate, ArrayAccess, Cou * * This method just forwards the request to the inner iterator * - * @param int $offset - * @return mixed + * @param int $offset + * @return mixed */ public function offsetGet($offset) { @@ -118,8 +118,8 @@ abstract class Sabre_VObject_Node implements IteratorAggregate, ArrayAccess, Cou * * This method just forwards the request to the inner iterator * - * @param int $offset - * @param mixed $value + * @param int $offset + * @param mixed $value * @return void */ public function offsetSet($offset,$value) { @@ -134,7 +134,7 @@ abstract class Sabre_VObject_Node implements IteratorAggregate, ArrayAccess, Cou * * This method just forwards the request to the inner iterator * - * @param int $offset + * @param int $offset * @return void */ public function offsetUnset($offset) { diff --git a/3rdparty/Sabre/VObject/Parameter.php b/3rdparty/Sabre/VObject/Parameter.php index 9ebab6ec69..2e39af5f78 100644 --- a/3rdparty/Sabre/VObject/Parameter.php +++ b/3rdparty/Sabre/VObject/Parameter.php @@ -5,51 +5,54 @@ * * This class represents a parameter. A parameter is always tied to a property. * In the case of: - * DTSTART;VALUE=DATE:20101108 + * DTSTART;VALUE=DATE:20101108 * VALUE=DATE would be the parameter name and value. - * + * * @package Sabre * @subpackage VObject - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. - * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. + * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ class Sabre_VObject_Parameter extends Sabre_VObject_Node { /** - * Parameter name - * - * @var string + * Parameter name + * + * @var string */ public $name; /** - * Parameter value - * - * @var string + * Parameter value + * + * @var string */ public $value; /** - * Sets up the object - * - * @param string $name - * @param string $value + * Sets up the object + * + * @param string $name + * @param string $value */ public function __construct($name, $value = null) { $this->name = strtoupper($name); $this->value = $value; - } + } /** - * Turns the object back into a serialized blob. - * - * @return string + * Turns the object back into a serialized blob. + * + * @return string */ public function serialize() { + if (is_null($this->value)) { + return $this->name; + } $src = array( '\\', "\n", @@ -68,9 +71,9 @@ class Sabre_VObject_Parameter extends Sabre_VObject_Node { } /** - * Called when this object is being cast to a string - * - * @return string + * Called when this object is being cast to a string + * + * @return string */ public function __toString() { diff --git a/3rdparty/Sabre/VObject/ParseException.php b/3rdparty/Sabre/VObject/ParseException.php index ed4ef2e859..1b5e95bf16 100644 --- a/3rdparty/Sabre/VObject/ParseException.php +++ b/3rdparty/Sabre/VObject/ParseException.php @@ -2,11 +2,11 @@ /** * Exception thrown by Sabre_VObject_Reader if an invalid object was attempted to be parsed. - * + * * @package Sabre * @subpackage VObject - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. - * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. + * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ class Sabre_VObject_ParseException extends Exception { } diff --git a/3rdparty/Sabre/VObject/Property.php b/3rdparty/Sabre/VObject/Property.php index 0605822904..ce74fe3865 100644 --- a/3rdparty/Sabre/VObject/Property.php +++ b/3rdparty/Sabre/VObject/Property.php @@ -4,58 +4,103 @@ * VObject Property * * A property in VObject is usually in the form PARAMNAME:paramValue. - * An example is : SUMMARY:Weekly meeting + * An example is : SUMMARY:Weekly meeting * * Properties can also have parameters: * SUMMARY;LANG=en:Weekly meeting. * - * Parameters can be accessed using the ArrayAccess interface. + * Parameters can be accessed using the ArrayAccess interface. * * @package Sabre * @subpackage VObject - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. - * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. + * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ class Sabre_VObject_Property extends Sabre_VObject_Element { /** - * Propertyname - * - * @var string + * Propertyname + * + * @var string */ public $name; /** * Group name - * + * * This may be something like 'HOME' for vcards. * - * @var string + * @var string */ public $group; /** - * Property parameters - * - * @var array + * Property parameters + * + * @var array */ public $parameters = array(); /** - * Property value - * - * @var string + * Property value + * + * @var string */ public $value; + /** + * If properties are added to this map, they will be automatically mapped + * to their respective classes, if parsed by the reader or constructed with + * the 'create' method. + * + * @var array + */ + static public $classMap = array( + 'COMPLETED' => 'Sabre_VObject_Property_DateTime', + 'CREATED' => 'Sabre_VObject_Property_DateTime', + 'DTEND' => 'Sabre_VObject_Property_DateTime', + 'DTSTAMP' => 'Sabre_VObject_Property_DateTime', + 'DTSTART' => 'Sabre_VObject_Property_DateTime', + 'DUE' => 'Sabre_VObject_Property_DateTime', + 'EXDATE' => 'Sabre_VObject_Property_MultiDateTime', + 'LAST-MODIFIED' => 'Sabre_VObject_Property_DateTime', + 'RECURRENCE-ID' => 'Sabre_VObject_Property_DateTime', + 'TRIGGER' => 'Sabre_VObject_Property_DateTime', + ); + + /** + * Creates the new property by name, but in addition will also see if + * there's a class mapped to the property name. + * + * @param string $name + * @param string $value + * @return void + */ + static public function create($name, $value = null) { + + $name = strtoupper($name); + $shortName = $name; + $group = null; + if (strpos($shortName,'.')!==false) { + list($group, $shortName) = explode('.', $shortName); + } + + if (isset(self::$classMap[$shortName])) { + return new self::$classMap[$shortName]($name, $value); + } else { + return new self($name, $value); + } + + } + /** * Creates a new property object - * - * By default this object will iterate over its own children, but this can + * + * By default this object will iterate over its own children, but this can * be overridden with the iterator argument - * - * @param string $name + * + * @param string $name * @param string $value * @param Sabre_VObject_ElementList $iterator */ @@ -73,10 +118,12 @@ class Sabre_VObject_Property extends Sabre_VObject_Element { } + + /** - * Updates the internal value - * - * @param string $value + * Updates the internal value + * + * @param string $value * @return void */ public function setValue($value) { @@ -86,9 +133,9 @@ class Sabre_VObject_Property extends Sabre_VObject_Element { } /** - * Turns the object back into a serialized blob. - * - * @return string + * Turns the object back into a serialized blob. + * + * @return string */ public function serialize() { @@ -97,7 +144,7 @@ class Sabre_VObject_Property extends Sabre_VObject_Element { if (count($this->parameters)) { foreach($this->parameters as $param) { - + $str.=';' . $param->serialize(); } @@ -115,8 +162,8 @@ class Sabre_VObject_Property extends Sabre_VObject_Element { $out = ''; while(strlen($str)>0) { if (strlen($str)>75) { - $out.= substr($str,0,75) . "\r\n"; - $str = ' ' . substr($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=''; @@ -136,11 +183,11 @@ class Sabre_VObject_Property extends Sabre_VObject_Element { * 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 + * 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) { @@ -153,7 +200,7 @@ class Sabre_VObject_Property extends Sabre_VObject_Element { $this->parameters[] = $item; } elseif(is_string($item)) { - if (!is_scalar($itemValue)) { + if (!is_scalar($itemValue) && !is_null($itemValue)) { throw new InvalidArgumentException('The second argument must be scalar'); } $parameter = new Sabre_VObject_Parameter($item,$itemValue); @@ -161,21 +208,20 @@ class Sabre_VObject_Property extends Sabre_VObject_Element { $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 + * + * @param mixed $name + * @return bool */ public function offsetExists($name) { @@ -191,16 +237,16 @@ class Sabre_VObject_Property extends Sabre_VObject_Element { } /** - * Returns a parameter, or parameter list. - * - * @param string $name - * @return Sabre_VObject_Element + * 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) @@ -219,8 +265,8 @@ class Sabre_VObject_Property extends Sabre_VObject_Element { } /** - * Creates a new parameter - * + * Creates a new parameter + * * @param string $name * @param mixed $value * @return void @@ -230,7 +276,7 @@ class Sabre_VObject_Property extends Sabre_VObject_Element { if (is_int($name)) return parent::offsetSet($name, $value); if (is_scalar($value)) { - if (!is_string($name)) + 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); @@ -242,7 +288,7 @@ class Sabre_VObject_Property extends Sabre_VObject_Element { 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; + $value->parent = $this; $this->parameters[] = $value; } else { throw new InvalidArgumentException('You can only add parameters to the property object'); @@ -251,17 +297,16 @@ class Sabre_VObject_Property extends Sabre_VObject_Element { } /** - * Removes one or more parameters with the specified name - * - * @param string $name - * @return void + * 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, $value); + if (is_int($name)) return parent::offsetUnset($name); $name = strtoupper($name); - - $result = array(); + foreach($this->parameters as $key=>$parameter) { if ($parameter->name == $name) { $parameter->parent = null; @@ -275,15 +320,29 @@ class Sabre_VObject_Property extends Sabre_VObject_Element { /* }}} */ /** - * Called when this object is being cast to a string - * - * @return string + * Called when this object is being cast to a string + * + * @return string */ public function __toString() { - return $this->value; + return (string)$this->value; } + /** + * This method is automatically called when the object is cloned. + * Specifically, this will ensure all child elements are also cloned. + * + * @return void + */ + public function __clone() { + + foreach($this->parameters as $key=>$child) { + $this->parameters[$key] = clone $child; + $this->parameters[$key]->parent = $this; + } + + } } diff --git a/3rdparty/Sabre/VObject/Property/DateTime.php b/3rdparty/Sabre/VObject/Property/DateTime.php new file mode 100644 index 0000000000..fe2372caa8 --- /dev/null +++ b/3rdparty/Sabre/VObject/Property/DateTime.php @@ -0,0 +1,260 @@ +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 new file mode 100644 index 0000000000..ae53ab6a61 --- /dev/null +++ b/3rdparty/Sabre/VObject/Property/MultiDateTime.php @@ -0,0 +1,166 @@ +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 index 7d1c282838..eea73fa3dc 100644 --- a/3rdparty/Sabre/VObject/Reader.php +++ b/3rdparty/Sabre/VObject/Reader.php @@ -5,40 +5,22 @@ * * This class reads the vobject file, and returns a full element tree. * + * TODO: this class currently completely works 'statically'. This is pointless, + * and defeats OOP principals. Needs refactoring in a future version. * - * TODO: this class currently completely works 'statically'. This is pointless, - * and defeats OOP principals. Needs refaxtoring in a future version. - * * @package Sabre * @subpackage VObject - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. - * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. + * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ class Sabre_VObject_Reader { /** - * This array contains a list of Property names that are automatically - * mapped to specific class names. + * Parses the file and returns the top component * - * Adding to this list allows you to specify custom property classes, - * adding extra functionality. - * - * @var array - */ - static public $elementMap = array( - 'DTSTART' => 'Sabre_VObject_Element_DateTime', - 'DTEND' => 'Sabre_VObject_Element_DateTime', - 'COMPLETED' => 'Sabre_VObject_Element_DateTime', - 'DUE' => 'Sabre_VObject_Element_DateTime', - 'EXDATE' => 'Sabre_VObject_Element_MultiDateTime', - ); - - /** - * Parses the file and returns the top component - * - * @param string $data - * @return Sabre_VObject_Element + * @param string $data + * @return Sabre_VObject_Element */ static function read($data) { @@ -63,11 +45,11 @@ class Sabre_VObject_Reader { } unset($lines); - + reset($lines2); return self::readLine($lines2); - + } /** @@ -75,9 +57,9 @@ class Sabre_VObject_Reader { * * This method receives the full array of lines. The array pointer is used * to traverse. - * - * @param array $lines - * @return Sabre_VObject_Element + * + * @param array $lines + * @return Sabre_VObject_Element */ static private function readLine(&$lines) { @@ -88,22 +70,23 @@ class Sabre_VObject_Reader { // Components if (stripos($line,"BEGIN:")===0) { - // This is a component - $obj = new Sabre_VObject_Component(strtoupper(substr($line,6))); + $componentName = strtoupper(substr($line,6)); + $obj = Sabre_VObject_Component::create($componentName); $nextLine = current($lines); while(stripos($nextLine,"END:")!==0) { $obj->add(self::readLine($lines)); + $nextLine = current($lines); - if ($nextLine===false) + if ($nextLine===false) throw new Sabre_VObject_ParseException('Invalid VObject. Document ended prematurely.'); } - // Checking component name of the 'END:' line. + // 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 . '"'); } @@ -117,7 +100,7 @@ class Sabre_VObject_Reader { //$result = preg_match('/(?P[A-Z0-9-]+)(?:;(?P^(?([^:^\"]|\"([^\"]*)\")*))?"; $regex = "/^(?P$token)$parameters:(?P.*)$/i"; @@ -128,22 +111,23 @@ class Sabre_VObject_Reader { } $propertyName = strtoupper($matches['name']); - $propertyValue = stripcslashes($matches['value']); + $propertyValue = preg_replace_callback('#(\\\\(\\\\|N|n|;|,))#',function($matches) { + if ($matches[2]==='n' || $matches[2]==='N') { + return "\n"; + } else { + return $matches[2]; + } + }, $matches['value']); - if (isset(self::$elementMap[$propertyName])) { - $className = self::$elementMap[$propertyName]; - } else { - $className = 'Sabre_VObject_Property'; - } - - $obj = new $className($propertyName, $propertyValue); + $obj = Sabre_VObject_Property::create($propertyName, $propertyValue); if ($matches['parameters']) { foreach(self::readParameters($matches['parameters']) as $param) { $obj->add($param); } - } + + } return $obj; @@ -151,12 +135,12 @@ class Sabre_VObject_Reader { } /** - * Reads a parameter list from a property + * Reads a parameter list from a property * * This method returns an array of Sabre_VObject_Parameter * - * @param string $parameters - * @return array + * @param string $parameters + * @return array */ static private function readParameters($parameters) { @@ -179,7 +163,15 @@ class Sabre_VObject_Reader { $value = ''; } - $params[] = new Sabre_VObject_Parameter($match['paramName'], stripcslashes($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); } diff --git a/3rdparty/Sabre/VObject/RecurrenceIterator.php b/3rdparty/Sabre/VObject/RecurrenceIterator.php new file mode 100644 index 0000000000..833aa091ab --- /dev/null +++ b/3rdparty/Sabre/VObject/RecurrenceIterator.php @@ -0,0 +1,1009 @@ + 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)); + } + } + $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() { + + if (!$this->byMonth) { + $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 { + + // no byDay or byMonthDay, so we can just loop through the + // months. + 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 index 937c367e22..00110febc0 100644 --- a/3rdparty/Sabre/VObject/Version.php +++ b/3rdparty/Sabre/VObject/Version.php @@ -2,10 +2,10 @@ /** * This class contains the version number for the VObject package - * + * * @package Sabre - * @subpackage VObject - * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved. + * @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 */ @@ -14,7 +14,7 @@ class Sabre_VObject_Version { /** * Full version number */ - const VERSION = '1.2.4'; + const VERSION = '1.3.2'; /** * Stability : alpha, beta, stable diff --git a/3rdparty/Sabre/VObject/WindowsTimezoneMap.php b/3rdparty/Sabre/VObject/WindowsTimezoneMap.php new file mode 100644 index 0000000000..5e1cc5d479 --- /dev/null +++ b/3rdparty/Sabre/VObject/WindowsTimezoneMap.php @@ -0,0 +1,128 @@ +'Australia/Darwin', + 'AUS Eastern Standard Time'=>'Australia/Sydney', + 'Afghanistan Standard Time'=>'Asia/Kabul', + 'Alaskan Standard Time'=>'America/Anchorage', + 'Arab Standard Time'=>'Asia/Riyadh', + 'Arabian Standard Time'=>'Asia/Dubai', + 'Arabic Standard Time'=>'Asia/Baghdad', + 'Argentina Standard Time'=>'America/Buenos_Aires', + 'Armenian Standard Time'=>'Asia/Yerevan', + 'Atlantic Standard Time'=>'America/Halifax', + 'Azerbaijan Standard Time'=>'Asia/Baku', + 'Azores Standard Time'=>'Atlantic/Azores', + 'Bangladesh Standard Time'=>'Asia/Dhaka', + 'Canada Central Standard Time'=>'America/Regina', + 'Cape Verde Standard Time'=>'Atlantic/Cape_Verde', + 'Caucasus Standard Time'=>'Asia/Yerevan', + 'Cen. Australia Standard Time'=>'Australia/Adelaide', + 'Central America Standard Time'=>'America/Guatemala', + 'Central Asia Standard Time'=>'Asia/Almaty', + 'Central Brazilian Standard Time'=>'America/Cuiaba', + 'Central Europe Standard Time'=>'Europe/Budapest', + 'Central European Standard Time'=>'Europe/Warsaw', + 'Central Pacific Standard Time'=>'Pacific/Guadalcanal', + 'Central Standard Time'=>'America/Chicago', + 'Central Standard Time (Mexico)'=>'America/Mexico_City', + 'China Standard Time'=>'Asia/Shanghai', + 'Dateline Standard Time'=>'Etc/GMT+12', + 'E. Africa Standard Time'=>'Africa/Nairobi', + 'E. Australia Standard Time'=>'Australia/Brisbane', + 'E. Europe Standard Time'=>'Europe/Minsk', + 'E. South America Standard Time'=>'America/Sao_Paulo', + 'Eastern Standard Time'=>'America/New_York', + 'Egypt Standard Time'=>'Africa/Cairo', + 'Ekaterinburg Standard Time'=>'Asia/Yekaterinburg', + 'FLE Standard Time'=>'Europe/Kiev', + 'Fiji Standard Time'=>'Pacific/Fiji', + 'GMT Standard Time'=>'Europe/London', + 'GTB Standard Time'=>'Europe/Istanbul', + 'Georgian Standard Time'=>'Asia/Tbilisi', + 'Greenland Standard Time'=>'America/Godthab', + 'Greenwich Standard Time'=>'Atlantic/Reykjavik', + 'Hawaiian Standard Time'=>'Pacific/Honolulu', + 'India Standard Time'=>'Asia/Calcutta', + 'Iran Standard Time'=>'Asia/Tehran', + 'Israel Standard Time'=>'Asia/Jerusalem', + 'Jordan Standard Time'=>'Asia/Amman', + 'Kamchatka Standard Time'=>'Asia/Kamchatka', + 'Korea Standard Time'=>'Asia/Seoul', + 'Magadan Standard Time'=>'Asia/Magadan', + 'Mauritius Standard Time'=>'Indian/Mauritius', + 'Mexico Standard Time'=>'America/Mexico_City', + 'Mexico Standard Time 2'=>'America/Chihuahua', + 'Mid-Atlantic Standard Time'=>'Etc/GMT+2', + 'Middle East Standard Time'=>'Asia/Beirut', + 'Montevideo Standard Time'=>'America/Montevideo', + 'Morocco Standard Time'=>'Africa/Casablanca', + 'Mountain Standard Time'=>'America/Denver', + 'Mountain Standard Time (Mexico)'=>'America/Chihuahua', + 'Myanmar Standard Time'=>'Asia/Rangoon', + 'N. Central Asia Standard Time'=>'Asia/Novosibirsk', + 'Namibia Standard Time'=>'Africa/Windhoek', + 'Nepal Standard Time'=>'Asia/Katmandu', + 'New Zealand Standard Time'=>'Pacific/Auckland', + 'Newfoundland Standard Time'=>'America/St_Johns', + 'North Asia East Standard Time'=>'Asia/Irkutsk', + 'North Asia Standard Time'=>'Asia/Krasnoyarsk', + 'Pacific SA Standard Time'=>'America/Santiago', + 'Pacific Standard Time'=>'America/Los_Angeles', + 'Pacific Standard Time (Mexico)'=>'America/Santa_Isabel', + 'Pakistan Standard Time'=>'Asia/Karachi', + 'Paraguay Standard Time'=>'America/Asuncion', + 'Romance Standard Time'=>'Europe/Paris', + 'Russian Standard Time'=>'Europe/Moscow', + 'SA Eastern Standard Time'=>'America/Cayenne', + 'SA Pacific Standard Time'=>'America/Bogota', + 'SA Western Standard Time'=>'America/La_Paz', + 'SE Asia Standard Time'=>'Asia/Bangkok', + 'Samoa Standard Time'=>'Pacific/Apia', + 'Singapore Standard Time'=>'Asia/Singapore', + 'South Africa Standard Time'=>'Africa/Johannesburg', + 'Sri Lanka Standard Time'=>'Asia/Colombo', + 'Syria Standard Time'=>'Asia/Damascus', + 'Taipei Standard Time'=>'Asia/Taipei', + 'Tasmania Standard Time'=>'Australia/Hobart', + 'Tokyo Standard Time'=>'Asia/Tokyo', + 'Tonga Standard Time'=>'Pacific/Tongatapu', + 'US Eastern Standard Time'=>'America/Indianapolis', + 'US Mountain Standard Time'=>'America/Phoenix', + 'UTC'=>'Etc/GMT', + 'UTC+12'=>'Etc/GMT-12', + 'UTC-02'=>'Etc/GMT+2', + 'UTC-11'=>'Etc/GMT+11', + 'Ulaanbaatar Standard Time'=>'Asia/Ulaanbaatar', + 'Venezuela Standard Time'=>'America/Caracas', + 'Vladivostok Standard Time'=>'Asia/Vladivostok', + 'W. Australia Standard Time'=>'Australia/Perth', + 'W. Central Africa Standard Time'=>'Africa/Lagos', + 'W. Europe Standard Time'=>'Europe/Berlin', + 'West Asia Standard Time'=>'Asia/Tashkent', + 'West Pacific Standard Time'=>'Pacific/Port_Moresby', + 'Yakutsk Standard Time'=>'Asia/Yakutsk', + ); + + static public function lookup($tzid) { + return isset(self::$map[$tzid]) ? self::$map[$tzid] : null; + } +} diff --git a/3rdparty/Sabre/VObject/includes.php b/3rdparty/Sabre/VObject/includes.php index f21010fb27..0177a8f1ba 100644 --- a/3rdparty/Sabre/VObject/includes.php +++ b/3rdparty/Sabre/VObject/includes.php @@ -1,29 +1,41 @@ exceptions = ($exceptions == true); + } + + /** + * Sets message type to HTML. + * @param bool $ishtml + * @return void + */ + public function IsHTML($ishtml = true) { + if ($ishtml) { + $this->ContentType = 'text/html'; + } else { + $this->ContentType = 'text/plain'; + } + } + + /** + * Sets Mailer to send message using SMTP. + * @return void + */ + public function IsSMTP() { + $this->Mailer = 'smtp'; + } + + /** + * Sets Mailer to send message using PHP mail() function. + * @return void + */ + public function IsMail() { + $this->Mailer = 'mail'; + } + + /** + * Sets Mailer to send message using the $Sendmail program. + * @return void + */ + public function IsSendmail() { + if (!stristr(ini_get('sendmail_path'), 'sendmail')) { + $this->Sendmail = '/var/qmail/bin/sendmail'; + } + $this->Mailer = 'sendmail'; + } + + /** + * Sets Mailer to send message using the qmail MTA. + * @return void + */ + public function IsQmail() { + if (stristr(ini_get('sendmail_path'), 'qmail')) { + $this->Sendmail = '/var/qmail/bin/sendmail'; + } + $this->Mailer = 'sendmail'; + } + + ///////////////////////////////////////////////// + // METHODS, RECIPIENTS + ///////////////////////////////////////////////// + + /** + * Adds a "To" address. + * @param string $address + * @param string $name + * @return boolean true on success, false if address already used + */ + public function AddAddress($address, $name = '') { + return $this->AddAnAddress('to', $address, $name); + } + + /** + * Adds a "Cc" address. + * Note: this function works with the SMTP mailer on win32, not with the "mail" mailer. + * @param string $address + * @param string $name + * @return boolean true on success, false if address already used + */ + public function AddCC($address, $name = '') { + return $this->AddAnAddress('cc', $address, $name); + } + + /** + * Adds a "Bcc" address. + * Note: this function works with the SMTP mailer on win32, not with the "mail" mailer. + * @param string $address + * @param string $name + * @return boolean true on success, false if address already used + */ + public function AddBCC($address, $name = '') { + return $this->AddAnAddress('bcc', $address, $name); + } + + /** + * Adds a "Reply-to" address. + * @param string $address + * @param string $name + * @return boolean + */ + public function AddReplyTo($address, $name = '') { + return $this->AddAnAddress('ReplyTo', $address, $name); + } + + /** + * Adds an address to one of the recipient arrays + * Addresses that have been added already return false, but do not throw exceptions + * @param string $kind One of 'to', 'cc', 'bcc', 'ReplyTo' + * @param string $address The email address to send to + * @param string $name + * @return boolean true on success, false if address already used or invalid in some way + * @access protected + */ + protected function AddAnAddress($kind, $address, $name = '') { + if (!preg_match('/^(to|cc|bcc|ReplyTo)$/', $kind)) { + $this->SetError($this->Lang('Invalid recipient array').': '.$kind); + if ($this->exceptions) { + throw new phpmailerException('Invalid recipient array: ' . $kind); + } + echo $this->Lang('Invalid recipient array').': '.$kind; + return false; + } + $address = trim($address); + $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim + if (!self::ValidateAddress($address)) { + $this->SetError($this->Lang('invalid_address').': '. $address); + if ($this->exceptions) { + throw new phpmailerException($this->Lang('invalid_address').': '.$address); + } + echo $this->Lang('invalid_address').': '.$address; + return false; + } + if ($kind != 'ReplyTo') { + if (!isset($this->all_recipients[strtolower($address)])) { + array_push($this->$kind, array($address, $name)); + $this->all_recipients[strtolower($address)] = true; + return true; + } + } else { + if (!array_key_exists(strtolower($address), $this->ReplyTo)) { + $this->ReplyTo[strtolower($address)] = array($address, $name); + return true; + } + } + return false; +} + +/** + * Set the From and FromName properties + * @param string $address + * @param string $name + * @return boolean + */ + public function SetFrom($address, $name = '', $auto = 1) { + $address = trim($address); + $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim + if (!self::ValidateAddress($address)) { + $this->SetError($this->Lang('invalid_address').': '. $address); + if ($this->exceptions) { + throw new phpmailerException($this->Lang('invalid_address').': '.$address); + } + echo $this->Lang('invalid_address').': '.$address; + return false; + } + $this->From = $address; + $this->FromName = $name; + if ($auto) { + if (empty($this->ReplyTo)) { + $this->AddAnAddress('ReplyTo', $address, $name); + } + if (empty($this->Sender)) { + $this->Sender = $address; + } + } + return true; + } + + /** + * Check that a string looks roughly like an email address should + * Static so it can be used without instantiation + * Tries to use PHP built-in validator in the filter extension (from PHP 5.2), falls back to a reasonably competent regex validator + * Conforms approximately to RFC2822 + * @link http://www.hexillion.com/samples/#Regex Original pattern found here + * @param string $address The email address to check + * @return boolean + * @static + * @access public + */ + public static function ValidateAddress($address) { + if (function_exists('filter_var')) { //Introduced in PHP 5.2 + if(filter_var($address, FILTER_VALIDATE_EMAIL) === FALSE) { + return false; + } else { + return true; + } + } else { + return preg_match('/^(?:[\w\!\#\$\%\&\'\*\+\-\/\=\?\^\`\{\|\}\~]+\.)*[\w\!\#\$\%\&\'\*\+\-\/\=\?\^\`\{\|\}\~]+@(?:(?:(?:[a-zA-Z0-9_](?:[a-zA-Z0-9_\-](?!\.)){0,61}[a-zA-Z0-9_-]?\.)+[a-zA-Z0-9_](?:[a-zA-Z0-9_\-](?!$)){0,61}[a-zA-Z0-9_]?)|(?:\[(?:(?:[01]?\d{1,2}|2[0-4]\d|25[0-5])\.){3}(?:[01]?\d{1,2}|2[0-4]\d|25[0-5])\]))$/', $address); + } + } + + ///////////////////////////////////////////////// + // METHODS, MAIL SENDING + ///////////////////////////////////////////////// + + /** + * Creates message and assigns Mailer. If the message is + * not sent successfully then it returns false. Use the ErrorInfo + * variable to view description of the error. + * @return bool + */ + public function Send() { + try { + if(!$this->PreSend()) return false; + return $this->PostSend(); + } catch (phpmailerException $e) { + $this->SetError($e->getMessage()); + if ($this->exceptions) { + throw $e; + } + return false; + } + } + + protected function PreSend() { + try { + if ((count($this->to) + count($this->cc) + count($this->bcc)) < 1) { + throw new phpmailerException($this->Lang('provide_address'), self::STOP_CRITICAL); + } + + // Set whether the message is multipart/alternative + if(!empty($this->AltBody)) { + $this->ContentType = 'multipart/alternative'; + } + + $this->error_count = 0; // reset errors + $this->SetMessageType(); + //Refuse to send an empty message + if (empty($this->Body)) { + throw new phpmailerException($this->Lang('empty_message'), self::STOP_CRITICAL); + } + + $this->MIMEHeader = $this->CreateHeader(); + $this->MIMEBody = $this->CreateBody(); + + + // digitally sign with DKIM if enabled + if ($this->DKIM_domain && $this->DKIM_private) { + $header_dkim = $this->DKIM_Add($this->MIMEHeader, $this->EncodeHeader($this->SecureHeader($this->Subject)), $this->MIMEBody); + $this->MIMEHeader = str_replace("\r\n", "\n", $header_dkim) . $this->MIMEHeader; + } + + return true; + } catch (phpmailerException $e) { + $this->SetError($e->getMessage()); + if ($this->exceptions) { + throw $e; + } + return false; + } + } + + protected function PostSend() { + try { + // Choose the mailer and send through it + switch($this->Mailer) { + case 'sendmail': + return $this->SendmailSend($this->MIMEHeader, $this->MIMEBody); + case 'smtp': + return $this->SmtpSend($this->MIMEHeader, $this->MIMEBody); + default: + return $this->MailSend($this->MIMEHeader, $this->MIMEBody); + } + + } catch (phpmailerException $e) { + $this->SetError($e->getMessage()); + if ($this->exceptions) { + throw $e; + } + echo $e->getMessage()."\n"; + return false; + } + } + + /** + * Sends mail using the $Sendmail program. + * @param string $header The message headers + * @param string $body The message body + * @access protected + * @return bool + */ + protected function SendmailSend($header, $body) { + if ($this->Sender != '') { + $sendmail = sprintf("%s -oi -f %s -t", escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender)); + } else { + $sendmail = sprintf("%s -oi -t", escapeshellcmd($this->Sendmail)); + } + if ($this->SingleTo === true) { + foreach ($this->SingleToArray as $key => $val) { + if(!@$mail = popen($sendmail, 'w')) { + throw new phpmailerException($this->Lang('execute') . $this->Sendmail, self::STOP_CRITICAL); + } + fputs($mail, "To: " . $val . "\n"); + fputs($mail, $header); + fputs($mail, $body); + $result = pclose($mail); + // implement call back function if it exists + $isSent = ($result == 0) ? 1 : 0; + $this->doCallback($isSent, $val, $this->cc, $this->bcc, $this->Subject, $body); + if($result != 0) { + throw new phpmailerException($this->Lang('execute') . $this->Sendmail, self::STOP_CRITICAL); + } + } + } else { + if(!@$mail = popen($sendmail, 'w')) { + throw new phpmailerException($this->Lang('execute') . $this->Sendmail, self::STOP_CRITICAL); + } + fputs($mail, $header); + fputs($mail, $body); + $result = pclose($mail); + // implement call back function if it exists + $isSent = ($result == 0) ? 1 : 0; + $this->doCallback($isSent, $this->to, $this->cc, $this->bcc, $this->Subject, $body); + if($result != 0) { + throw new phpmailerException($this->Lang('execute') . $this->Sendmail, self::STOP_CRITICAL); + } + } + return true; + } + + /** + * Sends mail using the PHP mail() function. + * @param string $header The message headers + * @param string $body The message body + * @access protected + * @return bool + */ + protected function MailSend($header, $body) { + $toArr = array(); + foreach($this->to as $t) { + $toArr[] = $this->AddrFormat($t); + } + $to = implode(', ', $toArr); + + if (empty($this->Sender)) { + $params = "-oi -f %s"; + } else { + $params = sprintf("-oi -f %s", $this->Sender); + } + if ($this->Sender != '' and !ini_get('safe_mode')) { + $old_from = ini_get('sendmail_from'); + ini_set('sendmail_from', $this->Sender); + if ($this->SingleTo === true && count($toArr) > 1) { + foreach ($toArr as $key => $val) { + $rt = @mail($val, $this->EncodeHeader($this->SecureHeader($this->Subject)), $body, $header, $params); + // implement call back function if it exists + $isSent = ($rt == 1) ? 1 : 0; + $this->doCallback($isSent, $val, $this->cc, $this->bcc, $this->Subject, $body); + } + } else { + $rt = @mail($to, $this->EncodeHeader($this->SecureHeader($this->Subject)), $body, $header, $params); + // implement call back function if it exists + $isSent = ($rt == 1) ? 1 : 0; + $this->doCallback($isSent, $to, $this->cc, $this->bcc, $this->Subject, $body); + } + } else { + if ($this->SingleTo === true && count($toArr) > 1) { + foreach ($toArr as $key => $val) { + $rt = @mail($val, $this->EncodeHeader($this->SecureHeader($this->Subject)), $body, $header, $params); + // implement call back function if it exists + $isSent = ($rt == 1) ? 1 : 0; + $this->doCallback($isSent, $val, $this->cc, $this->bcc, $this->Subject, $body); + } + } else { + $rt = @mail($to, $this->EncodeHeader($this->SecureHeader($this->Subject)), $body, $header); + // implement call back function if it exists + $isSent = ($rt == 1) ? 1 : 0; + $this->doCallback($isSent, $to, $this->cc, $this->bcc, $this->Subject, $body); + } + } + if (isset($old_from)) { + ini_set('sendmail_from', $old_from); + } + if(!$rt) { + throw new phpmailerException($this->Lang('instantiate'), self::STOP_CRITICAL); + } + return true; + } + + /** + * Sends mail via SMTP using PhpSMTP + * Returns false if there is a bad MAIL FROM, RCPT, or DATA input. + * @param string $header The message headers + * @param string $body The message body + * @uses SMTP + * @access protected + * @return bool + */ + protected function SmtpSend($header, $body) { + require_once $this->PluginDir . 'class.smtp.php'; + $bad_rcpt = array(); + + if(!$this->SmtpConnect()) { + throw new phpmailerException($this->Lang('smtp_connect_failed'), self::STOP_CRITICAL); + } + $smtp_from = ($this->Sender == '') ? $this->From : $this->Sender; + if(!$this->smtp->Mail($smtp_from)) { + throw new phpmailerException($this->Lang('from_failed') . $smtp_from, self::STOP_CRITICAL); + } + + // Attempt to send attach all recipients + foreach($this->to as $to) { + if (!$this->smtp->Recipient($to[0])) { + $bad_rcpt[] = $to[0]; + // implement call back function if it exists + $isSent = 0; + $this->doCallback($isSent, $to[0], '', '', $this->Subject, $body); + } else { + // implement call back function if it exists + $isSent = 1; + $this->doCallback($isSent, $to[0], '', '', $this->Subject, $body); + } + } + foreach($this->cc as $cc) { + if (!$this->smtp->Recipient($cc[0])) { + $bad_rcpt[] = $cc[0]; + // implement call back function if it exists + $isSent = 0; + $this->doCallback($isSent, '', $cc[0], '', $this->Subject, $body); + } else { + // implement call back function if it exists + $isSent = 1; + $this->doCallback($isSent, '', $cc[0], '', $this->Subject, $body); + } + } + foreach($this->bcc as $bcc) { + if (!$this->smtp->Recipient($bcc[0])) { + $bad_rcpt[] = $bcc[0]; + // implement call back function if it exists + $isSent = 0; + $this->doCallback($isSent, '', '', $bcc[0], $this->Subject, $body); + } else { + // implement call back function if it exists + $isSent = 1; + $this->doCallback($isSent, '', '', $bcc[0], $this->Subject, $body); + } + } + + + if (count($bad_rcpt) > 0 ) { //Create error message for any bad addresses + $badaddresses = implode(', ', $bad_rcpt); + throw new phpmailerException($this->Lang('recipients_failed') . $badaddresses); + } + if(!$this->smtp->Data($header . $body)) { + throw new phpmailerException($this->Lang('data_not_accepted'), self::STOP_CRITICAL); + } + if($this->SMTPKeepAlive == true) { + $this->smtp->Reset(); + } + return true; + } + + /** + * Initiates a connection to an SMTP server. + * Returns false if the operation failed. + * @uses SMTP + * @access public + * @return bool + */ + public function SmtpConnect() { + if(is_null($this->smtp)) { + $this->smtp = new SMTP(); + } + + $this->smtp->do_debug = $this->SMTPDebug; + $hosts = explode(';', $this->Host); + $index = 0; + $connection = $this->smtp->Connected(); + + // Retry while there is no connection + try { + while($index < count($hosts) && !$connection) { + $hostinfo = array(); + if (preg_match('/^(.+):([0-9]+)$/', $hosts[$index], $hostinfo)) { + $host = $hostinfo[1]; + $port = $hostinfo[2]; + } else { + $host = $hosts[$index]; + $port = $this->Port; + } + + $tls = ($this->SMTPSecure == 'tls'); + $ssl = ($this->SMTPSecure == 'ssl'); + + if ($this->smtp->Connect(($ssl ? 'ssl://':'').$host, $port, $this->Timeout)) { + + $hello = ($this->Helo != '' ? $this->Helo : $this->ServerHostname()); + $this->smtp->Hello($hello); + + if ($tls) { + if (!$this->smtp->StartTLS()) { + throw new phpmailerException($this->Lang('tls')); + } + + //We must resend HELO after tls negotiation + $this->smtp->Hello($hello); + } + + $connection = true; + if ($this->SMTPAuth) { + if (!$this->smtp->Authenticate($this->Username, $this->Password)) { + throw new phpmailerException($this->Lang('authenticate')); + } + } + } + $index++; + if (!$connection) { + throw new phpmailerException($this->Lang('connect_host')); + } + } + } catch (phpmailerException $e) { + $this->smtp->Reset(); + throw $e; + } + return true; + } + + /** + * Closes the active SMTP session if one exists. + * @return void + */ + public function SmtpClose() { + if(!is_null($this->smtp)) { + if($this->smtp->Connected()) { + $this->smtp->Quit(); + $this->smtp->Close(); + } + } + } + + /** + * Sets the language for all class error messages. + * Returns false if it cannot load the language file. The default language is English. + * @param string $langcode ISO 639-1 2-character language code (e.g. Portuguese: "br") + * @param string $lang_path Path to the language file directory + * @access public + */ + function SetLanguage($langcode = 'en', $lang_path = 'language/') { + //Define full set of translatable strings + $PHPMAILER_LANG = array( + 'provide_address' => 'You must provide at least one recipient email address.', + 'mailer_not_supported' => ' mailer is not supported.', + 'execute' => 'Could not execute: ', + 'instantiate' => 'Could not instantiate mail function.', + 'authenticate' => 'SMTP Error: Could not authenticate.', + 'from_failed' => 'The following From address failed: ', + 'recipients_failed' => 'SMTP Error: The following recipients failed: ', + 'data_not_accepted' => 'SMTP Error: Data not accepted.', + 'connect_host' => 'SMTP Error: Could not connect to SMTP host.', + 'file_access' => 'Could not access file: ', + 'file_open' => 'File Error: Could not open file: ', + 'encoding' => 'Unknown encoding: ', + 'signing' => 'Signing Error: ', + 'smtp_error' => 'SMTP server error: ', + 'empty_message' => 'Message body empty', + 'invalid_address' => 'Invalid address', + 'variable_set' => 'Cannot set or reset variable: ' + ); + //Overwrite language-specific strings. This way we'll never have missing translations - no more "language string failed to load"! + $l = true; + if ($langcode != 'en') { //There is no English translation file + $l = @include $lang_path.'phpmailer.lang-'.$langcode.'.php'; + } + $this->language = $PHPMAILER_LANG; + return ($l == true); //Returns false if language not found + } + + /** + * Return the current array of language strings + * @return array + */ + public function GetTranslations() { + return $this->language; + } + + ///////////////////////////////////////////////// + // METHODS, MESSAGE CREATION + ///////////////////////////////////////////////// + + /** + * Creates recipient headers. + * @access public + * @return string + */ + public function AddrAppend($type, $addr) { + $addr_str = $type . ': '; + $addresses = array(); + foreach ($addr as $a) { + $addresses[] = $this->AddrFormat($a); + } + $addr_str .= implode(', ', $addresses); + $addr_str .= $this->LE; + + return $addr_str; + } + + /** + * Formats an address correctly. + * @access public + * @return string + */ + public function AddrFormat($addr) { + if (empty($addr[1])) { + return $this->SecureHeader($addr[0]); + } else { + return $this->EncodeHeader($this->SecureHeader($addr[1]), 'phrase') . " <" . $this->SecureHeader($addr[0]) . ">"; + } + } + + /** + * Wraps message for use with mailers that do not + * automatically perform wrapping and for quoted-printable. + * Original written by philippe. + * @param string $message The message to wrap + * @param integer $length The line length to wrap to + * @param boolean $qp_mode Whether to run in Quoted-Printable mode + * @access public + * @return string + */ + public function WrapText($message, $length, $qp_mode = false) { + $soft_break = ($qp_mode) ? sprintf(" =%s", $this->LE) : $this->LE; + // If utf-8 encoding is used, we will need to make sure we don't + // split multibyte characters when we wrap + $is_utf8 = (strtolower($this->CharSet) == "utf-8"); + + $message = $this->FixEOL($message); + if (substr($message, -1) == $this->LE) { + $message = substr($message, 0, -1); + } + + $line = explode($this->LE, $message); + $message = ''; + for ($i = 0 ;$i < count($line); $i++) { + $line_part = explode(' ', $line[$i]); + $buf = ''; + for ($e = 0; $e $length)) { + $space_left = $length - strlen($buf) - 1; + if ($e != 0) { + if ($space_left > 20) { + $len = $space_left; + if ($is_utf8) { + $len = $this->UTF8CharBoundary($word, $len); + } elseif (substr($word, $len - 1, 1) == "=") { + $len--; + } elseif (substr($word, $len - 2, 1) == "=") { + $len -= 2; + } + $part = substr($word, 0, $len); + $word = substr($word, $len); + $buf .= ' ' . $part; + $message .= $buf . sprintf("=%s", $this->LE); + } else { + $message .= $buf . $soft_break; + } + $buf = ''; + } + while (strlen($word) > 0) { + $len = $length; + if ($is_utf8) { + $len = $this->UTF8CharBoundary($word, $len); + } elseif (substr($word, $len - 1, 1) == "=") { + $len--; + } elseif (substr($word, $len - 2, 1) == "=") { + $len -= 2; + } + $part = substr($word, 0, $len); + $word = substr($word, $len); + + if (strlen($word) > 0) { + $message .= $part . sprintf("=%s", $this->LE); + } else { + $buf = $part; + } + } + } else { + $buf_o = $buf; + $buf .= ($e == 0) ? $word : (' ' . $word); + + if (strlen($buf) > $length and $buf_o != '') { + $message .= $buf_o . $soft_break; + $buf = $word; + } + } + } + $message .= $buf . $this->LE; + } + + return $message; + } + + /** + * Finds last character boundary prior to maxLength in a utf-8 + * quoted (printable) encoded string. + * Original written by Colin Brown. + * @access public + * @param string $encodedText utf-8 QP text + * @param int $maxLength find last character boundary prior to this length + * @return int + */ + public function UTF8CharBoundary($encodedText, $maxLength) { + $foundSplitPos = false; + $lookBack = 3; + while (!$foundSplitPos) { + $lastChunk = substr($encodedText, $maxLength - $lookBack, $lookBack); + $encodedCharPos = strpos($lastChunk, "="); + if ($encodedCharPos !== false) { + // Found start of encoded character byte within $lookBack block. + // Check the encoded byte value (the 2 chars after the '=') + $hex = substr($encodedText, $maxLength - $lookBack + $encodedCharPos + 1, 2); + $dec = hexdec($hex); + if ($dec < 128) { // Single byte character. + // If the encoded char was found at pos 0, it will fit + // otherwise reduce maxLength to start of the encoded char + $maxLength = ($encodedCharPos == 0) ? $maxLength : + $maxLength - ($lookBack - $encodedCharPos); + $foundSplitPos = true; + } elseif ($dec >= 192) { // First byte of a multi byte character + // Reduce maxLength to split at start of character + $maxLength = $maxLength - ($lookBack - $encodedCharPos); + $foundSplitPos = true; + } elseif ($dec < 192) { // Middle byte of a multi byte character, look further back + $lookBack += 3; + } + } else { + // No encoded character found + $foundSplitPos = true; + } + } + return $maxLength; + } + + + /** + * Set the body wrapping. + * @access public + * @return void + */ + public function SetWordWrap() { + if($this->WordWrap < 1) { + return; + } + + switch($this->message_type) { + case 'alt': + case 'alt_inline': + case 'alt_attach': + case 'alt_inline_attach': + $this->AltBody = $this->WrapText($this->AltBody, $this->WordWrap); + break; + default: + $this->Body = $this->WrapText($this->Body, $this->WordWrap); + break; + } + } + + /** + * Assembles message header. + * @access public + * @return string The assembled header + */ + public function CreateHeader() { + $result = ''; + + // Set the boundaries + $uniq_id = md5(uniqid(time())); + $this->boundary[1] = 'b1_' . $uniq_id; + $this->boundary[2] = 'b2_' . $uniq_id; + $this->boundary[3] = 'b3_' . $uniq_id; + + $result .= $this->HeaderLine('Date', self::RFCDate()); + if($this->Sender == '') { + $result .= $this->HeaderLine('Return-Path', trim($this->From)); + } else { + $result .= $this->HeaderLine('Return-Path', trim($this->Sender)); + } + + // To be created automatically by mail() + if($this->Mailer != 'mail') { + if ($this->SingleTo === true) { + foreach($this->to as $t) { + $this->SingleToArray[] = $this->AddrFormat($t); + } + } else { + if(count($this->to) > 0) { + $result .= $this->AddrAppend('To', $this->to); + } elseif (count($this->cc) == 0) { + $result .= $this->HeaderLine('To', 'undisclosed-recipients:;'); + } + } + } + + $from = array(); + $from[0][0] = trim($this->From); + $from[0][1] = $this->FromName; + $result .= $this->AddrAppend('From', $from); + + // sendmail and mail() extract Cc from the header before sending + if(count($this->cc) > 0) { + $result .= $this->AddrAppend('Cc', $this->cc); + } + + // sendmail and mail() extract Bcc from the header before sending + if((($this->Mailer == 'sendmail') || ($this->Mailer == 'mail')) && (count($this->bcc) > 0)) { + $result .= $this->AddrAppend('Bcc', $this->bcc); + } + + if(count($this->ReplyTo) > 0) { + $result .= $this->AddrAppend('Reply-to', $this->ReplyTo); + } + + // mail() sets the subject itself + if($this->Mailer != 'mail') { + $result .= $this->HeaderLine('Subject', $this->EncodeHeader($this->SecureHeader($this->Subject))); + } + + if($this->MessageID != '') { + $result .= $this->HeaderLine('Message-ID', $this->MessageID); + } else { + $result .= sprintf("Message-ID: <%s@%s>%s", $uniq_id, $this->ServerHostname(), $this->LE); + } + $result .= $this->HeaderLine('X-Priority', $this->Priority); + if($this->XMailer) { + $result .= $this->HeaderLine('X-Mailer', $this->XMailer); + } else { + $result .= $this->HeaderLine('X-Mailer', 'PHPMailer '.$this->Version.' (http://code.google.com/a/apache-extras.org/p/phpmailer/)'); + } + + if($this->ConfirmReadingTo != '') { + $result .= $this->HeaderLine('Disposition-Notification-To', '<' . trim($this->ConfirmReadingTo) . '>'); + } + + // Add custom headers + for($index = 0; $index < count($this->CustomHeader); $index++) { + $result .= $this->HeaderLine(trim($this->CustomHeader[$index][0]), $this->EncodeHeader(trim($this->CustomHeader[$index][1]))); + } + if (!$this->sign_key_file) { + $result .= $this->HeaderLine('MIME-Version', '1.0'); + $result .= $this->GetMailMIME(); + } + + return $result; + } + + /** + * Returns the message MIME. + * @access public + * @return string + */ + public function GetMailMIME() { + $result = ''; + switch($this->message_type) { + case 'plain': + $result .= $this->HeaderLine('Content-Transfer-Encoding', $this->Encoding); + $result .= $this->TextLine('Content-Type: '.$this->ContentType.'; charset="'.$this->CharSet.'"'); + break; + case 'inline': + $result .= $this->HeaderLine('Content-Type', 'multipart/related;'); + $result .= $this->TextLine("\tboundary=\"" . $this->boundary[1] . '"'); + break; + case 'attach': + case 'inline_attach': + case 'alt_attach': + case 'alt_inline_attach': + $result .= $this->HeaderLine('Content-Type', 'multipart/mixed;'); + $result .= $this->TextLine("\tboundary=\"" . $this->boundary[1] . '"'); + break; + case 'alt': + case 'alt_inline': + $result .= $this->HeaderLine('Content-Type', 'multipart/alternative;'); + $result .= $this->TextLine("\tboundary=\"" . $this->boundary[1] . '"'); + break; + } + + if($this->Mailer != 'mail') { + $result .= $this->LE.$this->LE; + } + + return $result; + } + + /** + * Assembles the message body. Returns an empty string on failure. + * @access public + * @return string The assembled message body + */ + public function CreateBody() { + $body = ''; + + if ($this->sign_key_file) { + $body .= $this->GetMailMIME(); + } + + $this->SetWordWrap(); + + switch($this->message_type) { + case 'plain': + $body .= $this->EncodeString($this->Body, $this->Encoding); + break; + case 'inline': + $body .= $this->GetBoundary($this->boundary[1], '', '', ''); + $body .= $this->EncodeString($this->Body, $this->Encoding); + $body .= $this->LE.$this->LE; + $body .= $this->AttachAll("inline", $this->boundary[1]); + break; + case 'attach': + $body .= $this->GetBoundary($this->boundary[1], '', '', ''); + $body .= $this->EncodeString($this->Body, $this->Encoding); + $body .= $this->LE.$this->LE; + $body .= $this->AttachAll("attachment", $this->boundary[1]); + break; + case 'inline_attach': + $body .= $this->TextLine("--" . $this->boundary[1]); + $body .= $this->HeaderLine('Content-Type', 'multipart/related;'); + $body .= $this->TextLine("\tboundary=\"" . $this->boundary[2] . '"'); + $body .= $this->LE; + $body .= $this->GetBoundary($this->boundary[2], '', '', ''); + $body .= $this->EncodeString($this->Body, $this->Encoding); + $body .= $this->LE.$this->LE; + $body .= $this->AttachAll("inline", $this->boundary[2]); + $body .= $this->LE; + $body .= $this->AttachAll("attachment", $this->boundary[1]); + break; + case 'alt': + $body .= $this->GetBoundary($this->boundary[1], '', 'text/plain', ''); + $body .= $this->EncodeString($this->AltBody, $this->Encoding); + $body .= $this->LE.$this->LE; + $body .= $this->GetBoundary($this->boundary[1], '', 'text/html', ''); + $body .= $this->EncodeString($this->Body, $this->Encoding); + $body .= $this->LE.$this->LE; + $body .= $this->EndBoundary($this->boundary[1]); + break; + case 'alt_inline': + $body .= $this->GetBoundary($this->boundary[1], '', 'text/plain', ''); + $body .= $this->EncodeString($this->AltBody, $this->Encoding); + $body .= $this->LE.$this->LE; + $body .= $this->TextLine("--" . $this->boundary[1]); + $body .= $this->HeaderLine('Content-Type', 'multipart/related;'); + $body .= $this->TextLine("\tboundary=\"" . $this->boundary[2] . '"'); + $body .= $this->LE; + $body .= $this->GetBoundary($this->boundary[2], '', 'text/html', ''); + $body .= $this->EncodeString($this->Body, $this->Encoding); + $body .= $this->LE.$this->LE; + $body .= $this->AttachAll("inline", $this->boundary[2]); + $body .= $this->LE; + $body .= $this->EndBoundary($this->boundary[1]); + break; + case 'alt_attach': + $body .= $this->TextLine("--" . $this->boundary[1]); + $body .= $this->HeaderLine('Content-Type', 'multipart/alternative;'); + $body .= $this->TextLine("\tboundary=\"" . $this->boundary[2] . '"'); + $body .= $this->LE; + $body .= $this->GetBoundary($this->boundary[2], '', 'text/plain', ''); + $body .= $this->EncodeString($this->AltBody, $this->Encoding); + $body .= $this->LE.$this->LE; + $body .= $this->GetBoundary($this->boundary[2], '', 'text/html', ''); + $body .= $this->EncodeString($this->Body, $this->Encoding); + $body .= $this->LE.$this->LE; + $body .= $this->EndBoundary($this->boundary[2]); + $body .= $this->LE; + $body .= $this->AttachAll("attachment", $this->boundary[1]); + break; + case 'alt_inline_attach': + $body .= $this->TextLine("--" . $this->boundary[1]); + $body .= $this->HeaderLine('Content-Type', 'multipart/alternative;'); + $body .= $this->TextLine("\tboundary=\"" . $this->boundary[2] . '"'); + $body .= $this->LE; + $body .= $this->GetBoundary($this->boundary[2], '', 'text/plain', ''); + $body .= $this->EncodeString($this->AltBody, $this->Encoding); + $body .= $this->LE.$this->LE; + $body .= $this->TextLine("--" . $this->boundary[2]); + $body .= $this->HeaderLine('Content-Type', 'multipart/related;'); + $body .= $this->TextLine("\tboundary=\"" . $this->boundary[3] . '"'); + $body .= $this->LE; + $body .= $this->GetBoundary($this->boundary[3], '', 'text/html', ''); + $body .= $this->EncodeString($this->Body, $this->Encoding); + $body .= $this->LE.$this->LE; + $body .= $this->AttachAll("inline", $this->boundary[3]); + $body .= $this->LE; + $body .= $this->EndBoundary($this->boundary[2]); + $body .= $this->LE; + $body .= $this->AttachAll("attachment", $this->boundary[1]); + break; + } + + if ($this->IsError()) { + $body = ''; + } elseif ($this->sign_key_file) { + try { + $file = tempnam('', 'mail'); + file_put_contents($file, $body); //TODO check this worked + $signed = tempnam("", "signed"); + if (@openssl_pkcs7_sign($file, $signed, "file://".$this->sign_cert_file, array("file://".$this->sign_key_file, $this->sign_key_pass), NULL)) { + @unlink($file); + @unlink($signed); + $body = file_get_contents($signed); + } else { + @unlink($file); + @unlink($signed); + throw new phpmailerException($this->Lang("signing").openssl_error_string()); + } + } catch (phpmailerException $e) { + $body = ''; + if ($this->exceptions) { + throw $e; + } + } + } + + return $body; + } + + /** + * Returns the start of a message boundary. + * @access protected + * @return string + */ + protected function GetBoundary($boundary, $charSet, $contentType, $encoding) { + $result = ''; + if($charSet == '') { + $charSet = $this->CharSet; + } + if($contentType == '') { + $contentType = $this->ContentType; + } + if($encoding == '') { + $encoding = $this->Encoding; + } + $result .= $this->TextLine('--' . $boundary); + $result .= sprintf("Content-Type: %s; charset=\"%s\"", $contentType, $charSet); + $result .= $this->LE; + $result .= $this->HeaderLine('Content-Transfer-Encoding', $encoding); + $result .= $this->LE; + + return $result; + } + + /** + * Returns the end of a message boundary. + * @access protected + * @return string + */ + protected function EndBoundary($boundary) { + return $this->LE . '--' . $boundary . '--' . $this->LE; + } + + /** + * Sets the message type. + * @access protected + * @return void + */ + protected function SetMessageType() { + $this->message_type = array(); + if($this->AlternativeExists()) $this->message_type[] = "alt"; + if($this->InlineImageExists()) $this->message_type[] = "inline"; + if($this->AttachmentExists()) $this->message_type[] = "attach"; + $this->message_type = implode("_", $this->message_type); + if($this->message_type == "") $this->message_type = "plain"; + } + + /** + * Returns a formatted header line. + * @access public + * @return string + */ + public function HeaderLine($name, $value) { + return $name . ': ' . $value . $this->LE; + } + + /** + * Returns a formatted mail line. + * @access public + * @return string + */ + public function TextLine($value) { + return $value . $this->LE; + } + + ///////////////////////////////////////////////// + // CLASS METHODS, ATTACHMENTS + ///////////////////////////////////////////////// + + /** + * Adds an attachment from a path on the filesystem. + * Returns false if the file could not be found + * or accessed. + * @param string $path Path to the attachment. + * @param string $name Overrides the attachment name. + * @param string $encoding File encoding (see $Encoding). + * @param string $type File extension (MIME) type. + * @return bool + */ + public function AddAttachment($path, $name = '', $encoding = 'base64', $type = 'application/octet-stream') { + try { + if ( !@is_file($path) ) { + throw new phpmailerException($this->Lang('file_access') . $path, self::STOP_CONTINUE); + } + $filename = basename($path); + if ( $name == '' ) { + $name = $filename; + } + + $this->attachment[] = array( + 0 => $path, + 1 => $filename, + 2 => $name, + 3 => $encoding, + 4 => $type, + 5 => false, // isStringAttachment + 6 => 'attachment', + 7 => 0 + ); + + } catch (phpmailerException $e) { + $this->SetError($e->getMessage()); + if ($this->exceptions) { + throw $e; + } + echo $e->getMessage()."\n"; + if ( $e->getCode() == self::STOP_CRITICAL ) { + return false; + } + } + return true; + } + + /** + * Return the current array of attachments + * @return array + */ + public function GetAttachments() { + return $this->attachment; + } + + /** + * Attaches all fs, string, and binary attachments to the message. + * Returns an empty string on failure. + * @access protected + * @return string + */ + protected function AttachAll($disposition_type, $boundary) { + // Return text of body + $mime = array(); + $cidUniq = array(); + $incl = array(); + + // Add all attachments + foreach ($this->attachment as $attachment) { + // CHECK IF IT IS A VALID DISPOSITION_FILTER + if($attachment[6] == $disposition_type) { + // Check for string attachment + $bString = $attachment[5]; + if ($bString) { + $string = $attachment[0]; + } else { + $path = $attachment[0]; + } + + $inclhash = md5(serialize($attachment)); + if (in_array($inclhash, $incl)) { continue; } + $incl[] = $inclhash; + $filename = $attachment[1]; + $name = $attachment[2]; + $encoding = $attachment[3]; + $type = $attachment[4]; + $disposition = $attachment[6]; + $cid = $attachment[7]; + if ( $disposition == 'inline' && isset($cidUniq[$cid]) ) { continue; } + $cidUniq[$cid] = true; + + $mime[] = sprintf("--%s%s", $boundary, $this->LE); + $mime[] = sprintf("Content-Type: %s; name=\"%s\"%s", $type, $this->EncodeHeader($this->SecureHeader($name)), $this->LE); + $mime[] = sprintf("Content-Transfer-Encoding: %s%s", $encoding, $this->LE); + + if($disposition == 'inline') { + $mime[] = sprintf("Content-ID: <%s>%s", $cid, $this->LE); + } + + $mime[] = sprintf("Content-Disposition: %s; filename=\"%s\"%s", $disposition, $this->EncodeHeader($this->SecureHeader($name)), $this->LE.$this->LE); + + // Encode as string attachment + if($bString) { + $mime[] = $this->EncodeString($string, $encoding); + if($this->IsError()) { + return ''; + } + $mime[] = $this->LE.$this->LE; + } else { + $mime[] = $this->EncodeFile($path, $encoding); + if($this->IsError()) { + return ''; + } + $mime[] = $this->LE.$this->LE; + } + } + } + + $mime[] = sprintf("--%s--%s", $boundary, $this->LE); + + return implode("", $mime); + } + + /** + * Encodes attachment in requested format. + * Returns an empty string on failure. + * @param string $path The full path to the file + * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable' + * @see EncodeFile() + * @access protected + * @return string + */ + protected function EncodeFile($path, $encoding = 'base64') { + try { + if (!is_readable($path)) { + throw new phpmailerException($this->Lang('file_open') . $path, self::STOP_CONTINUE); + } + if (function_exists('get_magic_quotes')) { + function get_magic_quotes() { + return false; + } + } + if (version_compare(PHP_VERSION, '5.3.0', '<')) { + $magic_quotes = get_magic_quotes_runtime(); + set_magic_quotes_runtime(0); + } + $file_buffer = file_get_contents($path); + $file_buffer = $this->EncodeString($file_buffer, $encoding); + if (version_compare(PHP_VERSION, '5.3.0', '<')) { + set_magic_quotes_runtime($magic_quotes); + } + return $file_buffer; + } catch (Exception $e) { + $this->SetError($e->getMessage()); + return ''; + } + } + + /** + * Encodes string to requested format. + * Returns an empty string on failure. + * @param string $str The text to encode + * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable' + * @access public + * @return string + */ + public function EncodeString($str, $encoding = 'base64') { + $encoded = ''; + switch(strtolower($encoding)) { + case 'base64': + $encoded = chunk_split(base64_encode($str), 76, $this->LE); + break; + case '7bit': + case '8bit': + $encoded = $this->FixEOL($str); + //Make sure it ends with a line break + if (substr($encoded, -(strlen($this->LE))) != $this->LE) + $encoded .= $this->LE; + break; + case 'binary': + $encoded = $str; + break; + case 'quoted-printable': + $encoded = $this->EncodeQP($str); + break; + default: + $this->SetError($this->Lang('encoding') . $encoding); + break; + } + return $encoded; + } + + /** + * Encode a header string to best (shortest) of Q, B, quoted or none. + * @access public + * @return string + */ + public function EncodeHeader($str, $position = 'text') { + $x = 0; + + switch (strtolower($position)) { + case 'phrase': + if (!preg_match('/[\200-\377]/', $str)) { + // Can't use addslashes as we don't know what value has magic_quotes_sybase + $encoded = addcslashes($str, "\0..\37\177\\\""); + if (($str == $encoded) && !preg_match('/[^A-Za-z0-9!#$%&\'*+\/=?^_`{|}~ -]/', $str)) { + return ($encoded); + } else { + return ("\"$encoded\""); + } + } + $x = preg_match_all('/[^\040\041\043-\133\135-\176]/', $str, $matches); + break; + case 'comment': + $x = preg_match_all('/[()"]/', $str, $matches); + // Fall-through + case 'text': + default: + $x += preg_match_all('/[\000-\010\013\014\016-\037\177-\377]/', $str, $matches); + break; + } + + if ($x == 0) { + return ($str); + } + + $maxlen = 75 - 7 - strlen($this->CharSet); + // Try to select the encoding which should produce the shortest output + if (strlen($str)/3 < $x) { + $encoding = 'B'; + if (function_exists('mb_strlen') && $this->HasMultiBytes($str)) { + // Use a custom function which correctly encodes and wraps long + // multibyte strings without breaking lines within a character + $encoded = $this->Base64EncodeWrapMB($str); + } else { + $encoded = base64_encode($str); + $maxlen -= $maxlen % 4; + $encoded = trim(chunk_split($encoded, $maxlen, "\n")); + } + } else { + $encoding = 'Q'; + $encoded = $this->EncodeQ($str, $position); + $encoded = $this->WrapText($encoded, $maxlen, true); + $encoded = str_replace('='.$this->LE, "\n", trim($encoded)); + } + + $encoded = preg_replace('/^(.*)$/m', " =?".$this->CharSet."?$encoding?\\1?=", $encoded); + $encoded = trim(str_replace("\n", $this->LE, $encoded)); + + return $encoded; + } + + /** + * Checks if a string contains multibyte characters. + * @access public + * @param string $str multi-byte text to wrap encode + * @return bool + */ + public function HasMultiBytes($str) { + if (function_exists('mb_strlen')) { + return (strlen($str) > mb_strlen($str, $this->CharSet)); + } else { // Assume no multibytes (we can't handle without mbstring functions anyway) + return false; + } + } + + /** + * Correctly encodes and wraps long multibyte strings for mail headers + * without breaking lines within a character. + * Adapted from a function by paravoid at http://uk.php.net/manual/en/function.mb-encode-mimeheader.php + * @access public + * @param string $str multi-byte text to wrap encode + * @return string + */ + public function Base64EncodeWrapMB($str) { + $start = "=?".$this->CharSet."?B?"; + $end = "?="; + $encoded = ""; + + $mb_length = mb_strlen($str, $this->CharSet); + // Each line must have length <= 75, including $start and $end + $length = 75 - strlen($start) - strlen($end); + // Average multi-byte ratio + $ratio = $mb_length / strlen($str); + // Base64 has a 4:3 ratio + $offset = $avgLength = floor($length * $ratio * .75); + + for ($i = 0; $i < $mb_length; $i += $offset) { + $lookBack = 0; + + do { + $offset = $avgLength - $lookBack; + $chunk = mb_substr($str, $i, $offset, $this->CharSet); + $chunk = base64_encode($chunk); + $lookBack++; + } + while (strlen($chunk) > $length); + + $encoded .= $chunk . $this->LE; + } + + // Chomp the last linefeed + $encoded = substr($encoded, 0, -strlen($this->LE)); + return $encoded; + } + + /** + * Encode string to quoted-printable. + * Only uses standard PHP, slow, but will always work + * @access public + * @param string $string the text to encode + * @param integer $line_max Number of chars allowed on a line before wrapping + * @return string + */ + public function EncodeQPphp( $input = '', $line_max = 76, $space_conv = false) { + $hex = array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'); + $lines = preg_split('/(?:\r\n|\r|\n)/', $input); + $eol = "\r\n"; + $escape = '='; + $output = ''; + while( list(, $line) = each($lines) ) { + $linlen = strlen($line); + $newline = ''; + for($i = 0; $i < $linlen; $i++) { + $c = substr( $line, $i, 1 ); + $dec = ord( $c ); + if ( ( $i == 0 ) && ( $dec == 46 ) ) { // convert first point in the line into =2E + $c = '=2E'; + } + if ( $dec == 32 ) { + if ( $i == ( $linlen - 1 ) ) { // convert space at eol only + $c = '=20'; + } else if ( $space_conv ) { + $c = '=20'; + } + } elseif ( ($dec == 61) || ($dec < 32 ) || ($dec > 126) ) { // always encode "\t", which is *not* required + $h2 = floor($dec/16); + $h1 = floor($dec%16); + $c = $escape.$hex[$h2].$hex[$h1]; + } + if ( (strlen($newline) + strlen($c)) >= $line_max ) { // CRLF is not counted + $output .= $newline.$escape.$eol; // soft line break; " =\r\n" is okay + $newline = ''; + // check if newline first character will be point or not + if ( $dec == 46 ) { + $c = '=2E'; + } + } + $newline .= $c; + } // end of for + $output .= $newline.$eol; + } // end of while + return $output; + } + + /** + * Encode string to RFC2045 (6.7) quoted-printable format + * Uses a PHP5 stream filter to do the encoding about 64x faster than the old version + * Also results in same content as you started with after decoding + * @see EncodeQPphp() + * @access public + * @param string $string the text to encode + * @param integer $line_max Number of chars allowed on a line before wrapping + * @param boolean $space_conv Dummy param for compatibility with existing EncodeQP function + * @return string + * @author Marcus Bointon + */ + public function EncodeQP($string, $line_max = 76, $space_conv = false) { + if (function_exists('quoted_printable_encode')) { //Use native function if it's available (>= PHP5.3) + return quoted_printable_encode($string); + } + $filters = stream_get_filters(); + if (!in_array('convert.*', $filters)) { //Got convert stream filter? + return $this->EncodeQPphp($string, $line_max, $space_conv); //Fall back to old implementation + } + $fp = fopen('php://temp/', 'r+'); + $string = preg_replace('/\r\n?/', $this->LE, $string); //Normalise line breaks + $params = array('line-length' => $line_max, 'line-break-chars' => $this->LE); + $s = stream_filter_append($fp, 'convert.quoted-printable-encode', STREAM_FILTER_READ, $params); + fputs($fp, $string); + rewind($fp); + $out = stream_get_contents($fp); + stream_filter_remove($s); + $out = preg_replace('/^\./m', '=2E', $out); //Encode . if it is first char on a line, workaround for bug in Exchange + fclose($fp); + return $out; + } + + /** + * Encode string to q encoding. + * @link http://tools.ietf.org/html/rfc2047 + * @param string $str the text to encode + * @param string $position Where the text is going to be used, see the RFC for what that means + * @access public + * @return string + */ + public function EncodeQ($str, $position = 'text') { + // There should not be any EOL in the string + $encoded = preg_replace('/[\r\n]*/', '', $str); + + switch (strtolower($position)) { + case 'phrase': + $encoded = preg_replace("/([^A-Za-z0-9!*+\/ -])/e", "'='.sprintf('%02X', ord('\\1'))", $encoded); + break; + case 'comment': + $encoded = preg_replace("/([\(\)\"])/e", "'='.sprintf('%02X', ord('\\1'))", $encoded); + case 'text': + default: + // Replace every high ascii, control =, ? and _ characters + //TODO using /e (equivalent to eval()) is probably not a good idea + $encoded = preg_replace('/([\000-\011\013\014\016-\037\075\077\137\177-\377])/e', + "'='.sprintf('%02X', ord(stripslashes('\\1')))", $encoded); + break; + } + + // Replace every spaces to _ (more readable than =20) + $encoded = str_replace(' ', '_', $encoded); + + return $encoded; + } + + /** + * Adds a string or binary attachment (non-filesystem) to the list. + * This method can be used to attach ascii or binary data, + * such as a BLOB record from a database. + * @param string $string String attachment data. + * @param string $filename Name of the attachment. + * @param string $encoding File encoding (see $Encoding). + * @param string $type File extension (MIME) type. + * @return void + */ + public function AddStringAttachment($string, $filename, $encoding = 'base64', $type = 'application/octet-stream') { + // Append to $attachment array + $this->attachment[] = array( + 0 => $string, + 1 => $filename, + 2 => basename($filename), + 3 => $encoding, + 4 => $type, + 5 => true, // isStringAttachment + 6 => 'attachment', + 7 => 0 + ); + } + + /** + * Adds an embedded attachment. This can include images, sounds, and + * just about any other document. Make sure to set the $type to an + * image type. For JPEG images use "image/jpeg" and for GIF images + * use "image/gif". + * @param string $path Path to the attachment. + * @param string $cid Content ID of the attachment. Use this to identify + * the Id for accessing the image in an HTML form. + * @param string $name Overrides the attachment name. + * @param string $encoding File encoding (see $Encoding). + * @param string $type File extension (MIME) type. + * @return bool + */ + public function AddEmbeddedImage($path, $cid, $name = '', $encoding = 'base64', $type = 'application/octet-stream') { + + if ( !@is_file($path) ) { + $this->SetError($this->Lang('file_access') . $path); + return false; + } + + $filename = basename($path); + if ( $name == '' ) { + $name = $filename; + } + + // Append to $attachment array + $this->attachment[] = array( + 0 => $path, + 1 => $filename, + 2 => $name, + 3 => $encoding, + 4 => $type, + 5 => false, // isStringAttachment + 6 => 'inline', + 7 => $cid + ); + + return true; + } + + public function AddStringEmbeddedImage($string, $cid, $filename = '', $encoding = 'base64', $type = 'application/octet-stream') { + // Append to $attachment array + $this->attachment[] = array( + 0 => $string, + 1 => $filename, + 2 => basename($filename), + 3 => $encoding, + 4 => $type, + 5 => true, // isStringAttachment + 6 => 'inline', + 7 => $cid + ); + } + + /** + * Returns true if an inline attachment is present. + * @access public + * @return bool + */ + public function InlineImageExists() { + foreach($this->attachment as $attachment) { + if ($attachment[6] == 'inline') { + return true; + } + } + return false; + } + + public function AttachmentExists() { + foreach($this->attachment as $attachment) { + if ($attachment[6] == 'attachment') { + return true; + } + } + return false; + } + + public function AlternativeExists() { + return strlen($this->AltBody)>0; + } + + ///////////////////////////////////////////////// + // CLASS METHODS, MESSAGE RESET + ///////////////////////////////////////////////// + + /** + * Clears all recipients assigned in the TO array. Returns void. + * @return void + */ + public function ClearAddresses() { + foreach($this->to as $to) { + unset($this->all_recipients[strtolower($to[0])]); + } + $this->to = array(); + } + + /** + * Clears all recipients assigned in the CC array. Returns void. + * @return void + */ + public function ClearCCs() { + foreach($this->cc as $cc) { + unset($this->all_recipients[strtolower($cc[0])]); + } + $this->cc = array(); + } + + /** + * Clears all recipients assigned in the BCC array. Returns void. + * @return void + */ + public function ClearBCCs() { + foreach($this->bcc as $bcc) { + unset($this->all_recipients[strtolower($bcc[0])]); + } + $this->bcc = array(); + } + + /** + * Clears all recipients assigned in the ReplyTo array. Returns void. + * @return void + */ + public function ClearReplyTos() { + $this->ReplyTo = array(); + } + + /** + * Clears all recipients assigned in the TO, CC and BCC + * array. Returns void. + * @return void + */ + public function ClearAllRecipients() { + $this->to = array(); + $this->cc = array(); + $this->bcc = array(); + $this->all_recipients = array(); + } + + /** + * Clears all previously set filesystem, string, and binary + * attachments. Returns void. + * @return void + */ + public function ClearAttachments() { + $this->attachment = array(); + } + + /** + * Clears all custom headers. Returns void. + * @return void + */ + public function ClearCustomHeaders() { + $this->CustomHeader = array(); + } + + ///////////////////////////////////////////////// + // CLASS METHODS, MISCELLANEOUS + ///////////////////////////////////////////////// + + /** + * Adds the error message to the error container. + * @access protected + * @return void + */ + protected function SetError($msg) { + $this->error_count++; + if ($this->Mailer == 'smtp' and !is_null($this->smtp)) { + $lasterror = $this->smtp->getError(); + if (!empty($lasterror) and array_key_exists('smtp_msg', $lasterror)) { + $msg .= '

' . $this->Lang('smtp_error') . $lasterror['smtp_msg'] . "

\n"; + } + } + $this->ErrorInfo = $msg; + } + + /** + * Returns the proper RFC 822 formatted date. + * @access public + * @return string + * @static + */ + public static function RFCDate() { + $tz = date('Z'); + $tzs = ($tz < 0) ? '-' : '+'; + $tz = abs($tz); + $tz = (int)($tz/3600)*100 + ($tz%3600)/60; + $result = sprintf("%s %s%04d", date('D, j M Y H:i:s'), $tzs, $tz); + + return $result; + } + + /** + * Returns the server hostname or 'localhost.localdomain' if unknown. + * @access protected + * @return string + */ + protected function ServerHostname() { + if (!empty($this->Hostname)) { + $result = $this->Hostname; + } elseif (isset($_SERVER['SERVER_NAME'])) { + $result = $_SERVER['SERVER_NAME']; + } else { + $result = 'localhost.localdomain'; + } + + return $result; + } + + /** + * Returns a message in the appropriate language. + * @access protected + * @return string + */ + protected function Lang($key) { + if(count($this->language) < 1) { + $this->SetLanguage('en'); // set the default language + } + + if(isset($this->language[$key])) { + return $this->language[$key]; + } else { + return 'Language string failed to load: ' . $key; + } + } + + /** + * Returns true if an error occurred. + * @access public + * @return bool + */ + public function IsError() { + return ($this->error_count > 0); + } + + /** + * Changes every end of line from CR or LF to CRLF. + * @access public + * @return string + */ + public function FixEOL($str) { + $str = str_replace("\r\n", "\n", $str); + $str = str_replace("\r", "\n", $str); + $str = str_replace("\n", $this->LE, $str); + return $str; + } + + /** + * Adds a custom header. + * @access public + * @return void + */ + public function AddCustomHeader($custom_header) { + $this->CustomHeader[] = explode(':', $custom_header, 2); + } + + /** + * Evaluates the message and returns modifications for inline images and backgrounds + * @access public + * @return $message + */ + public function MsgHTML($message, $basedir = '') { + preg_match_all("/(src|background)=\"(.*)\"/Ui", $message, $images); + if(isset($images[2])) { + foreach($images[2] as $i => $url) { + // do not change urls for absolute images (thanks to corvuscorax) + if (!preg_match('#^[A-z]+://#', $url)) { + $filename = basename($url); + $directory = dirname($url); + ($directory == '.') ? $directory='': ''; + $cid = 'cid:' . md5($filename); + $ext = pathinfo($filename, PATHINFO_EXTENSION); + $mimeType = self::_mime_types($ext); + if ( strlen($basedir) > 1 && substr($basedir, -1) != '/') { $basedir .= '/'; } + if ( strlen($directory) > 1 && substr($directory, -1) != '/') { $directory .= '/'; } + if ( $this->AddEmbeddedImage($basedir.$directory.$filename, md5($filename), $filename, 'base64', $mimeType) ) { + $message = preg_replace("/".$images[1][$i]."=\"".preg_quote($url, '/')."\"/Ui", $images[1][$i]."=\"".$cid."\"", $message); + } + } + } + } + $this->IsHTML(true); + $this->Body = $message; + $textMsg = trim(strip_tags(preg_replace('/<(head|title|style|script)[^>]*>.*?<\/\\1>/s', '', $message))); + if (!empty($textMsg) && empty($this->AltBody)) { + $this->AltBody = html_entity_decode($textMsg); + } + if (empty($this->AltBody)) { + $this->AltBody = 'To view this email message, open it in a program that understands HTML!' . "\n\n"; + } + } + + /** + * Gets the MIME type of the embedded or inline image + * @param string File extension + * @access public + * @return string MIME type of ext + * @static + */ + public static function _mime_types($ext = '') { + $mimes = array( + 'hqx' => 'application/mac-binhex40', + 'cpt' => 'application/mac-compactpro', + 'doc' => 'application/msword', + 'bin' => 'application/macbinary', + 'dms' => 'application/octet-stream', + 'lha' => 'application/octet-stream', + 'lzh' => 'application/octet-stream', + 'exe' => 'application/octet-stream', + 'class' => 'application/octet-stream', + 'psd' => 'application/octet-stream', + 'so' => 'application/octet-stream', + 'sea' => 'application/octet-stream', + 'dll' => 'application/octet-stream', + 'oda' => 'application/oda', + 'pdf' => 'application/pdf', + 'ai' => 'application/postscript', + 'eps' => 'application/postscript', + 'ps' => 'application/postscript', + 'smi' => 'application/smil', + 'smil' => 'application/smil', + 'mif' => 'application/vnd.mif', + 'xls' => 'application/vnd.ms-excel', + 'ppt' => 'application/vnd.ms-powerpoint', + 'wbxml' => 'application/vnd.wap.wbxml', + 'wmlc' => 'application/vnd.wap.wmlc', + 'dcr' => 'application/x-director', + 'dir' => 'application/x-director', + 'dxr' => 'application/x-director', + 'dvi' => 'application/x-dvi', + 'gtar' => 'application/x-gtar', + 'php' => 'application/x-httpd-php', + 'php4' => 'application/x-httpd-php', + 'php3' => 'application/x-httpd-php', + 'phtml' => 'application/x-httpd-php', + 'phps' => 'application/x-httpd-php-source', + 'js' => 'application/x-javascript', + 'swf' => 'application/x-shockwave-flash', + 'sit' => 'application/x-stuffit', + 'tar' => 'application/x-tar', + 'tgz' => 'application/x-tar', + 'xhtml' => 'application/xhtml+xml', + 'xht' => 'application/xhtml+xml', + 'zip' => 'application/zip', + 'mid' => 'audio/midi', + 'midi' => 'audio/midi', + 'mpga' => 'audio/mpeg', + 'mp2' => 'audio/mpeg', + 'mp3' => 'audio/mpeg', + 'aif' => 'audio/x-aiff', + 'aiff' => 'audio/x-aiff', + 'aifc' => 'audio/x-aiff', + 'ram' => 'audio/x-pn-realaudio', + 'rm' => 'audio/x-pn-realaudio', + 'rpm' => 'audio/x-pn-realaudio-plugin', + 'ra' => 'audio/x-realaudio', + 'rv' => 'video/vnd.rn-realvideo', + 'wav' => 'audio/x-wav', + 'bmp' => 'image/bmp', + 'gif' => 'image/gif', + 'jpeg' => 'image/jpeg', + 'jpg' => 'image/jpeg', + 'jpe' => 'image/jpeg', + 'png' => 'image/png', + 'tiff' => 'image/tiff', + 'tif' => 'image/tiff', + 'css' => 'text/css', + 'html' => 'text/html', + 'htm' => 'text/html', + 'shtml' => 'text/html', + 'txt' => 'text/plain', + 'text' => 'text/plain', + 'log' => 'text/plain', + 'rtx' => 'text/richtext', + 'rtf' => 'text/rtf', + 'xml' => 'text/xml', + 'xsl' => 'text/xml', + 'mpeg' => 'video/mpeg', + 'mpg' => 'video/mpeg', + 'mpe' => 'video/mpeg', + 'qt' => 'video/quicktime', + 'mov' => 'video/quicktime', + 'avi' => 'video/x-msvideo', + 'movie' => 'video/x-sgi-movie', + 'doc' => 'application/msword', + 'word' => 'application/msword', + 'xl' => 'application/excel', + 'eml' => 'message/rfc822' + ); + return (!isset($mimes[strtolower($ext)])) ? 'application/octet-stream' : $mimes[strtolower($ext)]; + } + + /** + * Set (or reset) Class Objects (variables) + * + * Usage Example: + * $page->set('X-Priority', '3'); + * + * @access public + * @param string $name Parameter Name + * @param mixed $value Parameter Value + * NOTE: will not work with arrays, there are no arrays to set/reset + * @todo Should this not be using __set() magic function? + */ + public function set($name, $value = '') { + try { + if (isset($this->$name) ) { + $this->$name = $value; + } else { + throw new phpmailerException($this->Lang('variable_set') . $name, self::STOP_CRITICAL); + } + } catch (Exception $e) { + $this->SetError($e->getMessage()); + if ($e->getCode() == self::STOP_CRITICAL) { + return false; + } + } + return true; + } + + /** + * Strips newlines to prevent header injection. + * @access public + * @param string $str String + * @return string + */ + public function SecureHeader($str) { + $str = str_replace("\r", '', $str); + $str = str_replace("\n", '', $str); + return trim($str); + } + + /** + * Set the private key file and password to sign the message. + * + * @access public + * @param string $key_filename Parameter File Name + * @param string $key_pass Password for private key + */ + public function Sign($cert_filename, $key_filename, $key_pass) { + $this->sign_cert_file = $cert_filename; + $this->sign_key_file = $key_filename; + $this->sign_key_pass = $key_pass; + } + + /** + * Set the private key file and password to sign the message. + * + * @access public + * @param string $key_filename Parameter File Name + * @param string $key_pass Password for private key + */ + public function DKIM_QP($txt) { + $tmp = ''; + $line = ''; + for ($i = 0; $i < strlen($txt); $i++) { + $ord = ord($txt[$i]); + if ( ((0x21 <= $ord) && ($ord <= 0x3A)) || $ord == 0x3C || ((0x3E <= $ord) && ($ord <= 0x7E)) ) { + $line .= $txt[$i]; + } else { + $line .= "=".sprintf("%02X", $ord); + } + } + return $line; + } + + /** + * Generate DKIM signature + * + * @access public + * @param string $s Header + */ + public function DKIM_Sign($s) { + $privKeyStr = file_get_contents($this->DKIM_private); + if ($this->DKIM_passphrase != '') { + $privKey = openssl_pkey_get_private($privKeyStr, $this->DKIM_passphrase); + } else { + $privKey = $privKeyStr; + } + if (openssl_sign($s, $signature, $privKey)) { + return base64_encode($signature); + } + } + + /** + * Generate DKIM Canonicalization Header + * + * @access public + * @param string $s Header + */ + public function DKIM_HeaderC($s) { + $s = preg_replace("/\r\n\s+/", " ", $s); + $lines = explode("\r\n", $s); + foreach ($lines as $key => $line) { + list($heading, $value) = explode(":", $line, 2); + $heading = strtolower($heading); + $value = preg_replace("/\s+/", " ", $value) ; // Compress useless spaces + $lines[$key] = $heading.":".trim($value) ; // Don't forget to remove WSP around the value + } + $s = implode("\r\n", $lines); + return $s; + } + + /** + * Generate DKIM Canonicalization Body + * + * @access public + * @param string $body Message Body + */ + public function DKIM_BodyC($body) { + if ($body == '') return "\r\n"; + // stabilize line endings + $body = str_replace("\r\n", "\n", $body); + $body = str_replace("\n", "\r\n", $body); + // END stabilize line endings + while (substr($body, strlen($body) - 4, 4) == "\r\n\r\n") { + $body = substr($body, 0, strlen($body) - 2); + } + return $body; + } + + /** + * Create the DKIM header, body, as new header + * + * @access public + * @param string $headers_line Header lines + * @param string $subject Subject + * @param string $body Body + */ + public function DKIM_Add($headers_line, $subject, $body) { + $DKIMsignatureType = 'rsa-sha1'; // Signature & hash algorithms + $DKIMcanonicalization = 'relaxed/simple'; // Canonicalization of header/body + $DKIMquery = 'dns/txt'; // Query method + $DKIMtime = time() ; // Signature Timestamp = seconds since 00:00:00 - Jan 1, 1970 (UTC time zone) + $subject_header = "Subject: $subject"; + $headers = explode($this->LE, $headers_line); + foreach($headers as $header) { + if (strpos($header, 'From:') === 0) { + $from_header = $header; + } elseif (strpos($header, 'To:') === 0) { + $to_header = $header; + } + } + $from = str_replace('|', '=7C', $this->DKIM_QP($from_header)); + $to = str_replace('|', '=7C', $this->DKIM_QP($to_header)); + $subject = str_replace('|', '=7C', $this->DKIM_QP($subject_header)) ; // Copied header fields (dkim-quoted-printable + $body = $this->DKIM_BodyC($body); + $DKIMlen = strlen($body) ; // Length of body + $DKIMb64 = base64_encode(pack("H*", sha1($body))) ; // Base64 of packed binary SHA-1 hash of body + $ident = ($this->DKIM_identity == '')? '' : " i=" . $this->DKIM_identity . ";"; + $dkimhdrs = "DKIM-Signature: v=1; a=" . $DKIMsignatureType . "; q=" . $DKIMquery . "; l=" . $DKIMlen . "; s=" . $this->DKIM_selector . ";\r\n". + "\tt=" . $DKIMtime . "; c=" . $DKIMcanonicalization . ";\r\n". + "\th=From:To:Subject;\r\n". + "\td=" . $this->DKIM_domain . ";" . $ident . "\r\n". + "\tz=$from\r\n". + "\t|$to\r\n". + "\t|$subject;\r\n". + "\tbh=" . $DKIMb64 . ";\r\n". + "\tb="; + $toSign = $this->DKIM_HeaderC($from_header . "\r\n" . $to_header . "\r\n" . $subject_header . "\r\n" . $dkimhdrs); + $signed = $this->DKIM_Sign($toSign); + return "X-PHPMAILER-DKIM: phpmailer.worxware.com\r\n".$dkimhdrs.$signed."\r\n"; + } + + protected function doCallback($isSent, $to, $cc, $bcc, $subject, $body) { + if (!empty($this->action_function) && function_exists($this->action_function)) { + $params = array($isSent, $to, $cc, $bcc, $subject, $body); + call_user_func_array($this->action_function, $params); + } + } +} + +class phpmailerException extends Exception { + public function errorMessage() { + $errorMsg = '' . $this->getMessage() . "
\n"; + return $errorMsg; + } +} +?> diff --git a/3rdparty/class.smtp.php b/3rdparty/class.smtp.php new file mode 100644 index 0000000000..07c275936c --- /dev/null +++ b/3rdparty/class.smtp.php @@ -0,0 +1,817 @@ +smtp_conn = 0; + $this->error = null; + $this->helo_rply = null; + + $this->do_debug = 0; + } + + ///////////////////////////////////////////////// + // CONNECTION FUNCTIONS + ///////////////////////////////////////////////// + + /** + * Connect to the server specified on the port specified. + * If the port is not specified use the default SMTP_PORT. + * If tval is specified then a connection will try and be + * established with the server for that number of seconds. + * If tval is not specified the default is 30 seconds to + * try on the connection. + * + * SMTP CODE SUCCESS: 220 + * SMTP CODE FAILURE: 421 + * @access public + * @return bool + */ + public function Connect($host, $port = 0, $tval = 30) { + // set the error val to null so there is no confusion + $this->error = null; + + // make sure we are __not__ connected + if($this->connected()) { + // already connected, generate error + $this->error = array("error" => "Already connected to a server"); + return false; + } + + if(empty($port)) { + $port = $this->SMTP_PORT; + } + + // connect to the smtp server + $this->smtp_conn = @fsockopen($host, // the host of the server + $port, // the port to use + $errno, // error number if any + $errstr, // error message if any + $tval); // give up after ? secs + // verify we connected properly + if(empty($this->smtp_conn)) { + $this->error = array("error" => "Failed to connect to server", + "errno" => $errno, + "errstr" => $errstr); + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $this->error["error"] . ": $errstr ($errno)" . $this->CRLF . '
'; + } + return false; + } + + // SMTP server can take longer to respond, give longer timeout for first read + // Windows does not have support for this timeout function + if(substr(PHP_OS, 0, 3) != "WIN") + socket_set_timeout($this->smtp_conn, $tval, 0); + + // get any announcement + $announce = $this->get_lines(); + + if($this->do_debug >= 2) { + echo "SMTP -> FROM SERVER:" . $announce . $this->CRLF . '
'; + } + + return true; + } + + /** + * Initiate a TLS communication with the server. + * + * SMTP CODE 220 Ready to start TLS + * SMTP CODE 501 Syntax error (no parameters allowed) + * SMTP CODE 454 TLS not available due to temporary reason + * @access public + * @return bool success + */ + public function StartTLS() { + $this->error = null; # to avoid confusion + + if(!$this->connected()) { + $this->error = array("error" => "Called StartTLS() without being connected"); + return false; + } + + fputs($this->smtp_conn,"STARTTLS" . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply,0,3); + + if($this->do_debug >= 2) { + echo "SMTP -> FROM SERVER:" . $rply . $this->CRLF . '
'; + } + + if($code != 220) { + $this->error = + array("error" => "STARTTLS not accepted from server", + "smtp_code" => $code, + "smtp_msg" => substr($rply,4)); + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
'; + } + return false; + } + + // Begin encrypted connection + if(!stream_socket_enable_crypto($this->smtp_conn, true, STREAM_CRYPTO_METHOD_TLS_CLIENT)) { + return false; + } + + return true; + } + + /** + * Performs SMTP authentication. Must be run after running the + * Hello() method. Returns true if successfully authenticated. + * @access public + * @return bool + */ + public function Authenticate($username, $password) { + // Start authentication + fputs($this->smtp_conn,"AUTH LOGIN" . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply,0,3); + + if($code != 334) { + $this->error = + array("error" => "AUTH not accepted from server", + "smtp_code" => $code, + "smtp_msg" => substr($rply,4)); + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
'; + } + return false; + } + + // Send encoded username + fputs($this->smtp_conn, base64_encode($username) . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply,0,3); + + if($code != 334) { + $this->error = + array("error" => "Username not accepted from server", + "smtp_code" => $code, + "smtp_msg" => substr($rply,4)); + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
'; + } + return false; + } + + // Send encoded password + fputs($this->smtp_conn, base64_encode($password) . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply,0,3); + + if($code != 235) { + $this->error = + array("error" => "Password not accepted from server", + "smtp_code" => $code, + "smtp_msg" => substr($rply,4)); + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
'; + } + return false; + } + + return true; + } + + /** + * Returns true if connected to a server otherwise false + * @access public + * @return bool + */ + public function Connected() { + if(!empty($this->smtp_conn)) { + $sock_status = socket_get_status($this->smtp_conn); + if($sock_status["eof"]) { + // the socket is valid but we are not connected + if($this->do_debug >= 1) { + echo "SMTP -> NOTICE:" . $this->CRLF . "EOF caught while checking if connected"; + } + $this->Close(); + return false; + } + return true; // everything looks good + } + return false; + } + + /** + * Closes the socket and cleans up the state of the class. + * It is not considered good to use this function without + * first trying to use QUIT. + * @access public + * @return void + */ + public function Close() { + $this->error = null; // so there is no confusion + $this->helo_rply = null; + if(!empty($this->smtp_conn)) { + // close the connection and cleanup + fclose($this->smtp_conn); + $this->smtp_conn = 0; + } + } + + ///////////////////////////////////////////////// + // SMTP COMMANDS + ///////////////////////////////////////////////// + + /** + * Issues a data command and sends the msg_data to the server + * finializing the mail transaction. $msg_data is the message + * that is to be send with the headers. Each header needs to be + * on a single line followed by a with the message headers + * and the message body being seperated by and additional . + * + * Implements rfc 821: DATA + * + * SMTP CODE INTERMEDIATE: 354 + * [data] + * . + * SMTP CODE SUCCESS: 250 + * SMTP CODE FAILURE: 552,554,451,452 + * SMTP CODE FAILURE: 451,554 + * SMTP CODE ERROR : 500,501,503,421 + * @access public + * @return bool + */ + public function Data($msg_data) { + $this->error = null; // so no confusion is caused + + if(!$this->connected()) { + $this->error = array( + "error" => "Called Data() without being connected"); + return false; + } + + fputs($this->smtp_conn,"DATA" . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply,0,3); + + if($this->do_debug >= 2) { + echo "SMTP -> FROM SERVER:" . $rply . $this->CRLF . '
'; + } + + if($code != 354) { + $this->error = + array("error" => "DATA command not accepted from server", + "smtp_code" => $code, + "smtp_msg" => substr($rply,4)); + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
'; + } + return false; + } + + /* the server is ready to accept data! + * according to rfc 821 we should not send more than 1000 + * including the CRLF + * characters on a single line so we will break the data up + * into lines by \r and/or \n then if needed we will break + * each of those into smaller lines to fit within the limit. + * in addition we will be looking for lines that start with + * a period '.' and append and additional period '.' to that + * line. NOTE: this does not count towards limit. + */ + + // normalize the line breaks so we know the explode works + $msg_data = str_replace("\r\n","\n",$msg_data); + $msg_data = str_replace("\r","\n",$msg_data); + $lines = explode("\n",$msg_data); + + /* we need to find a good way to determine is headers are + * in the msg_data or if it is a straight msg body + * currently I am assuming rfc 822 definitions of msg headers + * and if the first field of the first line (':' sperated) + * does not contain a space then it _should_ be a header + * and we can process all lines before a blank "" line as + * headers. + */ + + $field = substr($lines[0],0,strpos($lines[0],":")); + $in_headers = false; + if(!empty($field) && !strstr($field," ")) { + $in_headers = true; + } + + $max_line_length = 998; // used below; set here for ease in change + + while(list(,$line) = @each($lines)) { + $lines_out = null; + if($line == "" && $in_headers) { + $in_headers = false; + } + // ok we need to break this line up into several smaller lines + while(strlen($line) > $max_line_length) { + $pos = strrpos(substr($line,0,$max_line_length)," "); + + // Patch to fix DOS attack + if(!$pos) { + $pos = $max_line_length - 1; + $lines_out[] = substr($line,0,$pos); + $line = substr($line,$pos); + } else { + $lines_out[] = substr($line,0,$pos); + $line = substr($line,$pos + 1); + } + + /* if processing headers add a LWSP-char to the front of new line + * rfc 822 on long msg headers + */ + if($in_headers) { + $line = "\t" . $line; + } + } + $lines_out[] = $line; + + // send the lines to the server + while(list(,$line_out) = @each($lines_out)) { + if(strlen($line_out) > 0) + { + if(substr($line_out, 0, 1) == ".") { + $line_out = "." . $line_out; + } + } + fputs($this->smtp_conn,$line_out . $this->CRLF); + } + } + + // message data has been sent + fputs($this->smtp_conn, $this->CRLF . "." . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply,0,3); + + if($this->do_debug >= 2) { + echo "SMTP -> FROM SERVER:" . $rply . $this->CRLF . '
'; + } + + if($code != 250) { + $this->error = + array("error" => "DATA not accepted from server", + "smtp_code" => $code, + "smtp_msg" => substr($rply,4)); + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
'; + } + return false; + } + return true; + } + + /** + * Sends the HELO command to the smtp server. + * This makes sure that we and the server are in + * the same known state. + * + * Implements from rfc 821: HELO + * + * SMTP CODE SUCCESS: 250 + * SMTP CODE ERROR : 500, 501, 504, 421 + * @access public + * @return bool + */ + public function Hello($host = '') { + $this->error = null; // so no confusion is caused + + if(!$this->connected()) { + $this->error = array( + "error" => "Called Hello() without being connected"); + return false; + } + + // if hostname for HELO was not specified send default + if(empty($host)) { + // determine appropriate default to send to server + $host = "localhost"; + } + + // Send extended hello first (RFC 2821) + if(!$this->SendHello("EHLO", $host)) { + if(!$this->SendHello("HELO", $host)) { + return false; + } + } + + return true; + } + + /** + * Sends a HELO/EHLO command. + * @access private + * @return bool + */ + private function SendHello($hello, $host) { + fputs($this->smtp_conn, $hello . " " . $host . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply,0,3); + + if($this->do_debug >= 2) { + echo "SMTP -> FROM SERVER: " . $rply . $this->CRLF . '
'; + } + + if($code != 250) { + $this->error = + array("error" => $hello . " not accepted from server", + "smtp_code" => $code, + "smtp_msg" => substr($rply,4)); + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
'; + } + return false; + } + + $this->helo_rply = $rply; + + return true; + } + + /** + * Starts a mail transaction from the email address specified in + * $from. Returns true if successful or false otherwise. If True + * the mail transaction is started and then one or more Recipient + * commands may be called followed by a Data command. + * + * Implements rfc 821: MAIL FROM: + * + * SMTP CODE SUCCESS: 250 + * SMTP CODE SUCCESS: 552,451,452 + * SMTP CODE SUCCESS: 500,501,421 + * @access public + * @return bool + */ + public function Mail($from) { + $this->error = null; // so no confusion is caused + + if(!$this->connected()) { + $this->error = array( + "error" => "Called Mail() without being connected"); + return false; + } + + $useVerp = ($this->do_verp ? "XVERP" : ""); + fputs($this->smtp_conn,"MAIL FROM:<" . $from . ">" . $useVerp . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply,0,3); + + if($this->do_debug >= 2) { + echo "SMTP -> FROM SERVER:" . $rply . $this->CRLF . '
'; + } + + if($code != 250) { + $this->error = + array("error" => "MAIL not accepted from server", + "smtp_code" => $code, + "smtp_msg" => substr($rply,4)); + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
'; + } + return false; + } + return true; + } + + /** + * Sends the quit command to the server and then closes the socket + * if there is no error or the $close_on_error argument is true. + * + * Implements from rfc 821: QUIT + * + * SMTP CODE SUCCESS: 221 + * SMTP CODE ERROR : 500 + * @access public + * @return bool + */ + public function Quit($close_on_error = true) { + $this->error = null; // so there is no confusion + + if(!$this->connected()) { + $this->error = array( + "error" => "Called Quit() without being connected"); + return false; + } + + // send the quit command to the server + fputs($this->smtp_conn,"quit" . $this->CRLF); + + // get any good-bye messages + $byemsg = $this->get_lines(); + + if($this->do_debug >= 2) { + echo "SMTP -> FROM SERVER:" . $byemsg . $this->CRLF . '
'; + } + + $rval = true; + $e = null; + + $code = substr($byemsg,0,3); + if($code != 221) { + // use e as a tmp var cause Close will overwrite $this->error + $e = array("error" => "SMTP server rejected quit command", + "smtp_code" => $code, + "smtp_rply" => substr($byemsg,4)); + $rval = false; + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $e["error"] . ": " . $byemsg . $this->CRLF . '
'; + } + } + + if(empty($e) || $close_on_error) { + $this->Close(); + } + + return $rval; + } + + /** + * Sends the command RCPT to the SMTP server with the TO: argument of $to. + * Returns true if the recipient was accepted false if it was rejected. + * + * Implements from rfc 821: RCPT TO: + * + * SMTP CODE SUCCESS: 250,251 + * SMTP CODE FAILURE: 550,551,552,553,450,451,452 + * SMTP CODE ERROR : 500,501,503,421 + * @access public + * @return bool + */ + public function Recipient($to) { + $this->error = null; // so no confusion is caused + + if(!$this->connected()) { + $this->error = array( + "error" => "Called Recipient() without being connected"); + return false; + } + + fputs($this->smtp_conn,"RCPT TO:<" . $to . ">" . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply,0,3); + + if($this->do_debug >= 2) { + echo "SMTP -> FROM SERVER:" . $rply . $this->CRLF . '
'; + } + + if($code != 250 && $code != 251) { + $this->error = + array("error" => "RCPT not accepted from server", + "smtp_code" => $code, + "smtp_msg" => substr($rply,4)); + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
'; + } + return false; + } + return true; + } + + /** + * Sends the RSET command to abort and transaction that is + * currently in progress. Returns true if successful false + * otherwise. + * + * Implements rfc 821: RSET + * + * SMTP CODE SUCCESS: 250 + * SMTP CODE ERROR : 500,501,504,421 + * @access public + * @return bool + */ + public function Reset() { + $this->error = null; // so no confusion is caused + + if(!$this->connected()) { + $this->error = array( + "error" => "Called Reset() without being connected"); + return false; + } + + fputs($this->smtp_conn,"RSET" . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply,0,3); + + if($this->do_debug >= 2) { + echo "SMTP -> FROM SERVER:" . $rply . $this->CRLF . '
'; + } + + if($code != 250) { + $this->error = + array("error" => "RSET failed", + "smtp_code" => $code, + "smtp_msg" => substr($rply,4)); + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
'; + } + return false; + } + + return true; + } + + /** + * Starts a mail transaction from the email address specified in + * $from. Returns true if successful or false otherwise. If True + * the mail transaction is started and then one or more Recipient + * commands may be called followed by a Data command. This command + * will send the message to the users terminal if they are logged + * in and send them an email. + * + * Implements rfc 821: SAML FROM: + * + * SMTP CODE SUCCESS: 250 + * SMTP CODE SUCCESS: 552,451,452 + * SMTP CODE SUCCESS: 500,501,502,421 + * @access public + * @return bool + */ + public function SendAndMail($from) { + $this->error = null; // so no confusion is caused + + if(!$this->connected()) { + $this->error = array( + "error" => "Called SendAndMail() without being connected"); + return false; + } + + fputs($this->smtp_conn,"SAML FROM:" . $from . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply,0,3); + + if($this->do_debug >= 2) { + echo "SMTP -> FROM SERVER:" . $rply . $this->CRLF . '
'; + } + + if($code != 250) { + $this->error = + array("error" => "SAML not accepted from server", + "smtp_code" => $code, + "smtp_msg" => substr($rply,4)); + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
'; + } + return false; + } + return true; + } + + /** + * This is an optional command for SMTP that this class does not + * support. This method is here to make the RFC821 Definition + * complete for this class and __may__ be implimented in the future + * + * Implements from rfc 821: TURN + * + * SMTP CODE SUCCESS: 250 + * SMTP CODE FAILURE: 502 + * SMTP CODE ERROR : 500, 503 + * @access public + * @return bool + */ + public function Turn() { + $this->error = array("error" => "This method, TURN, of the SMTP ". + "is not implemented"); + if($this->do_debug >= 1) { + echo "SMTP -> NOTICE: " . $this->error["error"] . $this->CRLF . '
'; + } + return false; + } + + /** + * Get the current error + * @access public + * @return array + */ + public function getError() { + return $this->error; + } + + ///////////////////////////////////////////////// + // INTERNAL FUNCTIONS + ///////////////////////////////////////////////// + + /** + * Read in as many lines as possible + * either before eof or socket timeout occurs on the operation. + * With SMTP we can tell if we have more lines to read if the + * 4th character is '-' symbol. If it is a space then we don't + * need to read anything else. + * @access private + * @return string + */ + private function get_lines() { + $data = ""; + while($str = @fgets($this->smtp_conn,515)) { + if($this->do_debug >= 4) { + echo "SMTP -> get_lines(): \$data was \"$data\"" . $this->CRLF . '
'; + echo "SMTP -> get_lines(): \$str is \"$str\"" . $this->CRLF . '
'; + } + $data .= $str; + if($this->do_debug >= 4) { + echo "SMTP -> get_lines(): \$data is \"$data\"" . $this->CRLF . '
'; + } + // if 4th character is a space, we are done reading, break the loop + if(substr($str,3,1) == " ") { break; } + } + return $data; + } + +} + +?> diff --git a/3rdparty/fullcalendar/changelog.txt b/3rdparty/fullcalendar/changelog.txt deleted file mode 100644 index 50d0880fd7..0000000000 --- a/3rdparty/fullcalendar/changelog.txt +++ /dev/null @@ -1,313 +0,0 @@ - -version 1.5.2 (8/21/11) - - correctly process UTC "Z" ISO8601 date strings (issue 750) - -version 1.5.1 (4/9/11) - - more flexible ISO8601 date parsing (issue 814) - - more flexible parsing of UNIX timestamps (issue 826) - - FullCalendar now buildable from source on a Mac (issue 795) - - FullCalendar QA'd in FF4 (issue 883) - - upgraded to jQuery 1.5.2 (which supports IE9) and jQuery UI 1.8.11 - -version 1.5 (3/19/11) - - slicker default styling for buttons - - reworked a lot of the calendar's HTML and accompanying CSS - (solves issues 327 and 395) - - more printer-friendly (fullcalendar-print.css) - - fullcalendar now inherits styles from jquery-ui themes differently. - styles for buttons are distinct from styles for calendar cells. - (solves issue 299) - - can now color events through FullCalendar options and Event-Object properties (issue 117) - THIS IS NOW THE PREFERRED METHOD OF COLORING EVENTS (as opposed to using className and CSS) - - FullCalendar options: - - eventColor (changes both background and border) - - eventBackgroundColor - - eventBorderColor - - eventTextColor - - Event-Object options: - - color (changes both background and border) - - backgroundColor - - borderColor - - textColor - - can now specify an event source as an *object* with a `url` property (json feed) or - an `events` property (function or array) with additional properties that will - be applied to the entire event source: - - color (changes both background and border) - - backgroudColor - - borderColor - - textColor - - className - - editable - - allDayDefault - - ignoreTimezone - - startParam (for a feed) - - endParam (for a feed) - - ANY OF THE JQUERY $.ajax OPTIONS - allows for easily changing from GET to POST and sending additional parameters (issue 386) - allows for easily attaching ajax handlers such as `error` (issue 754) - allows for turning caching on (issue 355) - - Google Calendar feeds are now specified differently: - - specify a simple string of your feed's URL - - specify an *object* with a `url` property of your feed's URL. - you can include any of the new Event-Source options in this object. - - the old `$.fullCalendar.gcalFeed` method still works - - no more IE7 SSL popup (issue 504) - - remove `cacheParam` - use json event source `cache` option instead - - latest jquery/jquery-ui - -version 1.4.11 (2/22/11) - - fixed rerenderEvents bug (issue 790) - - fixed bug with faulty dragging of events from all-day slot in agenda views - - bundled with jquery 1.5 and jquery-ui 1.8.9 - -version 1.4.10 (1/2/11) - - fixed bug with resizing event to different week in 5-day month view (issue 740) - - fixed bug with events not sticking after a removeEvents call (issue 757) - - fixed bug with underlying parseTime method, and other uses of parseInt (issue 688) - -version 1.4.9 (11/16/10) - - new algorithm for vertically stacking events (issue 111) - - resizing an event to a different week (issue 306) - - bug: some events not rendered with consecutive calls to addEventSource (issue 679) - -version 1.4.8 (10/16/10) - - ignoreTimezone option (set to `false` to process UTC offsets in ISO8601 dates) - - bugfixes - - event refetching not being called under certain conditions (issues 417, 554) - - event refetching being called multiple times under certain conditions (issues 586, 616) - - selection cannot be triggered by right mouse button (issue 558) - - agenda view left axis sized incorrectly (issue 465) - - IE js error when calendar is too narrow (issue 517) - - agenda view looks strange when no scrollbars (issue 235) - - improved parsing of ISO8601 dates with UTC offsets - - $.fullCalendar.version - - an internal refactor of the code, for easier future development and modularity - -version 1.4.7 (7/5/10) - - "dropping" external objects onto the calendar - - droppable (boolean, to turn on/off) - - dropAccept (to filter which events the calendar will accept) - - drop (trigger) - - selectable options can now be specified with a View Option Hash - - bugfixes - - dragged & reverted events having wrong time text (issue 406) - - bug rendering events that have an endtime with seconds, but no hours/minutes (issue 477) - - gotoDate date overflow bug (issue 429) - - wrong date reported when clicking on edge of last column in agenda views (412) - - support newlines in event titles - - select/unselect callbacks now passes native js event - -version 1.4.6 (5/31/10) - - "selecting" days or timeslots - - options: selectable, selectHelper, unselectAuto, unselectCancel - - callbacks: select, unselect - - methods: select, unselect - - when dragging an event, the highlighting reflects the duration of the event - - code compressing by Google Closure Compiler - - bundled with jQuery 1.4.2 and jQuery UI 1.8.1 - -version 1.4.5 (2/21/10) - - lazyFetching option, which can force the calendar to fetch events on every view/date change - - scroll state of agenda views are preserved when switching back to view - - bugfixes - - calling methods on an uninitialized fullcalendar throws error - - IE6/7 bug where an entire view becomes invisible (issue 320) - - error when rendering a hidden calendar (in jquery ui tabs for example) in IE (issue 340) - - interconnected bugs related to calendar resizing and scrollbars - - when switching views or clicking prev/next, calendar would "blink" (issue 333) - - liquid-width calendar's events shifted (depending on initial height of browser) (issue 341) - - more robust underlying algorithm for calendar resizing - -version 1.4.4 (2/3/10) - - optimized event rendering in all views (events render in 1/10 the time) - - gotoDate() does not force the calendar to unnecessarily rerender - - render() method now correctly readjusts height - -version 1.4.3 (12/22/09) - - added destroy method - - Google Calendar event pages respect currentTimezone - - caching now handled by jQuery's ajax - - protection from setting aspectRatio to zero - - bugfixes - - parseISO8601 and DST caused certain events to display day before - - button positioning problem in IE6 - - ajax event source removed after recently being added, events still displayed - - event not displayed when end is an empty string - - dynamically setting calendar height when no events have been fetched, throws error - -version 1.4.2 (12/02/09) - - eventAfterRender trigger - - getDate & getView methods - - height & contentHeight options (explicitly sets the pixel height) - - minTime & maxTime options (restricts shown hours in agenda view) - - getters [for all options] and setters [for height, contentHeight, and aspectRatio ONLY! stay tuned..] - - render method now readjusts calendar's size - - bugfixes - - lightbox scripts that use iframes (like fancybox) - - day-of-week classNames were off when firstDay=1 - - guaranteed space on right side of agenda events (even when stacked) - - accepts ISO8601 dates with a space (instead of 'T') - -version 1.4.1 (10/31/09) - - can exclude weekends with new 'weekends' option - - gcal feed 'currentTimezone' option - - bugfixes - - year/month/date option sometimes wouldn't set correctly (depending on current date) - - daylight savings issue caused agenda views to start at 1am (for BST users) - - cleanup of gcal.js code - -version 1.4 (10/19/09) - - agendaWeek and agendaDay views - - added some options for agenda views: - - allDaySlot - - allDayText - - firstHour - - slotMinutes - - defaultEventMinutes - - axisFormat - - modified some existing options/triggers to work with agenda views: - - dragOpacity and timeFormat can now accept a "View Hash" (a new concept) - - dayClick now has an allDay parameter - - eventDrop now has an an allDay parameter - (this will affect those who use revertFunc, adjust parameter list) - - added 'prevYear' and 'nextYear' for buttons in header - - minor change for theme users, ui-state-hover not applied to active/inactive buttons - - added event-color-changing example in docs - - better defaults for right-to-left themed button icons - -version 1.3.2 (10/13/09) - - Bugfixes (please upgrade from 1.3.1!) - - squashed potential infinite loop when addMonths and addDays - is called with an invalid date - - $.fullCalendar.parseDate() now correctly parses IETF format - - when switching views, the 'today' button sticks inactive, fixed - - gotoDate now can accept a single Date argument - - documentation for changes in 1.3.1 and 1.3.2 now on website - -version 1.3.1 (9/30/09) - - Important Bugfixes (please upgrade from 1.3!) - - When current date was late in the month, for long months, and prev/next buttons - were clicked in month-view, some months would be skipped/repeated - - In certain time zones, daylight savings time would cause certain days - to be misnumbered in month-view - - Subtle change in way week interval is chosen when switching from month to basicWeek/basicDay view - - Added 'allDayDefault' option - - Added 'changeView' and 'render' methods - -version 1.3 (9/21/09) - - different 'views': month/basicWeek/basicDay - - more flexible 'header' system for buttons - - themable by jQuery UI themes - - resizable events (require jQuery UI resizable plugin) - - rescoped & rewritten CSS, enhanced default look - - cleaner css & rendering techniques for right-to-left - - reworked options & API to support multiple views / be consistent with jQuery UI - - refactoring of entire codebase - - broken into different JS & CSS files, assembled w/ build scripts - - new test suite for new features, uses firebug-lite - - refactored docs - - Options - + date - + defaultView - + aspectRatio - + disableResizing - + monthNames (use instead of $.fullCalendar.monthNames) - + monthNamesShort (use instead of $.fullCalendar.monthAbbrevs) - + dayNames (use instead of $.fullCalendar.dayNames) - + dayNamesShort (use instead of $.fullCalendar.dayAbbrevs) - + theme - + buttonText - + buttonIcons - x draggable -> editable/disableDragging - x fixedWeeks -> weekMode - x abbrevDayHeadings -> columnFormat - x buttons/title -> header - x eventDragOpacity -> dragOpacity - x eventRevertDuration -> dragRevertDuration - x weekStart -> firstDay - x rightToLeft -> isRTL - x showTime (use 'allDay' CalEvent property instead) - - Triggered Actions - + eventResizeStart - + eventResizeStop - + eventResize - x monthDisplay -> viewDisplay - x resize -> windowResize - 'eventDrop' params changed, can revert if ajax cuts out - - CalEvent Properties - x showTime -> allDay - x draggable -> editable - 'end' is now INCLUSIVE when allDay=true - 'url' now produces a real tag, more native clicking/tab behavior - - Methods: - + renderEvent - x prevMonth -> prev - x nextMonth -> next - x prevYear/nextYear -> moveDate - x refresh -> rerenderEvents/refetchEvents - x removeEvent -> removeEvents - x getEventsByID -> clientEvents - - Utilities: - 'formatDate' format string completely changed (inspired by jQuery UI datepicker + datejs) - 'formatDates' added to support date-ranges - - Google Calendar Options: - x draggable -> editable - - Bugfixes - - gcal extension fetched 25 results max, now fetches all - -version 1.2.1 (6/29/09) - - bugfixes - - allows and corrects invalid end dates for events - - doesn't throw an error in IE while rendering when display:none - - fixed 'loading' callback when used w/ multiple addEventSource calls - - gcal className can now be an array - -version 1.2 (5/31/09) - - expanded API - - 'className' CalEvent attribute - - 'source' CalEvent attribute - - dynamically get/add/remove/update events of current month - - locale improvements: change month/day name text - - better date formatting ($.fullCalendar.formatDate) - - multiple 'event sources' allowed - - dynamically add/remove event sources - - options for prevYear and nextYear buttons - - docs have been reworked (include addition of Google Calendar docs) - - changed behavior of parseDate for number strings - (now interpets as unix timestamp, not MS times) - - bugfixes - - rightToLeft month start bug - - off-by-one errors with month formatting commands - - events from previous months sticking when clicking prev/next quickly - - Google Calendar API changed to work w/ multiple event sources - - can also provide 'className' and 'draggable' options - - date utilties moved from $ to $.fullCalendar - - more documentation in source code - - minified version of fullcalendar.js - - test suit (available from svn) - - top buttons now use