1<?php
2/**
3 * @copyright Copyright (c) 2016, ownCloud, Inc.
4 *
5 * @author Brice Maron <brice@bmaron.net>
6 * @author Christopher Schäpers <kondou@ts.unde.re>
7 * @author Christoph Wurst <christoph@winzerhof-wurst.at>
8 * @author Georg Ehrke <oc.list@georgehrke.com>
9 * @author Joas Schilling <coding@schilljs.com>
10 * @author Jörn Friedrich Dreyer <jfd@butonic.de>
11 * @author Lukas Reschke <lukas@statuscode.ch>
12 * @author Morris Jobke <hey@morrisjobke.de>
13 * @author Robin Appelman <robin@icewind.nl>
14 * @author Robin McCorkell <robin@mccorkell.me.uk>
15 * @author Roeland Jago Douma <roeland@famdouma.nl>
16 * @author Thomas Müller <thomas.mueller@tmit.eu>
17 * @author Vincent Petry <vincent@nextcloud.com>
18 *
19 * @license AGPL-3.0
20 *
21 * This code is free software: you can redistribute it and/or modify
22 * it under the terms of the GNU Affero General Public License, version 3,
23 * as published by the Free Software Foundation.
24 *
25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 * GNU Affero General Public License for more details.
29 *
30 * You should have received a copy of the GNU Affero General Public License, version 3,
31 * along with this program. If not, see <http://www.gnu.org/licenses/>
32 *
33 */
34require_once __DIR__ . '/lib/versioncheck.php';
35
36use OCA\DAV\Connector\Sabre\ExceptionLoggerPlugin;
37use Sabre\DAV\Exception\ServiceUnavailable;
38use Sabre\DAV\Server;
39
40/**
41 * Class RemoteException
42 * Dummy exception class to be use locally to identify certain conditions
43 * Will not be logged to avoid DoS
44 */
45class RemoteException extends Exception {
46}
47
48/**
49 * @param Exception|Error $e
50 */
51function handleException($e) {
52	$request = \OC::$server->getRequest();
53	// in case the request content type is text/xml - we assume it's a WebDAV request
54	$isXmlContentType = strpos($request->getHeader('Content-Type'), 'text/xml');
55	if ($isXmlContentType === 0) {
56		// fire up a simple server to properly process the exception
57		$server = new Server();
58		if (!($e instanceof RemoteException)) {
59			// we shall not log on RemoteException
60			$server->addPlugin(new ExceptionLoggerPlugin('webdav', \OC::$server->getLogger()));
61		}
62		$server->on('beforeMethod:*', function () use ($e) {
63			if ($e instanceof RemoteException) {
64				switch ($e->getCode()) {
65					case 503:
66						throw new ServiceUnavailable($e->getMessage());
67					case 404:
68						throw new \Sabre\DAV\Exception\NotFound($e->getMessage());
69				}
70			}
71			$class = get_class($e);
72			$msg = $e->getMessage();
73			throw new ServiceUnavailable("$class: $msg");
74		});
75		$server->exec();
76	} else {
77		$statusCode = 500;
78		if ($e instanceof \OC\ServiceUnavailableException) {
79			$statusCode = 503;
80		}
81		if ($e instanceof RemoteException) {
82			// we shall not log on RemoteException
83			OC_Template::printErrorPage($e->getMessage(), '', $e->getCode());
84		} else {
85			\OC::$server->getLogger()->logException($e, ['app' => 'remote']);
86			OC_Template::printExceptionErrorPage($e, $statusCode);
87		}
88	}
89}
90
91/**
92 * @param $service
93 * @return string
94 */
95function resolveService($service) {
96	$services = [
97		'webdav' => 'dav/appinfo/v1/webdav.php',
98		'dav' => 'dav/appinfo/v2/remote.php',
99		'caldav' => 'dav/appinfo/v1/caldav.php',
100		'calendar' => 'dav/appinfo/v1/caldav.php',
101		'carddav' => 'dav/appinfo/v1/carddav.php',
102		'contacts' => 'dav/appinfo/v1/carddav.php',
103		'files' => 'dav/appinfo/v1/webdav.php',
104		'direct' => 'dav/appinfo/v2/direct.php',
105	];
106	if (isset($services[$service])) {
107		return $services[$service];
108	}
109
110	return \OC::$server->getConfig()->getAppValue('core', 'remote_' . $service);
111}
112
113try {
114	require_once __DIR__ . '/lib/base.php';
115
116	// All resources served via the DAV endpoint should have the strictest possible
117	// policy. Exempted from this is the SabreDAV browser plugin which overwrites
118	// this policy with a softer one if debug mode is enabled.
119	header("Content-Security-Policy: default-src 'none';");
120
121	if (\OCP\Util::needUpgrade()) {
122		// since the behavior of apps or remotes are unpredictable during
123		// an upgrade, return a 503 directly
124		throw new RemoteException('Service unavailable', 503);
125	}
126
127	$request = \OC::$server->getRequest();
128	$pathInfo = $request->getPathInfo();
129	if ($pathInfo === false || $pathInfo === '') {
130		throw new RemoteException('Path not found', 404);
131	}
132	if (!$pos = strpos($pathInfo, '/', 1)) {
133		$pos = strlen($pathInfo);
134	}
135	$service = substr($pathInfo, 1, $pos - 1);
136
137	$file = resolveService($service);
138
139	if (is_null($file)) {
140		throw new RemoteException('Path not found', 404);
141	}
142
143	$file = ltrim($file, '/');
144
145	$parts = explode('/', $file, 2);
146	$app = $parts[0];
147
148	// Load all required applications
149	\OC::$REQUESTEDAPP = $app;
150	OC_App::loadApps(['authentication']);
151	OC_App::loadApps(['filesystem', 'logging']);
152
153	switch ($app) {
154		case 'core':
155			$file = OC::$SERVERROOT .'/'. $file;
156			break;
157		default:
158			if (!\OC::$server->getAppManager()->isInstalled($app)) {
159				throw new RemoteException('App not installed: ' . $app);
160			}
161			OC_App::loadApp($app);
162			$file = OC_App::getAppPath($app) .'/'. $parts[1];
163			break;
164	}
165	$baseuri = OC::$WEBROOT . '/remote.php/'.$service.'/';
166	require_once $file;
167} catch (Exception $ex) {
168	handleException($ex);
169} catch (Error $e) {
170	handleException($e);
171}
172