Blame view
sources/3rdparty/sabre/dav/lib/Sabre/DAV/PartialUpdate/Plugin.php
7.58 KB
|
03e52840d
|
1 |
<?php |
|
6d9380f96
|
2 3 4 5 |
namespace Sabre\DAV\PartialUpdate; use Sabre\DAV; |
|
03e52840d
|
6 7 8 9 10 11 12 |
/** * 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. * |
|
6d9380f96
|
13 |
* $patchPlugin = new \Sabre\DAV\PartialUpdate\Plugin(); |
|
03e52840d
|
14 15 |
* $server->addPlugin($patchPlugin); * |
|
6d9380f96
|
16 |
* @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/). |
|
03e52840d
|
17 |
* @author Jean-Tiare LE BIGOT (http://www.jtlebi.fr/) |
|
6d9380f96
|
18 |
* @license http://sabre.io/license/ Modified BSD License |
|
03e52840d
|
19 |
*/ |
|
6d9380f96
|
20 21 22 23 24 |
class Plugin extends DAV\ServerPlugin {
const RANGE_APPEND = 1;
const RANGE_START = 2;
const RANGE_END = 3;
|
|
03e52840d
|
25 26 27 28 |
/**
* Reference to server
*
|
|
6d9380f96
|
29 |
* @var Sabre\DAV\Server |
|
03e52840d
|
30 31 32 33 34 35 36 37 |
*/
protected $server;
/**
* Initializes the plugin
*
* This method is automatically called by the Server class after addPlugin.
*
|
|
6d9380f96
|
38 |
* @param DAV\Server $server |
|
03e52840d
|
39 40 |
* @return void
*/
|
|
6d9380f96
|
41 |
public function initialize(DAV\Server $server) {
|
|
03e52840d
|
42 43 44 45 46 47 48 49 50 51 |
$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
|
|
6d9380f96
|
52 |
* using DAV\Server::getPlugin |
|
03e52840d
|
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
*
* @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) {
|
|
6d9380f96
|
75 |
|
|
03e52840d
|
76 77 78 79 80 81 82 83 84 85 86 87 88 |
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.
|
|
6d9380f96
|
89 |
* |
|
03e52840d
|
90 91 92 93 94 95 96 97 |
* 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) {
|
|
6d9380f96
|
98 |
|
|
03e52840d
|
99 |
$tree = $this->server->tree; |
|
6d9380f96
|
100 101 102 103 104 105 106 |
if ($tree->nodeExists($uri)) {
$node = $tree->getNodeForPath($uri);
if ($node instanceof IFile || $node instanceof IPatchSupport) {
return array('PATCH');
}
}
return array();
|
|
03e52840d
|
107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 |
}
/**
* Returns a list of features for the HTTP OPTIONS Dav: header.
*
* @return array
*/
public function getFeatures() {
return array('sabredav-partialupdate');
}
/**
* Patch an uri
*
|
|
6d9380f96
|
124 |
* The WebDAV patch request can be used to modify only a part of an |
|
03e52840d
|
125 126 127 128 129 130 131 132 133 134 |
* 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);
|
|
6d9380f96
|
135 136 |
if (!$node instanceof IFile && !$node instanceof IPatchSupport) {
throw new DAV\Exception\MethodNotAllowed('The target resource does not support the PATCH method.');
|
|
03e52840d
|
137 138 139 140 141 |
}
$range = $this->getHTTPUpdateRange();
if (!$range) {
|
|
6d9380f96
|
142 |
throw new DAV\Exception\BadRequest('No valid "X-Update-Range" found in the headers');
|
|
03e52840d
|
143 |
} |
|
6d9380f96
|
144 |
|
|
03e52840d
|
145 146 147 |
$contentType = strtolower(
$this->server->httpRequest->getHeader('Content-Type')
);
|
|
6d9380f96
|
148 |
|
|
03e52840d
|
149 |
if ($contentType != 'application/x-sabredav-partialupdate') {
|
|
6d9380f96
|
150 |
throw new DAV\Exception\UnsupportedMediaType('Unknown Content-Type header "' . $contentType . '"');
|
|
03e52840d
|
151 152 153 |
}
$len = $this->server->httpRequest->getHeader('Content-Length');
|
|
6d9380f96
|
154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 |
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;
}
|
|
03e52840d
|
171 172 173 174 175 176 177 |
// 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();
|
|
6d9380f96
|
178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 |
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;
}
}
|
|
03e52840d
|
195 196 197 198 199 200 201 202 203 204 |
$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;
}
|
|
6d9380f96
|
205 |
|
|
03e52840d
|
206 207 208 209 |
/**
* Returns the HTTP custom range update header
*
* This method returns null if there is no well-formed HTTP range request
|
|
6d9380f96
|
210 211 212 213 |
* 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.
|
|
03e52840d
|
214 |
* |
|
6d9380f96
|
215 |
* Examples: |
|
03e52840d
|
216 |
* |
|
6d9380f96
|
217 218 219 220 221 |
* 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.
|
|
03e52840d
|
222 223 224 225 226 227 228 229 230 |
*
* @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
|
|
6d9380f96
|
231 |
if (!preg_match('/^(append)|(?:bytes=([0-9]+)-([0-9]*))|(?:bytes=(-[0-9]+))$/i',$range,$matches)) return null;
|
|
03e52840d
|
232 |
|
|
6d9380f96
|
233 234 235 236 237 238 239 240 241 |
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;
}
|
|
03e52840d
|
242 243 244 |
}
}
|