ILIAS  release_5-4 Revision v5.4.26-12-gabc799a52e6
Sabre\DAV\CorePlugin Class Reference

The core plugin provides all the basic features for a WebDAV server. More...

+ Inheritance diagram for Sabre\DAV\CorePlugin:
+ Collaboration diagram for Sabre\DAV\CorePlugin:

Public Member Functions

 initialize (Server $server)
 Sets up the plugin. More...
 
 getPluginName ()
 Returns a plugin name. More...
 
 httpGet (RequestInterface $request, ResponseInterface $response)
 This is the default implementation for the GET method. More...
 
 httpOptions (RequestInterface $request, ResponseInterface $response)
 HTTP OPTIONS. More...
 
 httpHead (RequestInterface $request, ResponseInterface $response)
 HTTP HEAD. More...
 
 httpDelete (RequestInterface $request, ResponseInterface $response)
 HTTP Delete. More...
 
 httpPropFind (RequestInterface $request, ResponseInterface $response)
 WebDAV PROPFIND. More...
 
 httpPropPatch (RequestInterface $request, ResponseInterface $response)
 WebDAV PROPPATCH. More...
 
 httpPut (RequestInterface $request, ResponseInterface $response)
 HTTP PUT method. More...
 
 httpMkcol (RequestInterface $request, ResponseInterface $response)
 WebDAV MKCOL. More...
 
 httpMove (RequestInterface $request, ResponseInterface $response)
 WebDAV HTTP MOVE method. More...
 
 httpCopy (RequestInterface $request, ResponseInterface $response)
 WebDAV HTTP COPY method. More...
 
 httpReport (RequestInterface $request, ResponseInterface $response)
 HTTP REPORT method implementation. More...
 
 propPatchProtectedPropertyCheck ($path, PropPatch $propPatch)
 This method is called during property updates. More...
 
 propPatchNodeUpdate ($path, PropPatch $propPatch)
 This method is called during property updates. More...
 
 propFind (PropFind $propFind, INode $node)
 This method is called when properties are retrieved. More...
 
 propFindNode (PropFind $propFind, INode $node)
 Fetches properties for a node. More...
 
 propFindLate (PropFind $propFind, INode $node)
 This method is called when properties are retrieved. More...
 
 exception ($e)
 Listens for exception events, and automatically logs them. More...
 
 getPluginInfo ()
 Returns a bunch of meta-data about the plugin. More...
 
- Public Member Functions inherited from Sabre\DAV\ServerPlugin
 initialize (Server $server)
 This initializes the plugin. More...
 
 getFeatures ()
 This method should return a list of server-features. More...
 
 getHTTPMethods ($path)
 Use this method to tell the server this plugin defines additional HTTP methods. More...
 
 getPluginName ()
 Returns a plugin name. More...
 
 getSupportedReportSet ($uri)
 Returns a list of reports this plugin supports. More...
 
 getPluginInfo ()
 Returns a bunch of meta-data about the plugin. More...
 

Protected Attributes

 $server
 

Detailed Description

The core plugin provides all the basic features for a WebDAV server.

