diff options
author | Daniel Lange <DLange@git.local> | 2016-03-07 15:53:16 +0100 |
---|---|---|
committer | Daniel Lange <DLange@git.local> | 2016-03-07 15:53:16 +0100 |
commit | 50569114acdc64e7c7cae1498635d3f821517c30 (patch) | |
tree | 13d6fe76af33134fbfb2286930fb6603047f9299 /calendar/lib/SabreDAV/lib/OldSabre/DAV/PartialUpdate | |
parent | c210d30de6c62e7f7867bb32651349ddf455d9e6 (diff) | |
download | roundcube_calendar-50569114acdc64e7c7cae1498635d3f821517c30.tar.gz roundcube_calendar-50569114acdc64e7c7cae1498635d3f821517c30.tar.bz2 roundcube_calendar-50569114acdc64e7c7cae1498635d3f821517c30.zip |
Initial commit of the Faster IT roundcube_calendar plugin distribution
This includes:
* Kolab plugins 3.2.9 (calendar and libcalendaring)
* CalDAV driver 3.2.8
* .htaccess files for at least some security
* SabreDAV updated to 1.8.12 (Jan 2015 release)
* Support for CURLOPT_SSL_* settings to allow self-signed certificates
* Small fixes & improved documentation
Diffstat (limited to 'calendar/lib/SabreDAV/lib/OldSabre/DAV/PartialUpdate')
3 files changed, 333 insertions, 0 deletions
diff --git a/calendar/lib/SabreDAV/lib/OldSabre/DAV/PartialUpdate/IFile.php b/calendar/lib/SabreDAV/lib/OldSabre/DAV/PartialUpdate/IFile.php new file mode 100644 index 0000000..2fbdf1e --- /dev/null +++ b/calendar/lib/SabreDAV/lib/OldSabre/DAV/PartialUpdate/IFile.php @@ -0,0 +1,39 @@ +<?php + +namespace OldSabre\DAV\PartialUpdate; + +use OldSabre\DAV; + +/** + * This interface is deprecated. Use IPatchSupport instead. + * + * @copyright Copyright (C) 2007-2015 fruux GmbH (https://fruux.com/). + * @author Jean-Tiare LE BIGOT (http://www.jtlebi.fr/) + * @license http://sabre.io/license/ Modified BSD License + * @deprecated + */ +interface IFile extends DAV\IFile { + + /** + * Updates the data at a given offset + * + * The data argument is a readable stream resource. + * The offset argument is an integer describing the offset. Contrary to + * what's sent in the request, the offset here is a 0-based index. + * + * After a successful put operation, you may choose to return an ETag. The + * etag must always be surrounded by double-quotes. These quotes must + * appear in the actual string you're returning. + * + * Clients may use the ETag from a PUT request to later on make sure that + * when they update the file, the contents haven't changed in the mean + * time. + * + * @param resource $data + * @param integer $offset + * @return string|null + */ + function putRange($data, $offset); + +} + diff --git a/calendar/lib/SabreDAV/lib/OldSabre/DAV/PartialUpdate/IPatchSupport.php b/calendar/lib/SabreDAV/lib/OldSabre/DAV/PartialUpdate/IPatchSupport.php new file mode 100644 index 0000000..e6e0989 --- /dev/null +++ b/calendar/lib/SabreDAV/lib/OldSabre/DAV/PartialUpdate/IPatchSupport.php @@ -0,0 +1,48 @@ +<?php + +namespace OldSabre\DAV\PartialUpdate; + +use OldSabre\DAV; + +/** + * This interface provides a way to modify only part of a target resource + * It may be used to update a file chunk, upload big a file into smaller + * chunks or resume an upload + * + * @copyright Copyright (C) 2007-2015 fruux GmbH (https://fruux.com/). + * @author Jean-Tiare LE BIGOT (http://www.jtlebi.fr/) + * @license http://sabre.io/license/ Modified BSD License + */ +interface IPatchSupport extends DAV\IFile { + + /** + * Updates the file based on a range specification. + * + * The first argument is the data, which is either a readable stream + * resource or a string. + * + * The second argument is the type of update we're doing. + * This is either: + * * 1. append + * * 2. update based on a start byte + * * 3. update based on an end byte + *; + * The third argument is the start or end byte. + * + * After a successful put operation, you may choose to return an ETag. The + * etag must always be surrounded by double-quotes. These quotes must + * appear in the actual string you're returning. + * + * Clients may use the ETag from a PUT request to later on make sure that + * when they update the file, the contents haven't changed in the mean + * time. + * + * @param resource|string $data + * @param int $rangeType + * @param int $offset + * @return string|null + */ + function patch($data, $rangeType, $offset = null); + +} + diff --git a/calendar/lib/SabreDAV/lib/OldSabre/DAV/PartialUpdate/Plugin.php b/calendar/lib/SabreDAV/lib/OldSabre/DAV/PartialUpdate/Plugin.php new file mode 100644 index 0000000..c1e5363 --- /dev/null +++ b/calendar/lib/SabreDAV/lib/OldSabre/DAV/PartialUpdate/Plugin.php @@ -0,0 +1,246 @@ +<?php + +namespace OldSabre\DAV\PartialUpdate; + +use OldSabre\DAV; + +/** + * Partial update plugin (Patch method) + * + * This plugin provides a way to modify only part of a target resource + * It may bu used to update a file chunk, upload big a file into smaller + * chunks or resume an upload. + * + * $patchPlugin = new \OldSabre\DAV\PartialUpdate\Plugin(); + * $server->addPlugin($patchPlugin); + * + * @copyright Copyright (C) 2007-2015 fruux GmbH (https://fruux.com/). + * @author Jean-Tiare LE BIGOT (http://www.jtlebi.fr/) + * @license http://sabre.io/license/ Modified BSD License + */ +class Plugin extends DAV\ServerPlugin { + + const RANGE_APPEND = 1; + const RANGE_START = 2; + const RANGE_END = 3; + + /** + * Reference to server + * + * @var OldSabre\DAV\Server + */ + protected $server; + + /** + * Initializes the plugin + * + * This method is automatically called by the Server class after addPlugin. + * + * @param DAV\Server $server + * @return void + */ + public function initialize(DAV\Server $server) { + + $this->server = $server; + $server->subscribeEvent('unknownMethod',array($this,'unknownMethod')); + + } + + /** + * Returns a plugin name. + * + * Using this name other plugins will be able to access other plugins + * using DAV\Server::getPlugin + * + * @return string + */ + public function getPluginName() { + + return 'partialupdate'; + + } + + /** + * This method is called by the Server if the user used an HTTP method + * the server didn't recognize. + * + * This plugin intercepts the PATCH methods. + * + * @param string $method + * @param string $uri + * @return bool|null + */ + public function unknownMethod($method, $uri) { + + switch($method) { + + case 'PATCH': + return $this->httpPatch($uri); + + } + + } + + /** + * Use this method to tell the server this plugin defines additional + * HTTP methods. + * + * This method is passed a uri. It should only return HTTP methods that are + * available for the specified uri. + * + * We claim to support PATCH method (partial update) if and only if + * - the node exist + * - the node implements our partial update interface + * + * @param string $uri + * @return array + */ + public function getHTTPMethods($uri) { + + $tree = $this->server->tree; + if ($tree->nodeExists($uri)) { + $node = $tree->getNodeForPath($uri); + if ($node instanceof IFile || $node instanceof IPatchSupport) { + return array('PATCH'); + } + } + return array(); + + } + + /** + * Returns a list of features for the HTTP OPTIONS Dav: header. + * + * @return array + */ + public function getFeatures() { + + return array('sabredav-partialupdate'); + + } + + /** + * Patch an uri + * + * The WebDAV patch request can be used to modify only a part of an + * existing resource. If the resource does not exist yet and the first + * offset is not 0, the request fails + * + * @param string $uri + * @return void + */ + protected function httpPatch($uri) { + + // Get the node. Will throw a 404 if not found + $node = $this->server->tree->getNodeForPath($uri); + if (!$node instanceof IFile && !$node instanceof IPatchSupport) { + throw new DAV\Exception\MethodNotAllowed('The target resource does not support the PATCH method.'); + } + + $range = $this->getHTTPUpdateRange(); + + if (!$range) { + throw new DAV\Exception\BadRequest('No valid "X-Update-Range" found in the headers'); + } + + $contentType = strtolower( + $this->server->httpRequest->getHeader('Content-Type') + ); + + if ($contentType != 'application/x-sabredav-partialupdate') { + throw new DAV\Exception\UnsupportedMediaType('Unknown Content-Type header "' . $contentType . '"'); + } + + $len = $this->server->httpRequest->getHeader('Content-Length'); + if (!$len) throw new DAV\Exception\LengthRequired('A Content-Length header is required'); + + switch($range[0]) { + case self::RANGE_START : + // Calculate the end-range if it doesn't exist. + if (!$range[2]) { + $range[2] = $range[1] + $len - 1; + } else { + if ($range[2] < $range[1]) { + throw new DAV\Exception\RequestedRangeNotSatisfiable('The end offset (' . $range[2] . ') is lower than the start offset (' . $range[1] . ')'); + } + if($range[2] - $range[1] + 1 != $len) { + throw new DAV\Exception\RequestedRangeNotSatisfiable('Actual data length (' . $len . ') is not consistent with begin (' . $range[1] . ') and end (' . $range[2] . ') offsets'); + } + } + break; + } + // Checking If-None-Match and related headers. + if (!$this->server->checkPreconditions()) return; + + if (!$this->server->broadcastEvent('beforeWriteContent',array($uri, $node, null))) + return; + + $body = $this->server->httpRequest->getBody(); + + + if ($node instanceof IPatchSupport) { + $etag = $node->patch($body, $range[0], isset($range[1])?$range[1]:null); + } else { + // The old interface + switch($range[0]) { + case self::RANGE_APPEND : + throw new DAV\Exception\NotImplemented('This node does not support the append syntax. Please upgrade it to IPatchSupport'); + case self::RANGE_START : + $etag = $node->putRange($body, $range[1]); + break; + case self::RANGE_END : + throw new DAV\Exception\NotImplemented('This node does not support the end-range syntax. Please upgrade it to IPatchSupport'); + break; + } + } + + $this->server->broadcastEvent('afterWriteContent',array($uri, $node)); + + $this->server->httpResponse->setHeader('Content-Length','0'); + if ($etag) $this->server->httpResponse->setHeader('ETag',$etag); + $this->server->httpResponse->sendStatus(204); + + return false; + + } + + /** + * Returns the HTTP custom range update header + * + * This method returns null if there is no well-formed HTTP range request + * header. It returns array(1) if it was an append request, array(2, + * $start, $end) if it's a start and end range, lastly it's array(3, + * $endoffset) if the offset was negative, and should be calculated from + * the end of the file. + * + * Examples: + * + * null - invalid + * array(1) - append + * array(2,10,15) - update bytes 10, 11, 12, 13, 14, 15 + * array(2,10,null) - update bytes 10 until the end of the patch body + * array(3,-5) - update from 5 bytes from the end of the file. + * + * @return array|null + */ + public function getHTTPUpdateRange() { + + $range = $this->server->httpRequest->getHeader('X-Update-Range'); + if (is_null($range)) return null; + + // Matching "Range: bytes=1234-5678: both numbers are optional + + if (!preg_match('/^(append)|(?:bytes=([0-9]+)-([0-9]*))|(?:bytes=(-[0-9]+))$/i',$range,$matches)) return null; + + if ($matches[1]==='append') { + return array(self::RANGE_APPEND); + } elseif (strlen($matches[2])>0) { + return array(self::RANGE_START, $matches[2], $matches[3]?:null); + } elseif ($matches[4]) { + return array(self::RANGE_END, $matches[4]); + } else { + return null; + } + + } +} |