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/) @license 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.

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

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

◆ 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

Reimplemented from Sabre\DAV\ServerPlugin.

Definition at line 950 of file CorePlugin.php.

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

References Sabre\DAV\CorePlugin\getPluginName().

+ 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\Server::getPlugin

Returns
string

Reimplemented from Sabre\DAV\ServerPlugin.

Definition at line 65 of file CorePlugin.php.

65 {
66
67 return 'core';
68
69 }

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

+ 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.

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

References $path, $request, and $response.

◆ 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.

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 }

References $path, $request, and $response.

◆ 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.

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 }
$stream
PHP stream implementation.
$start
Definition: bench.php:8

References $end, $path, $request, $response, $start, and GuzzleHttp\Psr7\$stream.

◆ 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.

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 }
$subRequest

References $request, $response, and $subRequest.

◆ 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.

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 }
$result
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

References $contentType, $path, $request, $response, and $result.

◆ 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.

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 }

References $path, $request, and $response.

◆ 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.

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 }

References $request, and $response.

◆ 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.

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 }
$data
Definition: bench.php:6

References $data, $path, $request, and $response.

◆ 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.

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 }

References $code, $ok, $path, $request, $response, and $result.

◆ 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.

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 }

References $path, $request, and $response.

◆ 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.

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 }

References $path, $request, and $result.

◆ initialize()

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

Sets up the plugin.

Parameters
Server$server
Returns
void

Reimplemented from Sabre\DAV\ServerPlugin.

Definition at line 32 of file CorePlugin.php.

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 }

References Sabre\DAV\CorePlugin\$server.

◆ 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.

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 }

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

+ 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.

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 }

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

+ 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.

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 }

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

+ 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.

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 }

References $path.

◆ 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.

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 }

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

+ 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: