/usr/share/php/sabre21/Sabre/DAV/PartialUpdate/Plugin.php is in php-sabre-dav-2.1 2.1.10-1ubuntu1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 | <?php
namespace Sabre\DAV\PartialUpdate;
use
Sabre\DAV,
Sabre\HTTP\RequestInterface,
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 IFile || $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 IFile && !$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();
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->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];
} elseif ($matches[4]) {
return [self::RANGE_END, $matches[4]];
} else {
return null;
}
}
}
|