/usr/share/php/Sabre/DAV/PartialUpdate/Plugin.php is in php-horde-dav 1.0.3-2.
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 | <?php
namespace Sabre\DAV\PartialUpdate;
use Sabre\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 \Sabre\DAV\PartialUpdate\Plugin();
* $server->addPlugin($patchPlugin);
*
* @copyright Copyright (C) 2007-2013 fruux GmbH (https://fruux.com/).
* @author Jean-Tiare LE BIGOT (http://www.jtlebi.fr/)
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
*/
class Plugin extends DAV\ServerPlugin {
/**
* 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
*/
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) &&
$tree->getNodeForPath($uri) instanceof IFile) {
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)) {
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');
// Load the begin and end data
$start = ($range[0])?$range[0]:0;
$end = ($range[1])?$range[1]:$len-1;
// Check consistency
if($end < $start)
throw new DAV\Exception\RequestedRangeNotSatisfiable('The end offset (' . $range[1] . ') is lower than the start offset (' . $range[0] . ')');
if($end - $start + 1 != $len)
throw new DAV\Exception\RequestedRangeNotSatisfiable('Actual data length (' . $len . ') is not consistent with begin (' . $range[0] . ') and end (' . $range[1] . ') offsets');
// 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();
$etag = $node->putRange($body, $start-1);
$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 or array($start, $end).
*
* The first number is the offset of the first byte in the range.
* 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
*
* @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('/^bytes=([0-9]*)-([0-9]*)$/i',$range,$matches)) return null;
if ($matches[1]==='' && $matches[2]==='') return null;
return array(
$matches[1]!==''?$matches[1]:null,
$matches[2]!==''?$matches[2]:null,
);
}
}
|