Passed
Push — master ( c1a99c...d56ee8 )
by Julius
13:56 queued 12s
created

OC::initSession()   C

Complexity

Conditions 13
Paths 42

Size

Total Lines 56
Code Lines 29

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 13
eloc 29
c 2
b 0
f 0
nc 42
nop 0
dl 0
loc 56
rs 6.6166

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\Files\Filesystem;
72
use OC\Share20\Hooks;
73
use OCP\EventDispatcher\IEventDispatcher;
74
use OCP\Group\Events\UserRemovedEvent;
75
use OCP\ILogger;
76
use OCP\IRequest;
77
use OCP\IURLGenerator;
78
use OCP\IUserSession;
79
use OCP\Server;
80
use OCP\Share;
81
use OCP\User\Events\UserChangedEvent;
82
use Psr\Log\LoggerInterface;
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
	/**
118
	 * requested app
119
	 */
120
	public static string $REQUESTEDAPP = '';
121
122
	/**
123
	 * check if Nextcloud runs in cli mode
124
	 */
125
	public static bool $CLI = false;
126
127
	public static \OC\Autoloader $loader;
128
129
	public static \Composer\Autoload\ClassLoader $composerAutoloader;
130
131
	public static \OC\Server $server;
132
133
	private static \OC\Config $config;
134
135
	/**
136
	 * @throws \RuntimeException when the 3rdparty directory is missing or
137
	 * the app path list is empty or contains an invalid path
138
	 */
139
	public static function initPaths(): void {
140
		if (defined('PHPUNIT_CONFIG_DIR')) {
141
			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...
142
		} 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...
143
			self::$configDir = OC::$SERVERROOT . '/tests/config/';
144
		} elseif ($dir = getenv('NEXTCLOUD_CONFIG_DIR')) {
145
			self::$configDir = rtrim($dir, '/') . '/';
146
		} else {
147
			self::$configDir = OC::$SERVERROOT . '/config/';
148
		}
149
		self::$config = new \OC\Config(self::$configDir);
150
151
		OC::$SUBURI = str_replace("\\", "/", substr(realpath($_SERVER["SCRIPT_FILENAME"] ?? ''), strlen(OC::$SERVERROOT)));
152
		/**
153
		 * FIXME: The following lines are required because we can't yet instantiate
154
		 *        Server::get(\OCP\IRequest::class) since \OC::$server does not yet exist.
155
		 */
156
		$params = [
157
			'server' => [
158
				'SCRIPT_NAME' => $_SERVER['SCRIPT_NAME'] ?? null,
159
				'SCRIPT_FILENAME' => $_SERVER['SCRIPT_FILENAME'] ?? null,
160
			],
161
		];
162
		$fakeRequest = new \OC\AppFramework\Http\Request(
163
			$params,
164
			new \OC\AppFramework\Http\RequestId($_SERVER['UNIQUE_ID'] ?? '', new \OC\Security\SecureRandom()),
165
			new \OC\AllConfig(new \OC\SystemConfig(self::$config))
166
		);
167
		$scriptName = $fakeRequest->getScriptName();
168
		if (substr($scriptName, -1) == '/') {
169
			$scriptName .= 'index.php';
170
			//make sure suburi follows the same rules as scriptName
171
			if (substr(OC::$SUBURI, -9) != 'index.php') {
172
				if (substr(OC::$SUBURI, -1) != '/') {
173
					OC::$SUBURI = OC::$SUBURI . '/';
174
				}
175
				OC::$SUBURI = OC::$SUBURI . 'index.php';
176
			}
177
		}
178
179
180
		if (OC::$CLI) {
181
			OC::$WEBROOT = self::$config->getValue('overwritewebroot', '');
182
		} else {
183
			if (substr($scriptName, 0 - strlen(OC::$SUBURI)) === OC::$SUBURI) {
184
				OC::$WEBROOT = substr($scriptName, 0, 0 - strlen(OC::$SUBURI));
185
186
				if (OC::$WEBROOT != '' && OC::$WEBROOT[0] !== '/') {
187
					OC::$WEBROOT = '/' . OC::$WEBROOT;
188
				}
189
			} else {
190
				// The scriptName is not ending with OC::$SUBURI
191
				// This most likely means that we are calling from CLI.
192
				// However some cron jobs still need to generate
193
				// a web URL, so we use overwritewebroot as a fallback.
194
				OC::$WEBROOT = self::$config->getValue('overwritewebroot', '');
195
			}
196
197
			// Resolve /nextcloud to /nextcloud/ to ensure to always have a trailing
198
			// slash which is required by URL generation.
199
			if (isset($_SERVER['REQUEST_URI']) && $_SERVER['REQUEST_URI'] === \OC::$WEBROOT &&
200
					substr($_SERVER['REQUEST_URI'], -1) !== '/') {
201
				header('Location: '.\OC::$WEBROOT.'/');
202
				exit();
203
			}
204
		}
205
206
		// search the apps folder
207
		$config_paths = self::$config->getValue('apps_paths', []);
208
		if (!empty($config_paths)) {
209
			foreach ($config_paths as $paths) {
210
				if (isset($paths['url']) && isset($paths['path'])) {
211
					$paths['url'] = rtrim($paths['url'], '/');
212
					$paths['path'] = rtrim($paths['path'], '/');
213
					OC::$APPSROOTS[] = $paths;
214
				}
215
			}
216
		} elseif (file_exists(OC::$SERVERROOT . '/apps')) {
217
			OC::$APPSROOTS[] = ['path' => OC::$SERVERROOT . '/apps', 'url' => '/apps', 'writable' => true];
218
		}
219
220
		if (empty(OC::$APPSROOTS)) {
221
			throw new \RuntimeException('apps directory not found! Please put the Nextcloud apps folder in the Nextcloud folder'
222
				. '. You can also configure the location in the config.php file.');
223
		}
