remote.php 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. <?php
  2. /**
  3. * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
  4. * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
  5. * SPDX-License-Identifier: AGPL-3.0-only
  6. */
  7. require_once __DIR__ . '/lib/versioncheck.php';
  8. use OCA\DAV\Connector\Sabre\ExceptionLoggerPlugin;
  9. use Psr\Log\LoggerInterface;
  10. use Sabre\DAV\Exception\ServiceUnavailable;
  11. use Sabre\DAV\Server;
  12. /**
  13. * Class RemoteException
  14. * Dummy exception class to be use locally to identify certain conditions
  15. * Will not be logged to avoid DoS
  16. */
  17. class RemoteException extends Exception {
  18. }
  19. /**
  20. * @param Exception|Error $e
  21. */
  22. function handleException($e) {
  23. try {
  24. $request = \OC::$server->getRequest();
  25. // in case the request content type is text/xml - we assume it's a WebDAV request
  26. $isXmlContentType = strpos($request->getHeader('Content-Type'), 'text/xml');
  27. if ($isXmlContentType === 0) {
  28. // fire up a simple server to properly process the exception
  29. $server = new Server();
  30. if (!($e instanceof RemoteException)) {
  31. // we shall not log on RemoteException
  32. $server->addPlugin(new ExceptionLoggerPlugin('webdav', \OC::$server->get(LoggerInterface::class)));
  33. }
  34. $server->on('beforeMethod:*', function () use ($e) {
  35. if ($e instanceof RemoteException) {
  36. switch ($e->getCode()) {
  37. case 503:
  38. throw new ServiceUnavailable($e->getMessage());
  39. case 404:
  40. throw new \Sabre\DAV\Exception\NotFound($e->getMessage());
  41. }
  42. }
  43. $class = get_class($e);
  44. $msg = $e->getMessage();
  45. throw new ServiceUnavailable("$class: $msg");
  46. });
  47. $server->exec();
  48. } else {
  49. $statusCode = 500;
  50. if ($e instanceof \OC\ServiceUnavailableException) {
  51. $statusCode = 503;
  52. }
  53. if ($e instanceof RemoteException) {
  54. // we shall not log on RemoteException
  55. OC_Template::printErrorPage($e->getMessage(), '', $e->getCode());
  56. } else {
  57. \OC::$server->get(LoggerInterface::class)->error($e->getMessage(), ['app' => 'remote','exception' => $e]);
  58. OC_Template::printExceptionErrorPage($e, $statusCode);
  59. }
  60. }
  61. } catch (\Exception $e) {
  62. OC_Template::printExceptionErrorPage($e, 500);
  63. }
  64. }
  65. /**
  66. * @param $service
  67. * @return string
  68. */
  69. function resolveService($service) {
  70. $services = [
  71. 'webdav' => 'dav/appinfo/v1/webdav.php',
  72. 'dav' => 'dav/appinfo/v2/remote.php',
  73. 'caldav' => 'dav/appinfo/v1/caldav.php',
  74. 'calendar' => 'dav/appinfo/v1/caldav.php',
  75. 'carddav' => 'dav/appinfo/v1/carddav.php',
  76. 'contacts' => 'dav/appinfo/v1/carddav.php',
  77. 'files' => 'dav/appinfo/v1/webdav.php',
  78. 'direct' => 'dav/appinfo/v2/direct.php',
  79. ];
  80. if (isset($services[$service])) {
  81. return $services[$service];
  82. }
  83. return \OC::$server->getConfig()->getAppValue('core', 'remote_' . $service);
  84. }
  85. try {
  86. require_once __DIR__ . '/lib/base.php';
  87. // All resources served via the DAV endpoint should have the strictest possible
  88. // policy. Exempted from this is the SabreDAV browser plugin which overwrites
  89. // this policy with a softer one if debug mode is enabled.
  90. header("Content-Security-Policy: default-src 'none';");
  91. if (\OCP\Util::needUpgrade()) {
  92. // since the behavior of apps or remotes are unpredictable during
  93. // an upgrade, return a 503 directly
  94. throw new RemoteException('Service unavailable', 503);
  95. }
  96. $request = \OC::$server->getRequest();
  97. $pathInfo = $request->getPathInfo();
  98. if ($pathInfo === false || $pathInfo === '') {
  99. throw new RemoteException('Path not found', 404);
  100. }
  101. if (!$pos = strpos($pathInfo, '/', 1)) {
  102. $pos = strlen($pathInfo);
  103. }
  104. $service = substr($pathInfo, 1, $pos - 1);
  105. $file = resolveService($service);
  106. if (is_null($file)) {
  107. throw new RemoteException('Path not found', 404);
  108. }
  109. $file = ltrim($file, '/');
  110. $parts = explode('/', $file, 2);
  111. $app = $parts[0];
  112. // Load all required applications
  113. \OC::$REQUESTEDAPP = $app;
  114. OC_App::loadApps(['authentication']);
  115. OC_App::loadApps(['extended_authentication']);
  116. OC_App::loadApps(['filesystem', 'logging']);
  117. switch ($app) {
  118. case 'core':
  119. $file = OC::$SERVERROOT .'/'. $file;
  120. break;
  121. default:
  122. if (!\OC::$server->getAppManager()->isInstalled($app)) {
  123. throw new RemoteException('App not installed: ' . $app);
  124. }
  125. OC_App::loadApp($app);
  126. $file = OC_App::getAppPath($app) .'/'. $parts[1];
  127. break;
  128. }
  129. $baseuri = OC::$WEBROOT . '/remote.php/'.$service.'/';
  130. require_once $file;
  131. } catch (Exception $ex) {
  132. handleException($ex);
  133. } catch (Error $e) {
  134. handleException($e);
  135. }