remote.php 4.4 KB

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