224
		$paths = [];
225
		foreach (OC::$APPSROOTS as $path) {
226
			$paths[] = $path['path'];
227
			if (!is_dir($path['path'])) {
228
				throw new \RuntimeException(sprintf('App directory "%s" not found! Please put the Nextcloud apps folder in the'
229
					. ' Nextcloud folder. You can also configure the location in the config.php file.', $path['path']));
230
			}
231
		}
232
233
		// set the right include path
234
		set_include_path(
235
			implode(PATH_SEPARATOR, $paths)
236
		);
237
	}
238
239
	public static function checkConfig(): void {
240
		$l = Server::get(\OCP\L10N\IFactory::class)->get('lib');
241
242
		// Create config if it does not already exist
243
		$configFilePath = self::$configDir .'/config.php';
244
		if (!file_exists($configFilePath)) {
245
			@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

245
			/** @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...
246
		}
247
248
		// Check if config is writable
249
		$configFileWritable = is_writable($configFilePath);
250
		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...
251
			|| !$configFileWritable && \OCP\Util::needUpgrade()) {
252
			$urlGenerator = Server::get(IURLGenerator::class);
253
254
			if (self::$CLI) {
255
				echo $l->t('Cannot write into "config" directory!')."\n";
256
				echo $l->t('This can usually be fixed by giving the web server write access to the config directory.')."\n";
257
				echo "\n";
258
				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";
259
				echo $l->t('See %s', [ $urlGenerator->linkToDocs('admin-config') ])."\n";
260
				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...
261
			} else {
262
				OC_Template::printErrorPage(
263
					$l->t('Cannot write into "config" directory!'),
264
					$l->t('This can usually be fixed by giving the web server write access to the config directory.') . ' '
265
					. $l->t('But, if you prefer to keep config.php file read only, set the option "config_is_read_only" to true in it.') . ' '
266
					. $l->t('See %s', [ $urlGenerator->linkToDocs('admin-config') ]),
267
					503
268
				);
269
			}
270
		}
271
	}
272
273
	public static function checkInstalled(\OC\SystemConfig $systemConfig): void {
274
		if (defined('OC_CONSOLE')) {
275
			return;
276
		}
277
		// Redirect to installer if not installed
278
		if (!$systemConfig->getValue('installed', false) && OC::$SUBURI !== '/index.php' && OC::$SUBURI !== '/status.php') {
279
			if (OC::$CLI) {
280
				throw new Exception('Not installed');
281
			} else {
282
				$url = OC::$WEBROOT . '/index.php';
283
				header('Location: ' . $url);
284
			}
285
			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...
286
		}
287
	}
288
289
	public static function checkMaintenanceMode(\OC\SystemConfig $systemConfig): void {
290
		// Allow ajax update script to execute without being stopped
291
		if (((bool) $systemConfig->getValue('maintenance', false)) && OC::$SUBURI != '/core/ajax/update.php') {
292
			// send http status 503
293
			http_response_code(503);
294
			header('X-Nextcloud-Maintenance-Mode: 1');
295
			header('Retry-After: 120');
296
297
			// render error page
298
			$template = new OC_Template('', 'update.user', 'guest');
299
			\OCP\Util::addScript('core', 'maintenance');
300
			\OCP\Util::addStyle('core', 'guest');
301
			$template->printPage();
302
			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...
303
		}
304
	}
305
306
	/**
307
	 * Prints the upgrade page
308
	 */
309
	private static function printUpgradePage(\OC\SystemConfig $systemConfig): void {
310
		$disableWebUpdater = $systemConfig->getValue('upgrade.disable-web', false);
311
		$tooBig = false;
312
		if (!$disableWebUpdater) {
313
			$apps = Server::get(\OCP\App\IAppManager::class);
314
			if ($apps->isInstalled('user_ldap')) {
315
				$qb = Server::get(\OCP\IDBConnection::class)->getQueryBuilder();
316
317
				$result = $qb->select($qb->func()->count('*', 'user_count'))
318
					->from('ldap_user_mapping')
319
					->executeQuery();
320
				$row = $result->fetch();
321
				$result->closeCursor();
322
323
				$tooBig = ($row['user_count'] > 50);
324
			}
325
			if (!$tooBig && $apps->isInstalled('user_saml')) {
326
				$qb = Server::get(\OCP\IDBConnection::class)->getQueryBuilder();
327
328
				$result = $qb->select($qb->func()->count('*', 'user_count'))
329
					->from('user_saml_users')
330
					->executeQuery();
331
				$row = $result->fetch();
332
				$result->closeCursor();
333
334
				$tooBig = ($row['user_count'] > 50);
335
			}
336
			if (!$tooBig) {
337
				// count users
338
				$stats = Server::get(\OCP\IUserManager::class)->countUsers();
339
				$totalUsers = array_sum($stats);
340
				$tooBig = ($totalUsers > 50);
341
			}
342
		}
343
		$ignoreTooBigWarning = isset($_GET['IKnowThatThisIsABigInstanceAndTheUpdateRequestCouldRunIntoATimeoutAndHowToRestoreABackup']) &&
344
			$_GET['IKnowThatThisIsABigInstanceAndTheUpdateRequestCouldRunIntoATimeoutAndHowToRestoreABackup'] === 'IAmSuperSureToDoThis';
345
346
		if ($disableWebUpdater || ($tooBig && !$ignoreTooBigWarning)) {
347
			// send http status 503
348
			http_response_code(503);
349
			header('Retry-After: 120');
350
351
			// render error page
352
			$template = new OC_Template('', 'update.use-cli', 'guest');
353
			$template->assign('productName', 'nextcloud'); // for now
354
			$template->assign('version', OC_Util::getVersionString());
355
			$template->assign('tooBig', $tooBig);
356
357
			$template->printPage();
358
			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...
359
		}
360
361
		// check whether this is a core update or apps update
362
		$installedVersion = $systemConfig->getValue('version', '0.0.0');
363
		$currentVersion = implode('.', \OCP\Util::getVersion());
364
365
		// if not a core upgrade, then it's apps upgrade
366
		$isAppsOnlyUpgrade = version_compare($currentVersion, $installedVersion, '=');
367
368
		$oldTheme = $systemConfig->getValue('theme');
369
		$systemConfig->setValue('theme', '');
370
		\OCP\Util::addScript('core', 'common');
371
		\OCP\Util::addScript('core', 'main');
372
		\OCP\Util::addTranslations('core');
373
		\OCP\Util::addScript('core', 'update');
374
375
		/** @var \OC\App\AppManager $appManager */
376
		$appManager = Server::get(\OCP\App\IAppManager::class);
377
378
		$tmpl = new OC_Template('', 'update.admin', 'guest');
379
		$tmpl->assign('version', OC_Util::getVersionString());
380
		$tmpl->assign('isAppsOnlyUpgrade', $isAppsOnlyUpgrade);
381
382
		// get third party apps
383
		$ocVersion = \OCP\Util::getVersion();
384
		$ocVersion = implode('.', $ocVersion);
385
		$incompatibleApps = $appManager->getIncompatibleApps($ocVersion);
386
		$incompatibleShippedApps = [];
387
		foreach ($incompatibleApps as $appInfo) {
388
			if ($appManager->isShipped($appInfo['id'])) {
389
				$incompatibleShippedApps[] = $appInfo['name'] . ' (' . $appInfo['id'] . ')';
390
			}
391
		}
392
393
		if (!empty($incompatibleShippedApps)) {
394
			$l = Server::get(\OCP\L10N\IFactory::class)->get('core');
395
			$hint = $l->t('The files of the app %1$s were not replaced correctly. Make sure it is a version compatible with the server.', [implode(', ', $incompatibleShippedApps)]);
396
			throw new \OCP\HintException('The files of the app ' . implode(', ', $incompatibleShippedApps) . ' were not replaced correctly. Make sure it is a version compatible with the server.', $hint);
397
		}
398
399
		$tmpl->assign('appsToUpgrade', $appManager->getAppsNeedingUpgrade($ocVersion));
400
		$tmpl->assign('incompatibleAppsList', $incompatibleApps);
401
		try {
402
			$defaults = new \OC_Defaults();
403
			$tmpl->assign('productName', $defaults->getName());
404
		} catch (Throwable $error) {
405
			$tmpl->assign('productName', 'Nextcloud');
406
		}
407
		$tmpl->assign('oldTheme', $oldTheme);
408
		$tmpl->printPage();
409
	}
410
411
	public static function initSession(): void {
412
		$request = Server::get(IRequest::class);
413
		$isDavRequest = strpos($request->getRequestUri(), '/remote.php/dav') === 0 || strpos($request->getRequestUri(), '/remote.php/webdav') === 0;
414
		if ($request->getHeader('Authorization') !== '' && is_null($request->getCookie('cookie_test')) && $isDavRequest) {
415
			setcookie('cookie_test', 'test', time() + 3600);
416
			// Do not initialize the session if a request is authenticated directly
417
			// unless there is a session cookie already sent along
418
			return;
419
		}
420
421
		if ($request->getServerProtocol() === 'https') {
422
			ini_set('session.cookie_secure', 'true');
423
		}
424
425
		// prevents javascript from accessing php session cookies
426
		ini_set('session.cookie_httponly', 'true');
427
428
		// set the cookie path to the Nextcloud directory
429
		$cookie_path = OC::$WEBROOT ? : '/';
430
		ini_set('session.cookie_path', $cookie_path);
431
432
		// Let the session name be changed in the initSession Hook
433
		$sessionName = OC_Util::getInstanceId();
434
435
		try {
436
			// set the session name to the instance id - which is unique
437
			$session = new \OC\Session\Internal($sessionName);
438
439
			$cryptoWrapper = Server::get(\OC\Session\CryptoWrapper::class);
440
			$session = $cryptoWrapper->wrapSession($session);
441
			self::$server->setSession($session);
442
443
			// if session can't be started break with http 500 error
444
		} catch (Exception $e) {
445
			Server::get(LoggerInterface::class)->error($e->getMessage(), ['app' => 'base','exception' => $e]);
446
			//show the user a detailed error page
447
			OC_Template::printExceptionErrorPage($e, 500);
448
			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...
449
		}
450
451
		//try to set the session lifetime
452
		$sessionLifeTime = self::getSessionLifeTime();
453
		@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

453
		/** @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...
454
455
		// session timeout
456
		if ($session->exists('LAST_ACTIVITY') && (time() - $session->get('LAST_ACTIVITY') > $sessionLifeTime)) {
457
			if (isset($_COOKIE[session_name()])) {
458
				setcookie(session_name(), '', -1, self::$WEBROOT ? : '/');
459
			}
460
			Server::get(IUserSession::class)->logout();
461
		}
462
463
		if (!self::hasSessionRelaxedExpiry()) {
464
			$session->set('LAST_ACTIVITY', time());
465
		}
466
		$session->close();
467
	}
468
469
	private static function getSessionLifeTime(): int {
470
		return Server::get(\OC\AllConfig::class)->getSystemValueInt('session_lifetime', 60 * 60 * 24);
471
	}
472
473
	/**
474
	 * @return bool true if the session expiry should only be done by gc instead of an explicit timeout
475
	 */
476
	public static function hasSessionRelaxedExpiry(): bool {
477
		return Server::get(\OC\AllConfig::class)->getSystemValueBool('session_relaxed_expiry', false);
478
	}
479
480
	/**
481
	 * Try to set some values to the required Nextcloud default
482
	 */
483
	public static function setRequiredIniValues(): void {
484
		@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

484
		/** @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...
485
		@ini_set('gd.jpeg_ignore_warning', '1');
486
	}
487
488
	/**
489
	 * Send the same site cookies
490
	 */
491
	private static function sendSameSiteCookies(): void {
492
		$cookieParams = session_get_cookie_params();
493
		$secureCookie = ($cookieParams['secure'] === true) ? 'secure; ' : '';
494
		$policies = [
495
			'lax',
496
			'strict',
497
		];
498
499
		// Append __Host to the cookie if it meets the requirements
500
		$cookiePrefix = '';
501
		if ($cookieParams['secure'] === true && $cookieParams['path'] === '/') {
502
			$cookiePrefix = '__Host-';
503
		}
504
505
		foreach ($policies as $policy) {
506
			header(
507
				sprintf(
508
					'Set-Cookie: %snc_sameSiteCookie%s=true; path=%s; httponly;' . $secureCookie . 'expires=Fri, 31-Dec-2100 23:59:59 GMT; SameSite=%s',
509
					$cookiePrefix,
510
					$policy,
511
					$cookieParams['path'],
512
					$policy
513
				),
514
				false
515
			);
516
		}
517
	}
518
519
	/**
520
	 * Same Site cookie to further mitigate CSRF attacks. This cookie has to
521
	 * be set in every request if cookies are sent to add a second level of
522
	 * defense against CSRF.
523
	 *
524
	 * If the cookie is not sent this will set the cookie and reload the page.
525
	 * We use an additional cookie since we want to protect logout CSRF and
526
	 * also we can't directly interfere with PHP's session mechanism.
527
	 */
528
	private static function performSameSiteCookieProtection(\OCP\IConfig $config): void {
529
		$request = Server::get(IRequest::class);
530
531
		// Some user agents are notorious and don't really properly follow HTTP
532
		// specifications. For those, have an automated opt-out. Since the protection
533
		// for remote.php is applied in base.php as starting point we need to opt out
534
		// here.
535
		$incompatibleUserAgents = $config->getSystemValue('csrf.optout');
536
537
		// Fallback, if csrf.optout is unset
538
		if (!is_array($incompatibleUserAgents)) {
539
			$incompatibleUserAgents = [
540
				// OS X Finder
541
				'/^WebDAVFS/',
542
				// Windows webdav drive
543
				'/^Microsoft-WebDAV-MiniRedir/',
544
			];
545
		}
546
547
		if ($request->isUserAgent($incompatibleUserAgents)) {
548
			return;
549
		}
550
551
		if (count($_COOKIE) > 0) {
552
			$requestUri = $request->getScriptName();
553
			$processingScript = explode('/', $requestUri);
554
			$processingScript = $processingScript[count($processingScript) - 1];
555
556
			// index.php routes are handled in the middleware
557
			if ($processingScript === 'index.php') {
558
				return;
559
			}
560
561
			// All other endpoints require the lax and the strict cookie
562
			if (!$request->passesStrictCookieCheck()) {
563
				self::sendSameSiteCookies();
564
				// Debug mode gets access to the resources without strict cookie
565
				// due to the fact that the SabreDAV browser also lives there.
566
				if (!$config->getSystemValue('debug', false)) {
567
					http_response_code(\OCP\AppFramework\Http::STATUS_SERVICE_UNAVAILABLE);
568
					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...
569
				}
570
			}
571
		} elseif (!isset($_COOKIE['nc_sameSiteCookielax']) || !isset($_COOKIE['nc_sameSiteCookiestrict'])) {
572
			self::sendSameSiteCookies();
573
		}
574
	}
575
576
	public static function init(): void {
577
		// calculate the root directories
578
		OC::$SERVERROOT = str_replace("\\", '/', substr(__DIR__, 0, -4));
579
580
		// register autoloader
581
		$loaderStart = microtime(true);
582
		require_once __DIR__ . '/autoloader.php';
583
		self::$loader = new \OC\Autoloader([
584
			OC::$SERVERROOT . '/lib/private/legacy',
585
		]);
586
		if (defined('PHPUNIT_RUN')) {
587
			self::$loader->addValidRoot(OC::$SERVERROOT . '/tests');
588
		}
589
		spl_autoload_register([self::$loader, 'load']);
590
		$loaderEnd = microtime(true);
591
592
		self::$CLI = (php_sapi_name() == 'cli');
593
594
		// Add default composer PSR-4 autoloader
595
		self::$composerAutoloader = require_once OC::$SERVERROOT . '/lib/composer/autoload.php';
596
		self::$composerAutoloader->setApcuPrefix('composer_autoload');
597
598
		try {
599
			self::initPaths();
600
			// setup 3rdparty autoloader
601
			$vendorAutoLoad = OC::$SERVERROOT. '/3rdparty/autoload.php';
602
			if (!file_exists($vendorAutoLoad)) {
603
				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".');
604
			}
605
			require_once $vendorAutoLoad;
606
		} catch (\RuntimeException $e) {
607
			if (!self::$CLI) {
608
				http_response_code(503);
609
			}
610
			// we can't use the template error page here, because this needs the
611
			// DI container which isn't available yet
612
			print($e->getMessage());
613
			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...
614
		}
615
616
		// setup the basic server
617
		self::$server = new \OC\Server(\OC::$WEBROOT, self::$config);
618
		self::$server->boot();
619
620
		$eventLogger = Server::get(\OCP\Diagnostics\IEventLogger::class);
621
		$eventLogger->log('autoloader', 'Autoloader', $loaderStart, $loaderEnd);
622
		$eventLogger->start('boot', 'Initialize');
623
624
		// Override php.ini and log everything if we're troubleshooting
625
		if (self::$config->getValue('loglevel') === ILogger::DEBUG) {
626
			error_reporting(E_ALL);
627
		}
628
629
		// Don't display errors and log them
630
		@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

630
		/** @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...
631
		@ini_set('log_errors', '1');
632
633
		if (!date_default_timezone_set('UTC')) {
634
			throw new \RuntimeException('Could not set timezone to UTC');
635
		}
636
637
638
		//try to configure php to enable big file uploads.
639
		//this doesn´t work always depending on the webserver and php configuration.
640
		//Let´s try to overwrite some defaults if they are smaller than 1 hour
641
642
		if (intval(@ini_get('max_execution_time') ?? 0) < 3600) {
643
			@ini_set('max_execution_time', strval(3600));
644
		}
645
646
		if (intval(@ini_get('max_input_time') ?? 0) < 3600) {
647
			@ini_set('max_input_time', strval(3600));
648
		}
649
650
		//try to set the maximum execution time to the largest time limit we have
651
		if (strpos(@ini_get('disable_functions'), 'set_time_limit') === false) {
652
			@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

652
			/** @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...
653
		}
654
655
		self::setRequiredIniValues();
656
		self::handleAuthHeaders();
657
		$systemConfig = Server::get(\OC\SystemConfig::class);
658
		self::registerAutoloaderCache($systemConfig);
659
660
		// initialize intl fallback if necessary
661
		OC_Util::isSetLocaleWorking();
662
663
		$config = Server::get(\OCP\IConfig::class);
664
		if (!defined('PHPUNIT_RUN')) {
665
			$errorHandler = new OC\Log\ErrorHandler(
666
				\OCP\Server::get(\Psr\Log\LoggerInterface::class),
667
			);
668
			$exceptionHandler = [$errorHandler, 'onException'];
669
			if ($config->getSystemValue('debug', false)) {
670
				set_error_handler([$errorHandler, 'onAll'], E_ALL);
671
				if (\OC::$CLI) {
672
					$exceptionHandler = ['OC_Template', 'printExceptionErrorPage'];
673
				}
674
			} else {
675
				set_error_handler([$errorHandler, 'onError']);
676
			}
677
			register_shutdown_function([$errorHandler, 'onShutdown']);
678
			set_exception_handler($exceptionHandler);
679
		}
680
681
		/** @var \OC\AppFramework\Bootstrap\Coordinator $bootstrapCoordinator */
682
		$bootstrapCoordinator = Server::get(\OC\AppFramework\Bootstrap\Coordinator::class);
683
		$bootstrapCoordinator->runInitialRegistration();
684
685
		$eventLogger->start('init_session', 'Initialize session');
686
		OC_App::loadApps(['session']);
687
		if (!self::$CLI) {
688
			self::initSession();
689
		}
690
		$eventLogger->end('init_session');
691
		self::checkConfig();
692
		self::checkInstalled($systemConfig);
693
694
		OC_Response::addSecurityHeaders();
695
696
		self::performSameSiteCookieProtection($config);
697
698
		if (!defined('OC_CONSOLE')) {
699
			$errors = OC_Util::checkServer($systemConfig);
700
			if (count($errors) > 0) {
701
				if (!self::$CLI) {
702
					http_response_code(503);
703
					OC_Util::addStyle('guest');
704
					try {
705
						OC_Template::printGuestPage('', 'error', ['errors' => $errors]);
706
						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...
707
					} catch (\Exception $e) {
708
						// In case any error happens when showing the error page, we simply fall back to posting the text.
709
						// This might be the case when e.g. the data directory is broken and we can not load/write SCSS to/from it.
710
					}
711
				}
712
713
				// Convert l10n string into regular string for usage in database
714
				$staticErrors = [];
715
				foreach ($errors as $error) {
716
					echo $error['error'] . "\n";
717
					echo $error['hint'] . "\n\n";
718
					$staticErrors[] = [
719
						'error' => (string)$error['error'],
720
						'hint' => (string)$error['hint'],
721
					];
722
				}
723
724
				try {
725
					$config->setAppValue('core', 'cronErrors', json_encode($staticErrors));
726
				} catch (\Exception $e) {
727
					echo('Writing to database failed');
728
				}
729
				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...
730
			} elseif (self::$CLI && $config->getSystemValue('installed', false)) {
731
				$config->deleteAppValue('core', 'cronErrors');
732
			}
733
		}
734
735
		// User and Groups
736
		if (!$systemConfig->getValue("installed", false)) {
737
			self::$server->getSession()->set('user_id', '');
738
		}
739
740
		OC_User::useBackend(new \OC\User\Database());
741
		Server::get(\OCP\IGroupManager::class)->addBackend(new \OC\Group\Database());
742
743
		// Subscribe to the hook
744
		\OCP\Util::connectHook(
745
			'\OCA\Files_Sharing\API\Server2Server',
746
			'preLoginNameUsedAsUserName',
747
			'\OC\User\Database',
748
			'preLoginNameUsedAsUserName'
749
		);
750
751
		//setup extra user backends
752
		if (!\OCP\Util::needUpgrade()) {
753
			OC_User::setupBackends();
754
		} else {
755
			// Run upgrades in incognito mode
756
			OC_User::setIncognitoMode(true);
757
		}
758
759
		self::registerCleanupHooks($systemConfig);
760
		self::registerShareHooks($systemConfig);
761
		self::registerEncryptionWrapperAndHooks();
762
		self::registerAccountHooks();
763
		self::registerResourceCollectionHooks();
764
		self::registerFileReferenceEventListener();
765
		self::registerAppRestrictionsHooks();
766
767
		// Make sure that the application class is not loaded before the database is setup
768
		if ($systemConfig->getValue("installed", false)) {
769
			OC_App::loadApp('settings');
770
			/* Build core application to make sure that listeners are registered */
771
			Server::get(\OC\Core\Application::class);
772
		}
773
774
		//make sure temporary files are cleaned up
775
		$tmpManager = Server::get(\OCP\ITempManager::class);
776
		register_shutdown_function([$tmpManager, 'clean']);
777
		$lockProvider = Server::get(\OCP\Lock\ILockingProvider::class);
778
		register_shutdown_function([$lockProvider, 'releaseAll']);
779
780
		// Check whether the sample configuration has been copied
781
		if ($systemConfig->getValue('copied_sample_config', false)) {
782
			$l = Server::get(\OCP\L10N\IFactory::class)->get('lib');
783
			OC_Template::printErrorPage(
784
				$l->t('Sample configuration detected'),
785
				$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'),
786
				503
787
			);
788
			return;
789
		}
790
791
		$request = Server::get(IRequest::class);
792
		$host = $request->getInsecureServerHost();
793
		/**
794
		 * if the host passed in headers isn't trusted
795
		 * FIXME: Should not be in here at all :see_no_evil:
796
		 */
797
		if (!OC::$CLI
798
			&& !Server::get(\OC\Security\TrustedDomainHelper::class)->isTrustedDomain($host)
799
			&& $config->getSystemValue('installed', false)
800
		) {
801
			// Allow access to CSS resources
802
			$isScssRequest = false;
803
			if (strpos($request->getPathInfo() ?: '', '/css/') === 0) {
804
				$isScssRequest = true;
805
			}
806
807
			if (substr($request->getRequestUri(), -11) === '/status.php') {
808
				http_response_code(400);
809
				header('Content-Type: application/json');
810
				echo '{"error": "Trusted domain error.", "code": 15}';
811
				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...
812
			}
813
814
			if (!$isScssRequest) {
815
				http_response_code(400);
816
				Server::get(LoggerInterface::class)->info(
817
					'Trusted domain error. "{remoteAddress}" tried to access using "{host}" as host.',
818
					[
819
						'app' => 'core',
820
						'remoteAddress' => $request->getRemoteAddress(),
821
						'host' => $host,
822
					]
823
				);
824
825
				$tmpl = new OCP\Template('core', 'untrustedDomain', 'guest');
826
				$tmpl->assign('docUrl', Server::get(IURLGenerator::class)->linkToDocs('admin-trusted-domains'));
827
				$tmpl->printPage();
828
829
				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...
830
			}
831
		}
832
		$eventLogger->end('boot');
833
		$eventLogger->log('init', 'OC::init', $loaderStart, microtime(true));
834
		$eventLogger->start('runtime', 'Runtime');
835
		$eventLogger->start('request', 'Full request after boot');
836
		register_shutdown_function(function () use ($eventLogger) {
837
			$eventLogger->end('request');
838
		});
839
	}
840
841
	/**
842
	 * register hooks for the cleanup of cache and bruteforce protection
843
	 */
844
	public static function registerCleanupHooks(\OC\SystemConfig $systemConfig): void {
845
		//don't try to do this before we are properly setup
846
		if ($systemConfig->getValue('installed', false) && !\OCP\Util::needUpgrade()) {
847
			// NOTE: This will be replaced to use OCP
848
			$userSession = Server::get(\OC\User\Session::class);
849
			$userSession->listen('\OC\User', 'postLogin', function () use ($userSession) {
850
				if (!defined('PHPUNIT_RUN') && $userSession->isLoggedIn()) {
851
					// reset brute force delay for this IP address and username
852
					$uid = $userSession->getUser()->getUID();
853
					$request = Server::get(IRequest::class);
854
					$throttler = Server::get(\OC\Security\Bruteforce\Throttler::class);
855
					$throttler->resetDelay($request->getRemoteAddress(), 'login', ['user' => $uid]);
856
				}
857
858
				try {
859
					$cache = new \OC\Cache\File();
860
					$cache->gc();
861
				} catch (\OC\ServerNotAvailableException $e) {
862
					// not a GC exception, pass it on
863
					throw $e;
864
				} catch (\OC\ForbiddenException $e) {
865
					// filesystem blocked for this request, ignore
866
				} catch (\Exception $e) {
867
					// a GC exception should not prevent users from using OC,
868
					// so log the exception
869
					Server::get(LoggerInterface::class)->warning('Exception when running cache gc.', [
870
						'app' => 'core',
871
						'exception' => $e,
872
					]);
873
				}
874
			});
875
		}
876
	}
877
878
	private static function registerEncryptionWrapperAndHooks(): void {
879
		$manager = Server::get(\OCP\Encryption\IManager::class);
880
		\OCP\Util::connectHook('OC_Filesystem', 'preSetup', $manager, 'setupStorage');
881
882
		$enabled = $manager->isEnabled();
883
		if ($enabled) {
884
			\OCP\Util::connectHook(Share::class, 'post_shared', HookManager::class, 'postShared');
885
			\OCP\Util::connectHook(Share::class, 'post_unshare', HookManager::class, 'postUnshared');
886
			\OCP\Util::connectHook('OC_Filesystem', 'post_rename', HookManager::class, 'postRename');
887
			\OCP\Util::connectHook('\OCA\Files_Trashbin\Trashbin', 'post_restore', HookManager::class, 'postRestore');
888
		}
889
	}
890
891
	private static function registerAccountHooks(): void {
892
		/** @var IEventDispatcher $dispatcher */
893
		$dispatcher = Server::get(IEventDispatcher::class);
894
		$dispatcher->addServiceListener(UserChangedEvent::class, \OC\Accounts\Hooks::class);
895
	}
896
897
	private static function registerAppRestrictionsHooks(): void {
898
		/** @var \OC\Group\Manager $groupManager */
899
		$groupManager = Server::get(\OCP\IGroupManager::class);
900
		$groupManager->listen('\OC\Group', 'postDelete', function (\OCP\IGroup $group) {
901
			$appManager = Server::get(\OCP\App\IAppManager::class);
902
			$apps = $appManager->getEnabledAppsForGroup($group);
903
			foreach ($apps as $appId) {
904
				$restrictions = $appManager->getAppRestriction($appId);
905
				if (empty($restrictions)) {
906
					continue;
907
				}
908
				$key = array_search($group->getGID(), $restrictions);
909
				unset($restrictions[$key]);
910
				$restrictions = array_values($restrictions);
911
				if (empty($restrictions)) {
912
					$appManager->disableApp($appId);
913
				} else {
914
					$appManager->enableAppForGroups($appId, $restrictions);
915
				}
916
			}
917
		});
918
	}
919
920
	private static function registerResourceCollectionHooks(): void {
921
		\OC\Collaboration\Resources\Listener::register(Server::get(SymfonyAdapter::class), Server::get(IEventDispatcher::class));
922
	}
923
924
	private static function registerFileReferenceEventListener(): void {
925
		\OC\Collaboration\Reference\File\FileReferenceEventListener::register(Server::get(IEventDispatcher::class));
926
	}
927
928
	/**
929
	 * register hooks for sharing
930
	 */
931
	public static function registerShareHooks(\OC\SystemConfig $systemConfig): void {
932
		if ($systemConfig->getValue('installed')) {
933
			OC_Hook::connect('OC_User', 'post_deleteUser', Hooks::class, 'post_deleteUser');
934
			OC_Hook::connect('OC_User', 'post_deleteGroup', Hooks::class, 'post_deleteGroup');
935
936
			/** @var IEventDispatcher $dispatcher */
937
			$dispatcher = Server::get(IEventDispatcher::class);
938
			$dispatcher->addServiceListener(UserRemovedEvent::class, \OC\Share20\UserRemovedListener::class);
939
		}
940
	}
941
942
	protected static function registerAutoloaderCache(\OC\SystemConfig $systemConfig): void {
943
		// The class loader takes an optional low-latency cache, which MUST be
944
		// namespaced. The instanceid is used for namespacing, but might be
945
		// unavailable at this point. Furthermore, it might not be possible to
946
		// generate an instanceid via \OC_Util::getInstanceId() because the
947
		// config file may not be writable. As such, we only register a class
948
		// loader cache if instanceid is available without trying to create one.
949
		$instanceId = $systemConfig->getValue('instanceid', null);
950
		if ($instanceId) {
951
			try {
952
				$memcacheFactory = Server::get(\OCP\ICacheFactory::class);
953
				self::$loader->setMemoryCache($memcacheFactory->createLocal('Autoloader'));
954
			} catch (\Exception $ex) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
955
			}
956
		}
957
	}
958
959
	/**
960
	 * Handle the request
961
	 */
962
	public static function handleRequest(): void {
963
		Server::get(\OCP\Diagnostics\IEventLogger::class)->start('handle_request', 'Handle request');
964
		$systemConfig = Server::get(\OC\SystemConfig::class);
965
966
		// Check if Nextcloud is installed or in maintenance (update) mode
967
		if (!$systemConfig->getValue('installed', false)) {
968
			\OC::$server->getSession()->clear();
969
			$setupHelper = new OC\Setup(
970
				$systemConfig,
971
				Server::get(\bantu\IniGetWrapper\IniGetWrapper::class),
972
				Server::get(\OCP\L10N\IFactory::class)->get('lib'),
973
				Server::get(\OCP\Defaults::class),
974
				Server::get(\Psr\Log\LoggerInterface::class),
975
				Server::get(\OCP\Security\ISecureRandom::class),
976
				Server::get(\OC\Installer::class)
977
			);
978
			$controller = new OC\Core\Controller\SetupController($setupHelper);
979
			$controller->run($_POST);
980
			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...
981
		}
982
983
		$request = Server::get(IRequest::class);
984
		$requestPath = $request->getRawPathInfo();
985
		if ($requestPath === '/heartbeat') {
986
			return;
987
		}
988
		if (substr($requestPath, -3) !== '.js') { // we need these files during the upgrade
989
			self::checkMaintenanceMode($systemConfig);
990
991
			if (\OCP\Util::needUpgrade()) {
992
				if (function_exists('opcache_reset')) {
993
					opcache_reset();
994
				}
995
				if (!((bool) $systemConfig->getValue('maintenance', false))) {
996
					self::printUpgradePage($systemConfig);
997
					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...
998
				}
999
			}
1000
		}
1001
1002
		// emergency app disabling
1003
		if ($requestPath === '/disableapp'
1004
			&& $request->getMethod() === 'POST'
1005
		) {
1006
			\OC_JSON::callCheck();
1007
			\OC_JSON::checkAdminUser();
1008
			$appIds = (array)$request->getParam('appid');
1009
			foreach ($appIds as $appId) {
1010
				$appId = \OC_App::cleanAppId($appId);
1011
				Server::get(\OCP\App\IAppManager::class)->disableApp($appId);
1012
			}
1013
			\OC_JSON::success();
1014
			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...
1015
		}
1016
1017
		// Always load authentication apps
1018
		OC_App::loadApps(['authentication']);
1019
1020
		// Load minimum set of apps
1021
		if (!\OCP\Util::needUpgrade()
1022
			&& !((bool) $systemConfig->getValue('maintenance', false))) {
1023
			// For logged-in users: Load everything
1024
			if (Server::get(IUserSession::class)->isLoggedIn()) {
1025
				OC_App::loadApps();
1026
			} else {
1027
				// For guests: Load only filesystem and logging
1028
				OC_App::loadApps(['filesystem', 'logging']);
1029
1030
				// Don't try to login when a client is trying to get a OAuth token.
1031
				// OAuth needs to support basic auth too, so the login is not valid
1032
				// inside Nextcloud and the Login exception would ruin it.
1033
				if ($request->getRawPathInfo() !== '/apps/oauth2/api/v1/token') {
1034
					self::handleLogin($request);
1035
				}
1036
			}
1037
		}
1038
1039
		if (!self::$CLI) {
1040
			try {
1041
				if (!((bool) $systemConfig->getValue('maintenance', false)) && !\OCP\Util::needUpgrade()) {
1042
					OC_App::loadApps(['filesystem', 'logging']);
1043
					OC_App::loadApps();
1044
				}
1045
				Server::get(\OC\Route\Router::class)->match($request->getRawPathInfo());
1046
				return;
1047
			} catch (Symfony\Component\Routing\Exception\ResourceNotFoundException $e) {
1048
				//header('HTTP/1.0 404 Not Found');
1049
			} catch (Symfony\Component\Routing\Exception\MethodNotAllowedException $e) {
1050
				http_response_code(405);
1051
				return;
1052
			}
1053
		}
1054
1055
		// Handle WebDAV
1056
		if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'PROPFIND') {
1057
			// not allowed any more to prevent people
1058
			// mounting this root directly.
1059
			// Users need to mount remote.php/webdav instead.
1060
			http_response_code(405);
1061
			return;
1062
		}
1063
1064
		// Handle requests for JSON or XML
1065
		$acceptHeader = $request->getHeader('Accept');
1066
		if (in_array($acceptHeader, ['application/json', 'application/xml'], true)) {
1067
			http_response_code(404);
1068
			return;
1069
		}
1070
1071
		// Handle resources that can't be found
1072
		// This prevents browsers from redirecting to the default page and then
1073
		// attempting to parse HTML as CSS and similar.
1074
		$destinationHeader = $request->getHeader('Sec-Fetch-Dest');
1075
		if (in_array($destinationHeader, ['font', 'script', 'style'])) {
1076
			http_response_code(404);
1077
			return;
1078
		}
1079
1080
		// Redirect to the default app or login only as an entry point
1081
		if ($requestPath === '') {
1082
			// Someone is logged in
1083
			if (Server::get(IUserSession::class)->isLoggedIn()) {
1084
				header('Location: ' . Server::get(IURLGenerator::class)->linkToDefaultPageUrl());
1085
			} else {
1086
				// Not handled and not logged in
1087
				header('Location: ' . Server::get(IURLGenerator::class)->linkToRouteAbsolute('core.login.showLoginForm'));
1088
			}
1089
			return;
1090
		}
1091
1092
		try {
1093
			Server::get(\OC\Route\Router::class)->match('/error/404');
1094
		} catch (\Exception $e) {
1095
			logger('core')->emergency($e->getMessage(), ['exception' => $e]);
1096
			$l = Server::get(\OCP\L10N\IFactory::class)->get('lib');
1097
			OC_Template::printErrorPage(
1098
				$l->t('404'),
1099
				$l->t('The page could not be found on the server.'),
1100
				404
1101
			);
1102
		}
1103
	}
1104
1105
	/**
1106
	 * Check login: apache auth, auth token, basic auth
1107
	 */
1108
	public static function handleLogin(OCP\IRequest $request): bool {
1109
		$userSession = Server::get(\OC\User\Session::class);
1110
		if (OC_User::handleApacheAuth()) {
1111
			return true;
1112
		}
1113
		if ($userSession->tryTokenLogin($request)) {
1114
			return true;
1115
		}
1116
		if (isset($_COOKIE['nc_username'])
1117
			&& isset($_COOKIE['nc_token'])
1118
			&& isset($_COOKIE['nc_session_id'])
1119
			&& $userSession->loginWithCookie($_COOKIE['nc_username'], $_COOKIE['nc_token'], $_COOKIE['nc_session_id'])) {
1120
			return true;
1121
		}
1122
		if ($userSession->tryBasicAuthLogin($request, Server::get(\OC\Security\Bruteforce\Throttler::class))) {
1123
			return true;
1124
		}
1125
		return false;
1126
	}
1127
1128
	protected static function handleAuthHeaders(): void {
1129
		//copy http auth headers for apache+php-fcgid work around
1130
		if (isset($_SERVER['HTTP_XAUTHORIZATION']) && !isset($_SERVER['HTTP_AUTHORIZATION'])) {
1131
			$_SERVER['HTTP_AUTHORIZATION'] = $_SERVER['HTTP_XAUTHORIZATION'];
1132
		}
1133
1134
		// Extract PHP_AUTH_USER/PHP_AUTH_PW from other headers if necessary.
1135
		$vars = [
1136
			'HTTP_AUTHORIZATION', // apache+php-cgi work around
1137
			'REDIRECT_HTTP_AUTHORIZATION', // apache+php-cgi alternative
1138
		];
1139
		foreach ($vars as $var) {
1140
			if (isset($_SERVER[$var]) && is_string($_SERVER[$var]) && preg_match('/Basic\s+(.*)$/i', $_SERVER[$var], $matches)) {
1141
				$credentials = explode(':', base64_decode($matches[1]), 2);
1142
				if (count($credentials) === 2) {
1143
					$_SERVER['PHP_AUTH_USER'] = $credentials[0];
1144
					$_SERVER['PHP_AUTH_PW'] = $credentials[1];
1145
					break;
1146
				}
1147
			}
1148
		}
1149
	}
1150
}
1151
1152
OC::init();
1153