ILIAS  release_5-4 Revision v5.4.26-12-gabc799a52e6
CorePlugin.php
Go to the documentation of this file.
1 <?php
2 
3 namespace Sabre\DAV;
4 
9 
17 class 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
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  }
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 }
This interface represents a HTTP response.
httpMove(RequestInterface $request, ResponseInterface $response)
WebDAV HTTP MOVE method.
Definition: CorePlugin.php:620
httpPropFind(RequestInterface $request, ResponseInterface $response)
WebDAV PROPFIND.
Definition: CorePlugin.php:315
The RequestInterface represents a HTTP request.
handle($propertyName, $valueOrCallBack)
Handles a specific property.
Definition: PropFind.php:94
get($propertyName)
Returns the current value for a property.
Definition: PropFind.php:149
$path
Definition: aliased.php:25
initialize(Server $server)
Sets up the plugin.
Definition: CorePlugin.php:32
httpCopy(RequestInterface $request, ResponseInterface $response)
WebDAV HTTP COPY method.
Definition: CorePlugin.php:672
This is a base exception for any exception related to parsing xml files.
The baseclass for all server plugins.
IQuota interface.
Definition: IQuota.php:16
setBody($body)
Updates the body resource with a new stream.
on($eventName, callable $callBack, $priority=100)
Subscribe to an event.
This class represents a set of properties that are going to be updated.
Definition: PropPatch.php:20
$result
propPatchNodeUpdate($path, PropPatch $propPatch)
This method is called during property updates.
Definition: CorePlugin.php:767
foreach($paths as $path) $request
Definition: asyncclient.php:32
getPath()
Returns the path this PROPFIND request is for.
Definition: PropFind.php:187
httpGet(RequestInterface $request, ResponseInterface $response)
This is the default implementation for the GET method.
Definition: CorePlugin.php:78
$code
Definition: example_050.php:99
getBodyAsString()
Returns the body as a string.
httpMkcol(RequestInterface $request, ResponseInterface $response)
WebDAV MKCOL.
Definition: CorePlugin.php:552
This class holds all the information about a PROPFIND request.
Definition: PropFind.php:11
httpHead(RequestInterface $request, ResponseInterface $response)
HTTP HEAD.
Definition: CorePlugin.php:245
set($propertyName, $value, $status=null)
Sets the value of the property.
Definition: PropFind.php:121
httpPropPatch(RequestInterface $request, ResponseInterface $response)
WebDAV PROPPATCH.
Definition: CorePlugin.php:374
get404Properties()
Returns all propertynames that have a 404 status, and thus don&#39;t have a value yet.
Definition: PropFind.php:222
httpDelete(RequestInterface $request, ResponseInterface $response)
HTTP Delete.
Definition: CorePlugin.php:282
$stream
PHP stream implementation.
getPluginName()
Returns a plugin name.
Definition: CorePlugin.php:65
$subRequest
$start
Definition: bench.php:8
getBodyAsStream()
Returns the body as a readable stream resource.
{DAV:}resourcetype property
propFind(PropFind $propFind, INode $node)
This method is called when properties are retrieved.
Definition: CorePlugin.php:787
setResultCode($properties, $resultCode)
Sets the result code for one or more properties.
Definition: PropPatch.php:150
httpPut(RequestInterface $request, ResponseInterface $response)
HTTP PUT method.
Definition: CorePlugin.php:447
exception($e)
Listens for exception events, and automatically logs them.
Definition: CorePlugin.php:912
setStatus($status)
Sets the HTTP status code.
getUrl()
Returns the request url.
getPluginInfo()
Returns a bunch of meta-data about the plugin.
Definition: CorePlugin.php:950
WebDAV PROPFIND request parser.
Definition: PropFind.php:20
Main DAV server class.
Definition: Server.php:23
getHeader($name)
Returns a specific HTTP header, based on it&#39;s name.
httpOptions(RequestInterface $request, ResponseInterface $response)
HTTP OPTIONS.
Definition: CorePlugin.php:210
The core plugin provides all the basic features for a WebDAV server.
Definition: CorePlugin.php:17
httpReport(RequestInterface $request, ResponseInterface $response)
HTTP REPORT method implementation.
Definition: CorePlugin.php:708
This interface represents a file in the directory tree.
Definition: IFile.php:16
getMutations()
Returns the full list of mutations.
Definition: PropPatch.php:367
The INode interface is the base interface, and the parent class of both ICollection and IFile...
Definition: INode.php:12
getPath()
Returns the relative path.
This class represents a MKCOL operation.
Definition: MkCol.php:23
This property represents the {DAV:}getlastmodified property.
IProperties interface.
Definition: IProperties.php:14
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
propPatchProtectedPropertyCheck($path, PropPatch $propPatch)
This method is called during property updates.
Definition: CorePlugin.php:741
propFindNode(PropFind $propFind, INode $node)
Fetches properties for a node.
Definition: CorePlugin.php:844
$response
propFindLate(PropFind $propFind, INode $node)
This method is called when properties are retrieved.
Definition: CorePlugin.php:867
setHeader($name, $value)
Updates a HTTP header.
getBody()
Returns the message body, as it&#39;s internal representation.
getLastModified()
Returns the last modification time, as a unix timestamp.
addHeaders(array $headers)
Adds a new set of HTTP headers.
$data
Definition: bench.php:6