Author
Evert Pot (http://evertpot.com/) http://sabre.io/license/ Modified BSD License

Definition at line 17 of file CorePlugin.php.

Member Function Documentation

◆ exception()

Sabre\DAV\CorePlugin::exception (   $e)

Listens for exception events, and automatically logs them.

Parameters
Exception$e

Definition at line 912 of file CorePlugin.php.

References $code, Psr\Log\LogLevel\CRITICAL, Psr\Log\LogLevel\ERROR, and Psr\Log\LogLevel\INFO.

912  {
913 
914  $logLevel = \Psr\Log\LogLevel::CRITICAL;
915  if ($e instanceof \Sabre\DAV\Exception) {
916  // If it's a standard sabre/dav exception, it means we have a http
917  // status code available.
918  $code = $e->getHTTPCode();
919 
920  if ($code >= 400 && $code < 500) {
921  // user error
922  $logLevel = \Psr\Log\LogLevel::INFO;
923  } else {
924  // Server-side error. We mark it's as an error, but it's not
925  // critical.
926  $logLevel = \Psr\Log\LogLevel::ERROR;
927  }
928  }
929 
930  $this->server->getLogger()->log(
931  $logLevel,
932  'Uncaught exception',
933  [
934  'exception' => $e,
935  ]
936  );
937  }
$code
Definition: example_050.php:99

◆ getPluginInfo()

Sabre\DAV\CorePlugin::getPluginInfo ( )

Returns a bunch of meta-data about the plugin.

Providing this information is optional, and is mainly displayed by the Browser plugin.

The description key in the returned array may contain html and will not be sanitized.

Returns
array

Definition at line 950 of file CorePlugin.php.

References Sabre\DAV\CorePlugin\getPluginName().

950  {
951 
952  return [
953  'name' => $this->getPluginName(),
954  'description' => 'The Core plugin provides a lot of the basic functionality required by WebDAV, such as a default implementation for all HTTP and WebDAV methods.',
955  'link' => null,
956  ];
957 
958  }
getPluginName()
Returns a plugin name.
Definition: CorePlugin.php:65
+ Here is the call graph for this function:

◆ getPluginName()

Sabre\DAV\CorePlugin::getPluginName ( )

Returns a plugin name.

Using this name other plugins will be able to access other plugins using DAV::getPlugin

Returns
string

Definition at line 65 of file CorePlugin.php.

Referenced by Sabre\DAV\CorePlugin\getPluginInfo().

65  {
66 
67  return 'core';
68 
69  }
+ Here is the caller graph for this function:

◆ httpCopy()

Sabre\DAV\CorePlugin::httpCopy ( RequestInterface  $request,
ResponseInterface  $response 
)

WebDAV HTTP COPY method.

This method copies one uri to a different uri, and works much like the MOVE request A lot of the actual request processing is done in getCopyMoveInfo

Parameters
RequestInterface$request
ResponseInterface$response
Returns
bool

Definition at line 672 of file CorePlugin.php.

References $path, Sabre\HTTP\RequestInterface\getPath(), Sabre\HTTP\MessageInterface\setHeader(), and Sabre\HTTP\ResponseInterface\setStatus().

672  {
673 
674  $path = $request->getPath();
675 
676  $copyInfo = $this->server->getCopyAndMoveInfo($request);
677 
678  if (!$this->server->emit('beforeBind', [$copyInfo['destination']])) return false;
679  if ($copyInfo['destinationExists']) {
680  if (!$this->server->emit('beforeUnbind', [$copyInfo['destination']])) return false;
681  $this->server->tree->delete($copyInfo['destination']);
682  }
683 
684  $this->server->tree->copy($path, $copyInfo['destination']);
685  $this->server->emit('afterBind', [$copyInfo['destination']]);
686 
687  // If a resource was overwritten we should send a 204, otherwise a 201
688  $response->setHeader('Content-Length', '0');
689  $response->setStatus($copyInfo['destinationExists'] ? 204 : 201);
690 
691  // Sending back false will interrupt the event chain and tell the server
692  // we've handled this method.
693  return false;
694 
695 
696  }
$path
Definition: aliased.php:25
foreach($paths as $path) $request
Definition: asyncclient.php:32
$response
+ Here is the call graph for this function:

◆ httpDelete()

Sabre\DAV\CorePlugin::httpDelete ( RequestInterface  $request,
ResponseInterface  $response 
)

HTTP Delete.

The HTTP delete method, deletes a given uri

Parameters
RequestInterface$request
ResponseInterface$response
Returns
void

Definition at line 282 of file CorePlugin.php.

References $path, Sabre\HTTP\RequestInterface\getPath(), Sabre\HTTP\MessageInterface\setHeader(), and Sabre\HTTP\ResponseInterface\setStatus().

282  {
283 
284  $path = $request->getPath();
285 
286  if (!$this->server->emit('beforeUnbind', [$path])) return false;
287  $this->server->tree->delete($path);
288  $this->server->emit('afterUnbind', [$path]);
289 
290  $response->setStatus(204);
291  $response->setHeader('Content-Length', '0');
292 
293  // Sending back false will interrupt the event chain and tell the server
294  // we've handled this method.
295  return false;
296 
297  }
$path
Definition: aliased.php:25
foreach($paths as $path) $request
Definition: asyncclient.php:32
$response
+ Here is the call graph for this function:

◆ httpGet()

Sabre\DAV\CorePlugin::httpGet ( RequestInterface  $request,
ResponseInterface  $response 
)

This is the default implementation for the GET method.

Parameters
RequestInterface$request
ResponseInterface$response
Returns
bool

Definition at line 78 of file CorePlugin.php.

References $end, $path, $start, GuzzleHttp\Psr7\$stream, Sabre\HTTP\MessageInterface\addHeaders(), Sabre\HTTP\MessageInterface\getHeader(), Sabre\HTTP\RequestInterface\getPath(), Sabre\HTTP\MessageInterface\setBody(), Sabre\HTTP\MessageInterface\setHeader(), and Sabre\HTTP\ResponseInterface\setStatus().

78  {
79 
80  $path = $request->getPath();
81  $node = $this->server->tree->getNodeForPath($path);
82 
83  if (!$node instanceof IFile) return;
84 
85  $body = $node->get();
86 
87  // Converting string into stream, if needed.
88  if (is_string($body)) {
89  $stream = fopen('php://temp', 'r+');
90  fwrite($stream, $body);
91  rewind($stream);
92  $body = $stream;
93  }
94 
95  /*
96  * TODO: getetag, getlastmodified, getsize should also be used using
97  * this method
98  */
99  $httpHeaders = $this->server->getHTTPHeaders($path);
100 
101  /* ContentType needs to get a default, because many webservers will otherwise
102  * default to text/html, and we don't want this for security reasons.
103  */
104  if (!isset($httpHeaders['Content-Type'])) {
105  $httpHeaders['Content-Type'] = 'application/octet-stream';
106  }
107 
108 
109  if (isset($httpHeaders['Content-Length'])) {
110 
111  $nodeSize = $httpHeaders['Content-Length'];
112 
113  // Need to unset Content-Length, because we'll handle that during figuring out the range
114  unset($httpHeaders['Content-Length']);
115 
116  } else {
117  $nodeSize = null;
118  }
119 
120  $response->addHeaders($httpHeaders);
121 
122  $range = $this->server->getHTTPRange();
123  $ifRange = $request->getHeader('If-Range');
124  $ignoreRangeHeader = false;
125 
126  // If ifRange is set, and range is specified, we first need to check
127  // the precondition.
128  if ($nodeSize && $range && $ifRange) {
129 
130  // if IfRange is parsable as a date we'll treat it as a DateTime
131  // otherwise, we must treat it as an etag.
132  try {
133  $ifRangeDate = new \DateTime($ifRange);
134 
135  // It's a date. We must check if the entity is modified since
136  // the specified date.
137  if (!isset($httpHeaders['Last-Modified'])) $ignoreRangeHeader = true;
138  else {
139  $modified = new \DateTime($httpHeaders['Last-Modified']);
140  if ($modified > $ifRangeDate) $ignoreRangeHeader = true;
141  }
142 
143  } catch (\Exception $e) {
144 
145  // It's an entity. We can do a simple comparison.
146  if (!isset($httpHeaders['ETag'])) $ignoreRangeHeader = true;
147  elseif ($httpHeaders['ETag'] !== $ifRange) $ignoreRangeHeader = true;
148  }
149  }
150 
151  // We're only going to support HTTP ranges if the backend provided a filesize
152  if (!$ignoreRangeHeader && $nodeSize && $range) {
153 
154  // Determining the exact byte offsets
155  if (!is_null($range[0])) {
156 
157  $start = $range[0];
158  $end = $range[1] ? $range[1] : $nodeSize - 1;
159  if ($start >= $nodeSize)
160  throw new Exception\RequestedRangeNotSatisfiable('The start offset (' . $range[0] . ') exceeded the size of the entity (' . $nodeSize . ')');
161 
162  if ($end < $start) throw new Exception\RequestedRangeNotSatisfiable('The end offset (' . $range[1] . ') is lower than the start offset (' . $range[0] . ')');
163  if ($end >= $nodeSize) $end = $nodeSize - 1;
164 
165  } else {
166 
167  $start = $nodeSize - $range[1];
168  $end = $nodeSize - 1;
169 
170  if ($start < 0) $start = 0;
171 
172  }
173 
174  // Streams may advertise themselves as seekable, but still not
175  // actually allow fseek. We'll manually go forward in the stream
176  // if fseek failed.
177  if (!stream_get_meta_data($body)['seekable'] || fseek($body, $start, SEEK_SET) === -1) {
178  $consumeBlock = 8192;
179  for ($consumed = 0; $start - $consumed > 0;){
180  if (feof($body)) throw new Exception\RequestedRangeNotSatisfiable('The start offset (' . $start . ') exceeded the size of the entity (' . $consumed . ')');
181  $consumed += strlen(fread($body, min($start - $consumed, $consumeBlock)));
182  }
183  }
184 
185  $response->setHeader('Content-Length', $end - $start + 1);
186  $response->setHeader('Content-Range', 'bytes ' . $start . '-' . $end . '/' . $nodeSize);
187  $response->setStatus(206);
188  $response->setBody($body);
189 
190  } else {
191 
192  if ($nodeSize) $response->setHeader('Content-Length', $nodeSize);
193  $response->setStatus(200);
194  $response->setBody($body);
195 
196  }
197  // Sending back false will interrupt the event chain and tell the server
198  // we've handled this method.
199  return false;
200 
201  }
$path
Definition: aliased.php:25
foreach($paths as $path) $request
Definition: asyncclient.php:32
$stream
PHP stream implementation.
$start
Definition: bench.php:8
$response
+ Here is the call graph for this function:

◆ httpHead()

Sabre\DAV\CorePlugin::httpHead ( RequestInterface  $request,
ResponseInterface  $response 
)

HTTP HEAD.

This method is normally used to take a peak at a url, and only get the HTTP response headers, without the body. This is used by clients to determine if a remote file was changed, so they can use a local cached version, instead of downloading it again

Parameters
RequestInterface$request
ResponseInterface$response
Returns
bool

Definition at line 245 of file CorePlugin.php.

References $request, $subRequest, Sabre\HTTP\MessageInterface\setBody(), Sabre\HTTP\MessageInterface\setHeader(), and Sabre\HTTP\ResponseInterface\setStatus().

245  {
246 
247  // This is implemented by changing the HEAD request to a GET request,
248  // and dropping the response body.
249  $subRequest = clone $request;
250  $subRequest->setMethod('GET');
251 
252  try {
253  $this->server->invokeMethod($subRequest, $response, false);
254  $response->setBody('');
255  } catch (Exception\NotImplemented $e) {
256  // Some clients may do HEAD requests on collections, however, GET
257  // requests and HEAD requests _may_ not be defined on a collection,
258  // which would trigger a 501.
259  // This breaks some clients though, so we're transforming these
260  // 501s into 200s.
261  $response->setStatus(200);
262  $response->setBody('');
263  $response->setHeader('Content-Type', 'text/plain');
264  $response->setHeader('X-Sabre-Real-Status', $e->getHTTPCode());
265  }
266 
267  // Sending back false will interrupt the event chain and tell the server
268  // we've handled this method.
269  return false;
270 
271  }
foreach($paths as $path) $request
Definition: asyncclient.php:32
$subRequest
$response
+ Here is the call graph for this function:

◆ httpMkcol()

Sabre\DAV\CorePlugin::httpMkcol ( RequestInterface  $request,
ResponseInterface  $response 
)

WebDAV MKCOL.

The MKCOL method is used to create a new collection (directory) on the server

Parameters
RequestInterface$request
ResponseInterface$response
Returns
bool

Definition at line 552 of file CorePlugin.php.

References $contentType, $path, $result, Sabre\HTTP\MessageInterface\getBodyAsString(), Sabre\HTTP\MessageInterface\getHeader(), Sabre\HTTP\RequestInterface\getPath(), Sabre\HTTP\MessageInterface\setBody(), Sabre\HTTP\MessageInterface\setHeader(), and Sabre\HTTP\ResponseInterface\setStatus().

552  {
553 
554  $requestBody = $request->getBodyAsString();
555  $path = $request->getPath();
556 
557  if ($requestBody) {
558 
559  $contentType = $request->getHeader('Content-Type');
560  if (strpos($contentType, 'application/xml') !== 0 && strpos($contentType, 'text/xml') !== 0) {
561 
562  // We must throw 415 for unsupported mkcol bodies
563  throw new Exception\UnsupportedMediaType('The request body for the MKCOL request must have an xml Content-Type');
564 
565  }
566 
567  try {
568  $mkcol = $this->server->xml->expect('{DAV:}mkcol', $requestBody);
569  } catch (\Sabre\Xml\ParseException $e) {
570  throw new Exception\BadRequest($e->getMessage(), null, $e);
571  }
572 
573  $properties = $mkcol->getProperties();
574 
575  if (!isset($properties['{DAV:}resourcetype']))
576  throw new Exception\BadRequest('The mkcol request must include a {DAV:}resourcetype property');
577 
578  $resourceType = $properties['{DAV:}resourcetype']->getValue();
579  unset($properties['{DAV:}resourcetype']);
580 
581  } else {
582 
583  $properties = [];
584  $resourceType = ['{DAV:}collection'];
585 
586  }
587 
588  $mkcol = new MkCol($resourceType, $properties);
589 
590  $result = $this->server->createCollection($path, $mkcol);
591 
592  if (is_array($result)) {
593  $response->setStatus(207);
594  $response->setHeader('Content-Type', 'application/xml; charset=utf-8');
595 
596  $response->setBody(
597  $this->server->generateMultiStatus([$result])
598  );
599 
600  } else {
601  $response->setHeader('Content-Length', '0');
602  $response->setStatus(201);
603  }
604 
605  // Sending back false will interrupt the event chain and tell the server
606  // we've handled this method.
607  return false;
608 
609  }
$path
Definition: aliased.php:25
$result
foreach($paths as $path) $request
Definition: asyncclient.php:32
if($path[strlen($path) - 1]==='/') if(is_dir($path)) if(!file_exists($path)) if(preg_match('#\.php$#D', mb_strtolower($path, 'UTF-8'))) $contentType
Definition: module.php:144
$response
+ Here is the call graph for this function:

◆ httpMove()

Sabre\DAV\CorePlugin::httpMove ( RequestInterface  $request,
ResponseInterface  $response 
)

WebDAV HTTP MOVE method.

This method moves one uri to a different uri. A lot of the actual request processing is done in getCopyMoveInfo

Parameters
RequestInterface$request
ResponseInterface$response
Returns
bool

Definition at line 620 of file CorePlugin.php.

References $path, Sabre\HTTP\RequestInterface\getPath(), Sabre\HTTP\MessageInterface\setHeader(), and Sabre\HTTP\ResponseInterface\setStatus().

620  {
621 
622  $path = $request->getPath();
623 
624  $moveInfo = $this->server->getCopyAndMoveInfo($request);
625 
626  if ($moveInfo['destinationExists']) {
627 
628  if (!$this->server->emit('beforeUnbind', [$moveInfo['destination']])) return false;
629 
630  }
631  if (!$this->server->emit('beforeUnbind', [$path])) return false;
632  if (!$this->server->emit('beforeBind', [$moveInfo['destination']])) return false;
633  if (!$this->server->emit('beforeMove', [$path, $moveInfo['destination']])) return false;
634 
635  if ($moveInfo['destinationExists']) {
636 
637  $this->server->tree->delete($moveInfo['destination']);
638  $this->server->emit('afterUnbind', [$moveInfo['destination']]);
639 
640  }
641 
642  $this->server->tree->move($path, $moveInfo['destination']);
643 
644  // Its important afterMove is called before afterUnbind, because it
645  // allows systems to transfer data from one path to another.
646  // PropertyStorage uses this. If afterUnbind was first, it would clean
647  // up all the properties before it has a chance.
648  $this->server->emit('afterMove', [$path, $moveInfo['destination']]);
649  $this->server->emit('afterUnbind', [$path]);
650  $this->server->emit('afterBind', [$moveInfo['destination']]);
651 
652  // If a resource was overwritten we should send a 204, otherwise a 201
653  $response->setHeader('Content-Length', '0');
654  $response->setStatus($moveInfo['destinationExists'] ? 204 : 201);
655 
656  // Sending back false will interrupt the event chain and tell the server
657  // we've handled this method.
658  return false;
659 
660  }
$path
Definition: aliased.php:25
foreach($paths as $path) $request
Definition: asyncclient.php:32
$response
+ Here is the call graph for this function:

◆ httpOptions()

Sabre\DAV\CorePlugin::httpOptions ( RequestInterface  $request,
ResponseInterface  $response 
)

HTTP OPTIONS.

Parameters
RequestInterface$request
ResponseInterface$response
Returns
bool

Definition at line 210 of file CorePlugin.php.

References Sabre\HTTP\RequestInterface\getPath(), Sabre\HTTP\MessageInterface\setHeader(), and Sabre\HTTP\ResponseInterface\setStatus().

210  {
211 
212  $methods = $this->server->getAllowedMethods($request->getPath());
213 
214  $response->setHeader('Allow', strtoupper(implode(', ', $methods)));
215  $features = ['1', '3', 'extended-mkcol'];
216 
217  foreach ($this->server->getPlugins() as $plugin) {
218  $features = array_merge($features, $plugin->getFeatures());
219  }
220 
221  $response->setHeader('DAV', implode(', ', $features));
222  $response->setHeader('MS-Author-Via', 'DAV');
223  $response->setHeader('Accept-Ranges', 'bytes');
224  $response->setHeader('Content-Length', '0');
225  $response->setStatus(200);
226 
227  // Sending back false will interrupt the event chain and tell the server
228  // we've handled this method.
229  return false;
230 
231  }
foreach($paths as $path) $request
Definition: asyncclient.php:32
$response
+ Here is the call graph for this function:

◆ httpPropFind()

Sabre\DAV\CorePlugin::httpPropFind ( RequestInterface  $request,
ResponseInterface  $response 
)

WebDAV PROPFIND.

This WebDAV method requests information about an uri resource, or a list of resources If a client wants to receive the properties for a single resource it will add an HTTP Depth: header with a 0 value If the value is 1, it means that it also expects a list of sub-resources (e.g.: files in a directory)

The request body contains an XML data structure that has a list of properties the client understands The response body is also an xml document, containing information about every uri resource and the requested properties

It has to return a HTTP 207 Multi-status status code

Parameters
RequestInterface$request
ResponseInterface$response
Returns
void

Definition at line 315 of file CorePlugin.php.

References $data, $path, Sabre\HTTP\MessageInterface\getBodyAsString(), Sabre\HTTP\RequestInterface\getPath(), Sabre\HTTP\MessageInterface\setBody(), Sabre\HTTP\MessageInterface\setHeader(), and Sabre\HTTP\ResponseInterface\setStatus().

315  {
316 
317  $path = $request->getPath();
318 
319  $requestBody = $request->getBodyAsString();
320  if (strlen($requestBody)) {
321  try {
322  $propFindXml = $this->server->xml->expect('{DAV:}propfind', $requestBody);
323  } catch (ParseException $e) {
324  throw new BadRequest($e->getMessage(), null, $e);
325  }
326  } else {
327  $propFindXml = new Xml\Request\PropFind();
328  $propFindXml->allProp = true;
329  $propFindXml->properties = [];
330  }
331 
332  $depth = $this->server->getHTTPDepth(1);
333  // The only two options for the depth of a propfind is 0 or 1 - as long as depth infinity is not enabled
334  if (!$this->server->enablePropfindDepthInfinity && $depth != 0) $depth = 1;
335 
336  $newProperties = $this->server->getPropertiesIteratorForPath($path, $propFindXml->properties, $depth);
337 
338  // This is a multi-status response
339  $response->setStatus(207);
340  $response->setHeader('Content-Type', 'application/xml; charset=utf-8');
341  $response->setHeader('Vary', 'Brief,Prefer');
342 
343  // Normally this header is only needed for OPTIONS responses, however..
344  // iCal seems to also depend on these being set for PROPFIND. Since
345  // this is not harmful, we'll add it.
346  $features = ['1', '3', 'extended-mkcol'];
347  foreach ($this->server->getPlugins() as $plugin) {
348  $features = array_merge($features, $plugin->getFeatures());
349  }
350  $response->setHeader('DAV', implode(', ', $features));
351 
352  $prefer = $this->server->getHTTPPrefer();
353  $minimal = $prefer['return'] === 'minimal';
354 
355  $data = $this->server->generateMultiStatus($newProperties, $minimal);
356  $response->setBody($data);
357 
358  // Sending back false will interrupt the event chain and tell the server
359  // we've handled this method.
360  return false;
361 
362  }
$path
Definition: aliased.php:25
foreach($paths as $path) $request
Definition: asyncclient.php:32
$response
$data
Definition: bench.php:6
+ Here is the call graph for this function:

◆ httpPropPatch()

Sabre\DAV\CorePlugin::httpPropPatch ( RequestInterface  $request,
ResponseInterface  $response 
)

WebDAV PROPPATCH.

This method is called to update properties on a Node. The request is an XML body with all the mutations. In this XML body it is specified which properties should be set/updated and/or deleted

Parameters
RequestInterface$request
ResponseInterface$response
Returns
bool

Definition at line 374 of file CorePlugin.php.

References $code, $ok, $path, $result, Sabre\HTTP\MessageInterface\getBody(), Sabre\HTTP\RequestInterface\getPath(), Sabre\HTTP\MessageInterface\setBody(), Sabre\HTTP\MessageInterface\setHeader(), and Sabre\HTTP\ResponseInterface\setStatus().

374  {
375 
376  $path = $request->getPath();
377 
378  try {
379  $propPatch = $this->server->xml->expect('{DAV:}propertyupdate', $request->getBody());
380  } catch (ParseException $e) {
381  throw new BadRequest($e->getMessage(), null, $e);
382  }
383  $newProperties = $propPatch->properties;
384 
385  $result = $this->server->updateProperties($path, $newProperties);
386 
387  $prefer = $this->server->getHTTPPrefer();
388  $response->setHeader('Vary', 'Brief,Prefer');
389 
390  if ($prefer['return'] === 'minimal') {
391 
392  // If return-minimal is specified, we only have to check if the
393  // request was successful, and don't need to return the
394  // multi-status.
395  $ok = true;
396  foreach ($result as $prop => $code) {
397  if ((int)$code > 299) {
398  $ok = false;
399  }
400  }
401 
402  if ($ok) {
403 
404  $response->setStatus(204);
405  return false;
406 
407  }
408 
409  }
410 
411  $response->setStatus(207);
412  $response->setHeader('Content-Type', 'application/xml; charset=utf-8');
413 
414 
415  // Reorganizing the result for generateMultiStatus
416  $multiStatus = [];
417  foreach ($result as $propertyName => $code) {
418  if (isset($multiStatus[$code])) {
419  $multiStatus[$code][$propertyName] = null;
420  } else {
421  $multiStatus[$code] = [$propertyName => null];
422  }
423  }
424  $multiStatus['href'] = $path;
425 
426  $response->setBody(
427  $this->server->generateMultiStatus([$multiStatus])
428  );
429 
430  // Sending back false will interrupt the event chain and tell the server
431  // we've handled this method.
432  return false;
433 
434  }
$path
Definition: aliased.php:25
$result
foreach($paths as $path) $request
Definition: asyncclient.php:32
$code
Definition: example_050.php:99
$response
+ Here is the call graph for this function:

◆ httpPut()

Sabre\DAV\CorePlugin::httpPut ( RequestInterface  $request,
ResponseInterface  $response 
)

HTTP PUT method.

This HTTP method updates a file, or creates a new one.

If a new resource was created, a 201 Created status code should be returned. If an existing resource is updated, it's a 204 No Content

Parameters
RequestInterface$request
ResponseInterface$response
Returns
bool

Definition at line 447 of file CorePlugin.php.

References $path, Sabre\HTTP\MessageInterface\getBodyAsStream(), Sabre\HTTP\MessageInterface\getHeader(), Sabre\HTTP\RequestInterface\getPath(), Sabre\HTTP\MessageInterface\setHeader(), and Sabre\HTTP\ResponseInterface\setStatus().

447  {
448 
449  $body = $request->getBodyAsStream();
450  $path = $request->getPath();
451 
452  // Intercepting Content-Range
453  if ($request->getHeader('Content-Range')) {
454  /*
455  An origin server that allows PUT on a given target resource MUST send
456  a 400 (Bad Request) response to a PUT request that contains a
457  Content-Range header field.
458 
459  Reference: http://tools.ietf.org/html/rfc7231#section-4.3.4
460  */
461  throw new Exception\BadRequest('Content-Range on PUT requests are forbidden.');
462  }
463 
464  // Intercepting the Finder problem
465  if (($expected = $request->getHeader('X-Expected-Entity-Length')) && $expected > 0) {
466 
467  /*
468  Many webservers will not cooperate well with Finder PUT requests,
469  because it uses 'Chunked' transfer encoding for the request body.
470 
471  The symptom of this problem is that Finder sends files to the
472  server, but they arrive as 0-length files in PHP.
473 
474  If we don't do anything, the user might think they are uploading
475  files successfully, but they end up empty on the server. Instead,
476  we throw back an error if we detect this.
477 
478  The reason Finder uses Chunked, is because it thinks the files
479  might change as it's being uploaded, and therefore the
480  Content-Length can vary.
481 
482  Instead it sends the X-Expected-Entity-Length header with the size
483  of the file at the very start of the request. If this header is set,
484  but we don't get a request body we will fail the request to
485  protect the end-user.
486  */
487 
488  // Only reading first byte
489  $firstByte = fread($body, 1);
490  if (strlen($firstByte) !== 1) {
491  throw new Exception\Forbidden('This server is not compatible with OS/X finder. Consider using a different WebDAV client or webserver.');
492  }
493 
494  // The body needs to stay intact, so we copy everything to a
495  // temporary stream.
496 
497  $newBody = fopen('php://temp', 'r+');
498  fwrite($newBody, $firstByte);
499  stream_copy_to_stream($body, $newBody);
500  rewind($newBody);
501 
502  $body = $newBody;
503 
504  }
505 
506  if ($this->server->tree->nodeExists($path)) {
507 
508  $node = $this->server->tree->getNodeForPath($path);
509 
510  // If the node is a collection, we'll deny it
511  if (!($node instanceof IFile)) throw new Exception\Conflict('PUT is not allowed on non-files.');
512 
513  if (!$this->server->updateFile($path, $body, $etag)) {
514  return false;
515  }
516 
517  $response->setHeader('Content-Length', '0');
518  if ($etag) $response->setHeader('ETag', $etag);
519  $response->setStatus(204);
520 
521  } else {
522 
523  $etag = null;
524  // If we got here, the resource didn't exist yet.
525  if (!$this->server->createFile($path, $body, $etag)) {
526  // For one reason or another the file was not created.
527  return false;
528  }
529 
530  $response->setHeader('Content-Length', '0');
531  if ($etag) $response->setHeader('ETag', $etag);
532  $response->setStatus(201);
533 
534  }
535 
536  // Sending back false will interrupt the event chain and tell the server
537  // we've handled this method.
538  return false;
539 
540  }
$path
Definition: aliased.php:25
foreach($paths as $path) $request
Definition: asyncclient.php:32
$response
+ Here is the call graph for this function:

◆ httpReport()

Sabre\DAV\CorePlugin::httpReport ( RequestInterface  $request,
ResponseInterface  $response 
)

HTTP REPORT method implementation.

Although the REPORT method is not part of the standard WebDAV spec (it's from rfc3253) It's used in a lot of extensions, so it made sense to implement it into the core.

Parameters
RequestInterface$request
ResponseInterface$response
Returns
bool

Definition at line 708 of file CorePlugin.php.

References $path, $result, Sabre\HTTP\MessageInterface\getBody(), Sabre\HTTP\RequestInterface\getPath(), and Sabre\HTTP\RequestInterface\getUrl().

708  {
709 
710  $path = $request->getPath();
711 
712  $result = $this->server->xml->parse(
713  $request->getBody(),
714  $request->getUrl(),
715  $rootElementName
716  );
717 
718  if ($this->server->emit('report', [$rootElementName, $result, $path])) {
719 
720  // If emit returned true, it means the report was not supported
721  throw new Exception\ReportNotSupported();
722 
723  }
724 
725  // Sending back false will interrupt the event chain and tell the server
726  // we've handled this method.
727  return false;
728 
729  }
$path
Definition: aliased.php:25
$result
foreach($paths as $path) $request
Definition: asyncclient.php:32
+ Here is the call graph for this function:

◆ initialize()

Sabre\DAV\CorePlugin::initialize ( Server  $server)

Sets up the plugin.

Parameters
Server$server
Returns
void

Definition at line 32 of file CorePlugin.php.

References Sabre\DAV\CorePlugin\$server, and Sabre\Event\EventEmitterInterface\on().

32  {
33 
34  $this->server = $server;
35  $server->on('method:GET', [$this, 'httpGet']);
36  $server->on('method:OPTIONS', [$this, 'httpOptions']);
37  $server->on('method:HEAD', [$this, 'httpHead']);
38  $server->on('method:DELETE', [$this, 'httpDelete']);
39  $server->on('method:PROPFIND', [$this, 'httpPropFind']);
40  $server->on('method:PROPPATCH', [$this, 'httpPropPatch']);
41  $server->on('method:PUT', [$this, 'httpPut']);
42  $server->on('method:MKCOL', [$this, 'httpMkcol']);
43  $server->on('method:MOVE', [$this, 'httpMove']);
44  $server->on('method:COPY', [$this, 'httpCopy']);
45  $server->on('method:REPORT', [$this, 'httpReport']);
46 
47  $server->on('propPatch', [$this, 'propPatchProtectedPropertyCheck'], 90);
48  $server->on('propPatch', [$this, 'propPatchNodeUpdate'], 200);
49  $server->on('propFind', [$this, 'propFind']);
50  $server->on('propFind', [$this, 'propFindNode'], 120);
51  $server->on('propFind', [$this, 'propFindLate'], 200);
52 
53  $server->on('exception', [$this, 'exception']);
54 
55  }
+ Here is the call graph for this function:

◆ propFind()

Sabre\DAV\CorePlugin::propFind ( PropFind  $propFind,
INode  $node 
)

This method is called when properties are retrieved.

Here we add all the default properties.

Parameters
PropFind$propFind
INode$node
Returns
void

Definition at line 787 of file CorePlugin.php.

References Sabre\DAV\INode\getLastModified(), Sabre\DAV\PropFind\getPath(), and Sabre\DAV\PropFind\handle().

787  {
788 
789  $propFind->handle('{DAV:}getlastmodified', function() use ($node) {
790  $lm = $node->getLastModified();
791  if ($lm) {
792  return new Xml\Property\GetLastModified($lm);
793  }
794  });
795 
796  if ($node instanceof IFile) {
797  $propFind->handle('{DAV:}getcontentlength', [$node, 'getSize']);
798  $propFind->handle('{DAV:}getetag', [$node, 'getETag']);
799  $propFind->handle('{DAV:}getcontenttype', [$node, 'getContentType']);
800  }
801 
802  if ($node instanceof IQuota) {
803  $quotaInfo = null;
804  $propFind->handle('{DAV:}quota-used-bytes', function() use (&$quotaInfo, $node) {
805  $quotaInfo = $node->getQuotaInfo();
806  return $quotaInfo[0];
807  });
808  $propFind->handle('{DAV:}quota-available-bytes', function() use (&$quotaInfo, $node) {
809  if (!$quotaInfo) {
810  $quotaInfo = $node->getQuotaInfo();
811  }
812  return $quotaInfo[1];
813  });
814  }
815 
816  $propFind->handle('{DAV:}supported-report-set', function() use ($propFind) {
817  $reports = [];
818  foreach ($this->server->getPlugins() as $plugin) {
819  $reports = array_merge($reports, $plugin->getSupportedReportSet($propFind->getPath()));
820  }
821  return new Xml\Property\SupportedReportSet($reports);
822  });
823  $propFind->handle('{DAV:}resourcetype', function() use ($node) {
824  return new Xml\Property\ResourceType($this->server->getResourceTypeForNode($node));
825  });
826  $propFind->handle('{DAV:}supported-method-set', function() use ($propFind) {
827  return new Xml\Property\SupportedMethodSet(
828  $this->server->getAllowedMethods($propFind->getPath())
829  );
830  });
831 
832  }
+ Here is the call graph for this function:

◆ propFindLate()

Sabre\DAV\CorePlugin::propFindLate ( PropFind  $propFind,
INode  $node 
)

This method is called when properties are retrieved.

This specific handler is called very late in the process, because we want other systems to first have a chance to handle the properties.

Parameters
PropFind$propFind
INode$node
Returns
void

Definition at line 867 of file CorePlugin.php.

References $result, Sabre\DAV\PropFind\get(), Sabre\DAV\PropFind\getPath(), and Sabre\DAV\PropFind\handle().

867  {
868 
869  $propFind->handle('{http://calendarserver.org/ns/}getctag', function() use ($propFind) {
870 
871  // If we already have a sync-token from the current propFind
872  // request, we can re-use that.
873  $val = $propFind->get('{http://sabredav.org/ns}sync-token');
874  if ($val) return $val;
875 
876  $val = $propFind->get('{DAV:}sync-token');
877  if ($val && is_scalar($val)) {
878  return $val;
879  }
880  if ($val && $val instanceof Xml\Property\Href) {
881  return substr($val->getHref(), strlen(Sync\Plugin::SYNCTOKEN_PREFIX));
882  }
883 
884  // If we got here, the earlier two properties may simply not have
885  // been part of the earlier request. We're going to fetch them.
886  $result = $this->server->getProperties($propFind->getPath(), [
887  '{http://sabredav.org/ns}sync-token',
888  '{DAV:}sync-token',
889  ]);
890 
891  if (isset($result['{http://sabredav.org/ns}sync-token'])) {
892  return $result['{http://sabredav.org/ns}sync-token'];
893  }
894  if (isset($result['{DAV:}sync-token'])) {
895  $val = $result['{DAV:}sync-token'];
896  if (is_scalar($val)) {
897  return $val;
898  } elseif ($val instanceof Xml\Property\Href) {
899  return substr($val->getHref(), strlen(Sync\Plugin::SYNCTOKEN_PREFIX));
900  }
901  }
902 
903  });
904 
905  }
$result
+ Here is the call graph for this function:

◆ propFindNode()

Sabre\DAV\CorePlugin::propFindNode ( PropFind  $propFind,
INode  $node 
)

Fetches properties for a node.

This event is called a bit later, so plugins have a chance first to populate the result.

Parameters
PropFind$propFind
INode$node
Returns
void

Definition at line 844 of file CorePlugin.php.

References Sabre\DAV\PropFind\get404Properties(), and Sabre\DAV\PropFind\set().

844  {
845 
846  if ($node instanceof IProperties && $propertyNames = $propFind->get404Properties()) {
847 
848  $nodeProperties = $node->getProperties($propertyNames);
849  foreach ($nodeProperties as $propertyName => $propertyValue) {
850  $propFind->set($propertyName, $propertyValue, 200);
851  }
852 
853  }
854 
855  }
+ Here is the call graph for this function:

◆ propPatchNodeUpdate()

Sabre\DAV\CorePlugin::propPatchNodeUpdate (   $path,
PropPatch  $propPatch 
)

This method is called during property updates.

Here we check if a node implements IProperties and let the node handle updating of (some) properties.

Parameters
string$path
PropPatch$propPatch
Returns
void

Definition at line 767 of file CorePlugin.php.

References $path.

767  {
768 
769  // This should trigger a 404 if the node doesn't exist.
770  $node = $this->server->tree->getNodeForPath($path);
771 
772  if ($node instanceof IProperties) {
773  $node->propPatch($propPatch);
774  }
775 
776  }
$path
Definition: aliased.php:25

◆ propPatchProtectedPropertyCheck()

Sabre\DAV\CorePlugin::propPatchProtectedPropertyCheck (   $path,
PropPatch  $propPatch 
)

This method is called during property updates.

Here we check if a user attempted to update a protected property and ensure that the process fails if this is the case.

Parameters
string$path
PropPatch$propPatch
Returns
void

Definition at line 741 of file CorePlugin.php.

References Sabre\DAV\PropPatch\getMutations(), and Sabre\DAV\PropPatch\setResultCode().

741  {
742 
743  // Comparing the mutation list to the list of protected properties.
744  $mutations = $propPatch->getMutations();
745 
746  $protected = array_intersect(
747  $this->server->protectedProperties,
748  array_keys($mutations)
749  );
750 
751  if ($protected) {
752  $propPatch->setResultCode($protected, 403);
753  }
754 
755  }
+ Here is the call graph for this function:

Field Documentation

◆ $server

Sabre\DAV\CorePlugin::$server
protected

Definition at line 24 of file CorePlugin.php.

Referenced by Sabre\DAV\CorePlugin\initialize().


The documentation for this class was generated from the following file: