ILIAS  release_5-4 Revision v5.4.26-12-gabc799a52e6
CorePlugin.php
Go to the documentation of this file.
1<?php
2
3namespace Sabre\DAV;
4
9
17class CorePlugin extends ServerPlugin {
18
24 protected $server;
25
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 }
56
65 function getPluginName() {
66
67 return 'core';
68
69 }
70
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 }
202
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 }
232
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 }
272
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 }
298
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 }
363
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 }
435
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 }
541
542
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 }
610
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 }
661
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 }
697
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
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 }
730
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 }
756
767 function propPatchNodeUpdate($path, PropPatch $propPatch) {
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 }
777
787 function propFind(PropFind $propFind, INode $node) {
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) {
828 $this->server->getAllowedMethods($propFind->getPath())
829 );
830 });
831
832 }
833
844 function propFindNode(PropFind $propFind, INode $node) {
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 }
856
867 function propFindLate(PropFind $propFind, INode $node) {
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 }
906
912 function exception($e) {
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 }
938
950 function getPluginInfo() {
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 }
959}
$result
$path
Definition: aliased.php:25
foreach($paths as $path) $request
Definition: asyncclient.php:32
An exception for terminatinating execution or to throw for unit testing.
The core plugin provides all the basic features for a WebDAV server.
Definition: CorePlugin.php:17
propFindLate(PropFind $propFind, INode $node)
This method is called when properties are retrieved.
Definition: CorePlugin.php:867
httpCopy(RequestInterface $request, ResponseInterface $response)
WebDAV HTTP COPY method.
Definition: CorePlugin.php:672
initialize(Server $server)
Sets up the plugin.
Definition: CorePlugin.php:32
propPatchNodeUpdate($path, PropPatch $propPatch)
This method is called during property updates.
Definition: CorePlugin.php:767
httpHead(RequestInterface $request, ResponseInterface $response)
HTTP HEAD.
Definition: CorePlugin.php:245
getPluginInfo()
Returns a bunch of meta-data about the plugin.
Definition: CorePlugin.php:950
httpMove(RequestInterface $request, ResponseInterface $response)
WebDAV HTTP MOVE method.
Definition: CorePlugin.php:620
httpReport(RequestInterface $request, ResponseInterface $response)
HTTP REPORT method implementation.
Definition: CorePlugin.php:708
httpMkcol(RequestInterface $request, ResponseInterface $response)
WebDAV MKCOL.
Definition: CorePlugin.php:552
getPluginName()
Returns a plugin name.
Definition: CorePlugin.php:65
exception($e)
Listens for exception events, and automatically logs them.
Definition: CorePlugin.php:912
httpPropFind(RequestInterface $request, ResponseInterface $response)
WebDAV PROPFIND.
Definition: CorePlugin.php:315
propFind(PropFind $propFind, INode $node)
This method is called when properties are retrieved.
Definition: CorePlugin.php:787
propFindNode(PropFind $propFind, INode $node)
Fetches properties for a node.
Definition: CorePlugin.php:844
propPatchProtectedPropertyCheck($path, PropPatch $propPatch)
This method is called during property updates.
Definition: CorePlugin.php:741
httpDelete(RequestInterface $request, ResponseInterface $response)
HTTP Delete.
Definition: CorePlugin.php:282
httpPut(RequestInterface $request, ResponseInterface $response)
HTTP PUT method.
Definition: CorePlugin.php:447
httpPropPatch(RequestInterface $request, ResponseInterface $response)
WebDAV PROPPATCH.
Definition: CorePlugin.php:374
httpGet(RequestInterface $request, ResponseInterface $response)
This is the default implementation for the GET method.
Definition: CorePlugin.php:78
httpOptions(RequestInterface $request, ResponseInterface $response)
HTTP OPTIONS.
Definition: CorePlugin.php:210
Main Exception class.
Definition: Exception.php:18
This class represents a MKCOL operation.
Definition: MkCol.php:23
This class holds all the information about a PROPFIND request.
Definition: PropFind.php:11
set($propertyName, $value, $status=null)
Sets the value of the property.
Definition: PropFind.php:121
get($propertyName)
Returns the current value for a property.
Definition: PropFind.php:149
get404Properties()
Returns all propertynames that have a 404 status, and thus don't have a value yet.
Definition: PropFind.php:222
getPath()
Returns the path this PROPFIND request is for.
Definition: PropFind.php:187
handle($propertyName, $valueOrCallBack)
Handles a specific property.
Definition: PropFind.php:94
This class represents a set of properties that are going to be updated.
Definition: PropPatch.php:20
setResultCode($properties, $resultCode)
Sets the result code for one or more properties.
Definition: PropPatch.php:150
getMutations()
Returns the full list of mutations.
Definition: PropPatch.php:367
The baseclass for all server plugins.
Main DAV server class.
Definition: Server.php:23
This property represents the {DAV:}getlastmodified property.
{DAV:}resourcetype property
This is a base exception for any exception related to parsing xml files.
$code
Definition: example_050.php:99
This interface represents a file in the directory tree.
Definition: IFile.php:16
The INode interface is the base interface, and the parent class of both ICollection and IFile.
Definition: INode.php:12
getLastModified()
Returns the last modification time, as a unix timestamp.
IProperties interface.
Definition: IProperties.php:14
IQuota interface.
Definition: IQuota.php:16
The RequestInterface represents a HTTP request.
This interface represents a HTTP response.
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
$stream
PHP stream implementation.
$response
$subRequest
$start
Definition: bench.php:8
$data
Definition: bench.php:6