OC::handleRequest()   F
last analyzed

Complexity

Conditions 27
Paths 892

Size

Total Lines 142
Code Lines 85

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 27
eloc 85
nc 892
nop 0
dl 0
loc 142
rs 0.15
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * @copyright Copyright (c) 2016, ownCloud, Inc.
7
 *
8
 * @author Adam Williamson <[email protected]>
9
 * @author Andreas Fischer <[email protected]>
10
 * @author Arthur Schiwon <[email protected]>
11
 * @author Bart Visscher <[email protected]>
12
 * @author Bernhard Posselt <[email protected]>
13
 * @author Bjoern Schiessle <[email protected]>
14
 * @author Björn Schießle <[email protected]>
15
 * @author Christoph Wurst <[email protected]>
16
 * @author Côme Chilliet <[email protected]>
17
 * @author Damjan Georgievski <[email protected]>
18
 * @author Daniel Kesselberg <[email protected]>
19
 * @author davidgumberg <[email protected]>
20
 * @author Eric Masseran <[email protected]>
21
 * @author Florin Peter <[email protected]>
22
 * @author Greta Doci <[email protected]>
23
 * @author J0WI <[email protected]>
24
 * @author Jakob Sack <[email protected]>
25
 * @author jaltek <[email protected]>
26
 * @author Jan-Christoph Borchardt <[email protected]>
27
 * @author Joachim Sokolowski <[email protected]>
28
 * @author Joas Schilling <[email protected]>
29
 * @author John Molakvoæ <[email protected]>
30
 * @author Jörn Friedrich Dreyer <[email protected]>
31
 * @author Jose Quinteiro <[email protected]>
32
 * @author Juan Pablo Villafáñez <[email protected]>
33
 * @author Julius Härtl <[email protected]>
34
 * @author Ko- <[email protected]>
35
 * @author Lukas Reschke <[email protected]>
36
 * @author MartB <[email protected]>
37
 * @author Michael Gapczynski <[email protected]>
38
 * @author Morris Jobke <[email protected]>
39
 * @author Owen Winkler <[email protected]>
40
 * @author Phil Davis <[email protected]>
41
 * @author Ramiro Aparicio <[email protected]>
42
 * @author Robin Appelman <[email protected]>
43
 * @author Robin McCorkell <[email protected]>
44
 * @author Roeland Jago Douma <[email protected]>
45
 * @author Sebastian Wessalowski <[email protected]>
46
 * @author Stefan Weil <[email protected]>
47
 * @author Thomas Müller <[email protected]>
48
 * @author Thomas Tanghus <[email protected]>
49
 * @author Tobia De Koninck <[email protected]>
50
 * @author Vincent Petry <[email protected]>
51
 * @author Volkan Gezer <[email protected]>
52
 *
53
 * @license AGPL-3.0
54
 *
55
 * This code is free software: you can redistribute it and/or modify
56
 * it under the terms of the GNU Affero General Public License, version 3,
57
 * as published by the Free Software Foundation.
58
 *
59
 * This program is distributed in the hope that it will be useful,
60
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
61
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
62
 * GNU Affero General Public License for more details.
63
 *
64
 * You should have received a copy of the GNU Affero General Public License, version 3,
65
 * along with this program. If not, see <http://www.gnu.org/licenses/>
66
 *
67
 */
68
69
use OC\Encryption\HookManager;
70
use OC\EventDispatcher\SymfonyAdapter;
71
use OC\Share20\Hooks;
72
use OCP\EventDispatcher\IEventDispatcher;
73
use OCP\Group\Events\UserRemovedEvent;
74
use OCP\ILogger;
75
use OCP\IRequest;
76
use OCP\IURLGenerator;
77
use OCP\IUserSession;
78
use OCP\Server;
79
use OCP\Share;
80
use OCP\User\Events\UserChangedEvent;
81
use Psr\Log\LoggerInterface;
82
use Symfony\Component\Routing\Exception\MethodNotAllowedException;
83
use function OCP\Log\logger;
84
85
require_once 'public/Constants.php';
86
87
/**
88
 * Class that is a namespace for all global OC variables
89
 * No, we can not put this class in its own file because it is used by
90
 * OC_autoload!
91
 */
