aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/sabre/dav/lib/DAV/PartialUpdate
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/sabre/dav/lib/DAV/PartialUpdate')
-rw-r--r--vendor/sabre/dav/lib/DAV/PartialUpdate/IPatchSupport.php47
-rw-r--r--vendor/sabre/dav/lib/DAV/PartialUpdate/Plugin.php215
2 files changed, 262 insertions, 0 deletions
diff --git a/vendor/sabre/dav/lib/DAV/PartialUpdate/IPatchSupport.php b/vendor/sabre/dav/lib/DAV/PartialUpdate/IPatchSupport.php
new file mode 100644
index 000000000..97d24f9cb
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAV/PartialUpdate/IPatchSupport.php
@@ -0,0 +1,47 @@
+<?php
+
+namespace Sabre\DAV\PartialUpdate;
+
+use Sabre\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) 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/vendor/sabre/dav/lib/DAV/PartialUpdate/Plugin.php b/vendor/sabre/dav/lib/DAV/PartialUpdate/Plugin.php
new file mode 100644
index 000000000..24ba970b1
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAV/PartialUpdate/Plugin.php
@@ -0,0 +1,215 @@
+<?php
+
+namespace Sabre\DAV\PartialUpdate;
+
+use Sabre\DAV;
+use Sabre\HTTP\RequestInterface;
+use Sabre\HTTP\ResponseInterface;
+
+/**
+ * Partial update plugin (Patch method)
+ *
+ * This plugin provides a way to modify only part of a target resource
+ * It may bu used to update a file chunk, upload big a file into smaller
+ * chunks or resume an upload.
+ *
+ * $patchPlugin = new \Sabre\DAV\PartialUpdate\Plugin();
+ * $server->addPlugin($patchPlugin);
+ *
+ * @copyright Copyright (C) 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 Sabre\DAV\Server
+ */
+ protected $server;
+
+ /**
+ * Initializes the plugin
+ *
+ * This method is automatically called by the Server class after addPlugin.
+ *
+ * @param DAV\Server $server
+ * @return void
+ */
+ function initialize(DAV\Server $server) {
+
+ $this->server = $server;
+ $server->on('method:PATCH', [$this, 'httpPatch']);
+
+ }
+
+ /**
+ * Returns a plugin name.
+ *
+ * Using this name other plugins will be able to access other plugins
+ * using DAV\Server::getPlugin
+ *
+ * @return string
+ */
+ function getPluginName() {
+
+ return 'partialupdate';
+
+ }
+
+ /**
+ * 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 (partirl update) if and only if
+ * - the node exist
+ * - the node implements our partial update interface
+ *
+ * @param string $uri
+ * @return array
+ */
+ function getHTTPMethods($uri) {
+
+ $tree = $this->server->tree;
+
+ if ($tree->nodeExists($uri)) {
+ $node = $tree->getNodeForPath($uri);
+ if ($node instanceof IPatchSupport) {
+ return ['PATCH'];
+ }
+ }
+ return [];
+
+ }
+
+ /**
+ * Returns a list of features for the HTTP OPTIONS Dav: header.
+ *
+ * @return array
+ */
+ function getFeatures() {
+
+ return ['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 RequestInterface $request
+ * @param ResponseInterface $response
+ * @return void
+ */
+ function httpPatch(RequestInterface $request, ResponseInterface $response) {
+
+ $path = $request->getPath();
+
+ // Get the node. Will throw a 404 if not found
+ $node = $this->server->tree->getNodeForPath($path);
+ if (!$node instanceof IPatchSupport) {
+ throw new DAV\Exception\MethodNotAllowed('The target resource does not support the PATCH method.');
+ }
+
+ $range = $this->getHTTPUpdateRange($request);
+
+ if (!$range) {
+ throw new DAV\Exception\BadRequest('No valid "X-Update-Range" found in the headers');
+ }
+
+ $contentType = strtolower(
+ $request->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;
+ }
+
+ if (!$this->server->emit('beforeWriteContent', [$path, $node, null]))
+ return;
+
+ $body = $this->server->httpRequest->getBody();
+
+
+ $etag = $node->patch($body, $range[0], isset($range[1]) ? $range[1] : null);
+
+ $this->server->emit('afterWriteContent', [$path, $node]);
+
+ $response->setHeader('Content-Length', '0');
+ if ($etag) $response->setHeader('ETag', $etag);
+ $response->setStatus(204);
+
+ // Breaks the event chain
+ 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
+ * [1] - append
+ * [2,10,15] - update bytes 10, 11, 12, 13, 14, 15
+ * [2,10,null] - update bytes 10 until the end of the patch body
+ * [3,-5] - update from 5 bytes from the end of the file.
+ *
+ * @param RequestInterface $request
+ * @return array|null
+ */
+ function getHTTPUpdateRange(RequestInterface $request) {
+
+ $range = $request->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 [self::RANGE_APPEND];
+ } elseif (strlen($matches[2]) > 0) {
+ return [self::RANGE_START, $matches[2], $matches[3] ?: null];
+ } else {
+ return [self::RANGE_END, $matches[4]];
+ }
+
+ }
+}