92
class OC {
93
	/**
94
	 * Associative array for autoloading. classname => filename
95
	 */
96
	public static array $CLASSPATH = [];
97
	/**
98
	 * The installation path for Nextcloud  on the server (e.g. /srv/http/nextcloud)
99
	 */
100
	public static string $SERVERROOT = '';
101
	/**
102
	 * the current request path relative to the Nextcloud root (e.g. files/index.php)
103
	 */
104
	private static string $SUBURI = '';
105
	/**
106
	 * the Nextcloud root path for http requests (e.g. nextcloud/)
107
	 */
108
	public static string $WEBROOT = '';
109
	/**
110
	 * The installation path array of the apps folder on the server (e.g. /srv/http/nextcloud) 'path' and
111
	 * web path in 'url'
112
	 */
113
	public static array $APPSROOTS = [];
114
115
	public static string $configDir;
116
117
	public static int $VERSION_MTIME = 0;
118
119
	/**
120
	 * requested app
121
	 */
122
	public static string $REQUESTEDAPP = '';
123
124
	/**
125
	 * check if Nextcloud runs in cli mode
126
	 */
127
	public static bool $CLI = false;
128
129
	public static \OC\Autoloader $loader;
130
131
	public static \Composer\Autoload\ClassLoader $composerAutoloader;
132
133
	public static \OC\Server $server;
134
135
	private static \OC\Config $config;
136
137
	/**
138
	 * @throws \RuntimeException when the 3rdparty directory is missing or
139
	 * the app path list is empty or contains an invalid path
140
	 */
141
	public static function initPaths(): void {
142
		if (defined('PHPUNIT_CONFIG_DIR')) {
143
			self::$configDir = OC::$SERVERROOT . '/' . PHPUNIT_CONFIG_DIR . '/';
0 ignored issues
show
Bug introduced by
The constant PHPUNIT_CONFIG_DIR was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
144
		} elseif (defined('PHPUNIT_RUN') and PHPUNIT_RUN and is_dir(OC::$SERVERROOT . '/tests/config/')) {
0 ignored issues
show
Bug introduced by
The constant PHPUNIT_RUN was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
145
			self::$configDir = OC::$SERVERROOT . '/tests/config/';
146
		} elseif ($dir = getenv('NEXTCLOUD_CONFIG_DIR')) {
147
			self::$configDir = rtrim($dir, '/') . '/';
148
		} else {
149
			self::$configDir = OC::$SERVERROOT . '/config/';
150
		}
151
		self::$config = new \OC\Config(self::$configDir);
152
153
		OC::$SUBURI = str_replace("\\", "/", substr(realpath($_SERVER["SCRIPT_FILENAME"] ?? ''), strlen(OC::$SERVERROOT)));
154
		/**
155
		 * FIXME: The following lines are required because we can't yet instantiate
156
		 *        Server::get(\OCP\IRequest::class) since \OC::$server does not yet exist.
157
		 */
158
		$params = [
159
			'server' => [
160
				'SCRIPT_NAME' => $_SERVER['SCRIPT_NAME'] ?? null,
161
				'SCRIPT_FILENAME' => $_SERVER['SCRIPT_FILENAME'] ?? null,
162
			],
163
		];
164
		if (isset($_SERVER['REMOTE_ADDR'])) {
165
			$params['server']['REMOTE_ADDR'] = $_SERVER['REMOTE_ADDR'];
166
		}
167
		$fakeRequest = new \OC\AppFramework\Http\Request(
168
			$params,
169
			new \OC\AppFramework\Http\RequestId($_SERVER['UNIQUE_ID'] ?? '', new \OC\Security\SecureRandom()),
170
			new \OC\AllConfig(new \OC\SystemConfig(self::$config))
171
		);
172
		$scriptName = $fakeRequest->getScriptName();
173
		if (substr($scriptName, -1) == '/') {
174
			$scriptName .= 'index.php';
175
			//make sure suburi follows the same rules as scriptName
176
			if (substr(OC::$SUBURI, -9) != 'index.php') {
177
				if (substr(OC::$SUBURI, -1) != '/') {
178
					OC::$SUBURI = OC::$SUBURI . '/';
179
				}
180
				OC::$SUBURI = OC::$SUBURI . 'index.php';
181
			}
182
		}
183
184
185
		if (OC::$CLI) {
186
			OC::$WEBROOT = self::$config->getValue('overwritewebroot', '');
187
		} else {
188
			if (substr($scriptName, 0 - strlen(OC::$SUBURI)) === OC::$SUBURI) {
189
				OC::$WEBROOT = substr($scriptName, 0, 0 - strlen(OC::$SUBURI));
190
191
				if (OC::$WEBROOT != '' && OC::$WEBROOT[0] !== '/') {
192
					OC::$WEBROOT = '/' . OC::$WEBROOT;
193
				}
194
			} else {
195
				// The scriptName is not ending with OC::$SUBURI
196
				// This most likely means that we are calling from CLI.
197
				// However some cron jobs still need to generate
198
				// a web URL, so we use overwritewebroot as a fallback.
199
				OC::$WEBROOT = self::$config->getValue('overwritewebroot', '');
200
			}
201
202
			// Resolve /nextcloud to /nextcloud/ to ensure to always have a trailing
203
			// slash which is required by URL generation.
204
			if (isset($_SERVER['REQUEST_URI']) && $_SERVER['REQUEST_URI'] === \OC::$WEBROOT &&
205
					substr($_SERVER['REQUEST_URI'], -1) !== '/') {
206
				header('Location: '.\OC::$WEBROOT.'/');
207
				exit();
208
			}
209
		}
210
211
		// search the apps folder
212
		$config_paths = self::$config->getValue('apps_paths', []);
213
		if (!empty($config_paths)) {
214
			foreach ($config_paths as $paths) {
215
				if (isset($paths['url']) && isset($paths['path'])) {
216
					$paths['url'] = rtrim($paths['url'], '/');
217
					$paths['path'] = rtrim($paths['path'], '/');
218
					OC::$APPSROOTS[] = $paths;
219
				}
220
			}
221
		} elseif (file_exists(OC::$SERVERROOT . '/apps')) {
222
			OC::$APPSROOTS[] = ['path' => OC::$SERVERROOT . '/apps', 'url' => '/apps', 'writable' => true];
223
		}
224
225
		if (empty(OC::$APPSROOTS)) {
226
			throw new \RuntimeException('apps directory not found! Please put the Nextcloud apps folder in the Nextcloud folder'
227
				. '. You can also configure the location in the config.php file.');
228
		}
229
		$paths = [];
230
		foreach (OC::$APPSROOTS as $path) {
231
			$paths[] = $path['path'];
232
			if (!is_dir($path['path'])) {
233
				throw new \RuntimeException(sprintf('App directory "%s" not found! Please put the Nextcloud apps folder in the'
234
					. ' Nextcloud folder. You can also configure the location in the config.php file.', $path['path']));
235
			}
236
		}
237
238
		// set the right include path
239
		set_include_path(
240
			implode(PATH_SEPARATOR, $paths)
241
		);
242
	}
243
244
	public static function checkConfig(): void {
245
		$l = Server::get(\OCP\L10N\IFactory::class)->get('lib');
246
247
		// Create config if it does not already exist
248
		$configFilePath = self::$configDir .'/config.php';
249
		if (!file_exists($configFilePath)) {
250
			@touch($configFilePath);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for touch(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

250
			/** @scrutinizer ignore-unhandled */ @touch($configFilePath);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
251
		}
252
253
		// Check if config is writable
254
		$configFileWritable = is_writable($configFilePath);
255
		if (!$configFileWritable && !OC_Helper::isReadOnlyConfigEnabled()
0 ignored issues
show
introduced by
Consider adding parentheses for clarity. Current Interpretation: (! $configFileWritable &...OCP\Util::needUpgrade(), Probably Intended Meaning: ! $configFileWritable &&...CP\Util::needUpgrade())
Loading history...
256
			|| !$configFileWritable && \OCP\Util::needUpgrade()) {
257
			$urlGenerator = Server::get(IURLGenerator::class);
258
259
			if (self::$CLI) {
260
				echo $l->t('Cannot write into "config" directory!')."\n";
261
				echo $l->t('This can usually be fixed by giving the web server write access to the config directory.')."\n";
262
				echo "\n";
263
				echo $l->t('But, if you prefer to keep config.php file read only, set the option "config_is_read_only" to true in it.')."\n";
264
				echo $l->t('See %s', [ $urlGenerator->linkToDocs('admin-config') ])."\n";
265
				exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
266
			} else {
267
				OC_Template::printErrorPage(
268
					$l->t('Cannot write into "config" directory!'),
269
					$l->t('This can usually be fixed by giving the web server write access to the config directory.') . ' '
270
					. $l->t('But, if you prefer to keep config.php file read only, set the option "config_is_read_only" to true in it.') . ' '
271
					. $l->t('See %s', [ $urlGenerator->linkToDocs('admin-config') ]),
272
					503
273
				);
274
			}
275
		}
276
	}
277
278
	public static function checkInstalled(\OC\SystemConfig $systemConfig): void {
279
		if (defined('OC_CONSOLE')) {
280
			return;
281
		}
282
		// Redirect to installer if not installed
283
		if (!$systemConfig->getValue('installed', false) && OC::$SUBURI !== '/index.php' && OC::$SUBURI !== '/status.php') {
284
			if (OC::$CLI) {
285
				throw new Exception('Not installed');
286
			} else {
287
				$url = OC::$WEBROOT . '/index.php';
288
				header('Location: ' . $url);
289
			}
290
			exit();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
291
		}
292
	}
293
294
	public static function checkMaintenanceMode(\OC\SystemConfig $systemConfig): void {
295
		// Allow ajax update script to execute without being stopped
296
		if (((bool) $systemConfig->getValue('maintenance', false)) && OC::$SUBURI != '/core/ajax/update.php') {
297
			// send http status 503
298
			http_response_code(503);
299
			header('X-Nextcloud-Maintenance-Mode: 1');
300
			header('Retry-After: 120');
301
302
			// render error page
303
			$template = new OC_Template('', 'update.user', 'guest');
304
			\OCP\Util::addScript('core', 'maintenance');
305
			\OCP\Util::addStyle('core', 'guest');
306
			$template->printPage();
307
			die();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
308
		}
309
	}
310
311
	/**
312
	 * Prints the upgrade page
313
	 */
314
	private static function printUpgradePage(\OC\SystemConfig $systemConfig): void {
315
		$cliUpgradeLink = $systemConfig->getValue('upgrade.cli-upgrade-link', '');
316
		$disableWebUpdater = $systemConfig->getValue('upgrade.disable-web', false);
317
		$tooBig = false;
318
		if (!$disableWebUpdater) {
319
			$apps = Server::get(\OCP\App\IAppManager::class);
320
			if ($apps->isInstalled('user_ldap')) {
321
				$qb = Server::get(\OCP\IDBConnection::class)->getQueryBuilder();
322
323
				$result = $qb->select($qb->func()->count('*', 'user_count'))
324
					->from('ldap_user_mapping')
325
					->executeQuery();
326
				$row = $result->fetch();
327
				$result->closeCursor();
328
329
				$tooBig = ($row['user_count'] > 50);
330
			}
331
			if (!$tooBig && $apps->isInstalled('user_saml')) {
332
				$qb = Server::get(\OCP\IDBConnection::class)->getQueryBuilder();
333
334
				$result = $qb->select($qb->func()->count('*', 'user_count'))
335
					->from('user_saml_users')
336
					->executeQuery();
337
				$row = $result->fetch();
338
				$result->closeCursor();
339
340
				$tooBig = ($row['user_count'] > 50);
341
			}
342
			if (!$tooBig) {
343
				// count users
344
				$stats = Server::get(\OCP\IUserManager::class)->countUsers();
345
				$totalUsers = array_sum($stats);
346
				$tooBig = ($totalUsers > 50);
347
			}
348
		}
349
		$ignoreTooBigWarning = isset($_GET['IKnowThatThisIsABigInstanceAndTheUpdateRequestCouldRunIntoATimeoutAndHowToRestoreABackup']) &&
350
			$_GET['IKnowThatThisIsABigInstanceAndTheUpdateRequestCouldRunIntoATimeoutAndHowToRestoreABackup'] === 'IAmSuperSureToDoThis';
351
352
		if ($disableWebUpdater || ($tooBig && !$ignoreTooBigWarning)) {
353
			// send http status 503
354
			http_response_code(503);
355
			header('Retry-After: 120');
356
357
			// render error page
358
			$template = new OC_Template('', 'update.use-cli', 'guest');
359
			$template->assign('productName', 'nextcloud'); // for now
360
			$template->assign('version', OC_Util::getVersionString());
361
			$template->assign('tooBig', $tooBig);
362
			$template->assign('cliUpgradeLink', $cliUpgradeLink);
363
364
			$template->printPage();
365
			die();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
366
		}
367
368
		// check whether this is a core update or apps update
369
		$installedVersion = $systemConfig->getValue('version', '0.0.0');
370
		$currentVersion = implode('.', \OCP\Util::getVersion());
371
372
		// if not a core upgrade, then it's apps upgrade
373
		$isAppsOnlyUpgrade = version_compare($currentVersion, $installedVersion, '=');
374
375
		$oldTheme = $systemConfig->getValue('theme');
376
		$systemConfig->setValue('theme', '');
377
		\OCP\Util::addScript('core', 'common');
378
		\OCP\Util::addScript('core', 'main');
379
		\OCP\Util::addTranslations('core');
380
		\OCP\Util::addScript('core', 'update');
381
382
		/** @var \OC\App\AppManager $appManager */
383
		$appManager = Server::get(\OCP\App\IAppManager::class);
384
385
		$tmpl = new OC_Template('', 'update.admin', 'guest');
386
		$tmpl->assign('version', OC_Util::getVersionString());
387
		$tmpl->assign('isAppsOnlyUpgrade', $isAppsOnlyUpgrade);
388
389
		// get third party apps
390
		$ocVersion = \OCP\Util::getVersion();
391
		$ocVersion = implode('.', $ocVersion);
392
		$incompatibleApps = $appManager->getIncompatibleApps($ocVersion);
393
		$incompatibleShippedApps = [];
394
		foreach ($incompatibleApps as $appInfo) {
395
			if ($appManager->isShipped($appInfo['id'])) {
396
				$incompatibleShippedApps[] = $appInfo['name'] . ' (' . $appInfo['id'] . ')';
397
			}
398
		}
399
400
		if (!empty($incompatibleShippedApps)) {
401
			$l = Server::get(\OCP\L10N\IFactory::class)->get('core');
402
			$hint = $l->t('Application %1$s is not present or has a non-compatible version with this server. Please check the apps directory.', [implode(', ', $incompatibleShippedApps)]);
403
			throw new \OCP\HintException('Application ' . implode(', ', $incompatibleShippedApps) . ' is not present or has a non-compatible version with this server. Please check the apps directory.', $hint);
404
		}
405
406
		$tmpl->assign('appsToUpgrade', $appManager->getAppsNeedingUpgrade($ocVersion));
407
		$tmpl->assign('incompatibleAppsList', $incompatibleApps);
408
		try {
409
			$defaults = new \OC_Defaults();
410
			$tmpl->assign('productName', $defaults->getName());
411
		} catch (Throwable $error) {
412
			$tmpl->assign('productName', 'Nextcloud');
413
		}
414
		$tmpl->assign('oldTheme', $oldTheme);
415
		$tmpl->printPage();
416
	}
417
418
	public static function initSession(): void {
419
		$request = Server::get(IRequest::class);
420
421
		// TODO: Temporary disabled again to solve issues with CalDAV/CardDAV clients like DAVx5 that use cookies
422
		// TODO: See https://github.com/nextcloud/server/issues/37277#issuecomment-1476366147 and the other comments
423
		// TODO: for further information.
424
		// $isDavRequest = strpos($request->getRequestUri(), '/remote.php/dav') === 0 || strpos($request->getRequestUri(), '/remote.php/webdav') === 0;
425
		// if ($request->getHeader('Authorization') !== '' && is_null($request->getCookie('cookie_test')) && $isDavRequest && !isset($_COOKIE['nc_session_id'])) {
426
		// setcookie('cookie_test', 'test', time() + 3600);
427
		// // Do not initialize the session if a request is authenticated directly
428
		// // unless there is a session cookie already sent along
429
		// return;
430
		// }
431
432
		if ($request->getServerProtocol() === 'https') {
433
			ini_set('session.cookie_secure', 'true');
434
		}
435
436
		// prevents javascript from accessing php session cookies
437
		ini_set('session.cookie_httponly', 'true');
438
439
		// set the cookie path to the Nextcloud directory
440
		$cookie_path = OC::$WEBROOT ? : '/';
441
		ini_set('session.cookie_path', $cookie_path);
442
443
		// Let the session name be changed in the initSession Hook
444
		$sessionName = OC_Util::getInstanceId();
445
446
		try {
447
			// set the session name to the instance id - which is unique
448
			$session = new \OC\Session\Internal($sessionName);
449
450
			$cryptoWrapper = Server::get(\OC\Session\CryptoWrapper::class);
451
			$session = $cryptoWrapper->wrapSession($session);
452
			self::$server->setSession($session);
453
454
			// if session can't be started break with http 500 error
455
		} catch (Exception $e) {
456
			Server::get(LoggerInterface::class)->error($e->getMessage(), ['app' => 'base','exception' => $e]);
457
			//show the user a detailed error page
458
			OC_Template::printExceptionErrorPage($e, 500);
459
			die();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
460
		}
461
462
		//try to set the session lifetime
463
		$sessionLifeTime = self::getSessionLifeTime();
464
		@ini_set('gc_maxlifetime', (string)$sessionLifeTime);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for ini_set(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

464
		/** @scrutinizer ignore-unhandled */ @ini_set('gc_maxlifetime', (string)$sessionLifeTime);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
465
466
		// session timeout
467
		if ($session->exists('LAST_ACTIVITY') && (time() - $session->get('LAST_ACTIVITY') > $sessionLifeTime)) {
468
			if (isset($_COOKIE[session_name()])) {
469
				setcookie(session_name(), '', -1, self::$WEBROOT ? : '/');
470
			}
471
			Server::get(IUserSession::class)->logout();
472
		}
473
474
		if (!self::hasSessionRelaxedExpiry()) {
475
			$session->set('LAST_ACTIVITY', time());
476
		}
477
		$session->close();
478
	}
479
480
	private static function getSessionLifeTime(): int {
481
		return Server::get(\OC\AllConfig::class)->getSystemValueInt('session_lifetime', 60 * 60 * 24);
482
	}
483
484
	/**
485
	 * @return bool true if the session expiry should only be done by gc instead of an explicit timeout
486
	 */
487
	public static function hasSessionRelaxedExpiry(): bool {
488
		return Server::get(\OC\AllConfig::class)->getSystemValueBool('session_relaxed_expiry', false);
489
	}
490
491
	/**
492
	 * Try to set some values to the required Nextcloud default
493
	 */
494
	public static function setRequiredIniValues(): void {
495
		@ini_set('default_charset', 'UTF-8');
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for ini_set(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

495
		/** @scrutinizer ignore-unhandled */ @ini_set('default_charset', 'UTF-8');

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
496
		@ini_set('gd.jpeg_ignore_warning', '1');
497
	}
498
499
	/**
500
	 * Send the same site cookies
501
	 */
502
	private static function sendSameSiteCookies(): void {
503
		$cookieParams = session_get_cookie_params();
504
		$secureCookie = ($cookieParams['secure'] === true) ? 'secure; ' : '';
505
		$policies = [
506
			'lax',
507
			'strict',
508
		];
509
510
		// Append __Host to the cookie if it meets the requirements
511
		$cookiePrefix = '';
512
		if ($cookieParams['secure'] === true && $cookieParams['path'] === '/') {
513
			$cookiePrefix = '__Host-';
514
		}
515
516
		foreach ($policies as $policy) {
517
			header(
518
				sprintf(
519
					'Set-Cookie: %snc_sameSiteCookie%s=true; path=%s; httponly;' . $secureCookie . 'expires=Fri, 31-Dec-2100 23:59:59 GMT; SameSite=%s',
520
					$cookiePrefix,
521
					$policy,
522
					$cookieParams['path'],
523
					$policy
524
				),
525
				false
526
			);
527
		}
528
	}
529
530
	/**
531
	 * Same Site cookie to further mitigate CSRF attacks. This cookie has to
532
	 * be set in every request if cookies are sent to add a second level of
533
	 * defense against CSRF.
534
	 *
535
	 * If the cookie is not sent this will set the cookie and reload the page.
536
	 * We use an additional cookie since we want to protect logout CSRF and
537
	 * also we can't directly interfere with PHP's session mechanism.
538
	 */
539
	private static function performSameSiteCookieProtection(\OCP\IConfig $config): void {
540
		$request = Server::get(IRequest::class);
541
542
		// Some user agents are notorious and don't really properly follow HTTP
543
		// specifications. For those, have an automated opt-out. Since the protection
544
		// for remote.php is applied in base.php as starting point we need to opt out
545
		// here.
546
		$incompatibleUserAgents = $config->getSystemValue('csrf.optout');
547
548
		// Fallback, if csrf.optout is unset
549
		if (!is_array($incompatibleUserAgents)) {
550
			$incompatibleUserAgents = [
551
				// OS X Finder
552
				'/^WebDAVFS/',
553
				// Windows webdav drive
554
				'/^Microsoft-WebDAV-MiniRedir/',
555
			];
556
		}
557
558
		if ($request->isUserAgent($incompatibleUserAgents)) {
559
			return;
560
		}
561
562
		if (count($_COOKIE) > 0) {
563
			$requestUri = $request->getScriptName();
564
			$processingScript = explode('/', $requestUri);
565
			$processingScript = $processingScript[count($processingScript) - 1];
566
567
			// index.php routes are handled in the middleware
568
			if ($processingScript === 'index.php') {
569
				return;
570
			}
571
572
			// All other endpoints require the lax and the strict cookie
573
			if (!$request->passesStrictCookieCheck()) {
574
				logger('core')->warning('Request does not pass strict cookie check');
575
				self::sendSameSiteCookies();
576
				// Debug mode gets access to the resources without strict cookie
577
				// due to the fact that the SabreDAV browser also lives there.
578
				if (!$config->getSystemValueBool('debug', false)) {
579
					http_response_code(\OCP\AppFramework\Http::STATUS_PRECONDITION_FAILED);
580
					header('Content-Type: application/json');
581
					echo json_encode(['error' => 'Strict Cookie has not been found in request']);
582
					exit();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
583
				}
584
			}
585
		} elseif (!isset($_COOKIE['nc_sameSiteCookielax']) || !isset($_COOKIE['nc_sameSiteCookiestrict'])) {
586
			self::sendSameSiteCookies();
587
		}
588
	}
589
590
	public static function init(): void {
591
		// calculate the root directories
592
		OC::$SERVERROOT = str_replace("\\", '/', substr(__DIR__, 0, -4));
593
594
		// register autoloader
595
		$loaderStart = microtime(true);
596
		require_once __DIR__ . '/autoloader.php';
597
		self::$loader = new \OC\Autoloader([
598
			OC::$SERVERROOT . '/lib/private/legacy',
599
		]);
600
		if (defined('PHPUNIT_RUN')) {
601
			self::$loader->addValidRoot(OC::$SERVERROOT . '/tests');
602
		}
603
		spl_autoload_register([self::$loader, 'load']);
604
		$loaderEnd = microtime(true);
605
606
		self::$CLI = (php_sapi_name() == 'cli');
607
608
		// Add default composer PSR-4 autoloader
609
		self::$composerAutoloader = require_once OC::$SERVERROOT . '/lib/composer/autoload.php';
610
		OC::$VERSION_MTIME = filemtime(OC::$SERVERROOT . '/version.php');
611
		self::$composerAutoloader->setApcuPrefix('composer_autoload_' . md5(OC::$SERVERROOT . '_' . OC::$VERSION_MTIME));
612
613
		try {
614
			self::initPaths();
615
			// setup 3rdparty autoloader
616
			$vendorAutoLoad = OC::$SERVERROOT. '/3rdparty/autoload.php';
617
			if (!file_exists($vendorAutoLoad)) {
618
				throw new \RuntimeException('Composer autoloader not found, unable to continue. Check the folder "3rdparty". Running "git submodule update --init" will initialize the git submodule that handles the subfolder "3rdparty".');
619
			}
620
			require_once $vendorAutoLoad;
621
		} catch (\RuntimeException $e) {
622
			if (!self::$CLI) {
623
				http_response_code(503);
624
			}
625
			// we can't use the template error page here, because this needs the
626
			// DI container which isn't available yet
627
			print($e->getMessage());
628
			exit();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
629
		}
630
631
		// setup the basic server
632
		self::$server = new \OC\Server(\OC::$WEBROOT, self::$config);
633
		self::$server->boot();
634
635
		$eventLogger = Server::get(\OCP\Diagnostics\IEventLogger::class);
636
		$eventLogger->log('autoloader', 'Autoloader', $loaderStart, $loaderEnd);
637
		$eventLogger->start('boot', 'Initialize');
638
639
		// Override php.ini and log everything if we're troubleshooting
640
		if (self::$config->getValue('loglevel') === ILogger::DEBUG) {
641
			error_reporting(E_ALL);
642
		}
643
644
		// Don't display errors and log them
645
		@ini_set('display_errors', '0');
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for ini_set(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

645
		/** @scrutinizer ignore-unhandled */ @ini_set('display_errors', '0');

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
646
		@ini_set('log_errors', '1');
647
648
		if (!date_default_timezone_set('UTC')) {
649
			throw new \RuntimeException('Could not set timezone to UTC');
650
		}
651
652
653
		//try to configure php to enable big file uploads.
654
		//this doesn´t work always depending on the webserver and php configuration.
655
		//Let´s try to overwrite some defaults if they are smaller than 1 hour
656
657
		if (intval(@ini_get('max_execution_time') ?? 0) < 3600) {
658
			@ini_set('max_execution_time', strval(3600));
659
		}
660
661
		if (intval(@ini_get('max_input_time') ?? 0) < 3600) {
662
			@ini_set('max_input_time', strval(3600));
663
		}
664
665
		//try to set the maximum execution time to the largest time limit we have
666
		if (strpos(@ini_get('disable_functions'), 'set_time_limit') === false) {
667
			@set_time_limit(max(intval(@ini_get('max_execution_time')), intval(@ini_get('max_input_time'))));
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for set_time_limit(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

667
			/** @scrutinizer ignore-unhandled */ @set_time_limit(max(intval(@ini_get('max_execution_time')), intval(@ini_get('max_input_time'))));

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
668
		}
669
670
		self::setRequiredIniValues();
671
		self::handleAuthHeaders();
672
		$systemConfig = Server::get(\OC\SystemConfig::class);
673
		self::registerAutoloaderCache($systemConfig);
674
675
		// initialize intl fallback if necessary
676
		OC_Util::isSetLocaleWorking();
677
678
		$config = Server::get(\OCP\IConfig::class);
679
		if (!defined('PHPUNIT_RUN')) {
680
			$errorHandler = new OC\Log\ErrorHandler(
681
				\OCP\Server::get(\Psr\Log\LoggerInterface::class),
682
			);
683
			$exceptionHandler = [$errorHandler, 'onException'];
684
			if ($config->getSystemValueBool('debug', false)) {
685
				set_error_handler([$errorHandler, 'onAll'], E_ALL);
686
				if (\OC::$CLI) {
687
					$exceptionHandler = ['OC_Template', 'printExceptionErrorPage'];
688
				}
689
			} else {
690
				set_error_handler([$errorHandler, 'onError']);
691
			}
692
			register_shutdown_function([$errorHandler, 'onShutdown']);
693
			set_exception_handler($exceptionHandler);
694
		}
695
696
		/** @var \OC\AppFramework\Bootstrap\Coordinator $bootstrapCoordinator */
697
		$bootstrapCoordinator = Server::get(\OC\AppFramework\Bootstrap\Coordinator::class);
698
		$bootstrapCoordinator->runInitialRegistration();
699
700
		$eventLogger->start('init_session', 'Initialize session');
701
		OC_App::loadApps(['session']);
702
		if (!self::$CLI) {
703
			self::initSession();
704
		}
705
		$eventLogger->end('init_session');
706
		self::checkConfig();
707
		self::checkInstalled($systemConfig);
708
709
		OC_Response::addSecurityHeaders();
710
711
		self::performSameSiteCookieProtection($config);
712
713
		if (!defined('OC_CONSOLE')) {
714
			$errors = OC_Util::checkServer($systemConfig);
715
			if (count($errors) > 0) {
716
				if (!self::$CLI) {
717
					http_response_code(503);
718
					OC_Util::addStyle('guest');
719
					try {
720
						OC_Template::printGuestPage('', 'error', ['errors' => $errors]);
721
						exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
722
					} catch (\Exception $e) {
723
						// In case any error happens when showing the error page, we simply fall back to posting the text.
724
						// This might be the case when e.g. the data directory is broken and we can not load/write SCSS to/from it.
725
					}
726
				}
727
728
				// Convert l10n string into regular string for usage in database
729
				$staticErrors = [];
730
				foreach ($errors as $error) {
731
					echo $error['error'] . "\n";
732
					echo $error['hint'] . "\n\n";
733
					$staticErrors[] = [
734
						'error' => (string)$error['error'],
735
						'hint' => (string)$error['hint'],
736
					];
737
				}
738
739
				try {
740
					$config->setAppValue('core', 'cronErrors', json_encode($staticErrors));
741
				} catch (\Exception $e) {
742
					echo('Writing to database failed');
743
				}
744
				exit(1);
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
745
			} elseif (self::$CLI && $config->getSystemValueBool('installed', false)) {
746
				$config->deleteAppValue('core', 'cronErrors');
747
			}
748
		}
749
750
		// User and Groups
751
		if (!$systemConfig->getValue("installed", false)) {
752
			self::$server->getSession()->set('user_id', '');
753
		}
754
755
		OC_User::useBackend(new \OC\User\Database());
756
		Server::get(\OCP\IGroupManager::class)->addBackend(new \OC\Group\Database());
757
758
		// Subscribe to the hook
759
		\OCP\Util::connectHook(
760
			'\OCA\Files_Sharing\API\Server2Server',
761
			'preLoginNameUsedAsUserName',
762
			'\OC\User\Database',
763
			'preLoginNameUsedAsUserName'
764
		);
765
766
		//setup extra user backends
767
		if (!\OCP\Util::needUpgrade()) {
768
			OC_User::setupBackends();
769
		} else {
770
			// Run upgrades in incognito mode
771
			OC_User::setIncognitoMode(true);
772
		}
773
774
		self::registerCleanupHooks($systemConfig);
775
		self::registerShareHooks($systemConfig);
776
		self::registerEncryptionWrapperAndHooks();
777
		self::registerAccountHooks();
778
		self::registerResourceCollectionHooks();
779
		self::registerFileReferenceEventListener();
780
		self::registerRenderReferenceEventListener();
781
		self::registerAppRestrictionsHooks();
782
783
		// Make sure that the application class is not loaded before the database is setup
784
		if ($systemConfig->getValue("installed", false)) {
785
			OC_App::loadApp('settings');
786
			/* Build core application to make sure that listeners are registered */
787
			Server::get(\OC\Core\Application::class);
788
		}
789
790
		//make sure temporary files are cleaned up
791
		$tmpManager = Server::get(\OCP\ITempManager::class);
792
		register_shutdown_function([$tmpManager, 'clean']);
793
		$lockProvider = Server::get(\OCP\Lock\ILockingProvider::class);
794
		register_shutdown_function([$lockProvider, 'releaseAll']);
795
796
		// Check whether the sample configuration has been copied
797
		if ($systemConfig->getValue('copied_sample_config', false)) {
798
			$l = Server::get(\OCP\L10N\IFactory::class)->get('lib');
799
			OC_Template::printErrorPage(
800
				$l->t('Sample configuration detected'),
801
				$l->t('It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php'),
802
				503
803
			);
804
			return;
805
		}
806
807
		$request = Server::get(IRequest::class);
808
		$host = $request->getInsecureServerHost();
809
		/**
810
		 * if the host passed in headers isn't trusted
811
		 * FIXME: Should not be in here at all :see_no_evil:
812
		 */
813
		if (!OC::$CLI
814
			&& !Server::get(\OC\Security\TrustedDomainHelper::class)->isTrustedDomain($host)
815
			&& $config->getSystemValueBool('installed', false)
816
		) {
817
			// Allow access to CSS resources
818
			$isScssRequest = false;
819
			if (strpos($request->getPathInfo() ?: '', '/css/') === 0) {
820
				$isScssRequest = true;
821
			}
822
823
			if (substr($request->getRequestUri(), -11) === '/status.php') {
824
				http_response_code(400);
825
				header('Content-Type: application/json');
826
				echo '{"error": "Trusted domain error.", "code": 15}';
827
				exit();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
828
			}
829
830
			if (!$isScssRequest) {
831
				http_response_code(400);
832
				Server::get(LoggerInterface::class)->info(
833
					'Trusted domain error. "{remoteAddress}" tried to access using "{host}" as host.',
834
					[
835
						'app' => 'core',
836
						'remoteAddress' => $request->getRemoteAddress(),
837
						'host' => $host,
838
					]
839
				);
840
841
				$tmpl = new OCP\Template('core', 'untrustedDomain', 'guest');
842
				$tmpl->assign('docUrl', Server::get(IURLGenerator::class)->linkToDocs('admin-trusted-domains'));
843
				$tmpl->printPage();
844
845
				exit();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
846
			}
847
		}
848
		$eventLogger->end('boot');
849
		$eventLogger->log('init', 'OC::init', $loaderStart, microtime(true));
850
		$eventLogger->start('runtime', 'Runtime');
851
		$eventLogger->start('request', 'Full request after boot');
852
		register_shutdown_function(function () use ($eventLogger) {
853
			$eventLogger->end('request');
854
		});
855
	}
856
857
	/**
858
	 * register hooks for the cleanup of cache and bruteforce protection
859
	 */
860
	public static function registerCleanupHooks(\OC\SystemConfig $systemConfig): void {
861
		//don't try to do this before we are properly setup
862
		if ($systemConfig->getValue('installed', false) && !\OCP\Util::needUpgrade()) {
863
			// NOTE: This will be replaced to use OCP
864
			$userSession = Server::get(\OC\User\Session::class);
865
			$userSession->listen('\OC\User', 'postLogin', function () use ($userSession) {
866
				if (!defined('PHPUNIT_RUN') && $userSession->isLoggedIn()) {
867
					// reset brute force delay for this IP address and username
868
					$uid = $userSession->getUser()->getUID();
869
					$request = Server::get(IRequest::class);
870
					$throttler = Server::get(\OC\Security\Bruteforce\Throttler::class);
871
					$throttler->resetDelay($request->getRemoteAddress(), 'login', ['user' => $uid]);
872
				}
873
874
				try {
875
					$cache = new \OC\Cache\File();
876
					$cache->gc();
877
				} catch (\OC\ServerNotAvailableException $e) {
878
					// not a GC exception, pass it on
879
					throw $e;
880
				} catch (\OC\ForbiddenException $e) {
881
					// filesystem blocked for this request, ignore
882
				} catch (\Exception $e) {
883
					// a GC exception should not prevent users from using OC,
884
					// so log the exception
885
					Server::get(LoggerInterface::class)->warning('Exception when running cache gc.', [
886
						'app' => 'core',
887
						'exception' => $e,
888
					]);
889
				}
890
			});
891
		}
892
	}
893
894
	private static function registerEncryptionWrapperAndHooks(): void {
895
		$manager = Server::get(\OCP\Encryption\IManager::class);
896
		\OCP\Util::connectHook('OC_Filesystem', 'preSetup', $manager, 'setupStorage');
897
898
		$enabled = $manager->isEnabled();
899
		if ($enabled) {
900
			\OCP\Util::connectHook(Share::class, 'post_shared', HookManager::class, 'postShared');
901
			\OCP\Util::connectHook(Share::class, 'post_unshare', HookManager::class, 'postUnshared');
902
			\OCP\Util::connectHook('OC_Filesystem', 'post_rename', HookManager::class, 'postRename');
903
			\OCP\Util::connectHook('\OCA\Files_Trashbin\Trashbin', 'post_restore', HookManager::class, 'postRestore');
904
		}
905
	}
906
907
	private static function registerAccountHooks(): void {
908
		/** @var IEventDispatcher $dispatcher */
909
		$dispatcher = Server::get(IEventDispatcher::class);
910
		$dispatcher->addServiceListener(UserChangedEvent::class, \OC\Accounts\Hooks::class);
911
	}
912
913
	private static function registerAppRestrictionsHooks(): void {
914
		/** @var \OC\Group\Manager $groupManager */
915
		$groupManager = Server::get(\OCP\IGroupManager::class);
916
		$groupManager->listen('\OC\Group', 'postDelete', function (\OCP\IGroup $group) {
917
			$appManager = Server::get(\OCP\App\IAppManager::class);
918
			$apps = $appManager->getEnabledAppsForGroup($group);
919
			foreach ($apps as $appId) {
920
				$restrictions = $appManager->getAppRestriction($appId);
921
				if (empty($restrictions)) {
922
					continue;
923
				}
924
				$key = array_search($group->getGID(), $restrictions);
925
				unset($restrictions[$key]);
926
				$restrictions = array_values($restrictions);
927
				if (empty($restrictions)) {
928
					$appManager->disableApp($appId);
929
				} else {
930
					$appManager->enableAppForGroups($appId, $restrictions);
931
				}
932
			}
933
		});
934
	}
935
936
	private static function registerResourceCollectionHooks(): void {
937
		\OC\Collaboration\Resources\Listener::register(Server::get(SymfonyAdapter::class), Server::get(IEventDispatcher::class));
938
	}
939
940
	private static function registerFileReferenceEventListener(): void {
941
		\OC\Collaboration\Reference\File\FileReferenceEventListener::register(Server::get(IEventDispatcher::class));
942
	}
943
944
	private static function registerRenderReferenceEventListener() {
945
		\OC\Collaboration\Reference\RenderReferenceEventListener::register(Server::get(IEventDispatcher::class));
946
	}
947
948
	/**
949
	 * register hooks for sharing
950
	 */
951
	public static function registerShareHooks(\OC\SystemConfig $systemConfig): void {
952
		if ($systemConfig->getValue('installed')) {
953
			OC_Hook::connect('OC_User', 'post_deleteUser', Hooks::class, 'post_deleteUser');
954
			OC_Hook::connect('OC_User', 'post_deleteGroup', Hooks::class, 'post_deleteGroup');
955
956
			/** @var IEventDispatcher $dispatcher */
957
			$dispatcher = Server::get(IEventDispatcher::class);
958
			$dispatcher->addServiceListener(UserRemovedEvent::class, \OC\Share20\UserRemovedListener::class);
959
		}
960
	}
961
962
	protected static function registerAutoloaderCache(\OC\SystemConfig $systemConfig): void {
963
		// The class loader takes an optional low-latency cache, which MUST be
964
		// namespaced. The instanceid is used for namespacing, but might be
965
		// unavailable at this point. Furthermore, it might not be possible to
966
		// generate an instanceid via \OC_Util::getInstanceId() because the
967
		// config file may not be writable. As such, we only register a class
968
		// loader cache if instanceid is available without trying to create one.
969
		$instanceId = $systemConfig->getValue('instanceid', null);
970
		if ($instanceId) {
971
			try {
972
				$memcacheFactory = Server::get(\OCP\ICacheFactory::class);
973
				self::$loader->setMemoryCache($memcacheFactory->createLocal('Autoloader'));
974
			} catch (\Exception $ex) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
975
			}
976
		}
977
	}
978
979
	/**
980
	 * Handle the request
981
	 */
982
	public static function handleRequest(): void {
983
		Server::get(\OCP\Diagnostics\IEventLogger::class)->start('handle_request', 'Handle request');
984
		$systemConfig = Server::get(\OC\SystemConfig::class);
985
986
		// Check if Nextcloud is installed or in maintenance (update) mode
987
		if (!$systemConfig->getValue('installed', false)) {
988
			\OC::$server->getSession()->clear();
989
			$setupHelper = new OC\Setup(
990
				$systemConfig,
991
				Server::get(\bantu\IniGetWrapper\IniGetWrapper::class),
992
				Server::get(\OCP\L10N\IFactory::class)->get('lib'),
993
				Server::get(\OCP\Defaults::class),
994
				Server::get(\Psr\Log\LoggerInterface::class),
995
				Server::get(\OCP\Security\ISecureRandom::class),
996
				Server::get(\OC\Installer::class)
997
			);
998
			$controller = new OC\Core\Controller\SetupController($setupHelper);
999
			$controller->run($_POST);
1000
			exit();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
1001
		}
1002
1003
		$request = Server::get(IRequest::class);
1004
		$requestPath = $request->getRawPathInfo();
1005
		if ($requestPath === '/heartbeat') {
1006
			return;
1007
		}
1008
		if (substr($requestPath, -3) !== '.js') { // we need these files during the upgrade
1009
			self::checkMaintenanceMode($systemConfig);
1010
1011
			if (\OCP\Util::needUpgrade()) {
1012
				if (function_exists('opcache_reset')) {
1013
					opcache_reset();
1014
				}
1015
				if (!((bool) $systemConfig->getValue('maintenance', false))) {
1016
					self::printUpgradePage($systemConfig);
1017
					exit();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
1018
				}
1019
			}
1020
		}
1021
1022
		// emergency app disabling
1023
		if ($requestPath === '/disableapp'
1024
			&& $request->getMethod() === 'POST'
1025
		) {
1026
			\OC_JSON::callCheck();
1027
			\OC_JSON::checkAdminUser();
1028
			$appIds = (array)$request->getParam('appid');
1029
			foreach ($appIds as $appId) {
1030
				$appId = \OC_App::cleanAppId($appId);
1031
				Server::get(\OCP\App\IAppManager::class)->disableApp($appId);
1032
			}
1033
			\OC_JSON::success();
1034
			exit();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
1035
		}
1036
1037
		// Always load authentication apps
1038
		OC_App::loadApps(['authentication']);
1039
		OC_App::loadApps(['extended_authentication']);
1040
1041
		// Load minimum set of apps
1042
		if (!\OCP\Util::needUpgrade()
1043
			&& !((bool) $systemConfig->getValue('maintenance', false))) {
1044
			// For logged-in users: Load everything
1045
			if (Server::get(IUserSession::class)->isLoggedIn()) {
1046
				OC_App::loadApps();
1047
			} else {
1048
				// For guests: Load only filesystem and logging
1049
				OC_App::loadApps(['filesystem', 'logging']);
1050
1051
				// Don't try to login when a client is trying to get a OAuth token.
1052
				// OAuth needs to support basic auth too, so the login is not valid
1053
				// inside Nextcloud and the Login exception would ruin it.
1054
				if ($request->getRawPathInfo() !== '/apps/oauth2/api/v1/token') {
1055
					self::handleLogin($request);
1056
				}
1057
			}
1058
		}
1059
1060
		if (!self::$CLI) {
1061
			try {
1062
				if (!((bool) $systemConfig->getValue('maintenance', false)) && !\OCP\Util::needUpgrade()) {
1063
					OC_App::loadApps(['filesystem', 'logging']);
1064
					OC_App::loadApps();
1065
				}
1066
				Server::get(\OC\Route\Router::class)->match($request->getRawPathInfo());
1067
				return;
1068
			} catch (Symfony\Component\Routing\Exception\ResourceNotFoundException $e) {
1069
				//header('HTTP/1.0 404 Not Found');
1070
			} catch (Symfony\Component\Routing\Exception\MethodNotAllowedException $e) {
1071
				http_response_code(405);
1072
				return;
1073
			}
1074
		}
1075
1076
		// Handle WebDAV
1077
		if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'PROPFIND') {
1078
			// not allowed any more to prevent people
1079
			// mounting this root directly.
1080
			// Users need to mount remote.php/webdav instead.
1081
			http_response_code(405);
1082
			return;
1083
		}
1084
1085
		// Handle requests for JSON or XML
1086
		$acceptHeader = $request->getHeader('Accept');
1087
		if (in_array($acceptHeader, ['application/json', 'application/xml'], true)) {
1088
			http_response_code(404);
1089
			return;
1090
		}
1091
1092
		// Handle resources that can't be found
1093
		// This prevents browsers from redirecting to the default page and then
1094
		// attempting to parse HTML as CSS and similar.
1095
		$destinationHeader = $request->getHeader('Sec-Fetch-Dest');
1096
		if (in_array($destinationHeader, ['font', 'script', 'style'])) {
1097
			http_response_code(404);
1098
			return;
1099
		}
1100
1101
		// Redirect to the default app or login only as an entry point
1102
		if ($requestPath === '') {
1103
			// Someone is logged in
1104
			if (Server::get(IUserSession::class)->isLoggedIn()) {
1105
				header('Location: ' . Server::get(IURLGenerator::class)->linkToDefaultPageUrl());
1106
			} else {
1107
				// Not handled and not logged in
1108
				header('Location: ' . Server::get(IURLGenerator::class)->linkToRouteAbsolute('core.login.showLoginForm'));
1109
			}
1110
			return;
1111
		}
1112
1113
		try {
1114
			Server::get(\OC\Route\Router::class)->match('/error/404');
1115
		} catch (\Exception $e) {
1116
			if (!$e instanceof MethodNotAllowedException) {
1117
				logger('core')->emergency($e->getMessage(), ['exception' => $e]);
1118
			}
1119
			$l = Server::get(\OCP\L10N\IFactory::class)->get('lib');
1120
			OC_Template::printErrorPage(
1121
				$l->t('404'),
1122
				$l->t('The page could not be found on the server.'),
1123
				404
1124
			);
1125
		}
1126
	}
1127
1128
	/**
1129
	 * Check login: apache auth, auth token, basic auth
1130
	 */
1131
	public static function handleLogin(OCP\IRequest $request): bool {
1132
		$userSession = Server::get(\OC\User\Session::class);
1133
		if (OC_User::handleApacheAuth()) {
1134
			return true;
1135
		}
1136
		if ($userSession->tryTokenLogin($request)) {
1137
			return true;
1138
		}
1139
		if (isset($_COOKIE['nc_username'])
1140
			&& isset($_COOKIE['nc_token'])
1141
			&& isset($_COOKIE['nc_session_id'])
1142
			&& $userSession->loginWithCookie($_COOKIE['nc_username'], $_COOKIE['nc_token'], $_COOKIE['nc_session_id'])) {
1143
			return true;
1144
		}
1145
		if ($userSession->tryBasicAuthLogin($request, Server::get(\OC\Security\Bruteforce\Throttler::class))) {
1146
			return true;
1147
		}
1148
		return false;
1149
	}
1150
1151
	protected static function handleAuthHeaders(): void {
1152
		//copy http auth headers for apache+php-fcgid work around
1153
		if (isset($_SERVER['HTTP_XAUTHORIZATION']) && !isset($_SERVER['HTTP_AUTHORIZATION'])) {
1154
			$_SERVER['HTTP_AUTHORIZATION'] = $_SERVER['HTTP_XAUTHORIZATION'];
1155
		}
1156
1157
		// Extract PHP_AUTH_USER/PHP_AUTH_PW from other headers if necessary.
1158
		$vars = [
1159
			'HTTP_AUTHORIZATION', // apache+php-cgi work around
1160
			'REDIRECT_HTTP_AUTHORIZATION', // apache+php-cgi alternative
1161
		];
1162
		foreach ($vars as $var) {
1163
			if (isset($_SERVER[$var]) && is_string($_SERVER[$var]) && preg_match('/Basic\s+(.*)$/i', $_SERVER[$var], $matches)) {
1164
				$credentials = explode(':', base64_decode($matches[1]), 2);
1165
				if (count($credentials) === 2) {
1166
					$_SERVER['PHP_AUTH_USER'] = $credentials[0];
1167
					$_SERVER['PHP_AUTH_PW'] = $credentials[1];
1168
					break;
1169
				}
1170
			}
1171
		}
1172
	}
1173
}
1174
1175
OC::init();
1176