Passed
Push — master ( cbbb07...26e413 )
by Julius
16:48 queued 13s
created
lib/public/Log/functions.php 1 patch
Indentation   +20 added lines, -20 removed lines patch added patch discarded remove patch
@@ -49,26 +49,26 @@
 block discarded – undo
49 49
  * @since 24.0.0
50 50
  */
51 51
 function logger(string $appId = null): LoggerInterface {
52
-	/** @psalm-suppress TypeDoesNotContainNull false-positive, it may contain null if we are logging from initialization */
53
-	if (!class_exists(OC::class) || OC::$server === null) {
54
-		// If someone calls this log before Nextcloud is initialized, there is
55
-		// no logging available. In that case we return a noop implementation
56
-		// TODO: evaluate whether logging to error_log could be an alternative
57
-		return new NullLogger();
58
-	}
52
+    /** @psalm-suppress TypeDoesNotContainNull false-positive, it may contain null if we are logging from initialization */
53
+    if (!class_exists(OC::class) || OC::$server === null) {
54
+        // If someone calls this log before Nextcloud is initialized, there is
55
+        // no logging available. In that case we return a noop implementation
56
+        // TODO: evaluate whether logging to error_log could be an alternative
57
+        return new NullLogger();
58
+    }
59 59
 
60
-	if ($appId !== null) {
61
-		try {
62
-			$appContainer = OC::$server->getRegisteredAppContainer($appId);
63
-			return $appContainer->get(LoggerInterface::class);
64
-		} catch (QueryException $e) {
65
-			// Ignore and return the server logger below
66
-		}
67
-	}
60
+    if ($appId !== null) {
61
+        try {
62
+            $appContainer = OC::$server->getRegisteredAppContainer($appId);
63
+            return $appContainer->get(LoggerInterface::class);
64
+        } catch (QueryException $e) {
65
+            // Ignore and return the server logger below
66
+        }
67
+    }
68 68
 
69
-	try {
70
-		return OC::$server->get(LoggerInterface::class);
71
-	} catch (QueryException $e) {
72
-		return new NullLogger();
73
-	}
69
+    try {
70
+        return OC::$server->get(LoggerInterface::class);
71
+    } catch (QueryException $e) {
72
+        return new NullLogger();
73
+    }
74 74
 }
Please login to merge, or discard this patch.
lib/base.php 2 patches
Indentation   +1048 added lines, -1048 removed lines patch added patch discarded remove patch
@@ -89,1054 +89,1054 @@
 block discarded – undo
89 89
  * OC_autoload!
90 90
  */
91 91
 class OC {
92
-	/**
93
-	 * Associative array for autoloading. classname => filename
94
-	 */
95
-	public static array $CLASSPATH = [];
96
-	/**
97
-	 * The installation path for Nextcloud  on the server (e.g. /srv/http/nextcloud)
98
-	 */
99
-	public static string $SERVERROOT = '';
100
-	/**
101
-	 * the current request path relative to the Nextcloud root (e.g. files/index.php)
102
-	 */
103
-	private static string $SUBURI = '';
104
-	/**
105
-	 * the Nextcloud root path for http requests (e.g. nextcloud/)
106
-	 */
107
-	public static string $WEBROOT = '';
108
-	/**
109
-	 * The installation path array of the apps folder on the server (e.g. /srv/http/nextcloud) 'path' and
110
-	 * web path in 'url'
111
-	 */
112
-	public static array $APPSROOTS = [];
113
-
114
-	public static string $configDir;
115
-
116
-	/**
117
-	 * requested app
118
-	 */
119
-	public static string $REQUESTEDAPP = '';
120
-
121
-	/**
122
-	 * check if Nextcloud runs in cli mode
123
-	 */
124
-	public static bool $CLI = false;
125
-
126
-	public static \OC\Autoloader $loader;
127
-
128
-	public static \Composer\Autoload\ClassLoader $composerAutoloader;
129
-
130
-	public static \OC\Server $server;
131
-
132
-	private static \OC\Config $config;
133
-
134
-	/**
135
-	 * @throws \RuntimeException when the 3rdparty directory is missing or
136
-	 * the app path list is empty or contains an invalid path
137
-	 */
138
-	public static function initPaths(): void {
139
-		if (defined('PHPUNIT_CONFIG_DIR')) {
140
-			self::$configDir = OC::$SERVERROOT . '/' . PHPUNIT_CONFIG_DIR . '/';
141
-		} elseif (defined('PHPUNIT_RUN') and PHPUNIT_RUN and is_dir(OC::$SERVERROOT . '/tests/config/')) {
142
-			self::$configDir = OC::$SERVERROOT . '/tests/config/';
143
-		} elseif ($dir = getenv('NEXTCLOUD_CONFIG_DIR')) {
144
-			self::$configDir = rtrim($dir, '/') . '/';
145
-		} else {
146
-			self::$configDir = OC::$SERVERROOT . '/config/';
147
-		}
148
-		self::$config = new \OC\Config(self::$configDir);
149
-
150
-		OC::$SUBURI = str_replace("\\", "/", substr(realpath($_SERVER["SCRIPT_FILENAME"] ?? ''), strlen(OC::$SERVERROOT)));
151
-		/**
152
-		 * FIXME: The following lines are required because we can't yet instantiate
153
-		 *        Server::get(\OCP\IRequest::class) since \OC::$server does not yet exist.
154
-		 */
155
-		$params = [
156
-			'server' => [
157
-				'SCRIPT_NAME' => $_SERVER['SCRIPT_NAME'] ?? null,
158
-				'SCRIPT_FILENAME' => $_SERVER['SCRIPT_FILENAME'] ?? null,
159
-			],
160
-		];
161
-		$fakeRequest = new \OC\AppFramework\Http\Request(
162
-			$params,
163
-			new \OC\AppFramework\Http\RequestId($_SERVER['UNIQUE_ID'] ?? '', new \OC\Security\SecureRandom()),
164
-			new \OC\AllConfig(new \OC\SystemConfig(self::$config))
165
-		);
166
-		$scriptName = $fakeRequest->getScriptName();
167
-		if (substr($scriptName, -1) == '/') {
168
-			$scriptName .= 'index.php';
169
-			//make sure suburi follows the same rules as scriptName
170
-			if (substr(OC::$SUBURI, -9) != 'index.php') {
171
-				if (substr(OC::$SUBURI, -1) != '/') {
172
-					OC::$SUBURI = OC::$SUBURI . '/';
173
-				}
174
-				OC::$SUBURI = OC::$SUBURI . 'index.php';
175
-			}
176
-		}
177
-
178
-
179
-		if (OC::$CLI) {
180
-			OC::$WEBROOT = self::$config->getValue('overwritewebroot', '');
181
-		} else {
182
-			if (substr($scriptName, 0 - strlen(OC::$SUBURI)) === OC::$SUBURI) {
183
-				OC::$WEBROOT = substr($scriptName, 0, 0 - strlen(OC::$SUBURI));
184
-
185
-				if (OC::$WEBROOT != '' && OC::$WEBROOT[0] !== '/') {
186
-					OC::$WEBROOT = '/' . OC::$WEBROOT;
187
-				}
188
-			} else {
189
-				// The scriptName is not ending with OC::$SUBURI
190
-				// This most likely means that we are calling from CLI.
191
-				// However some cron jobs still need to generate
192
-				// a web URL, so we use overwritewebroot as a fallback.
193
-				OC::$WEBROOT = self::$config->getValue('overwritewebroot', '');
194
-			}
195
-
196
-			// Resolve /nextcloud to /nextcloud/ to ensure to always have a trailing
197
-			// slash which is required by URL generation.
198
-			if (isset($_SERVER['REQUEST_URI']) && $_SERVER['REQUEST_URI'] === \OC::$WEBROOT &&
199
-					substr($_SERVER['REQUEST_URI'], -1) !== '/') {
200
-				header('Location: '.\OC::$WEBROOT.'/');
201
-				exit();
202
-			}
203
-		}
204
-
205
-		// search the apps folder
206
-		$config_paths = self::$config->getValue('apps_paths', []);
207
-		if (!empty($config_paths)) {
208
-			foreach ($config_paths as $paths) {
209
-				if (isset($paths['url']) && isset($paths['path'])) {
210
-					$paths['url'] = rtrim($paths['url'], '/');
211
-					$paths['path'] = rtrim($paths['path'], '/');
212
-					OC::$APPSROOTS[] = $paths;
213
-				}
214
-			}
215
-		} elseif (file_exists(OC::$SERVERROOT . '/apps')) {
216
-			OC::$APPSROOTS[] = ['path' => OC::$SERVERROOT . '/apps', 'url' => '/apps', 'writable' => true];
217
-		}
218
-
219
-		if (empty(OC::$APPSROOTS)) {
220
-			throw new \RuntimeException('apps directory not found! Please put the Nextcloud apps folder in the Nextcloud folder'
221
-				. '. You can also configure the location in the config.php file.');
222
-		}
223
-		$paths = [];
224
-		foreach (OC::$APPSROOTS as $path) {
225
-			$paths[] = $path['path'];
226
-			if (!is_dir($path['path'])) {
227
-				throw new \RuntimeException(sprintf('App directory "%s" not found! Please put the Nextcloud apps folder in the'
228
-					. ' Nextcloud folder. You can also configure the location in the config.php file.', $path['path']));
229
-			}
230
-		}
231
-
232
-		// set the right include path
233
-		set_include_path(
234
-			implode(PATH_SEPARATOR, $paths)
235
-		);
236
-	}
237
-
238
-	public static function checkConfig(): void {
239
-		$l = Server::get(\OCP\L10N\IFactory::class)->get('lib');
240
-
241
-		// Create config if it does not already exist
242
-		$configFilePath = self::$configDir .'/config.php';
243
-		if (!file_exists($configFilePath)) {
244
-			@touch($configFilePath);
245
-		}
246
-
247
-		// Check if config is writable
248
-		$configFileWritable = is_writable($configFilePath);
249
-		if (!$configFileWritable && !OC_Helper::isReadOnlyConfigEnabled()
250
-			|| !$configFileWritable && \OCP\Util::needUpgrade()) {
251
-			$urlGenerator = Server::get(IURLGenerator::class);
252
-
253
-			if (self::$CLI) {
254
-				echo $l->t('Cannot write into "config" directory!')."\n";
255
-				echo $l->t('This can usually be fixed by giving the web server write access to the config directory.')."\n";
256
-				echo "\n";
257
-				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";
258
-				echo $l->t('See %s', [ $urlGenerator->linkToDocs('admin-config') ])."\n";
259
-				exit;
260
-			} else {
261
-				OC_Template::printErrorPage(
262
-					$l->t('Cannot write into "config" directory!'),
263
-					$l->t('This can usually be fixed by giving the web server write access to the config directory.') . ' '
264
-					. $l->t('But, if you prefer to keep config.php file read only, set the option "config_is_read_only" to true in it.') . ' '
265
-					. $l->t('See %s', [ $urlGenerator->linkToDocs('admin-config') ]),
266
-					503
267
-				);
268
-			}
269
-		}
270
-	}
271
-
272
-	public static function checkInstalled(\OC\SystemConfig $systemConfig): void {
273
-		if (defined('OC_CONSOLE')) {
274
-			return;
275
-		}
276
-		// Redirect to installer if not installed
277
-		if (!$systemConfig->getValue('installed', false) && OC::$SUBURI !== '/index.php' && OC::$SUBURI !== '/status.php') {
278
-			if (OC::$CLI) {
279
-				throw new Exception('Not installed');
280
-			} else {
281
-				$url = OC::$WEBROOT . '/index.php';
282
-				header('Location: ' . $url);
283
-			}
284
-			exit();
285
-		}
286
-	}
287
-
288
-	public static function checkMaintenanceMode(\OC\SystemConfig $systemConfig): void {
289
-		// Allow ajax update script to execute without being stopped
290
-		if (((bool) $systemConfig->getValue('maintenance', false)) && OC::$SUBURI != '/core/ajax/update.php') {
291
-			// send http status 503
292
-			http_response_code(503);
293
-			header('X-Nextcloud-Maintenance-Mode: 1');
294
-			header('Retry-After: 120');
295
-
296
-			// render error page
297
-			$template = new OC_Template('', 'update.user', 'guest');
298
-			\OCP\Util::addScript('core', 'maintenance');
299
-			\OCP\Util::addStyle('core', 'guest');
300
-			$template->printPage();
301
-			die();
302
-		}
303
-	}
304
-
305
-	/**
306
-	 * Prints the upgrade page
307
-	 */
308
-	private static function printUpgradePage(\OC\SystemConfig $systemConfig): void {
309
-		$disableWebUpdater = $systemConfig->getValue('upgrade.disable-web', false);
310
-		$tooBig = false;
311
-		if (!$disableWebUpdater) {
312
-			$apps = Server::get(\OCP\App\IAppManager::class);
313
-			if ($apps->isInstalled('user_ldap')) {
314
-				$qb = Server::get(\OCP\IDBConnection::class)->getQueryBuilder();
315
-
316
-				$result = $qb->select($qb->func()->count('*', 'user_count'))
317
-					->from('ldap_user_mapping')
318
-					->executeQuery();
319
-				$row = $result->fetch();
320
-				$result->closeCursor();
321
-
322
-				$tooBig = ($row['user_count'] > 50);
323
-			}
324
-			if (!$tooBig && $apps->isInstalled('user_saml')) {
325
-				$qb = Server::get(\OCP\IDBConnection::class)->getQueryBuilder();
326
-
327
-				$result = $qb->select($qb->func()->count('*', 'user_count'))
328
-					->from('user_saml_users')
329
-					->executeQuery();
330
-				$row = $result->fetch();
331
-				$result->closeCursor();
332
-
333
-				$tooBig = ($row['user_count'] > 50);
334
-			}
335
-			if (!$tooBig) {
336
-				// count users
337
-				$stats = Server::get(\OCP\IUserManager::class)->countUsers();
338
-				$totalUsers = array_sum($stats);
339
-				$tooBig = ($totalUsers > 50);
340
-			}
341
-		}
342
-		$ignoreTooBigWarning = isset($_GET['IKnowThatThisIsABigInstanceAndTheUpdateRequestCouldRunIntoATimeoutAndHowToRestoreABackup']) &&
343
-			$_GET['IKnowThatThisIsABigInstanceAndTheUpdateRequestCouldRunIntoATimeoutAndHowToRestoreABackup'] === 'IAmSuperSureToDoThis';
344
-
345
-		if ($disableWebUpdater || ($tooBig && !$ignoreTooBigWarning)) {
346
-			// send http status 503
347
-			http_response_code(503);
348
-			header('Retry-After: 120');
349
-
350
-			// render error page
351
-			$template = new OC_Template('', 'update.use-cli', 'guest');
352
-			$template->assign('productName', 'nextcloud'); // for now
353
-			$template->assign('version', OC_Util::getVersionString());
354
-			$template->assign('tooBig', $tooBig);
355
-
356
-			$template->printPage();
357
-			die();
358
-		}
359
-
360
-		// check whether this is a core update or apps update
361
-		$installedVersion = $systemConfig->getValue('version', '0.0.0');
362
-		$currentVersion = implode('.', \OCP\Util::getVersion());
363
-
364
-		// if not a core upgrade, then it's apps upgrade
365
-		$isAppsOnlyUpgrade = version_compare($currentVersion, $installedVersion, '=');
366
-
367
-		$oldTheme = $systemConfig->getValue('theme');
368
-		$systemConfig->setValue('theme', '');
369
-		\OCP\Util::addScript('core', 'common');
370
-		\OCP\Util::addScript('core', 'main');
371
-		\OCP\Util::addTranslations('core');
372
-		\OCP\Util::addScript('core', 'update');
373
-
374
-		/** @var \OC\App\AppManager $appManager */
375
-		$appManager = Server::get(\OCP\App\IAppManager::class);
376
-
377
-		$tmpl = new OC_Template('', 'update.admin', 'guest');
378
-		$tmpl->assign('version', OC_Util::getVersionString());
379
-		$tmpl->assign('isAppsOnlyUpgrade', $isAppsOnlyUpgrade);
380
-
381
-		// get third party apps
382
-		$ocVersion = \OCP\Util::getVersion();
383
-		$ocVersion = implode('.', $ocVersion);
384
-		$incompatibleApps = $appManager->getIncompatibleApps($ocVersion);
385
-		$incompatibleShippedApps = [];
386
-		foreach ($incompatibleApps as $appInfo) {
387
-			if ($appManager->isShipped($appInfo['id'])) {
388
-				$incompatibleShippedApps[] = $appInfo['name'] . ' (' . $appInfo['id'] . ')';
389
-			}
390
-		}
391
-
392
-		if (!empty($incompatibleShippedApps)) {
393
-			$l = Server::get(\OCP\L10N\IFactory::class)->get('core');
394
-			$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)]);
395
-			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);
396
-		}
397
-
398
-		$tmpl->assign('appsToUpgrade', $appManager->getAppsNeedingUpgrade($ocVersion));
399
-		$tmpl->assign('incompatibleAppsList', $incompatibleApps);
400
-		try {
401
-			$defaults = new \OC_Defaults();
402
-			$tmpl->assign('productName', $defaults->getName());
403
-		} catch (Throwable $error) {
404
-			$tmpl->assign('productName', 'Nextcloud');
405
-		}
406
-		$tmpl->assign('oldTheme', $oldTheme);
407
-		$tmpl->printPage();
408
-	}
409
-
410
-	public static function initSession(): void {
411
-		if (Server::get(\OCP\IRequest::class)->getServerProtocol() === 'https') {
412
-			ini_set('session.cookie_secure', 'true');
413
-		}
414
-
415
-		// prevents javascript from accessing php session cookies
416
-		ini_set('session.cookie_httponly', 'true');
417
-
418
-		// set the cookie path to the Nextcloud directory
419
-		$cookie_path = OC::$WEBROOT ? : '/';
420
-		ini_set('session.cookie_path', $cookie_path);
421
-
422
-		// Let the session name be changed in the initSession Hook
423
-		$sessionName = OC_Util::getInstanceId();
424
-
425
-		try {
426
-			// set the session name to the instance id - which is unique
427
-			$session = new \OC\Session\Internal($sessionName);
428
-
429
-			$cryptoWrapper = Server::get(\OC\Session\CryptoWrapper::class);
430
-			$session = $cryptoWrapper->wrapSession($session);
431
-			self::$server->setSession($session);
432
-
433
-			// if session can't be started break with http 500 error
434
-		} catch (Exception $e) {
435
-			Server::get(LoggerInterface::class)->error($e->getMessage(), ['app' => 'base','exception' => $e]);
436
-			//show the user a detailed error page
437
-			OC_Template::printExceptionErrorPage($e, 500);
438
-			die();
439
-		}
440
-
441
-		//try to set the session lifetime
442
-		$sessionLifeTime = self::getSessionLifeTime();
443
-		@ini_set('gc_maxlifetime', (string)$sessionLifeTime);
444
-
445
-		// session timeout
446
-		if ($session->exists('LAST_ACTIVITY') && (time() - $session->get('LAST_ACTIVITY') > $sessionLifeTime)) {
447
-			if (isset($_COOKIE[session_name()])) {
448
-				setcookie(session_name(), '', -1, self::$WEBROOT ? : '/');
449
-			}
450
-			Server::get(IUserSession::class)->logout();
451
-		}
452
-
453
-		if (!self::hasSessionRelaxedExpiry()) {
454
-			$session->set('LAST_ACTIVITY', time());
455
-		}
456
-		$session->close();
457
-	}
458
-
459
-	private static function getSessionLifeTime(): int {
460
-		return Server::get(\OC\AllConfig::class)->getSystemValueInt('session_lifetime', 60 * 60 * 24);
461
-	}
462
-
463
-	/**
464
-	 * @return bool true if the session expiry should only be done by gc instead of an explicit timeout
465
-	 */
466
-	public static function hasSessionRelaxedExpiry(): bool {
467
-		return Server::get(\OC\AllConfig::class)->getSystemValueBool('session_relaxed_expiry', false);
468
-	}
469
-
470
-	/**
471
-	 * Try to set some values to the required Nextcloud default
472
-	 */
473
-	public static function setRequiredIniValues(): void {
474
-		@ini_set('default_charset', 'UTF-8');
475
-		@ini_set('gd.jpeg_ignore_warning', '1');
476
-	}
477
-
478
-	/**
479
-	 * Send the same site cookies
480
-	 */
481
-	private static function sendSameSiteCookies(): void {
482
-		$cookieParams = session_get_cookie_params();
483
-		$secureCookie = ($cookieParams['secure'] === true) ? 'secure; ' : '';
484
-		$policies = [
485
-			'lax',
486
-			'strict',
487
-		];
488
-
489
-		// Append __Host to the cookie if it meets the requirements
490
-		$cookiePrefix = '';
491
-		if ($cookieParams['secure'] === true && $cookieParams['path'] === '/') {
492
-			$cookiePrefix = '__Host-';
493
-		}
494
-
495
-		foreach ($policies as $policy) {
496
-			header(
497
-				sprintf(
498
-					'Set-Cookie: %snc_sameSiteCookie%s=true; path=%s; httponly;' . $secureCookie . 'expires=Fri, 31-Dec-2100 23:59:59 GMT; SameSite=%s',
499
-					$cookiePrefix,
500
-					$policy,
501
-					$cookieParams['path'],
502
-					$policy
503
-				),
504
-				false
505
-			);
506
-		}
507
-	}
508
-
509
-	/**
510
-	 * Same Site cookie to further mitigate CSRF attacks. This cookie has to
511
-	 * be set in every request if cookies are sent to add a second level of
512
-	 * defense against CSRF.
513
-	 *
514
-	 * If the cookie is not sent this will set the cookie and reload the page.
515
-	 * We use an additional cookie since we want to protect logout CSRF and
516
-	 * also we can't directly interfere with PHP's session mechanism.
517
-	 */
518
-	private static function performSameSiteCookieProtection(\OCP\IConfig $config): void {
519
-		$request = Server::get(\OCP\IRequest::class);
520
-
521
-		// Some user agents are notorious and don't really properly follow HTTP
522
-		// specifications. For those, have an automated opt-out. Since the protection
523
-		// for remote.php is applied in base.php as starting point we need to opt out
524
-		// here.
525
-		$incompatibleUserAgents = $config->getSystemValue('csrf.optout');
526
-
527
-		// Fallback, if csrf.optout is unset
528
-		if (!is_array($incompatibleUserAgents)) {
529
-			$incompatibleUserAgents = [
530
-				// OS X Finder
531
-				'/^WebDAVFS/',
532
-				// Windows webdav drive
533
-				'/^Microsoft-WebDAV-MiniRedir/',
534
-			];
535
-		}
536
-
537
-		if ($request->isUserAgent($incompatibleUserAgents)) {
538
-			return;
539
-		}
540
-
541
-		if (count($_COOKIE) > 0) {
542
-			$requestUri = $request->getScriptName();
543
-			$processingScript = explode('/', $requestUri);
544
-			$processingScript = $processingScript[count($processingScript) - 1];
545
-
546
-			// index.php routes are handled in the middleware
547
-			if ($processingScript === 'index.php') {
548
-				return;
549
-			}
550
-
551
-			// All other endpoints require the lax and the strict cookie
552
-			if (!$request->passesStrictCookieCheck()) {
553
-				self::sendSameSiteCookies();
554
-				// Debug mode gets access to the resources without strict cookie
555
-				// due to the fact that the SabreDAV browser also lives there.
556
-				if (!$config->getSystemValue('debug', false)) {
557
-					http_response_code(\OCP\AppFramework\Http::STATUS_SERVICE_UNAVAILABLE);
558
-					exit();
559
-				}
560
-			}
561
-		} elseif (!isset($_COOKIE['nc_sameSiteCookielax']) || !isset($_COOKIE['nc_sameSiteCookiestrict'])) {
562
-			self::sendSameSiteCookies();
563
-		}
564
-	}
565
-
566
-	public static function init(): void {
567
-		// calculate the root directories
568
-		OC::$SERVERROOT = str_replace("\\", '/', substr(__DIR__, 0, -4));
569
-
570
-		// register autoloader
571
-		$loaderStart = microtime(true);
572
-		require_once __DIR__ . '/autoloader.php';
573
-		self::$loader = new \OC\Autoloader([
574
-			OC::$SERVERROOT . '/lib/private/legacy',
575
-		]);
576
-		if (defined('PHPUNIT_RUN')) {
577
-			self::$loader->addValidRoot(OC::$SERVERROOT . '/tests');
578
-		}
579
-		spl_autoload_register([self::$loader, 'load']);
580
-		$loaderEnd = microtime(true);
581
-
582
-		self::$CLI = (php_sapi_name() == 'cli');
583
-
584
-		// Add default composer PSR-4 autoloader
585
-		self::$composerAutoloader = require_once OC::$SERVERROOT . '/lib/composer/autoload.php';
586
-		self::$composerAutoloader->setApcuPrefix('composer_autoload');
587
-
588
-		try {
589
-			self::initPaths();
590
-			// setup 3rdparty autoloader
591
-			$vendorAutoLoad = OC::$SERVERROOT. '/3rdparty/autoload.php';
592
-			if (!file_exists($vendorAutoLoad)) {
593
-				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".');
594
-			}
595
-			require_once $vendorAutoLoad;
596
-		} catch (\RuntimeException $e) {
597
-			if (!self::$CLI) {
598
-				http_response_code(503);
599
-			}
600
-			// we can't use the template error page here, because this needs the
601
-			// DI container which isn't available yet
602
-			print($e->getMessage());
603
-			exit();
604
-		}
605
-
606
-		// setup the basic server
607
-		self::$server = new \OC\Server(\OC::$WEBROOT, self::$config);
608
-		self::$server->boot();
609
-
610
-		$eventLogger = Server::get(\OCP\Diagnostics\IEventLogger::class);
611
-		$eventLogger->log('autoloader', 'Autoloader', $loaderStart, $loaderEnd);
612
-		$eventLogger->start('boot', 'Initialize');
613
-
614
-		// Override php.ini and log everything if we're troubleshooting
615
-		if (self::$config->getValue('loglevel') === ILogger::DEBUG) {
616
-			error_reporting(E_ALL);
617
-		}
618
-
619
-		// Don't display errors and log them
620
-		@ini_set('display_errors', '0');
621
-		@ini_set('log_errors', '1');
622
-
623
-		if (!date_default_timezone_set('UTC')) {
624
-			throw new \RuntimeException('Could not set timezone to UTC');
625
-		}
626
-
627
-
628
-		//try to configure php to enable big file uploads.
629
-		//this doesn´t work always depending on the webserver and php configuration.
630
-		//Let´s try to overwrite some defaults if they are smaller than 1 hour
631
-
632
-		if (intval(@ini_get('max_execution_time') ?? 0) < 3600) {
633
-			@ini_set('max_execution_time', strval(3600));
634
-		}
635
-
636
-		if (intval(@ini_get('max_input_time') ?? 0) < 3600) {
637
-			@ini_set('max_input_time', strval(3600));
638
-		}
639
-
640
-		//try to set the maximum execution time to the largest time limit we have
641
-		if (strpos(@ini_get('disable_functions'), 'set_time_limit') === false) {
642
-			@set_time_limit(max(intval(@ini_get('max_execution_time')), intval(@ini_get('max_input_time'))));
643
-		}
644
-
645
-		self::setRequiredIniValues();
646
-		self::handleAuthHeaders();
647
-		$systemConfig = Server::get(\OC\SystemConfig::class);
648
-		self::registerAutoloaderCache($systemConfig);
649
-
650
-		// initialize intl fallback if necessary
651
-		OC_Util::isSetLocaleWorking();
652
-
653
-		$config = Server::get(\OCP\IConfig::class);
654
-		if (!defined('PHPUNIT_RUN')) {
655
-			$errorHandler = new OC\Log\ErrorHandler(
656
-				\OCP\Server::get(\Psr\Log\LoggerInterface::class),
657
-			);
658
-			$exceptionHandler = [$errorHandler, 'onException'];
659
-			if ($config->getSystemValue('debug', false)) {
660
-				set_error_handler([$errorHandler, 'onAll'], E_ALL);
661
-				if (\OC::$CLI) {
662
-					$exceptionHandler = ['OC_Template', 'printExceptionErrorPage'];
663
-				}
664
-			} else {
665
-				set_error_handler([$errorHandler, 'onError']);
666
-			}
667
-			register_shutdown_function([$errorHandler, 'onShutdown']);
668
-			set_exception_handler($exceptionHandler);
669
-		}
670
-
671
-		/** @var \OC\AppFramework\Bootstrap\Coordinator $bootstrapCoordinator */
672
-		$bootstrapCoordinator = Server::get(\OC\AppFramework\Bootstrap\Coordinator::class);
673
-		$bootstrapCoordinator->runInitialRegistration();
674
-
675
-		$eventLogger->start('init_session', 'Initialize session');
676
-		OC_App::loadApps(['session']);
677
-		if (!self::$CLI) {
678
-			self::initSession();
679
-		}
680
-		$eventLogger->end('init_session');
681
-		self::checkConfig();
682
-		self::checkInstalled($systemConfig);
683
-
684
-		OC_Response::addSecurityHeaders();
685
-
686
-		self::performSameSiteCookieProtection($config);
687
-
688
-		if (!defined('OC_CONSOLE')) {
689
-			$errors = OC_Util::checkServer($systemConfig);
690
-			if (count($errors) > 0) {
691
-				if (!self::$CLI) {
692
-					http_response_code(503);
693
-					OC_Util::addStyle('guest');
694
-					try {
695
-						OC_Template::printGuestPage('', 'error', ['errors' => $errors]);
696
-						exit;
697
-					} catch (\Exception $e) {
698
-						// In case any error happens when showing the error page, we simply fall back to posting the text.
699
-						// This might be the case when e.g. the data directory is broken and we can not load/write SCSS to/from it.
700
-					}
701
-				}
702
-
703
-				// Convert l10n string into regular string for usage in database
704
-				$staticErrors = [];
705
-				foreach ($errors as $error) {
706
-					echo $error['error'] . "\n";
707
-					echo $error['hint'] . "\n\n";
708
-					$staticErrors[] = [
709
-						'error' => (string)$error['error'],
710
-						'hint' => (string)$error['hint'],
711
-					];
712
-				}
713
-
714
-				try {
715
-					$config->setAppValue('core', 'cronErrors', json_encode($staticErrors));
716
-				} catch (\Exception $e) {
717
-					echo('Writing to database failed');
718
-				}
719
-				exit(1);
720
-			} elseif (self::$CLI && $config->getSystemValue('installed', false)) {
721
-				$config->deleteAppValue('core', 'cronErrors');
722
-			}
723
-		}
724
-
725
-		// User and Groups
726
-		if (!$systemConfig->getValue("installed", false)) {
727
-			self::$server->getSession()->set('user_id', '');
728
-		}
729
-
730
-		OC_User::useBackend(new \OC\User\Database());
731
-		Server::get(\OCP\IGroupManager::class)->addBackend(new \OC\Group\Database());
732
-
733
-		// Subscribe to the hook
734
-		\OCP\Util::connectHook(
735
-			'\OCA\Files_Sharing\API\Server2Server',
736
-			'preLoginNameUsedAsUserName',
737
-			'\OC\User\Database',
738
-			'preLoginNameUsedAsUserName'
739
-		);
740
-
741
-		//setup extra user backends
742
-		if (!\OCP\Util::needUpgrade()) {
743
-			OC_User::setupBackends();
744
-		} else {
745
-			// Run upgrades in incognito mode
746
-			OC_User::setIncognitoMode(true);
747
-		}
748
-
749
-		self::registerCleanupHooks($systemConfig);
750
-		self::registerShareHooks($systemConfig);
751
-		self::registerEncryptionWrapperAndHooks();
752
-		self::registerAccountHooks();
753
-		self::registerResourceCollectionHooks();
754
-		self::registerFileReferenceEventListener();
755
-		self::registerAppRestrictionsHooks();
756
-
757
-		// Make sure that the application class is not loaded before the database is setup
758
-		if ($systemConfig->getValue("installed", false)) {
759
-			OC_App::loadApp('settings');
760
-			/* Build core application to make sure that listeners are registered */
761
-			Server::get(\OC\Core\Application::class);
762
-		}
763
-
764
-		//make sure temporary files are cleaned up
765
-		$tmpManager = Server::get(\OCP\ITempManager::class);
766
-		register_shutdown_function([$tmpManager, 'clean']);
767
-		$lockProvider = Server::get(\OCP\Lock\ILockingProvider::class);
768
-		register_shutdown_function([$lockProvider, 'releaseAll']);
769
-
770
-		// Check whether the sample configuration has been copied
771
-		if ($systemConfig->getValue('copied_sample_config', false)) {
772
-			$l = Server::get(\OCP\L10N\IFactory::class)->get('lib');
773
-			OC_Template::printErrorPage(
774
-				$l->t('Sample configuration detected'),
775
-				$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'),
776
-				503
777
-			);
778
-			return;
779
-		}
780
-
781
-		$request = Server::get(\OCP\IRequest::class);
782
-		$host = $request->getInsecureServerHost();
783
-		/**
784
-		 * if the host passed in headers isn't trusted
785
-		 * FIXME: Should not be in here at all :see_no_evil:
786
-		 */
787
-		if (!OC::$CLI
788
-			&& !Server::get(\OC\Security\TrustedDomainHelper::class)->isTrustedDomain($host)
789
-			&& $config->getSystemValue('installed', false)
790
-		) {
791
-			// Allow access to CSS resources
792
-			$isScssRequest = false;
793
-			if (strpos($request->getPathInfo() ?: '', '/css/') === 0) {
794
-				$isScssRequest = true;
795
-			}
796
-
797
-			if (substr($request->getRequestUri(), -11) === '/status.php') {
798
-				http_response_code(400);
799
-				header('Content-Type: application/json');
800
-				echo '{"error": "Trusted domain error.", "code": 15}';
801
-				exit();
802
-			}
803
-
804
-			if (!$isScssRequest) {
805
-				http_response_code(400);
806
-				Server::get(LoggerInterface::class)->info(
807
-					'Trusted domain error. "{remoteAddress}" tried to access using "{host}" as host.',
808
-					[
809
-						'app' => 'core',
810
-						'remoteAddress' => $request->getRemoteAddress(),
811
-						'host' => $host,
812
-					]
813
-				);
814
-
815
-				$tmpl = new OCP\Template('core', 'untrustedDomain', 'guest');
816
-				$tmpl->assign('docUrl', Server::get(IURLGenerator::class)->linkToDocs('admin-trusted-domains'));
817
-				$tmpl->printPage();
818
-
819
-				exit();
820
-			}
821
-		}
822
-		$eventLogger->end('boot');
823
-		$eventLogger->log('init', 'OC::init', $loaderStart, microtime(true));
824
-		$eventLogger->start('runtime', 'Runtime');
825
-		$eventLogger->start('request', 'Full request after boot');
826
-		register_shutdown_function(function () use ($eventLogger) {
827
-			$eventLogger->end('request');
828
-		});
829
-	}
830
-
831
-	/**
832
-	 * register hooks for the cleanup of cache and bruteforce protection
833
-	 */
834
-	public static function registerCleanupHooks(\OC\SystemConfig $systemConfig): void {
835
-		//don't try to do this before we are properly setup
836
-		if ($systemConfig->getValue('installed', false) && !\OCP\Util::needUpgrade()) {
837
-			// NOTE: This will be replaced to use OCP
838
-			$userSession = Server::get(\OC\User\Session::class);
839
-			$userSession->listen('\OC\User', 'postLogin', function () use ($userSession) {
840
-				if (!defined('PHPUNIT_RUN') && $userSession->isLoggedIn()) {
841
-					// reset brute force delay for this IP address and username
842
-					$uid = $userSession->getUser()->getUID();
843
-					$request = Server::get(\OCP\IRequest::class);
844
-					$throttler = Server::get(\OC\Security\Bruteforce\Throttler::class);
845
-					$throttler->resetDelay($request->getRemoteAddress(), 'login', ['user' => $uid]);
846
-				}
847
-
848
-				try {
849
-					$cache = new \OC\Cache\File();
850
-					$cache->gc();
851
-				} catch (\OC\ServerNotAvailableException $e) {
852
-					// not a GC exception, pass it on
853
-					throw $e;
854
-				} catch (\OC\ForbiddenException $e) {
855
-					// filesystem blocked for this request, ignore
856
-				} catch (\Exception $e) {
857
-					// a GC exception should not prevent users from using OC,
858
-					// so log the exception
859
-					Server::get(LoggerInterface::class)->warning('Exception when running cache gc.', [
860
-						'app' => 'core',
861
-						'exception' => $e,
862
-					]);
863
-				}
864
-			});
865
-		}
866
-	}
867
-
868
-	private static function registerEncryptionWrapperAndHooks(): void {
869
-		$manager = Server::get(\OCP\Encryption\IManager::class);
870
-		\OCP\Util::connectHook('OC_Filesystem', 'preSetup', $manager, 'setupStorage');
871
-
872
-		$enabled = $manager->isEnabled();
873
-		if ($enabled) {
874
-			\OCP\Util::connectHook(Share::class, 'post_shared', HookManager::class, 'postShared');
875
-			\OCP\Util::connectHook(Share::class, 'post_unshare', HookManager::class, 'postUnshared');
876
-			\OCP\Util::connectHook('OC_Filesystem', 'post_rename', HookManager::class, 'postRename');
877
-			\OCP\Util::connectHook('\OCA\Files_Trashbin\Trashbin', 'post_restore', HookManager::class, 'postRestore');
878
-		}
879
-	}
880
-
881
-	private static function registerAccountHooks(): void {
882
-		/** @var IEventDispatcher $dispatcher */
883
-		$dispatcher = Server::get(IEventDispatcher::class);
884
-		$dispatcher->addServiceListener(UserChangedEvent::class, \OC\Accounts\Hooks::class);
885
-	}
886
-
887
-	private static function registerAppRestrictionsHooks(): void {
888
-		/** @var \OC\Group\Manager $groupManager */
889
-		$groupManager = Server::get(\OCP\IGroupManager::class);
890
-		$groupManager->listen('\OC\Group', 'postDelete', function (\OCP\IGroup $group) {
891
-			$appManager = Server::get(\OCP\App\IAppManager::class);
892
-			$apps = $appManager->getEnabledAppsForGroup($group);
893
-			foreach ($apps as $appId) {
894
-				$restrictions = $appManager->getAppRestriction($appId);
895
-				if (empty($restrictions)) {
896
-					continue;
897
-				}
898
-				$key = array_search($group->getGID(), $restrictions);
899
-				unset($restrictions[$key]);
900
-				$restrictions = array_values($restrictions);
901
-				if (empty($restrictions)) {
902
-					$appManager->disableApp($appId);
903
-				} else {
904
-					$appManager->enableAppForGroups($appId, $restrictions);
905
-				}
906
-			}
907
-		});
908
-	}
909
-
910
-	private static function registerResourceCollectionHooks(): void {
911
-		\OC\Collaboration\Resources\Listener::register(Server::get(SymfonyAdapter::class), Server::get(IEventDispatcher::class));
912
-	}
913
-
914
-	private static function registerFileReferenceEventListener(): void {
915
-		\OC\Collaboration\Reference\File\FileReferenceEventListener::register(Server::get(IEventDispatcher::class));
916
-	}
917
-
918
-	/**
919
-	 * register hooks for sharing
920
-	 */
921
-	public static function registerShareHooks(\OC\SystemConfig $systemConfig): void {
922
-		if ($systemConfig->getValue('installed')) {
923
-			OC_Hook::connect('OC_User', 'post_deleteUser', Hooks::class, 'post_deleteUser');
924
-			OC_Hook::connect('OC_User', 'post_deleteGroup', Hooks::class, 'post_deleteGroup');
925
-
926
-			/** @var IEventDispatcher $dispatcher */
927
-			$dispatcher = Server::get(IEventDispatcher::class);
928
-			$dispatcher->addServiceListener(UserRemovedEvent::class, \OC\Share20\UserRemovedListener::class);
929
-		}
930
-	}
931
-
932
-	protected static function registerAutoloaderCache(\OC\SystemConfig $systemConfig): void {
933
-		// The class loader takes an optional low-latency cache, which MUST be
934
-		// namespaced. The instanceid is used for namespacing, but might be
935
-		// unavailable at this point. Furthermore, it might not be possible to
936
-		// generate an instanceid via \OC_Util::getInstanceId() because the
937
-		// config file may not be writable. As such, we only register a class
938
-		// loader cache if instanceid is available without trying to create one.
939
-		$instanceId = $systemConfig->getValue('instanceid', null);
940
-		if ($instanceId) {
941
-			try {
942
-				$memcacheFactory = Server::get(\OCP\ICacheFactory::class);
943
-				self::$loader->setMemoryCache($memcacheFactory->createLocal('Autoloader'));
944
-			} catch (\Exception $ex) {
945
-			}
946
-		}
947
-	}
948
-
949
-	/**
950
-	 * Handle the request
951
-	 */
952
-	public static function handleRequest(): void {
953
-		Server::get(\OCP\Diagnostics\IEventLogger::class)->start('handle_request', 'Handle request');
954
-		$systemConfig = Server::get(\OC\SystemConfig::class);
955
-
956
-		// Check if Nextcloud is installed or in maintenance (update) mode
957
-		if (!$systemConfig->getValue('installed', false)) {
958
-			\OC::$server->getSession()->clear();
959
-			$setupHelper = new OC\Setup(
960
-				$systemConfig,
961
-				Server::get(\bantu\IniGetWrapper\IniGetWrapper::class),
962
-				Server::get(\OCP\L10N\IFactory::class)->get('lib'),
963
-				Server::get(\OCP\Defaults::class),
964
-				Server::get(\Psr\Log\LoggerInterface::class),
965
-				Server::get(\OCP\Security\ISecureRandom::class),
966
-				Server::get(\OC\Installer::class)
967
-			);
968
-			$controller = new OC\Core\Controller\SetupController($setupHelper);
969
-			$controller->run($_POST);
970
-			exit();
971
-		}
972
-
973
-		$request = Server::get(\OCP\IRequest::class);
974
-		$requestPath = $request->getRawPathInfo();
975
-		if ($requestPath === '/heartbeat') {
976
-			return;
977
-		}
978
-		if (substr($requestPath, -3) !== '.js') { // we need these files during the upgrade
979
-			self::checkMaintenanceMode($systemConfig);
980
-
981
-			if (\OCP\Util::needUpgrade()) {
982
-				if (function_exists('opcache_reset')) {
983
-					opcache_reset();
984
-				}
985
-				if (!((bool) $systemConfig->getValue('maintenance', false))) {
986
-					self::printUpgradePage($systemConfig);
987
-					exit();
988
-				}
989
-			}
990
-		}
991
-
992
-		// emergency app disabling
993
-		if ($requestPath === '/disableapp'
994
-			&& $request->getMethod() === 'POST'
995
-		) {
996
-			\OC_JSON::callCheck();
997
-			\OC_JSON::checkAdminUser();
998
-			$appIds = (array)$request->getParam('appid');
999
-			foreach ($appIds as $appId) {
1000
-				$appId = \OC_App::cleanAppId($appId);
1001
-				Server::get(\OCP\App\IAppManager::class)->disableApp($appId);
1002
-			}
1003
-			\OC_JSON::success();
1004
-			exit();
1005
-		}
1006
-
1007
-		// Always load authentication apps
1008
-		OC_App::loadApps(['authentication']);
1009
-
1010
-		// Load minimum set of apps
1011
-		if (!\OCP\Util::needUpgrade()
1012
-			&& !((bool) $systemConfig->getValue('maintenance', false))) {
1013
-			// For logged-in users: Load everything
1014
-			if (Server::get(IUserSession::class)->isLoggedIn()) {
1015
-				OC_App::loadApps();
1016
-			} else {
1017
-				// For guests: Load only filesystem and logging
1018
-				OC_App::loadApps(['filesystem', 'logging']);
1019
-
1020
-				// Don't try to login when a client is trying to get a OAuth token.
1021
-				// OAuth needs to support basic auth too, so the login is not valid
1022
-				// inside Nextcloud and the Login exception would ruin it.
1023
-				if ($request->getRawPathInfo() !== '/apps/oauth2/api/v1/token') {
1024
-					self::handleLogin($request);
1025
-				}
1026
-			}
1027
-		}
1028
-
1029
-		if (!self::$CLI) {
1030
-			try {
1031
-				if (!((bool) $systemConfig->getValue('maintenance', false)) && !\OCP\Util::needUpgrade()) {
1032
-					OC_App::loadApps(['filesystem', 'logging']);
1033
-					OC_App::loadApps();
1034
-				}
1035
-				Server::get(\OC\Route\Router::class)->match($request->getRawPathInfo());
1036
-				return;
1037
-			} catch (Symfony\Component\Routing\Exception\ResourceNotFoundException $e) {
1038
-				//header('HTTP/1.0 404 Not Found');
1039
-			} catch (Symfony\Component\Routing\Exception\MethodNotAllowedException $e) {
1040
-				http_response_code(405);
1041
-				return;
1042
-			}
1043
-		}
1044
-
1045
-		// Handle WebDAV
1046
-		if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'PROPFIND') {
1047
-			// not allowed any more to prevent people
1048
-			// mounting this root directly.
1049
-			// Users need to mount remote.php/webdav instead.
1050
-			http_response_code(405);
1051
-			return;
1052
-		}
1053
-
1054
-		// Handle requests for JSON or XML
1055
-		$acceptHeader = $request->getHeader('Accept');
1056
-		if (in_array($acceptHeader, ['application/json', 'application/xml'], true)) {
1057
-			http_response_code(404);
1058
-			return;
1059
-		}
1060
-
1061
-		// Handle resources that can't be found
1062
-		// This prevents browsers from redirecting to the default page and then
1063
-		// attempting to parse HTML as CSS and similar.
1064
-		$destinationHeader = $request->getHeader('Sec-Fetch-Dest');
1065
-		if (in_array($destinationHeader, ['font', 'script', 'style'])) {
1066
-			http_response_code(404);
1067
-			return;
1068
-		}
1069
-
1070
-		// Redirect to the default app or login only as an entry point
1071
-		if ($requestPath === '') {
1072
-			// Someone is logged in
1073
-			if (Server::get(IUserSession::class)->isLoggedIn()) {
1074
-				header('Location: ' . Server::get(IURLGenerator::class)->linkToDefaultPageUrl());
1075
-			} else {
1076
-				// Not handled and not logged in
1077
-				header('Location: ' . Server::get(IURLGenerator::class)->linkToRouteAbsolute('core.login.showLoginForm'));
1078
-			}
1079
-			return;
1080
-		}
1081
-
1082
-		try {
1083
-			Server::get(\OC\Route\Router::class)->match('/error/404');
1084
-		} catch (\Exception $e) {
1085
-			logger('core')->emergency($e->getMessage(), ['exception' => $e]);
1086
-			$l = Server::get(\OCP\L10N\IFactory::class)->get('lib');
1087
-			OC_Template::printErrorPage(
1088
-				$l->t('404'),
1089
-				$l->t('The page could not be found on the server.'),
1090
-				404
1091
-			);
1092
-		}
1093
-	}
1094
-
1095
-	/**
1096
-	 * Check login: apache auth, auth token, basic auth
1097
-	 */
1098
-	public static function handleLogin(OCP\IRequest $request): bool {
1099
-		$userSession = Server::get(\OC\User\Session::class);
1100
-		if (OC_User::handleApacheAuth()) {
1101
-			return true;
1102
-		}
1103
-		if ($userSession->tryTokenLogin($request)) {
1104
-			return true;
1105
-		}
1106
-		if (isset($_COOKIE['nc_username'])
1107
-			&& isset($_COOKIE['nc_token'])
1108
-			&& isset($_COOKIE['nc_session_id'])
1109
-			&& $userSession->loginWithCookie($_COOKIE['nc_username'], $_COOKIE['nc_token'], $_COOKIE['nc_session_id'])) {
1110
-			return true;
1111
-		}
1112
-		if ($userSession->tryBasicAuthLogin($request, Server::get(\OC\Security\Bruteforce\Throttler::class))) {
1113
-			return true;
1114
-		}
1115
-		return false;
1116
-	}
1117
-
1118
-	protected static function handleAuthHeaders(): void {
1119
-		//copy http auth headers for apache+php-fcgid work around
1120
-		if (isset($_SERVER['HTTP_XAUTHORIZATION']) && !isset($_SERVER['HTTP_AUTHORIZATION'])) {
1121
-			$_SERVER['HTTP_AUTHORIZATION'] = $_SERVER['HTTP_XAUTHORIZATION'];
1122
-		}
1123
-
1124
-		// Extract PHP_AUTH_USER/PHP_AUTH_PW from other headers if necessary.
1125
-		$vars = [
1126
-			'HTTP_AUTHORIZATION', // apache+php-cgi work around
1127
-			'REDIRECT_HTTP_AUTHORIZATION', // apache+php-cgi alternative
1128
-		];
1129
-		foreach ($vars as $var) {
1130
-			if (isset($_SERVER[$var]) && is_string($_SERVER[$var]) && preg_match('/Basic\s+(.*)$/i', $_SERVER[$var], $matches)) {
1131
-				$credentials = explode(':', base64_decode($matches[1]), 2);
1132
-				if (count($credentials) === 2) {
1133
-					$_SERVER['PHP_AUTH_USER'] = $credentials[0];
1134
-					$_SERVER['PHP_AUTH_PW'] = $credentials[1];
1135
-					break;
1136
-				}
1137
-			}
1138
-		}
1139
-	}
92
+    /**
93
+     * Associative array for autoloading. classname => filename
94
+     */
95
+    public static array $CLASSPATH = [];
96
+    /**
97
+     * The installation path for Nextcloud  on the server (e.g. /srv/http/nextcloud)
98
+     */
99
+    public static string $SERVERROOT = '';
100
+    /**
101
+     * the current request path relative to the Nextcloud root (e.g. files/index.php)
102
+     */
103
+    private static string $SUBURI = '';
104
+    /**
105
+     * the Nextcloud root path for http requests (e.g. nextcloud/)
106
+     */
107
+    public static string $WEBROOT = '';
108
+    /**
109
+     * The installation path array of the apps folder on the server (e.g. /srv/http/nextcloud) 'path' and
110
+     * web path in 'url'
111
+     */
112
+    public static array $APPSROOTS = [];
113
+
114
+    public static string $configDir;
115
+
116
+    /**
117
+     * requested app
118
+     */
119
+    public static string $REQUESTEDAPP = '';
120
+
121
+    /**
122
+     * check if Nextcloud runs in cli mode
123
+     */
124
+    public static bool $CLI = false;
125
+
126
+    public static \OC\Autoloader $loader;
127
+
128
+    public static \Composer\Autoload\ClassLoader $composerAutoloader;
129
+
130
+    public static \OC\Server $server;
131
+
132
+    private static \OC\Config $config;
133
+
134
+    /**
135
+     * @throws \RuntimeException when the 3rdparty directory is missing or
136
+     * the app path list is empty or contains an invalid path
137
+     */
138
+    public static function initPaths(): void {
139
+        if (defined('PHPUNIT_CONFIG_DIR')) {
140
+            self::$configDir = OC::$SERVERROOT . '/' . PHPUNIT_CONFIG_DIR . '/';
141
+        } elseif (defined('PHPUNIT_RUN') and PHPUNIT_RUN and is_dir(OC::$SERVERROOT . '/tests/config/')) {
142
+            self::$configDir = OC::$SERVERROOT . '/tests/config/';
143
+        } elseif ($dir = getenv('NEXTCLOUD_CONFIG_DIR')) {
144
+            self::$configDir = rtrim($dir, '/') . '/';
145
+        } else {
146
+            self::$configDir = OC::$SERVERROOT . '/config/';
147
+        }
148
+        self::$config = new \OC\Config(self::$configDir);
149
+
150
+        OC::$SUBURI = str_replace("\\", "/", substr(realpath($_SERVER["SCRIPT_FILENAME"] ?? ''), strlen(OC::$SERVERROOT)));
151
+        /**
152
+         * FIXME: The following lines are required because we can't yet instantiate
153
+         *        Server::get(\OCP\IRequest::class) since \OC::$server does not yet exist.
154
+         */
155
+        $params = [
156
+            'server' => [
157
+                'SCRIPT_NAME' => $_SERVER['SCRIPT_NAME'] ?? null,
158
+                'SCRIPT_FILENAME' => $_SERVER['SCRIPT_FILENAME'] ?? null,
159
+            ],
160
+        ];
161
+        $fakeRequest = new \OC\AppFramework\Http\Request(
162
+            $params,
163
+            new \OC\AppFramework\Http\RequestId($_SERVER['UNIQUE_ID'] ?? '', new \OC\Security\SecureRandom()),
164
+            new \OC\AllConfig(new \OC\SystemConfig(self::$config))
165
+        );
166
+        $scriptName = $fakeRequest->getScriptName();
167
+        if (substr($scriptName, -1) == '/') {
168
+            $scriptName .= 'index.php';
169
+            //make sure suburi follows the same rules as scriptName
170
+            if (substr(OC::$SUBURI, -9) != 'index.php') {
171
+                if (substr(OC::$SUBURI, -1) != '/') {
172
+                    OC::$SUBURI = OC::$SUBURI . '/';
173
+                }
174
+                OC::$SUBURI = OC::$SUBURI . 'index.php';
175
+            }
176
+        }
177
+
178
+
179
+        if (OC::$CLI) {
180
+            OC::$WEBROOT = self::$config->getValue('overwritewebroot', '');
181
+        } else {
182
+            if (substr($scriptName, 0 - strlen(OC::$SUBURI)) === OC::$SUBURI) {
183
+                OC::$WEBROOT = substr($scriptName, 0, 0 - strlen(OC::$SUBURI));
184
+
185
+                if (OC::$WEBROOT != '' && OC::$WEBROOT[0] !== '/') {
186
+                    OC::$WEBROOT = '/' . OC::$WEBROOT;
187
+                }
188
+            } else {
189
+                // The scriptName is not ending with OC::$SUBURI
190
+                // This most likely means that we are calling from CLI.
191
+                // However some cron jobs still need to generate
192
+                // a web URL, so we use overwritewebroot as a fallback.
193
+                OC::$WEBROOT = self::$config->getValue('overwritewebroot', '');
194
+            }
195
+
196
+            // Resolve /nextcloud to /nextcloud/ to ensure to always have a trailing
197
+            // slash which is required by URL generation.
198
+            if (isset($_SERVER['REQUEST_URI']) && $_SERVER['REQUEST_URI'] === \OC::$WEBROOT &&
199
+                    substr($_SERVER['REQUEST_URI'], -1) !== '/') {
200
+                header('Location: '.\OC::$WEBROOT.'/');
201
+                exit();
202
+            }
203
+        }
204
+
205
+        // search the apps folder
206
+        $config_paths = self::$config->getValue('apps_paths', []);
207
+        if (!empty($config_paths)) {
208
+            foreach ($config_paths as $paths) {
209
+                if (isset($paths['url']) && isset($paths['path'])) {
210
+                    $paths['url'] = rtrim($paths['url'], '/');
211
+                    $paths['path'] = rtrim($paths['path'], '/');
212
+                    OC::$APPSROOTS[] = $paths;
213
+                }
214
+            }
215
+        } elseif (file_exists(OC::$SERVERROOT . '/apps')) {
216
+            OC::$APPSROOTS[] = ['path' => OC::$SERVERROOT . '/apps', 'url' => '/apps', 'writable' => true];
217
+        }
218
+
219
+        if (empty(OC::$APPSROOTS)) {
220
+            throw new \RuntimeException('apps directory not found! Please put the Nextcloud apps folder in the Nextcloud folder'
221
+                . '. You can also configure the location in the config.php file.');
222
+        }
223
+        $paths = [];
224
+        foreach (OC::$APPSROOTS as $path) {
225
+            $paths[] = $path['path'];
226
+            if (!is_dir($path['path'])) {
227
+                throw new \RuntimeException(sprintf('App directory "%s" not found! Please put the Nextcloud apps folder in the'
228
+                    . ' Nextcloud folder. You can also configure the location in the config.php file.', $path['path']));
229
+            }
230
+        }
231
+
232
+        // set the right include path
233
+        set_include_path(
234
+            implode(PATH_SEPARATOR, $paths)
235
+        );
236
+    }
237
+
238
+    public static function checkConfig(): void {
239
+        $l = Server::get(\OCP\L10N\IFactory::class)->get('lib');
240
+
241
+        // Create config if it does not already exist
242
+        $configFilePath = self::$configDir .'/config.php';
243
+        if (!file_exists($configFilePath)) {
244
+            @touch($configFilePath);
245
+        }
246
+
247
+        // Check if config is writable
248
+        $configFileWritable = is_writable($configFilePath);
249
+        if (!$configFileWritable && !OC_Helper::isReadOnlyConfigEnabled()
250
+            || !$configFileWritable && \OCP\Util::needUpgrade()) {
251
+            $urlGenerator = Server::get(IURLGenerator::class);
252
+
253
+            if (self::$CLI) {
254
+                echo $l->t('Cannot write into "config" directory!')."\n";
255
+                echo $l->t('This can usually be fixed by giving the web server write access to the config directory.')."\n";
256
+                echo "\n";
257
+                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";
258
+                echo $l->t('See %s', [ $urlGenerator->linkToDocs('admin-config') ])."\n";
259
+                exit;
260
+            } else {
261
+                OC_Template::printErrorPage(
262
+                    $l->t('Cannot write into "config" directory!'),
263
+                    $l->t('This can usually be fixed by giving the web server write access to the config directory.') . ' '
264
+                    . $l->t('But, if you prefer to keep config.php file read only, set the option "config_is_read_only" to true in it.') . ' '
265
+                    . $l->t('See %s', [ $urlGenerator->linkToDocs('admin-config') ]),
266
+                    503
267
+                );
268
+            }
269
+        }
270
+    }
271
+
272
+    public static function checkInstalled(\OC\SystemConfig $systemConfig): void {
273
+        if (defined('OC_CONSOLE')) {
274
+            return;
275
+        }
276
+        // Redirect to installer if not installed
277
+        if (!$systemConfig->getValue('installed', false) && OC::$SUBURI !== '/index.php' && OC::$SUBURI !== '/status.php') {
278
+            if (OC::$CLI) {
279
+                throw new Exception('Not installed');
280
+            } else {
281
+                $url = OC::$WEBROOT . '/index.php';
282
+                header('Location: ' . $url);
283
+            }
284
+            exit();
285
+        }
286
+    }
287
+
288
+    public static function checkMaintenanceMode(\OC\SystemConfig $systemConfig): void {
289
+        // Allow ajax update script to execute without being stopped
290
+        if (((bool) $systemConfig->getValue('maintenance', false)) && OC::$SUBURI != '/core/ajax/update.php') {
291
+            // send http status 503
292
+            http_response_code(503);
293
+            header('X-Nextcloud-Maintenance-Mode: 1');
294
+            header('Retry-After: 120');
295
+
296
+            // render error page
297
+            $template = new OC_Template('', 'update.user', 'guest');
298
+            \OCP\Util::addScript('core', 'maintenance');
299
+            \OCP\Util::addStyle('core', 'guest');
300
+            $template->printPage();
301
+            die();
302
+        }
303
+    }
304
+
305
+    /**
306
+     * Prints the upgrade page
307
+     */
308
+    private static function printUpgradePage(\OC\SystemConfig $systemConfig): void {
309
+        $disableWebUpdater = $systemConfig->getValue('upgrade.disable-web', false);
310
+        $tooBig = false;
311
+        if (!$disableWebUpdater) {
312
+            $apps = Server::get(\OCP\App\IAppManager::class);
313
+            if ($apps->isInstalled('user_ldap')) {
314
+                $qb = Server::get(\OCP\IDBConnection::class)->getQueryBuilder();
315
+
316
+                $result = $qb->select($qb->func()->count('*', 'user_count'))
317
+                    ->from('ldap_user_mapping')
318
+                    ->executeQuery();
319
+                $row = $result->fetch();
320
+                $result->closeCursor();
321
+
322
+                $tooBig = ($row['user_count'] > 50);
323
+            }
324
+            if (!$tooBig && $apps->isInstalled('user_saml')) {
325
+                $qb = Server::get(\OCP\IDBConnection::class)->getQueryBuilder();
326
+
327
+                $result = $qb->select($qb->func()->count('*', 'user_count'))
328
+                    ->from('user_saml_users')
329
+                    ->executeQuery();
330
+                $row = $result->fetch();
331
+                $result->closeCursor();
332
+
333
+                $tooBig = ($row['user_count'] > 50);
334
+            }
335
+            if (!$tooBig) {
336
+                // count users
337
+                $stats = Server::get(\OCP\IUserManager::class)->countUsers();
338
+                $totalUsers = array_sum($stats);
339
+                $tooBig = ($totalUsers > 50);
340
+            }
341
+        }
342
+        $ignoreTooBigWarning = isset($_GET['IKnowThatThisIsABigInstanceAndTheUpdateRequestCouldRunIntoATimeoutAndHowToRestoreABackup']) &&
343
+            $_GET['IKnowThatThisIsABigInstanceAndTheUpdateRequestCouldRunIntoATimeoutAndHowToRestoreABackup'] === 'IAmSuperSureToDoThis';
344
+
345
+        if ($disableWebUpdater || ($tooBig && !$ignoreTooBigWarning)) {
346
+            // send http status 503
347
+            http_response_code(503);
348
+            header('Retry-After: 120');
349
+
350
+            // render error page
351
+            $template = new OC_Template('', 'update.use-cli', 'guest');
352
+            $template->assign('productName', 'nextcloud'); // for now
353
+            $template->assign('version', OC_Util::getVersionString());
354
+            $template->assign('tooBig', $tooBig);
355
+
356
+            $template->printPage();
357
+            die();
358
+        }
359
+
360
+        // check whether this is a core update or apps update
361
+        $installedVersion = $systemConfig->getValue('version', '0.0.0');
362
+        $currentVersion = implode('.', \OCP\Util::getVersion());
363
+
364
+        // if not a core upgrade, then it's apps upgrade
365
+        $isAppsOnlyUpgrade = version_compare($currentVersion, $installedVersion, '=');
366
+
367
+        $oldTheme = $systemConfig->getValue('theme');
368
+        $systemConfig->setValue('theme', '');
369
+        \OCP\Util::addScript('core', 'common');
370
+        \OCP\Util::addScript('core', 'main');
371
+        \OCP\Util::addTranslations('core');
372
+        \OCP\Util::addScript('core', 'update');
373
+
374
+        /** @var \OC\App\AppManager $appManager */
375
+        $appManager = Server::get(\OCP\App\IAppManager::class);
376
+
377
+        $tmpl = new OC_Template('', 'update.admin', 'guest');
378
+        $tmpl->assign('version', OC_Util::getVersionString());
379
+        $tmpl->assign('isAppsOnlyUpgrade', $isAppsOnlyUpgrade);
380
+
381
+        // get third party apps
382
+        $ocVersion = \OCP\Util::getVersion();
383
+        $ocVersion = implode('.', $ocVersion);
384
+        $incompatibleApps = $appManager->getIncompatibleApps($ocVersion);
385
+        $incompatibleShippedApps = [];
386
+        foreach ($incompatibleApps as $appInfo) {
387
+            if ($appManager->isShipped($appInfo['id'])) {
388
+                $incompatibleShippedApps[] = $appInfo['name'] . ' (' . $appInfo['id'] . ')';
389
+            }
390
+        }
391
+
392
+        if (!empty($incompatibleShippedApps)) {
393
+            $l = Server::get(\OCP\L10N\IFactory::class)->get('core');
394
+            $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)]);
395
+            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);
396
+        }
397
+
398
+        $tmpl->assign('appsToUpgrade', $appManager->getAppsNeedingUpgrade($ocVersion));
399
+        $tmpl->assign('incompatibleAppsList', $incompatibleApps);
400
+        try {
401
+            $defaults = new \OC_Defaults();
402
+            $tmpl->assign('productName', $defaults->getName());
403
+        } catch (Throwable $error) {
404
+            $tmpl->assign('productName', 'Nextcloud');
405
+        }
406
+        $tmpl->assign('oldTheme', $oldTheme);
407
+        $tmpl->printPage();
408
+    }
409
+
410
+    public static function initSession(): void {
411
+        if (Server::get(\OCP\IRequest::class)->getServerProtocol() === 'https') {
412
+            ini_set('session.cookie_secure', 'true');
413
+        }
414
+
415
+        // prevents javascript from accessing php session cookies
416
+        ini_set('session.cookie_httponly', 'true');
417
+
418
+        // set the cookie path to the Nextcloud directory
419
+        $cookie_path = OC::$WEBROOT ? : '/';
420
+        ini_set('session.cookie_path', $cookie_path);
421
+
422
+        // Let the session name be changed in the initSession Hook
423
+        $sessionName = OC_Util::getInstanceId();
424
+
425
+        try {
426
+            // set the session name to the instance id - which is unique
427
+            $session = new \OC\Session\Internal($sessionName);
428
+
429
+            $cryptoWrapper = Server::get(\OC\Session\CryptoWrapper::class);
430
+            $session = $cryptoWrapper->wrapSession($session);
431
+            self::$server->setSession($session);
432
+
433
+            // if session can't be started break with http 500 error
434
+        } catch (Exception $e) {
435
+            Server::get(LoggerInterface::class)->error($e->getMessage(), ['app' => 'base','exception' => $e]);
436
+            //show the user a detailed error page
437
+            OC_Template::printExceptionErrorPage($e, 500);
438
+            die();
439
+        }
440
+
441
+        //try to set the session lifetime
442
+        $sessionLifeTime = self::getSessionLifeTime();
443
+        @ini_set('gc_maxlifetime', (string)$sessionLifeTime);
444
+
445
+        // session timeout
446
+        if ($session->exists('LAST_ACTIVITY') && (time() - $session->get('LAST_ACTIVITY') > $sessionLifeTime)) {
447
+            if (isset($_COOKIE[session_name()])) {
448
+                setcookie(session_name(), '', -1, self::$WEBROOT ? : '/');
449
+            }
450
+            Server::get(IUserSession::class)->logout();
451
+        }
452
+
453
+        if (!self::hasSessionRelaxedExpiry()) {
454
+            $session->set('LAST_ACTIVITY', time());
455
+        }
456
+        $session->close();
457
+    }
458
+
459
+    private static function getSessionLifeTime(): int {
460
+        return Server::get(\OC\AllConfig::class)->getSystemValueInt('session_lifetime', 60 * 60 * 24);
461
+    }
462
+
463
+    /**
464
+     * @return bool true if the session expiry should only be done by gc instead of an explicit timeout
465
+     */
466
+    public static function hasSessionRelaxedExpiry(): bool {
467
+        return Server::get(\OC\AllConfig::class)->getSystemValueBool('session_relaxed_expiry', false);
468
+    }
469
+
470
+    /**
471
+     * Try to set some values to the required Nextcloud default
472
+     */
473
+    public static function setRequiredIniValues(): void {
474
+        @ini_set('default_charset', 'UTF-8');
475
+        @ini_set('gd.jpeg_ignore_warning', '1');
476
+    }
477
+
478
+    /**
479
+     * Send the same site cookies
480
+     */
481
+    private static function sendSameSiteCookies(): void {
482
+        $cookieParams = session_get_cookie_params();
483
+        $secureCookie = ($cookieParams['secure'] === true) ? 'secure; ' : '';
484
+        $policies = [
485
+            'lax',
486
+            'strict',
487
+        ];
488
+
489
+        // Append __Host to the cookie if it meets the requirements
490
+        $cookiePrefix = '';
491
+        if ($cookieParams['secure'] === true && $cookieParams['path'] === '/') {
492
+            $cookiePrefix = '__Host-';
493
+        }
494
+
495
+        foreach ($policies as $policy) {
496
+            header(
497
+                sprintf(
498
+                    'Set-Cookie: %snc_sameSiteCookie%s=true; path=%s; httponly;' . $secureCookie . 'expires=Fri, 31-Dec-2100 23:59:59 GMT; SameSite=%s',
499
+                    $cookiePrefix,
500
+                    $policy,
501
+                    $cookieParams['path'],
502
+                    $policy
503
+                ),
504
+                false
505
+            );
506
+        }
507
+    }
508
+
509
+    /**
510
+     * Same Site cookie to further mitigate CSRF attacks. This cookie has to
511
+     * be set in every request if cookies are sent to add a second level of
512
+     * defense against CSRF.
513
+     *
514
+     * If the cookie is not sent this will set the cookie and reload the page.
515
+     * We use an additional cookie since we want to protect logout CSRF and
516
+     * also we can't directly interfere with PHP's session mechanism.
517
+     */
518
+    private static function performSameSiteCookieProtection(\OCP\IConfig $config): void {
519
+        $request = Server::get(\OCP\IRequest::class);
520
+
521
+        // Some user agents are notorious and don't really properly follow HTTP
522
+        // specifications. For those, have an automated opt-out. Since the protection
523
+        // for remote.php is applied in base.php as starting point we need to opt out
524
+        // here.
525
+        $incompatibleUserAgents = $config->getSystemValue('csrf.optout');
526
+
527
+        // Fallback, if csrf.optout is unset
528
+        if (!is_array($incompatibleUserAgents)) {
529
+            $incompatibleUserAgents = [
530
+                // OS X Finder
531
+                '/^WebDAVFS/',
532
+                // Windows webdav drive
533
+                '/^Microsoft-WebDAV-MiniRedir/',
534
+            ];
535
+        }
536
+
537
+        if ($request->isUserAgent($incompatibleUserAgents)) {
538
+            return;
539
+        }
540
+
541
+        if (count($_COOKIE) > 0) {
542
+            $requestUri = $request->getScriptName();
543
+            $processingScript = explode('/', $requestUri);
544
+            $processingScript = $processingScript[count($processingScript) - 1];
545
+
546
+            // index.php routes are handled in the middleware
547
+            if ($processingScript === 'index.php') {
548
+                return;
549
+            }
550
+
551
+            // All other endpoints require the lax and the strict cookie
552
+            if (!$request->passesStrictCookieCheck()) {
553
+                self::sendSameSiteCookies();
554
+                // Debug mode gets access to the resources without strict cookie
555
+                // due to the fact that the SabreDAV browser also lives there.
556
+                if (!$config->getSystemValue('debug', false)) {
557
+                    http_response_code(\OCP\AppFramework\Http::STATUS_SERVICE_UNAVAILABLE);
558
+                    exit();
559
+                }
560
+            }
561
+        } elseif (!isset($_COOKIE['nc_sameSiteCookielax']) || !isset($_COOKIE['nc_sameSiteCookiestrict'])) {
562
+            self::sendSameSiteCookies();
563
+        }
564
+    }
565
+
566
+    public static function init(): void {
567
+        // calculate the root directories
568
+        OC::$SERVERROOT = str_replace("\\", '/', substr(__DIR__, 0, -4));
569
+
570
+        // register autoloader
571
+        $loaderStart = microtime(true);
572
+        require_once __DIR__ . '/autoloader.php';
573
+        self::$loader = new \OC\Autoloader([
574
+            OC::$SERVERROOT . '/lib/private/legacy',
575
+        ]);
576
+        if (defined('PHPUNIT_RUN')) {
577
+            self::$loader->addValidRoot(OC::$SERVERROOT . '/tests');
578
+        }
579
+        spl_autoload_register([self::$loader, 'load']);
580
+        $loaderEnd = microtime(true);
581
+
582
+        self::$CLI = (php_sapi_name() == 'cli');
583
+
584
+        // Add default composer PSR-4 autoloader
585
+        self::$composerAutoloader = require_once OC::$SERVERROOT . '/lib/composer/autoload.php';
586
+        self::$composerAutoloader->setApcuPrefix('composer_autoload');
587
+
588
+        try {
589
+            self::initPaths();
590
+            // setup 3rdparty autoloader
591
+            $vendorAutoLoad = OC::$SERVERROOT. '/3rdparty/autoload.php';
592
+            if (!file_exists($vendorAutoLoad)) {
593
+                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".');
594
+            }
595
+            require_once $vendorAutoLoad;
596
+        } catch (\RuntimeException $e) {
597
+            if (!self::$CLI) {
598
+                http_response_code(503);
599
+            }
600
+            // we can't use the template error page here, because this needs the
601
+            // DI container which isn't available yet
602
+            print($e->getMessage());
603
+            exit();
604
+        }
605
+
606
+        // setup the basic server
607
+        self::$server = new \OC\Server(\OC::$WEBROOT, self::$config);
608
+        self::$server->boot();
609
+
610
+        $eventLogger = Server::get(\OCP\Diagnostics\IEventLogger::class);
611
+        $eventLogger->log('autoloader', 'Autoloader', $loaderStart, $loaderEnd);
612
+        $eventLogger->start('boot', 'Initialize');
613
+
614
+        // Override php.ini and log everything if we're troubleshooting
615
+        if (self::$config->getValue('loglevel') === ILogger::DEBUG) {
616
+            error_reporting(E_ALL);
617
+        }
618
+
619
+        // Don't display errors and log them
620
+        @ini_set('display_errors', '0');
621
+        @ini_set('log_errors', '1');
622
+
623
+        if (!date_default_timezone_set('UTC')) {
624
+            throw new \RuntimeException('Could not set timezone to UTC');
625
+        }
626
+
627
+
628
+        //try to configure php to enable big file uploads.
629
+        //this doesn´t work always depending on the webserver and php configuration.
630
+        //Let´s try to overwrite some defaults if they are smaller than 1 hour
631
+
632
+        if (intval(@ini_get('max_execution_time') ?? 0) < 3600) {
633
+            @ini_set('max_execution_time', strval(3600));
634
+        }
635
+
636
+        if (intval(@ini_get('max_input_time') ?? 0) < 3600) {
637
+            @ini_set('max_input_time', strval(3600));
638
+        }
639
+
640
+        //try to set the maximum execution time to the largest time limit we have
641
+        if (strpos(@ini_get('disable_functions'), 'set_time_limit') === false) {
642
+            @set_time_limit(max(intval(@ini_get('max_execution_time')), intval(@ini_get('max_input_time'))));
643
+        }
644
+
645
+        self::setRequiredIniValues();
646
+        self::handleAuthHeaders();
647
+        $systemConfig = Server::get(\OC\SystemConfig::class);
648
+        self::registerAutoloaderCache($systemConfig);
649
+
650
+        // initialize intl fallback if necessary
651
+        OC_Util::isSetLocaleWorking();
652
+
653
+        $config = Server::get(\OCP\IConfig::class);
654
+        if (!defined('PHPUNIT_RUN')) {
655
+            $errorHandler = new OC\Log\ErrorHandler(
656
+                \OCP\Server::get(\Psr\Log\LoggerInterface::class),
657
+            );
658
+            $exceptionHandler = [$errorHandler, 'onException'];
659
+            if ($config->getSystemValue('debug', false)) {
660
+                set_error_handler([$errorHandler, 'onAll'], E_ALL);
661
+                if (\OC::$CLI) {
662
+                    $exceptionHandler = ['OC_Template', 'printExceptionErrorPage'];
663
+                }
664
+            } else {
665
+                set_error_handler([$errorHandler, 'onError']);
666
+            }
667
+            register_shutdown_function([$errorHandler, 'onShutdown']);
668
+            set_exception_handler($exceptionHandler);
669
+        }
670
+
671
+        /** @var \OC\AppFramework\Bootstrap\Coordinator $bootstrapCoordinator */
672
+        $bootstrapCoordinator = Server::get(\OC\AppFramework\Bootstrap\Coordinator::class);
673
+        $bootstrapCoordinator->runInitialRegistration();
674
+
675
+        $eventLogger->start('init_session', 'Initialize session');
676
+        OC_App::loadApps(['session']);
677
+        if (!self::$CLI) {
678
+            self::initSession();
679
+        }
680
+        $eventLogger->end('init_session');
681
+        self::checkConfig();
682
+        self::checkInstalled($systemConfig);
683
+
684
+        OC_Response::addSecurityHeaders();
685
+
686
+        self::performSameSiteCookieProtection($config);
687
+
688
+        if (!defined('OC_CONSOLE')) {
689
+            $errors = OC_Util::checkServer($systemConfig);
690
+            if (count($errors) > 0) {
691
+                if (!self::$CLI) {
692
+                    http_response_code(503);
693
+                    OC_Util::addStyle('guest');
694
+                    try {
695
+                        OC_Template::printGuestPage('', 'error', ['errors' => $errors]);
696
+                        exit;
697
+                    } catch (\Exception $e) {
698
+                        // In case any error happens when showing the error page, we simply fall back to posting the text.
699
+                        // This might be the case when e.g. the data directory is broken and we can not load/write SCSS to/from it.
700
+                    }
701
+                }
702
+
703
+                // Convert l10n string into regular string for usage in database
704
+                $staticErrors = [];
705
+                foreach ($errors as $error) {
706
+                    echo $error['error'] . "\n";
707
+                    echo $error['hint'] . "\n\n";
708
+                    $staticErrors[] = [
709
+                        'error' => (string)$error['error'],
710
+                        'hint' => (string)$error['hint'],
711
+                    ];
712
+                }
713
+
714
+                try {
715
+                    $config->setAppValue('core', 'cronErrors', json_encode($staticErrors));
716
+                } catch (\Exception $e) {
717
+                    echo('Writing to database failed');
718
+                }
719
+                exit(1);
720
+            } elseif (self::$CLI && $config->getSystemValue('installed', false)) {
721
+                $config->deleteAppValue('core', 'cronErrors');
722
+            }
723
+        }
724
+
725
+        // User and Groups
726
+        if (!$systemConfig->getValue("installed", false)) {
727
+            self::$server->getSession()->set('user_id', '');
728
+        }
729
+
730
+        OC_User::useBackend(new \OC\User\Database());
731
+        Server::get(\OCP\IGroupManager::class)->addBackend(new \OC\Group\Database());
732
+
733
+        // Subscribe to the hook
734
+        \OCP\Util::connectHook(
735
+            '\OCA\Files_Sharing\API\Server2Server',
736
+            'preLoginNameUsedAsUserName',
737
+            '\OC\User\Database',
738
+            'preLoginNameUsedAsUserName'
739
+        );
740
+
741
+        //setup extra user backends
742
+        if (!\OCP\Util::needUpgrade()) {
743
+            OC_User::setupBackends();
744
+        } else {
745
+            // Run upgrades in incognito mode
746
+            OC_User::setIncognitoMode(true);
747
+        }
748
+
749
+        self::registerCleanupHooks($systemConfig);
750
+        self::registerShareHooks($systemConfig);
751
+        self::registerEncryptionWrapperAndHooks();
752
+        self::registerAccountHooks();
753
+        self::registerResourceCollectionHooks();
754
+        self::registerFileReferenceEventListener();
755
+        self::registerAppRestrictionsHooks();
756
+
757
+        // Make sure that the application class is not loaded before the database is setup
758
+        if ($systemConfig->getValue("installed", false)) {
759
+            OC_App::loadApp('settings');
760
+            /* Build core application to make sure that listeners are registered */
761
+            Server::get(\OC\Core\Application::class);
762
+        }
763
+
764
+        //make sure temporary files are cleaned up
765
+        $tmpManager = Server::get(\OCP\ITempManager::class);
766
+        register_shutdown_function([$tmpManager, 'clean']);
767
+        $lockProvider = Server::get(\OCP\Lock\ILockingProvider::class);
768
+        register_shutdown_function([$lockProvider, 'releaseAll']);
769
+
770
+        // Check whether the sample configuration has been copied
771
+        if ($systemConfig->getValue('copied_sample_config', false)) {
772
+            $l = Server::get(\OCP\L10N\IFactory::class)->get('lib');
773
+            OC_Template::printErrorPage(
774
+                $l->t('Sample configuration detected'),
775
+                $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'),
776
+                503
777
+            );
778
+            return;
779
+        }
780
+
781
+        $request = Server::get(\OCP\IRequest::class);
782
+        $host = $request->getInsecureServerHost();
783
+        /**
784
+         * if the host passed in headers isn't trusted
785
+         * FIXME: Should not be in here at all :see_no_evil:
786
+         */
787
+        if (!OC::$CLI
788
+            && !Server::get(\OC\Security\TrustedDomainHelper::class)->isTrustedDomain($host)
789
+            && $config->getSystemValue('installed', false)
790
+        ) {
791
+            // Allow access to CSS resources
792
+            $isScssRequest = false;
793
+            if (strpos($request->getPathInfo() ?: '', '/css/') === 0) {
794
+                $isScssRequest = true;
795
+            }
796
+
797
+            if (substr($request->getRequestUri(), -11) === '/status.php') {
798
+                http_response_code(400);
799
+                header('Content-Type: application/json');
800
+                echo '{"error": "Trusted domain error.", "code": 15}';
801
+                exit();
802
+            }
803
+
804
+            if (!$isScssRequest) {
805
+                http_response_code(400);
806
+                Server::get(LoggerInterface::class)->info(
807
+                    'Trusted domain error. "{remoteAddress}" tried to access using "{host}" as host.',
808
+                    [
809
+                        'app' => 'core',
810
+                        'remoteAddress' => $request->getRemoteAddress(),
811
+                        'host' => $host,
812
+                    ]
813
+                );
814
+
815
+                $tmpl = new OCP\Template('core', 'untrustedDomain', 'guest');
816
+                $tmpl->assign('docUrl', Server::get(IURLGenerator::class)->linkToDocs('admin-trusted-domains'));
817
+                $tmpl->printPage();
818
+
819
+                exit();
820
+            }
821
+        }
822
+        $eventLogger->end('boot');
823
+        $eventLogger->log('init', 'OC::init', $loaderStart, microtime(true));
824
+        $eventLogger->start('runtime', 'Runtime');
825
+        $eventLogger->start('request', 'Full request after boot');
826
+        register_shutdown_function(function () use ($eventLogger) {
827
+            $eventLogger->end('request');
828
+        });
829
+    }
830
+
831
+    /**
832
+     * register hooks for the cleanup of cache and bruteforce protection
833
+     */
834
+    public static function registerCleanupHooks(\OC\SystemConfig $systemConfig): void {
835
+        //don't try to do this before we are properly setup
836
+        if ($systemConfig->getValue('installed', false) && !\OCP\Util::needUpgrade()) {
837
+            // NOTE: This will be replaced to use OCP
838
+            $userSession = Server::get(\OC\User\Session::class);
839
+            $userSession->listen('\OC\User', 'postLogin', function () use ($userSession) {
840
+                if (!defined('PHPUNIT_RUN') && $userSession->isLoggedIn()) {
841
+                    // reset brute force delay for this IP address and username
842
+                    $uid = $userSession->getUser()->getUID();
843
+                    $request = Server::get(\OCP\IRequest::class);
844
+                    $throttler = Server::get(\OC\Security\Bruteforce\Throttler::class);
845
+                    $throttler->resetDelay($request->getRemoteAddress(), 'login', ['user' => $uid]);
846
+                }
847
+
848
+                try {
849
+                    $cache = new \OC\Cache\File();
850
+                    $cache->gc();
851
+                } catch (\OC\ServerNotAvailableException $e) {
852
+                    // not a GC exception, pass it on
853
+                    throw $e;
854
+                } catch (\OC\ForbiddenException $e) {
855
+                    // filesystem blocked for this request, ignore
856
+                } catch (\Exception $e) {
857
+                    // a GC exception should not prevent users from using OC,
858
+                    // so log the exception
859
+                    Server::get(LoggerInterface::class)->warning('Exception when running cache gc.', [
860
+                        'app' => 'core',
861
+                        'exception' => $e,
862
+                    ]);
863
+                }
864
+            });
865
+        }
866
+    }
867
+
868
+    private static function registerEncryptionWrapperAndHooks(): void {
869
+        $manager = Server::get(\OCP\Encryption\IManager::class);
870
+        \OCP\Util::connectHook('OC_Filesystem', 'preSetup', $manager, 'setupStorage');
871
+
872
+        $enabled = $manager->isEnabled();
873
+        if ($enabled) {
874
+            \OCP\Util::connectHook(Share::class, 'post_shared', HookManager::class, 'postShared');
875
+            \OCP\Util::connectHook(Share::class, 'post_unshare', HookManager::class, 'postUnshared');
876
+            \OCP\Util::connectHook('OC_Filesystem', 'post_rename', HookManager::class, 'postRename');
877
+            \OCP\Util::connectHook('\OCA\Files_Trashbin\Trashbin', 'post_restore', HookManager::class, 'postRestore');
878
+        }
879
+    }
880
+
881
+    private static function registerAccountHooks(): void {
882
+        /** @var IEventDispatcher $dispatcher */
883
+        $dispatcher = Server::get(IEventDispatcher::class);
884
+        $dispatcher->addServiceListener(UserChangedEvent::class, \OC\Accounts\Hooks::class);
885
+    }
886
+
887
+    private static function registerAppRestrictionsHooks(): void {
888
+        /** @var \OC\Group\Manager $groupManager */
889
+        $groupManager = Server::get(\OCP\IGroupManager::class);
890
+        $groupManager->listen('\OC\Group', 'postDelete', function (\OCP\IGroup $group) {
891
+            $appManager = Server::get(\OCP\App\IAppManager::class);
892
+            $apps = $appManager->getEnabledAppsForGroup($group);
893
+            foreach ($apps as $appId) {
894
+                $restrictions = $appManager->getAppRestriction($appId);
895
+                if (empty($restrictions)) {
896
+                    continue;
897
+                }
898
+                $key = array_search($group->getGID(), $restrictions);
899
+                unset($restrictions[$key]);
900
+                $restrictions = array_values($restrictions);
901
+                if (empty($restrictions)) {
902
+                    $appManager->disableApp($appId);
903
+                } else {
904
+                    $appManager->enableAppForGroups($appId, $restrictions);
905
+                }
906
+            }
907
+        });
908
+    }
909
+
910
+    private static function registerResourceCollectionHooks(): void {
911
+        \OC\Collaboration\Resources\Listener::register(Server::get(SymfonyAdapter::class), Server::get(IEventDispatcher::class));
912
+    }
913
+
914
+    private static function registerFileReferenceEventListener(): void {
915
+        \OC\Collaboration\Reference\File\FileReferenceEventListener::register(Server::get(IEventDispatcher::class));
916
+    }
917
+
918
+    /**
919
+     * register hooks for sharing
920
+     */
921
+    public static function registerShareHooks(\OC\SystemConfig $systemConfig): void {
922
+        if ($systemConfig->getValue('installed')) {
923
+            OC_Hook::connect('OC_User', 'post_deleteUser', Hooks::class, 'post_deleteUser');
924
+            OC_Hook::connect('OC_User', 'post_deleteGroup', Hooks::class, 'post_deleteGroup');
925
+
926
+            /** @var IEventDispatcher $dispatcher */
927
+            $dispatcher = Server::get(IEventDispatcher::class);
928
+            $dispatcher->addServiceListener(UserRemovedEvent::class, \OC\Share20\UserRemovedListener::class);
929
+        }
930
+    }
931
+
932
+    protected static function registerAutoloaderCache(\OC\SystemConfig $systemConfig): void {
933
+        // The class loader takes an optional low-latency cache, which MUST be
934
+        // namespaced. The instanceid is used for namespacing, but might be
935
+        // unavailable at this point. Furthermore, it might not be possible to
936
+        // generate an instanceid via \OC_Util::getInstanceId() because the
937
+        // config file may not be writable. As such, we only register a class
938
+        // loader cache if instanceid is available without trying to create one.
939
+        $instanceId = $systemConfig->getValue('instanceid', null);
940
+        if ($instanceId) {
941
+            try {
942
+                $memcacheFactory = Server::get(\OCP\ICacheFactory::class);
943
+                self::$loader->setMemoryCache($memcacheFactory->createLocal('Autoloader'));
944
+            } catch (\Exception $ex) {
945
+            }
946
+        }
947
+    }
948
+
949
+    /**
950
+     * Handle the request
951
+     */
952
+    public static function handleRequest(): void {
953
+        Server::get(\OCP\Diagnostics\IEventLogger::class)->start('handle_request', 'Handle request');
954
+        $systemConfig = Server::get(\OC\SystemConfig::class);
955
+
956
+        // Check if Nextcloud is installed or in maintenance (update) mode
957
+        if (!$systemConfig->getValue('installed', false)) {
958
+            \OC::$server->getSession()->clear();
959
+            $setupHelper = new OC\Setup(
960
+                $systemConfig,
961
+                Server::get(\bantu\IniGetWrapper\IniGetWrapper::class),
962
+                Server::get(\OCP\L10N\IFactory::class)->get('lib'),
963
+                Server::get(\OCP\Defaults::class),
964
+                Server::get(\Psr\Log\LoggerInterface::class),
965
+                Server::get(\OCP\Security\ISecureRandom::class),
966
+                Server::get(\OC\Installer::class)
967
+            );
968
+            $controller = new OC\Core\Controller\SetupController($setupHelper);
969
+            $controller->run($_POST);
970
+            exit();
971
+        }
972
+
973
+        $request = Server::get(\OCP\IRequest::class);
974
+        $requestPath = $request->getRawPathInfo();
975
+        if ($requestPath === '/heartbeat') {
976
+            return;
977
+        }
978
+        if (substr($requestPath, -3) !== '.js') { // we need these files during the upgrade
979
+            self::checkMaintenanceMode($systemConfig);
980
+
981
+            if (\OCP\Util::needUpgrade()) {
982
+                if (function_exists('opcache_reset')) {
983
+                    opcache_reset();
984
+                }
985
+                if (!((bool) $systemConfig->getValue('maintenance', false))) {
986
+                    self::printUpgradePage($systemConfig);
987
+                    exit();
988
+                }
989
+            }
990
+        }
991
+
992
+        // emergency app disabling
993
+        if ($requestPath === '/disableapp'
994
+            && $request->getMethod() === 'POST'
995
+        ) {
996
+            \OC_JSON::callCheck();
997
+            \OC_JSON::checkAdminUser();
998
+            $appIds = (array)$request->getParam('appid');
999
+            foreach ($appIds as $appId) {
1000
+                $appId = \OC_App::cleanAppId($appId);
1001
+                Server::get(\OCP\App\IAppManager::class)->disableApp($appId);
1002
+            }
1003
+            \OC_JSON::success();
1004
+            exit();
1005
+        }
1006
+
1007
+        // Always load authentication apps
1008
+        OC_App::loadApps(['authentication']);
1009
+
1010
+        // Load minimum set of apps
1011
+        if (!\OCP\Util::needUpgrade()
1012
+            && !((bool) $systemConfig->getValue('maintenance', false))) {
1013
+            // For logged-in users: Load everything
1014
+            if (Server::get(IUserSession::class)->isLoggedIn()) {
1015
+                OC_App::loadApps();
1016
+            } else {
1017
+                // For guests: Load only filesystem and logging
1018
+                OC_App::loadApps(['filesystem', 'logging']);
1019
+
1020
+                // Don't try to login when a client is trying to get a OAuth token.
1021
+                // OAuth needs to support basic auth too, so the login is not valid
1022
+                // inside Nextcloud and the Login exception would ruin it.
1023
+                if ($request->getRawPathInfo() !== '/apps/oauth2/api/v1/token') {
1024
+                    self::handleLogin($request);
1025
+                }
1026
+            }
1027
+        }
1028
+
1029
+        if (!self::$CLI) {
1030
+            try {
1031
+                if (!((bool) $systemConfig->getValue('maintenance', false)) && !\OCP\Util::needUpgrade()) {
1032
+                    OC_App::loadApps(['filesystem', 'logging']);
1033
+                    OC_App::loadApps();
1034
+                }
1035
+                Server::get(\OC\Route\Router::class)->match($request->getRawPathInfo());
1036
+                return;
1037
+            } catch (Symfony\Component\Routing\Exception\ResourceNotFoundException $e) {
1038
+                //header('HTTP/1.0 404 Not Found');
1039
+            } catch (Symfony\Component\Routing\Exception\MethodNotAllowedException $e) {
1040
+                http_response_code(405);
1041
+                return;
1042
+            }
1043
+        }
1044
+
1045
+        // Handle WebDAV
1046
+        if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'PROPFIND') {
1047
+            // not allowed any more to prevent people
1048
+            // mounting this root directly.
1049
+            // Users need to mount remote.php/webdav instead.
1050
+            http_response_code(405);
1051
+            return;
1052
+        }
1053
+
1054
+        // Handle requests for JSON or XML
1055
+        $acceptHeader = $request->getHeader('Accept');
1056
+        if (in_array($acceptHeader, ['application/json', 'application/xml'], true)) {
1057
+            http_response_code(404);
1058
+            return;
1059
+        }
1060
+
1061
+        // Handle resources that can't be found
1062
+        // This prevents browsers from redirecting to the default page and then
1063
+        // attempting to parse HTML as CSS and similar.
1064
+        $destinationHeader = $request->getHeader('Sec-Fetch-Dest');
1065
+        if (in_array($destinationHeader, ['font', 'script', 'style'])) {
1066
+            http_response_code(404);
1067
+            return;
1068
+        }
1069
+
1070
+        // Redirect to the default app or login only as an entry point
1071
+        if ($requestPath === '') {
1072
+            // Someone is logged in
1073
+            if (Server::get(IUserSession::class)->isLoggedIn()) {
1074
+                header('Location: ' . Server::get(IURLGenerator::class)->linkToDefaultPageUrl());
1075
+            } else {
1076
+                // Not handled and not logged in
1077
+                header('Location: ' . Server::get(IURLGenerator::class)->linkToRouteAbsolute('core.login.showLoginForm'));
1078
+            }
1079
+            return;
1080
+        }
1081
+
1082
+        try {
1083
+            Server::get(\OC\Route\Router::class)->match('/error/404');
1084
+        } catch (\Exception $e) {
1085
+            logger('core')->emergency($e->getMessage(), ['exception' => $e]);
1086
+            $l = Server::get(\OCP\L10N\IFactory::class)->get('lib');
1087
+            OC_Template::printErrorPage(
1088
+                $l->t('404'),
1089
+                $l->t('The page could not be found on the server.'),
1090
+                404
1091
+            );
1092
+        }
1093
+    }
1094
+
1095
+    /**
1096
+     * Check login: apache auth, auth token, basic auth
1097
+     */
1098
+    public static function handleLogin(OCP\IRequest $request): bool {
1099
+        $userSession = Server::get(\OC\User\Session::class);
1100
+        if (OC_User::handleApacheAuth()) {
1101
+            return true;
1102
+        }
1103
+        if ($userSession->tryTokenLogin($request)) {
1104
+            return true;
1105
+        }
1106
+        if (isset($_COOKIE['nc_username'])
1107
+            && isset($_COOKIE['nc_token'])
1108
+            && isset($_COOKIE['nc_session_id'])
1109
+            && $userSession->loginWithCookie($_COOKIE['nc_username'], $_COOKIE['nc_token'], $_COOKIE['nc_session_id'])) {
1110
+            return true;
1111
+        }
1112
+        if ($userSession->tryBasicAuthLogin($request, Server::get(\OC\Security\Bruteforce\Throttler::class))) {
1113
+            return true;
1114
+        }
1115
+        return false;
1116
+    }
1117
+
1118
+    protected static function handleAuthHeaders(): void {
1119
+        //copy http auth headers for apache+php-fcgid work around
1120
+        if (isset($_SERVER['HTTP_XAUTHORIZATION']) && !isset($_SERVER['HTTP_AUTHORIZATION'])) {
1121
+            $_SERVER['HTTP_AUTHORIZATION'] = $_SERVER['HTTP_XAUTHORIZATION'];
1122
+        }
1123
+
1124
+        // Extract PHP_AUTH_USER/PHP_AUTH_PW from other headers if necessary.
1125
+        $vars = [
1126
+            'HTTP_AUTHORIZATION', // apache+php-cgi work around
1127
+            'REDIRECT_HTTP_AUTHORIZATION', // apache+php-cgi alternative
1128
+        ];
1129
+        foreach ($vars as $var) {
1130
+            if (isset($_SERVER[$var]) && is_string($_SERVER[$var]) && preg_match('/Basic\s+(.*)$/i', $_SERVER[$var], $matches)) {
1131
+                $credentials = explode(':', base64_decode($matches[1]), 2);
1132
+                if (count($credentials) === 2) {
1133
+                    $_SERVER['PHP_AUTH_USER'] = $credentials[0];
1134
+                    $_SERVER['PHP_AUTH_PW'] = $credentials[1];
1135
+                    break;
1136
+                }
1137
+            }
1138
+        }
1139
+    }
1140 1140
 }
1141 1141
 
1142 1142
 OC::init();
Please login to merge, or discard this patch.
Spacing   +39 added lines, -39 removed lines patch added patch discarded remove patch
@@ -137,13 +137,13 @@  discard block
 block discarded – undo
137 137
 	 */
138 138
 	public static function initPaths(): void {
139 139
 		if (defined('PHPUNIT_CONFIG_DIR')) {
140
-			self::$configDir = OC::$SERVERROOT . '/' . PHPUNIT_CONFIG_DIR . '/';
141
-		} elseif (defined('PHPUNIT_RUN') and PHPUNIT_RUN and is_dir(OC::$SERVERROOT . '/tests/config/')) {
142
-			self::$configDir = OC::$SERVERROOT . '/tests/config/';
140
+			self::$configDir = OC::$SERVERROOT.'/'.PHPUNIT_CONFIG_DIR.'/';
141
+		} elseif (defined('PHPUNIT_RUN') and PHPUNIT_RUN and is_dir(OC::$SERVERROOT.'/tests/config/')) {
142
+			self::$configDir = OC::$SERVERROOT.'/tests/config/';
143 143
 		} elseif ($dir = getenv('NEXTCLOUD_CONFIG_DIR')) {
144
-			self::$configDir = rtrim($dir, '/') . '/';
144
+			self::$configDir = rtrim($dir, '/').'/';
145 145
 		} else {
146
-			self::$configDir = OC::$SERVERROOT . '/config/';
146
+			self::$configDir = OC::$SERVERROOT.'/config/';
147 147
 		}
148 148
 		self::$config = new \OC\Config(self::$configDir);
149 149
 
@@ -169,9 +169,9 @@  discard block
 block discarded – undo
169 169
 			//make sure suburi follows the same rules as scriptName
170 170
 			if (substr(OC::$SUBURI, -9) != 'index.php') {
171 171
 				if (substr(OC::$SUBURI, -1) != '/') {
172
-					OC::$SUBURI = OC::$SUBURI . '/';
172
+					OC::$SUBURI = OC::$SUBURI.'/';
173 173
 				}
174
-				OC::$SUBURI = OC::$SUBURI . 'index.php';
174
+				OC::$SUBURI = OC::$SUBURI.'index.php';
175 175
 			}
176 176
 		}
177 177
 
@@ -183,7 +183,7 @@  discard block
 block discarded – undo
183 183
 				OC::$WEBROOT = substr($scriptName, 0, 0 - strlen(OC::$SUBURI));
184 184
 
185 185
 				if (OC::$WEBROOT != '' && OC::$WEBROOT[0] !== '/') {
186
-					OC::$WEBROOT = '/' . OC::$WEBROOT;
186
+					OC::$WEBROOT = '/'.OC::$WEBROOT;
187 187
 				}
188 188
 			} else {
189 189
 				// The scriptName is not ending with OC::$SUBURI
@@ -212,8 +212,8 @@  discard block
 block discarded – undo
212 212
 					OC::$APPSROOTS[] = $paths;
213 213
 				}
214 214
 			}
215
-		} elseif (file_exists(OC::$SERVERROOT . '/apps')) {
216
-			OC::$APPSROOTS[] = ['path' => OC::$SERVERROOT . '/apps', 'url' => '/apps', 'writable' => true];
215
+		} elseif (file_exists(OC::$SERVERROOT.'/apps')) {
216
+			OC::$APPSROOTS[] = ['path' => OC::$SERVERROOT.'/apps', 'url' => '/apps', 'writable' => true];
217 217
 		}
218 218
 
219 219
 		if (empty(OC::$APPSROOTS)) {
@@ -239,7 +239,7 @@  discard block
 block discarded – undo
239 239
 		$l = Server::get(\OCP\L10N\IFactory::class)->get('lib');
240 240
 
241 241
 		// Create config if it does not already exist
242
-		$configFilePath = self::$configDir .'/config.php';
242
+		$configFilePath = self::$configDir.'/config.php';
243 243
 		if (!file_exists($configFilePath)) {
244 244
 			@touch($configFilePath);
245 245
 		}
@@ -255,14 +255,14 @@  discard block
 block discarded – undo
255 255
 				echo $l->t('This can usually be fixed by giving the web server write access to the config directory.')."\n";
256 256
 				echo "\n";
257 257
 				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";
258
-				echo $l->t('See %s', [ $urlGenerator->linkToDocs('admin-config') ])."\n";
258
+				echo $l->t('See %s', [$urlGenerator->linkToDocs('admin-config')])."\n";
259 259
 				exit;
260 260
 			} else {
261 261
 				OC_Template::printErrorPage(
262 262
 					$l->t('Cannot write into "config" directory!'),
263
-					$l->t('This can usually be fixed by giving the web server write access to the config directory.') . ' '
264
-					. $l->t('But, if you prefer to keep config.php file read only, set the option "config_is_read_only" to true in it.') . ' '
265
-					. $l->t('See %s', [ $urlGenerator->linkToDocs('admin-config') ]),
263
+					$l->t('This can usually be fixed by giving the web server write access to the config directory.').' '
264
+					. $l->t('But, if you prefer to keep config.php file read only, set the option "config_is_read_only" to true in it.').' '
265
+					. $l->t('See %s', [$urlGenerator->linkToDocs('admin-config')]),
266 266
 					503
267 267
 				);
268 268
 			}
@@ -278,8 +278,8 @@  discard block
 block discarded – undo
278 278
 			if (OC::$CLI) {
279 279
 				throw new Exception('Not installed');
280 280
 			} else {
281
-				$url = OC::$WEBROOT . '/index.php';
282
-				header('Location: ' . $url);
281
+				$url = OC::$WEBROOT.'/index.php';
282
+				header('Location: '.$url);
283 283
 			}
284 284
 			exit();
285 285
 		}
@@ -385,14 +385,14 @@  discard block
 block discarded – undo
385 385
 		$incompatibleShippedApps = [];
386 386
 		foreach ($incompatibleApps as $appInfo) {
387 387
 			if ($appManager->isShipped($appInfo['id'])) {
388
-				$incompatibleShippedApps[] = $appInfo['name'] . ' (' . $appInfo['id'] . ')';
388
+				$incompatibleShippedApps[] = $appInfo['name'].' ('.$appInfo['id'].')';
389 389
 			}
390 390
 		}
391 391
 
392 392
 		if (!empty($incompatibleShippedApps)) {
393 393
 			$l = Server::get(\OCP\L10N\IFactory::class)->get('core');
394 394
 			$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)]);
395
-			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);
395
+			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);
396 396
 		}
397 397
 
398 398
 		$tmpl->assign('appsToUpgrade', $appManager->getAppsNeedingUpgrade($ocVersion));
@@ -416,7 +416,7 @@  discard block
 block discarded – undo
416 416
 		ini_set('session.cookie_httponly', 'true');
417 417
 
418 418
 		// set the cookie path to the Nextcloud directory
419
-		$cookie_path = OC::$WEBROOT ? : '/';
419
+		$cookie_path = OC::$WEBROOT ?: '/';
420 420
 		ini_set('session.cookie_path', $cookie_path);
421 421
 
422 422
 		// Let the session name be changed in the initSession Hook
@@ -432,7 +432,7 @@  discard block
 block discarded – undo
432 432
 
433 433
 			// if session can't be started break with http 500 error
434 434
 		} catch (Exception $e) {
435
-			Server::get(LoggerInterface::class)->error($e->getMessage(), ['app' => 'base','exception' => $e]);
435
+			Server::get(LoggerInterface::class)->error($e->getMessage(), ['app' => 'base', 'exception' => $e]);
436 436
 			//show the user a detailed error page
437 437
 			OC_Template::printExceptionErrorPage($e, 500);
438 438
 			die();
@@ -440,12 +440,12 @@  discard block
 block discarded – undo
440 440
 
441 441
 		//try to set the session lifetime
442 442
 		$sessionLifeTime = self::getSessionLifeTime();
443
-		@ini_set('gc_maxlifetime', (string)$sessionLifeTime);
443
+		@ini_set('gc_maxlifetime', (string) $sessionLifeTime);
444 444
 
445 445
 		// session timeout
446 446
 		if ($session->exists('LAST_ACTIVITY') && (time() - $session->get('LAST_ACTIVITY') > $sessionLifeTime)) {
447 447
 			if (isset($_COOKIE[session_name()])) {
448
-				setcookie(session_name(), '', -1, self::$WEBROOT ? : '/');
448
+				setcookie(session_name(), '', -1, self::$WEBROOT ?: '/');
449 449
 			}
450 450
 			Server::get(IUserSession::class)->logout();
451 451
 		}
@@ -495,7 +495,7 @@  discard block
 block discarded – undo
495 495
 		foreach ($policies as $policy) {
496 496
 			header(
497 497
 				sprintf(
498
-					'Set-Cookie: %snc_sameSiteCookie%s=true; path=%s; httponly;' . $secureCookie . 'expires=Fri, 31-Dec-2100 23:59:59 GMT; SameSite=%s',
498
+					'Set-Cookie: %snc_sameSiteCookie%s=true; path=%s; httponly;'.$secureCookie.'expires=Fri, 31-Dec-2100 23:59:59 GMT; SameSite=%s',
499 499
 					$cookiePrefix,
500 500
 					$policy,
501 501
 					$cookieParams['path'],
@@ -569,12 +569,12 @@  discard block
 block discarded – undo
569 569
 
570 570
 		// register autoloader
571 571
 		$loaderStart = microtime(true);
572
-		require_once __DIR__ . '/autoloader.php';
572
+		require_once __DIR__.'/autoloader.php';
573 573
 		self::$loader = new \OC\Autoloader([
574
-			OC::$SERVERROOT . '/lib/private/legacy',
574
+			OC::$SERVERROOT.'/lib/private/legacy',
575 575
 		]);
576 576
 		if (defined('PHPUNIT_RUN')) {
577
-			self::$loader->addValidRoot(OC::$SERVERROOT . '/tests');
577
+			self::$loader->addValidRoot(OC::$SERVERROOT.'/tests');
578 578
 		}
579 579
 		spl_autoload_register([self::$loader, 'load']);
580 580
 		$loaderEnd = microtime(true);
@@ -582,13 +582,13 @@  discard block
 block discarded – undo
582 582
 		self::$CLI = (php_sapi_name() == 'cli');
583 583
 
584 584
 		// Add default composer PSR-4 autoloader
585
-		self::$composerAutoloader = require_once OC::$SERVERROOT . '/lib/composer/autoload.php';
585
+		self::$composerAutoloader = require_once OC::$SERVERROOT.'/lib/composer/autoload.php';
586 586
 		self::$composerAutoloader->setApcuPrefix('composer_autoload');
587 587
 
588 588
 		try {
589 589
 			self::initPaths();
590 590
 			// setup 3rdparty autoloader
591
-			$vendorAutoLoad = OC::$SERVERROOT. '/3rdparty/autoload.php';
591
+			$vendorAutoLoad = OC::$SERVERROOT.'/3rdparty/autoload.php';
592 592
 			if (!file_exists($vendorAutoLoad)) {
593 593
 				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".');
594 594
 			}
@@ -703,11 +703,11 @@  discard block
 block discarded – undo
703 703
 				// Convert l10n string into regular string for usage in database
704 704
 				$staticErrors = [];
705 705
 				foreach ($errors as $error) {
706
-					echo $error['error'] . "\n";
707
-					echo $error['hint'] . "\n\n";
706
+					echo $error['error']."\n";
707
+					echo $error['hint']."\n\n";
708 708
 					$staticErrors[] = [
709
-						'error' => (string)$error['error'],
710
-						'hint' => (string)$error['hint'],
709
+						'error' => (string) $error['error'],
710
+						'hint' => (string) $error['hint'],
711 711
 					];
712 712
 				}
713 713
 
@@ -823,7 +823,7 @@  discard block
 block discarded – undo
823 823
 		$eventLogger->log('init', 'OC::init', $loaderStart, microtime(true));
824 824
 		$eventLogger->start('runtime', 'Runtime');
825 825
 		$eventLogger->start('request', 'Full request after boot');
826
-		register_shutdown_function(function () use ($eventLogger) {
826
+		register_shutdown_function(function() use ($eventLogger) {
827 827
 			$eventLogger->end('request');
828 828
 		});
829 829
 	}
@@ -836,7 +836,7 @@  discard block
 block discarded – undo
836 836
 		if ($systemConfig->getValue('installed', false) && !\OCP\Util::needUpgrade()) {
837 837
 			// NOTE: This will be replaced to use OCP
838 838
 			$userSession = Server::get(\OC\User\Session::class);
839
-			$userSession->listen('\OC\User', 'postLogin', function () use ($userSession) {
839
+			$userSession->listen('\OC\User', 'postLogin', function() use ($userSession) {
840 840
 				if (!defined('PHPUNIT_RUN') && $userSession->isLoggedIn()) {
841 841
 					// reset brute force delay for this IP address and username
842 842
 					$uid = $userSession->getUser()->getUID();
@@ -887,7 +887,7 @@  discard block
 block discarded – undo
887 887
 	private static function registerAppRestrictionsHooks(): void {
888 888
 		/** @var \OC\Group\Manager $groupManager */
889 889
 		$groupManager = Server::get(\OCP\IGroupManager::class);
890
-		$groupManager->listen('\OC\Group', 'postDelete', function (\OCP\IGroup $group) {
890
+		$groupManager->listen('\OC\Group', 'postDelete', function(\OCP\IGroup $group) {
891 891
 			$appManager = Server::get(\OCP\App\IAppManager::class);
892 892
 			$apps = $appManager->getEnabledAppsForGroup($group);
893 893
 			foreach ($apps as $appId) {
@@ -995,7 +995,7 @@  discard block
 block discarded – undo
995 995
 		) {
996 996
 			\OC_JSON::callCheck();
997 997
 			\OC_JSON::checkAdminUser();
998
-			$appIds = (array)$request->getParam('appid');
998
+			$appIds = (array) $request->getParam('appid');
999 999
 			foreach ($appIds as $appId) {
1000 1000
 				$appId = \OC_App::cleanAppId($appId);
1001 1001
 				Server::get(\OCP\App\IAppManager::class)->disableApp($appId);
@@ -1071,10 +1071,10 @@  discard block
 block discarded – undo
1071 1071
 		if ($requestPath === '') {
1072 1072
 			// Someone is logged in
1073 1073
 			if (Server::get(IUserSession::class)->isLoggedIn()) {
1074
-				header('Location: ' . Server::get(IURLGenerator::class)->linkToDefaultPageUrl());
1074
+				header('Location: '.Server::get(IURLGenerator::class)->linkToDefaultPageUrl());
1075 1075
 			} else {
1076 1076
 				// Not handled and not logged in
1077
-				header('Location: ' . Server::get(IURLGenerator::class)->linkToRouteAbsolute('core.login.showLoginForm'));
1077
+				header('Location: '.Server::get(IURLGenerator::class)->linkToRouteAbsolute('core.login.showLoginForm'));
1078 1078
 			}
1079 1079
 			return;
1080 1080
 		}
Please login to merge, or discard this patch.
lib/private/AppFramework/Http/Request.php 1 patch
Indentation   +800 added lines, -800 removed lines patch added patch discarded remove patch
@@ -64,804 +64,804 @@
 block discarded – undo
64 64
  * @property mixed[] server
65 65
  */
66 66
 class Request implements \ArrayAccess, \Countable, IRequest {
67
-	public const USER_AGENT_IE = '/(MSIE)|(Trident)/';
68
-	// Microsoft Edge User Agent from https://msdn.microsoft.com/en-us/library/hh869301(v=vs.85).aspx
69
-	public const USER_AGENT_MS_EDGE = '/^Mozilla\/5\.0 \([^)]+\) AppleWebKit\/[0-9.]+ \(KHTML, like Gecko\) Chrome\/[0-9.]+ (Mobile Safari|Safari)\/[0-9.]+ Edge\/[0-9.]+$/';
70
-	// Firefox User Agent from https://developer.mozilla.org/en-US/docs/Web/HTTP/Gecko_user_agent_string_reference
71
-	public const USER_AGENT_FIREFOX = '/^Mozilla\/5\.0 \([^)]+\) Gecko\/[0-9.]+ Firefox\/[0-9.]+$/';
72
-	// Chrome User Agent from https://developer.chrome.com/multidevice/user-agent
73
-	public const USER_AGENT_CHROME = '/^Mozilla\/5\.0 \([^)]+\) AppleWebKit\/[0-9.]+ \(KHTML, like Gecko\)( Ubuntu Chromium\/[0-9.]+|) Chrome\/[0-9.]+ (Mobile Safari|Safari)\/[0-9.]+( (Vivaldi|Brave|OPR)\/[0-9.]+|)$/';
74
-	// Safari User Agent from http://www.useragentstring.com/pages/Safari/
75
-	public const USER_AGENT_SAFARI = '/^Mozilla\/5\.0 \([^)]+\) AppleWebKit\/[0-9.]+ \(KHTML, like Gecko\) Version\/[0-9.]+ Safari\/[0-9.A-Z]+$/';
76
-	// Android Chrome user agent: https://developers.google.com/chrome/mobile/docs/user-agent
77
-	public const USER_AGENT_ANDROID_MOBILE_CHROME = '#Android.*Chrome/[.0-9]*#';
78
-	public const USER_AGENT_FREEBOX = '#^Mozilla/5\.0$#';
79
-	public const REGEX_LOCALHOST = '/^(127\.0\.0\.1|localhost|\[::1\])$/';
80
-
81
-	protected string $inputStream;
82
-	protected $content;
83
-	protected array $items = [];
84
-	protected array $allowedKeys = [
85
-		'get',
86
-		'post',
87
-		'files',
88
-		'server',
89
-		'env',
90
-		'cookies',
91
-		'urlParams',
92
-		'parameters',
93
-		'method',
94
-		'requesttoken',
95
-	];
96
-	protected IRequestId $requestId;
97
-	protected IConfig $config;
98
-	protected ?CsrfTokenManager $csrfTokenManager;
99
-
100
-	protected bool $contentDecoded = false;
101
-
102
-	/**
103
-	 * @param array $vars An associative array with the following optional values:
104
-	 *        - array 'urlParams' the parameters which were matched from the URL
105
-	 *        - array 'get' the $_GET array
106
-	 *        - array|string 'post' the $_POST array or JSON string
107
-	 *        - array 'files' the $_FILES array
108
-	 *        - array 'server' the $_SERVER array
109
-	 *        - array 'env' the $_ENV array
110
-	 *        - array 'cookies' the $_COOKIE array
111
-	 *        - string 'method' the request method (GET, POST etc)
112
-	 *        - string|false 'requesttoken' the requesttoken or false when not available
113
-	 * @param IRequestId $requestId
114
-	 * @param IConfig $config
115
-	 * @param CsrfTokenManager|null $csrfTokenManager
116
-	 * @param string $stream
117
-	 * @see https://www.php.net/manual/en/reserved.variables.php
118
-	 */
119
-	public function __construct(array $vars,
120
-								IRequestId $requestId,
121
-								IConfig $config,
122
-								CsrfTokenManager $csrfTokenManager = null,
123
-								string $stream = 'php://input') {
124
-		$this->inputStream = $stream;
125
-		$this->items['params'] = [];
126
-		$this->requestId = $requestId;
127
-		$this->config = $config;
128
-		$this->csrfTokenManager = $csrfTokenManager;
129
-
130
-		if (!array_key_exists('method', $vars)) {
131
-			$vars['method'] = 'GET';
132
-		}
133
-
134
-		foreach ($this->allowedKeys as $name) {
135
-			$this->items[$name] = $vars[$name] ?? [];
136
-		}
137
-
138
-		$this->items['parameters'] = array_merge(
139
-			$this->items['get'],
140
-			$this->items['post'],
141
-			$this->items['urlParams'],
142
-			$this->items['params']
143
-		);
144
-	}
145
-	/**
146
-	 * @param array $parameters
147
-	 */
148
-	public function setUrlParameters(array $parameters) {
149
-		$this->items['urlParams'] = $parameters;
150
-		$this->items['parameters'] = array_merge(
151
-			$this->items['parameters'],
152
-			$this->items['urlParams']
153
-		);
154
-	}
155
-
156
-	/**
157
-	 * Countable method
158
-	 * @return int
159
-	 */
160
-	public function count(): int {
161
-		return \count($this->items['parameters']);
162
-	}
163
-
164
-	/**
165
-	 * ArrayAccess methods
166
-	 *
167
-	 * Gives access to the combined GET, POST and urlParams arrays
168
-	 *
169
-	 * Examples:
170
-	 *
171
-	 * $var = $request['myvar'];
172
-	 *
173
-	 * or
174
-	 *
175
-	 * if(!isset($request['myvar']) {
176
-	 * 	// Do something
177
-	 * }
178
-	 *
179
-	 * $request['myvar'] = 'something'; // This throws an exception.
180
-	 *
181
-	 * @param string $offset The key to lookup
182
-	 * @return boolean
183
-	 */
184
-	public function offsetExists($offset): bool {
185
-		return isset($this->items['parameters'][$offset]);
186
-	}
187
-
188
-	/**
189
-	 * @see offsetExists
190
-	 * @param string $offset
191
-	 * @return mixed
192
-	 */
193
-	#[\ReturnTypeWillChange]
194
-	public function offsetGet($offset) {
195
-		return isset($this->items['parameters'][$offset])
196
-			? $this->items['parameters'][$offset]
197
-			: null;
198
-	}
199
-
200
-	/**
201
-	 * @see offsetExists
202
-	 * @param string $offset
203
-	 * @param mixed $value
204
-	 */
205
-	public function offsetSet($offset, $value): void {
206
-		throw new \RuntimeException('You cannot change the contents of the request object');
207
-	}
208
-
209
-	/**
210
-	 * @see offsetExists
211
-	 * @param string $offset
212
-	 */
213
-	public function offsetUnset($offset): void {
214
-		throw new \RuntimeException('You cannot change the contents of the request object');
215
-	}
216
-
217
-	/**
218
-	 * Magic property accessors
219
-	 * @param string $name
220
-	 * @param mixed $value
221
-	 */
222
-	public function __set($name, $value) {
223
-		throw new \RuntimeException('You cannot change the contents of the request object');
224
-	}
225
-
226
-	/**
227
-	 * Access request variables by method and name.
228
-	 * Examples:
229
-	 *
230
-	 * $request->post['myvar']; // Only look for POST variables
231
-	 * $request->myvar; or $request->{'myvar'}; or $request->{$myvar}
232
-	 * Looks in the combined GET, POST and urlParams array.
233
-	 *
234
-	 * If you access e.g. ->post but the current HTTP request method
235
-	 * is GET a \LogicException will be thrown.
236
-	 *
237
-	 * @param string $name The key to look for.
238
-	 * @throws \LogicException
239
-	 * @return mixed|null
240
-	 */
241
-	public function __get($name) {
242
-		switch ($name) {
243
-			case 'put':
244
-			case 'patch':
245
-			case 'get':
246
-			case 'post':
247
-				if ($this->method !== strtoupper($name)) {
248
-					throw new \LogicException(sprintf('%s cannot be accessed in a %s request.', $name, $this->method));
249
-				}
250
-				return $this->getContent();
251
-			case 'files':
252
-			case 'server':
253
-			case 'env':
254
-			case 'cookies':
255
-			case 'urlParams':
256
-			case 'method':
257
-				return isset($this->items[$name])
258
-					? $this->items[$name]
259
-					: null;
260
-			case 'parameters':
261
-			case 'params':
262
-				return $this->getContent();
263
-			default:
264
-				return isset($this[$name])
265
-					? $this[$name]
266
-					: null;
267
-		}
268
-	}
269
-
270
-	/**
271
-	 * @param string $name
272
-	 * @return bool
273
-	 */
274
-	public function __isset($name) {
275
-		if (\in_array($name, $this->allowedKeys, true)) {
276
-			return true;
277
-		}
278
-		return isset($this->items['parameters'][$name]);
279
-	}
280
-
281
-	/**
282
-	 * @param string $id
283
-	 */
284
-	public function __unset($id) {
285
-		throw new \RuntimeException('You cannot change the contents of the request object');
286
-	}
287
-
288
-	/**
289
-	 * Returns the value for a specific http header.
290
-	 *
291
-	 * This method returns an empty string if the header did not exist.
292
-	 *
293
-	 * @param string $name
294
-	 * @return string
295
-	 */
296
-	public function getHeader(string $name): string {
297
-		$name = strtoupper(str_replace('-', '_', $name));
298
-		if (isset($this->server['HTTP_' . $name])) {
299
-			return $this->server['HTTP_' . $name];
300
-		}
301
-
302
-		// There's a few headers that seem to end up in the top-level
303
-		// server array.
304
-		switch ($name) {
305
-			case 'CONTENT_TYPE':
306
-			case 'CONTENT_LENGTH':
307
-			case 'REMOTE_ADDR':
308
-				if (isset($this->server[$name])) {
309
-					return $this->server[$name];
310
-				}
311
-				break;
312
-		}
313
-
314
-		return '';
315
-	}
316
-
317
-	/**
318
-	 * Lets you access post and get parameters by the index
319
-	 * In case of json requests the encoded json body is accessed
320
-	 *
321
-	 * @param string $key the key which you want to access in the URL Parameter
322
-	 *                     placeholder, $_POST or $_GET array.
323
-	 *                     The priority how they're returned is the following:
324
-	 *                     1. URL parameters
325
-	 *                     2. POST parameters
326
-	 *                     3. GET parameters
327
-	 * @param mixed $default If the key is not found, this value will be returned
328
-	 * @return mixed the content of the array
329
-	 */
330
-	public function getParam(string $key, $default = null) {
331
-		return isset($this->parameters[$key])
332
-			? $this->parameters[$key]
333
-			: $default;
334
-	}
335
-
336
-	/**
337
-	 * Returns all params that were received, be it from the request
338
-	 * (as GET or POST) or through the URL by the route
339
-	 * @return array the array with all parameters
340
-	 */
341
-	public function getParams(): array {
342
-		return is_array($this->parameters) ? $this->parameters : [];
343
-	}
344
-
345
-	/**
346
-	 * Returns the method of the request
347
-	 * @return string the method of the request (POST, GET, etc)
348
-	 */
349
-	public function getMethod(): string {
350
-		return $this->method;
351
-	}
352
-
353
-	/**
354
-	 * Shortcut for accessing an uploaded file through the $_FILES array
355
-	 * @param string $key the key that will be taken from the $_FILES array
356
-	 * @return array the file in the $_FILES element
357
-	 */
358
-	public function getUploadedFile(string $key) {
359
-		return isset($this->files[$key]) ? $this->files[$key] : null;
360
-	}
361
-
362
-	/**
363
-	 * Shortcut for getting env variables
364
-	 * @param string $key the key that will be taken from the $_ENV array
365
-	 * @return array the value in the $_ENV element
366
-	 */
367
-	public function getEnv(string $key) {
368
-		return isset($this->env[$key]) ? $this->env[$key] : null;
369
-	}
370
-
371
-	/**
372
-	 * Shortcut for getting cookie variables
373
-	 * @param string $key the key that will be taken from the $_COOKIE array
374
-	 * @return string the value in the $_COOKIE element
375
-	 */
376
-	public function getCookie(string $key) {
377
-		return isset($this->cookies[$key]) ? $this->cookies[$key] : null;
378
-	}
379
-
380
-	/**
381
-	 * Returns the request body content.
382
-	 *
383
-	 * If the HTTP request method is PUT and the body
384
-	 * not application/x-www-form-urlencoded or application/json a stream
385
-	 * resource is returned, otherwise an array.
386
-	 *
387
-	 * @return array|string|resource The request body content or a resource to read the body stream.
388
-	 *
389
-	 * @throws \LogicException
390
-	 */
391
-	protected function getContent() {
392
-		// If the content can't be parsed into an array then return a stream resource.
393
-		if ($this->method === 'PUT'
394
-			&& $this->getHeader('Content-Length') !== '0'
395
-			&& $this->getHeader('Content-Length') !== ''
396
-			&& strpos($this->getHeader('Content-Type'), 'application/x-www-form-urlencoded') === false
397
-			&& strpos($this->getHeader('Content-Type'), 'application/json') === false
398
-		) {
399
-			if ($this->content === false) {
400
-				throw new \LogicException(
401
-					'"put" can only be accessed once if not '
402
-					. 'application/x-www-form-urlencoded or application/json.'
403
-				);
404
-			}
405
-			$this->content = false;
406
-			return fopen($this->inputStream, 'rb');
407
-		} else {
408
-			$this->decodeContent();
409
-			return $this->items['parameters'];
410
-		}
411
-	}
412
-
413
-	/**
414
-	 * Attempt to decode the content and populate parameters
415
-	 */
416
-	protected function decodeContent() {
417
-		if ($this->contentDecoded) {
418
-			return;
419
-		}
420
-		$params = [];
421
-
422
-		// 'application/json' must be decoded manually.
423
-		if (strpos($this->getHeader('Content-Type'), 'application/json') !== false) {
424
-			$params = json_decode(file_get_contents($this->inputStream), true);
425
-			if (\is_array($params) && \count($params) > 0) {
426
-				$this->items['params'] = $params;
427
-				if ($this->method === 'POST') {
428
-					$this->items['post'] = $params;
429
-				}
430
-			}
431
-			// Handle application/x-www-form-urlencoded for methods other than GET
432
-		// or post correctly
433
-		} elseif ($this->method !== 'GET'
434
-				&& $this->method !== 'POST'
435
-				&& strpos($this->getHeader('Content-Type'), 'application/x-www-form-urlencoded') !== false) {
436
-			parse_str(file_get_contents($this->inputStream), $params);
437
-			if (\is_array($params)) {
438
-				$this->items['params'] = $params;
439
-			}
440
-		}
441
-
442
-		if (\is_array($params)) {
443
-			$this->items['parameters'] = array_merge($this->items['parameters'], $params);
444
-		}
445
-		$this->contentDecoded = true;
446
-	}
447
-
448
-
449
-	/**
450
-	 * Checks if the CSRF check was correct
451
-	 * @return bool true if CSRF check passed
452
-	 */
453
-	public function passesCSRFCheck(): bool {
454
-		if ($this->csrfTokenManager === null) {
455
-			return false;
456
-		}
457
-
458
-		if (!$this->passesStrictCookieCheck()) {
459
-			return false;
460
-		}
461
-
462
-		if (isset($this->items['get']['requesttoken'])) {
463
-			$token = $this->items['get']['requesttoken'];
464
-		} elseif (isset($this->items['post']['requesttoken'])) {
465
-			$token = $this->items['post']['requesttoken'];
466
-		} elseif (isset($this->items['server']['HTTP_REQUESTTOKEN'])) {
467
-			$token = $this->items['server']['HTTP_REQUESTTOKEN'];
468
-		} else {
469
-			//no token found.
470
-			return false;
471
-		}
472
-		$token = new CsrfToken($token);
473
-
474
-		return $this->csrfTokenManager->isTokenValid($token);
475
-	}
476
-
477
-	/**
478
-	 * Whether the cookie checks are required
479
-	 *
480
-	 * @return bool
481
-	 */
482
-	private function cookieCheckRequired(): bool {
483
-		if ($this->getHeader('OCS-APIREQUEST')) {
484
-			return false;
485
-		}
486
-		if ($this->getCookie(session_name()) === null && $this->getCookie('nc_token') === null) {
487
-			return false;
488
-		}
489
-
490
-		return true;
491
-	}
492
-
493
-	/**
494
-	 * Wrapper around session_get_cookie_params
495
-	 *
496
-	 * @return array
497
-	 */
498
-	public function getCookieParams(): array {
499
-		return session_get_cookie_params();
500
-	}
501
-
502
-	/**
503
-	 * Appends the __Host- prefix to the cookie if applicable
504
-	 *
505
-	 * @param string $name
506
-	 * @return string
507
-	 */
508
-	protected function getProtectedCookieName(string $name): string {
509
-		$cookieParams = $this->getCookieParams();
510
-		$prefix = '';
511
-		if ($cookieParams['secure'] === true && $cookieParams['path'] === '/') {
512
-			$prefix = '__Host-';
513
-		}
514
-
515
-		return $prefix.$name;
516
-	}
517
-
518
-	/**
519
-	 * Checks if the strict cookie has been sent with the request if the request
520
-	 * is including any cookies.
521
-	 *
522
-	 * @return bool
523
-	 * @since 9.1.0
524
-	 */
525
-	public function passesStrictCookieCheck(): bool {
526
-		if (!$this->cookieCheckRequired()) {
527
-			return true;
528
-		}
529
-
530
-		$cookieName = $this->getProtectedCookieName('nc_sameSiteCookiestrict');
531
-		if ($this->getCookie($cookieName) === 'true'
532
-			&& $this->passesLaxCookieCheck()) {
533
-			return true;
534
-		}
535
-		return false;
536
-	}
537
-
538
-	/**
539
-	 * Checks if the lax cookie has been sent with the request if the request
540
-	 * is including any cookies.
541
-	 *
542
-	 * @return bool
543
-	 * @since 9.1.0
544
-	 */
545
-	public function passesLaxCookieCheck(): bool {
546
-		if (!$this->cookieCheckRequired()) {
547
-			return true;
548
-		}
549
-
550
-		$cookieName = $this->getProtectedCookieName('nc_sameSiteCookielax');
551
-		if ($this->getCookie($cookieName) === 'true') {
552
-			return true;
553
-		}
554
-		return false;
555
-	}
556
-
557
-
558
-	/**
559
-	 * Returns an ID for the request, value is not guaranteed to be unique and is mostly meant for logging
560
-	 * If `mod_unique_id` is installed this value will be taken.
561
-	 * @return string
562
-	 */
563
-	public function getId(): string {
564
-		return $this->requestId->getId();
565
-	}
566
-
567
-	/**
568
-	 * Checks if given $remoteAddress matches any entry in the given array $trustedProxies.
569
-	 * For details regarding what "match" means, refer to `matchesTrustedProxy`.
570
-	 * @return boolean true if $remoteAddress matches any entry in $trustedProxies, false otherwise
571
-	 */
572
-	protected function isTrustedProxy($trustedProxies, $remoteAddress) {
573
-		return IpUtils::checkIp($remoteAddress, $trustedProxies);
574
-	}
575
-
576
-	/**
577
-	 * Returns the remote address, if the connection came from a trusted proxy
578
-	 * and `forwarded_for_headers` has been configured then the IP address
579
-	 * specified in this header will be returned instead.
580
-	 * Do always use this instead of $_SERVER['REMOTE_ADDR']
581
-	 * @return string IP address
582
-	 */
583
-	public function getRemoteAddress(): string {
584
-		$remoteAddress = isset($this->server['REMOTE_ADDR']) ? $this->server['REMOTE_ADDR'] : '';
585
-		$trustedProxies = $this->config->getSystemValue('trusted_proxies', []);
586
-
587
-		if (\is_array($trustedProxies) && $this->isTrustedProxy($trustedProxies, $remoteAddress)) {
588
-			$forwardedForHeaders = $this->config->getSystemValue('forwarded_for_headers', [
589
-				'HTTP_X_FORWARDED_FOR'
590
-				// only have one default, so we cannot ship an insecure product out of the box
591
-			]);
592
-
593
-			foreach ($forwardedForHeaders as $header) {
594
-				if (isset($this->server[$header])) {
595
-					foreach (explode(',', $this->server[$header]) as $IP) {
596
-						$IP = trim($IP);
597
-
598
-						// remove brackets from IPv6 addresses
599
-						if (strpos($IP, '[') === 0 && substr($IP, -1) === ']') {
600
-							$IP = substr($IP, 1, -1);
601
-						}
602
-
603
-						if (filter_var($IP, FILTER_VALIDATE_IP) !== false) {
604
-							return $IP;
605
-						}
606
-					}
607
-				}
608
-			}
609
-		}
610
-
611
-		return $remoteAddress;
612
-	}
613
-
614
-	/**
615
-	 * Check overwrite condition
616
-	 * @param string $type
617
-	 * @return bool
618
-	 */
619
-	private function isOverwriteCondition(string $type = ''): bool {
620
-		$regex = '/' . $this->config->getSystemValue('overwritecondaddr', '')  . '/';
621
-		$remoteAddr = isset($this->server['REMOTE_ADDR']) ? $this->server['REMOTE_ADDR'] : '';
622
-		return $regex === '//' || preg_match($regex, $remoteAddr) === 1
623
-		|| $type !== 'protocol';
624
-	}
625
-
626
-	/**
627
-	 * Returns the server protocol. It respects one or more reverse proxies servers
628
-	 * and load balancers
629
-	 * @return string Server protocol (http or https)
630
-	 */
631
-	public function getServerProtocol(): string {
632
-		if ($this->config->getSystemValue('overwriteprotocol') !== ''
633
-			&& $this->isOverwriteCondition('protocol')) {
634
-			return $this->config->getSystemValue('overwriteprotocol');
635
-		}
636
-
637
-		if ($this->fromTrustedProxy() && isset($this->server['HTTP_X_FORWARDED_PROTO'])) {
638
-			if (strpos($this->server['HTTP_X_FORWARDED_PROTO'], ',') !== false) {
639
-				$parts = explode(',', $this->server['HTTP_X_FORWARDED_PROTO']);
640
-				$proto = strtolower(trim($parts[0]));
641
-			} else {
642
-				$proto = strtolower($this->server['HTTP_X_FORWARDED_PROTO']);
643
-			}
644
-
645
-			// Verify that the protocol is always HTTP or HTTPS
646
-			// default to http if an invalid value is provided
647
-			return $proto === 'https' ? 'https' : 'http';
648
-		}
649
-
650
-		if (isset($this->server['HTTPS'])
651
-			&& $this->server['HTTPS'] !== null
652
-			&& $this->server['HTTPS'] !== 'off'
653
-			&& $this->server['HTTPS'] !== '') {
654
-			return 'https';
655
-		}
656
-
657
-		return 'http';
658
-	}
659
-
660
-	/**
661
-	 * Returns the used HTTP protocol.
662
-	 *
663
-	 * @return string HTTP protocol. HTTP/2, HTTP/1.1 or HTTP/1.0.
664
-	 */
665
-	public function getHttpProtocol(): string {
666
-		$claimedProtocol = $this->server['SERVER_PROTOCOL'];
667
-
668
-		if (\is_string($claimedProtocol)) {
669
-			$claimedProtocol = strtoupper($claimedProtocol);
670
-		}
671
-
672
-		$validProtocols = [
673
-			'HTTP/1.0',
674
-			'HTTP/1.1',
675
-			'HTTP/2',
676
-		];
677
-
678
-		if (\in_array($claimedProtocol, $validProtocols, true)) {
679
-			return $claimedProtocol;
680
-		}
681
-
682
-		return 'HTTP/1.1';
683
-	}
684
-
685
-	/**
686
-	 * Returns the request uri, even if the website uses one or more
687
-	 * reverse proxies
688
-	 * @return string
689
-	 */
690
-	public function getRequestUri(): string {
691
-		$uri = isset($this->server['REQUEST_URI']) ? $this->server['REQUEST_URI'] : '';
692
-		if ($this->config->getSystemValue('overwritewebroot') !== '' && $this->isOverwriteCondition()) {
693
-			$uri = $this->getScriptName() . substr($uri, \strlen($this->server['SCRIPT_NAME']));
694
-		}
695
-		return $uri;
696
-	}
697
-
698
-	/**
699
-	 * Get raw PathInfo from request (not urldecoded)
700
-	 * @throws \Exception
701
-	 * @return string Path info
702
-	 */
703
-	public function getRawPathInfo(): string {
704
-		$requestUri = isset($this->server['REQUEST_URI']) ? $this->server['REQUEST_URI'] : '';
705
-		// remove too many slashes - can be caused by reverse proxy configuration
706
-		$requestUri = preg_replace('%/{2,}%', '/', $requestUri);
707
-
708
-		// Remove the query string from REQUEST_URI
709
-		if ($pos = strpos($requestUri, '?')) {
710
-			$requestUri = substr($requestUri, 0, $pos);
711
-		}
712
-
713
-		$scriptName = $this->server['SCRIPT_NAME'];
714
-		$pathInfo = $requestUri;
715
-
716
-		// strip off the script name's dir and file name
717
-		// FIXME: Sabre does not really belong here
718
-		[$path, $name] = \Sabre\Uri\split($scriptName);
719
-		if (!empty($path)) {
720
-			if ($path === $pathInfo || strpos($pathInfo, $path.'/') === 0) {
721
-				$pathInfo = substr($pathInfo, \strlen($path));
722
-			} else {
723
-				throw new \Exception("The requested uri($requestUri) cannot be processed by the script '$scriptName')");
724
-			}
725
-		}
726
-		if ($name === null) {
727
-			$name = '';
728
-		}
729
-
730
-		if (strpos($pathInfo, '/'.$name) === 0) {
731
-			$pathInfo = substr($pathInfo, \strlen($name) + 1);
732
-		}
733
-		if ($name !== '' && strpos($pathInfo, $name) === 0) {
734
-			$pathInfo = substr($pathInfo, \strlen($name));
735
-		}
736
-		if ($pathInfo === false || $pathInfo === '/') {
737
-			return '';
738
-		} else {
739
-			return $pathInfo;
740
-		}
741
-	}
742
-
743
-	/**
744
-	 * Get PathInfo from request
745
-	 * @throws \Exception
746
-	 * @return string|false Path info or false when not found
747
-	 */
748
-	public function getPathInfo() {
749
-		$pathInfo = $this->getRawPathInfo();
750
-		return \Sabre\HTTP\decodePath($pathInfo);
751
-	}
752
-
753
-	/**
754
-	 * Returns the script name, even if the website uses one or more
755
-	 * reverse proxies
756
-	 * @return string the script name
757
-	 */
758
-	public function getScriptName(): string {
759
-		$name = $this->server['SCRIPT_NAME'];
760
-		$overwriteWebRoot = $this->config->getSystemValue('overwritewebroot');
761
-		if ($overwriteWebRoot !== '' && $this->isOverwriteCondition()) {
762
-			// FIXME: This code is untestable due to __DIR__, also that hardcoded path is really dangerous
763
-			$serverRoot = str_replace('\\', '/', substr(__DIR__, 0, -\strlen('lib/private/appframework/http/')));
764
-			$suburi = str_replace('\\', '/', substr(realpath($this->server['SCRIPT_FILENAME']), \strlen($serverRoot)));
765
-			$name = '/' . ltrim($overwriteWebRoot . $suburi, '/');
766
-		}
767
-		return $name;
768
-	}
769
-
770
-	/**
771
-	 * Checks whether the user agent matches a given regex
772
-	 * @param array $agent array of agent names
773
-	 * @return bool true if at least one of the given agent matches, false otherwise
774
-	 */
775
-	public function isUserAgent(array $agent): bool {
776
-		if (!isset($this->server['HTTP_USER_AGENT'])) {
777
-			return false;
778
-		}
779
-		foreach ($agent as $regex) {
780
-			if (preg_match($regex, $this->server['HTTP_USER_AGENT'])) {
781
-				return true;
782
-			}
783
-		}
784
-		return false;
785
-	}
786
-
787
-	/**
788
-	 * Returns the unverified server host from the headers without checking
789
-	 * whether it is a trusted domain
790
-	 * @return string Server host
791
-	 */
792
-	public function getInsecureServerHost(): string {
793
-		if ($this->fromTrustedProxy() && $this->getOverwriteHost() !== null) {
794
-			return $this->getOverwriteHost();
795
-		}
796
-
797
-		$host = 'localhost';
798
-		if ($this->fromTrustedProxy() && isset($this->server['HTTP_X_FORWARDED_HOST'])) {
799
-			if (strpos($this->server['HTTP_X_FORWARDED_HOST'], ',') !== false) {
800
-				$parts = explode(',', $this->server['HTTP_X_FORWARDED_HOST']);
801
-				$host = trim(current($parts));
802
-			} else {
803
-				$host = $this->server['HTTP_X_FORWARDED_HOST'];
804
-			}
805
-		} else {
806
-			if (isset($this->server['HTTP_HOST'])) {
807
-				$host = $this->server['HTTP_HOST'];
808
-			} elseif (isset($this->server['SERVER_NAME'])) {
809
-				$host = $this->server['SERVER_NAME'];
810
-			}
811
-		}
812
-
813
-		return $host;
814
-	}
815
-
816
-
817
-	/**
818
-	 * Returns the server host from the headers, or the first configured
819
-	 * trusted domain if the host isn't in the trusted list
820
-	 * @return string Server host
821
-	 */
822
-	public function getServerHost(): string {
823
-		// overwritehost is always trusted
824
-		$host = $this->getOverwriteHost();
825
-		if ($host !== null) {
826
-			return $host;
827
-		}
828
-
829
-		// get the host from the headers
830
-		$host = $this->getInsecureServerHost();
831
-
832
-		// Verify that the host is a trusted domain if the trusted domains
833
-		// are defined
834
-		// If no trusted domain is provided the first trusted domain is returned
835
-		$trustedDomainHelper = new TrustedDomainHelper($this->config);
836
-		if ($trustedDomainHelper->isTrustedDomain($host)) {
837
-			return $host;
838
-		}
839
-
840
-		$trustedList = (array)$this->config->getSystemValue('trusted_domains', []);
841
-		if (count($trustedList) > 0) {
842
-			return reset($trustedList);
843
-		}
844
-
845
-		return '';
846
-	}
847
-
848
-	/**
849
-	 * Returns the overwritehost setting from the config if set and
850
-	 * if the overwrite condition is met
851
-	 * @return string|null overwritehost value or null if not defined or the defined condition
852
-	 * isn't met
853
-	 */
854
-	private function getOverwriteHost() {
855
-		if ($this->config->getSystemValue('overwritehost') !== '' && $this->isOverwriteCondition()) {
856
-			return $this->config->getSystemValue('overwritehost');
857
-		}
858
-		return null;
859
-	}
860
-
861
-	private function fromTrustedProxy(): bool {
862
-		$remoteAddress = isset($this->server['REMOTE_ADDR']) ? $this->server['REMOTE_ADDR'] : '';
863
-		$trustedProxies = $this->config->getSystemValue('trusted_proxies', []);
864
-
865
-		return \is_array($trustedProxies) && $this->isTrustedProxy($trustedProxies, $remoteAddress);
866
-	}
67
+    public const USER_AGENT_IE = '/(MSIE)|(Trident)/';
68
+    // Microsoft Edge User Agent from https://msdn.microsoft.com/en-us/library/hh869301(v=vs.85).aspx
69
+    public const USER_AGENT_MS_EDGE = '/^Mozilla\/5\.0 \([^)]+\) AppleWebKit\/[0-9.]+ \(KHTML, like Gecko\) Chrome\/[0-9.]+ (Mobile Safari|Safari)\/[0-9.]+ Edge\/[0-9.]+$/';
70
+    // Firefox User Agent from https://developer.mozilla.org/en-US/docs/Web/HTTP/Gecko_user_agent_string_reference
71
+    public const USER_AGENT_FIREFOX = '/^Mozilla\/5\.0 \([^)]+\) Gecko\/[0-9.]+ Firefox\/[0-9.]+$/';
72
+    // Chrome User Agent from https://developer.chrome.com/multidevice/user-agent
73
+    public const USER_AGENT_CHROME = '/^Mozilla\/5\.0 \([^)]+\) AppleWebKit\/[0-9.]+ \(KHTML, like Gecko\)( Ubuntu Chromium\/[0-9.]+|) Chrome\/[0-9.]+ (Mobile Safari|Safari)\/[0-9.]+( (Vivaldi|Brave|OPR)\/[0-9.]+|)$/';
74
+    // Safari User Agent from http://www.useragentstring.com/pages/Safari/
75
+    public const USER_AGENT_SAFARI = '/^Mozilla\/5\.0 \([^)]+\) AppleWebKit\/[0-9.]+ \(KHTML, like Gecko\) Version\/[0-9.]+ Safari\/[0-9.A-Z]+$/';
76
+    // Android Chrome user agent: https://developers.google.com/chrome/mobile/docs/user-agent
77
+    public const USER_AGENT_ANDROID_MOBILE_CHROME = '#Android.*Chrome/[.0-9]*#';
78
+    public const USER_AGENT_FREEBOX = '#^Mozilla/5\.0$#';
79
+    public const REGEX_LOCALHOST = '/^(127\.0\.0\.1|localhost|\[::1\])$/';
80
+
81
+    protected string $inputStream;
82
+    protected $content;
83
+    protected array $items = [];
84
+    protected array $allowedKeys = [
85
+        'get',
86
+        'post',
87
+        'files',
88
+        'server',
89
+        'env',
90
+        'cookies',
91
+        'urlParams',
92
+        'parameters',
93
+        'method',
94
+        'requesttoken',
95
+    ];
96
+    protected IRequestId $requestId;
97
+    protected IConfig $config;
98
+    protected ?CsrfTokenManager $csrfTokenManager;
99
+
100
+    protected bool $contentDecoded = false;
101
+
102
+    /**
103
+     * @param array $vars An associative array with the following optional values:
104
+     *        - array 'urlParams' the parameters which were matched from the URL
105
+     *        - array 'get' the $_GET array
106
+     *        - array|string 'post' the $_POST array or JSON string
107
+     *        - array 'files' the $_FILES array
108
+     *        - array 'server' the $_SERVER array
109
+     *        - array 'env' the $_ENV array
110
+     *        - array 'cookies' the $_COOKIE array
111
+     *        - string 'method' the request method (GET, POST etc)
112
+     *        - string|false 'requesttoken' the requesttoken or false when not available
113
+     * @param IRequestId $requestId
114
+     * @param IConfig $config
115
+     * @param CsrfTokenManager|null $csrfTokenManager
116
+     * @param string $stream
117
+     * @see https://www.php.net/manual/en/reserved.variables.php
118
+     */
119
+    public function __construct(array $vars,
120
+                                IRequestId $requestId,
121
+                                IConfig $config,
122
+                                CsrfTokenManager $csrfTokenManager = null,
123
+                                string $stream = 'php://input') {
124
+        $this->inputStream = $stream;
125
+        $this->items['params'] = [];
126
+        $this->requestId = $requestId;
127
+        $this->config = $config;
128
+        $this->csrfTokenManager = $csrfTokenManager;
129
+
130
+        if (!array_key_exists('method', $vars)) {
131
+            $vars['method'] = 'GET';
132
+        }
133
+
134
+        foreach ($this->allowedKeys as $name) {
135
+            $this->items[$name] = $vars[$name] ?? [];
136
+        }
137
+
138
+        $this->items['parameters'] = array_merge(
139
+            $this->items['get'],
140
+            $this->items['post'],
141
+            $this->items['urlParams'],
142
+            $this->items['params']
143
+        );
144
+    }
145
+    /**
146
+     * @param array $parameters
147
+     */
148
+    public function setUrlParameters(array $parameters) {
149
+        $this->items['urlParams'] = $parameters;
150
+        $this->items['parameters'] = array_merge(
151
+            $this->items['parameters'],
152
+            $this->items['urlParams']
153
+        );
154
+    }
155
+
156
+    /**
157
+     * Countable method
158
+     * @return int
159
+     */
160
+    public function count(): int {
161
+        return \count($this->items['parameters']);
162
+    }
163
+
164
+    /**
165
+     * ArrayAccess methods
166
+     *
167
+     * Gives access to the combined GET, POST and urlParams arrays
168
+     *
169
+     * Examples:
170
+     *
171
+     * $var = $request['myvar'];
172
+     *
173
+     * or
174
+     *
175
+     * if(!isset($request['myvar']) {
176
+     * 	// Do something
177
+     * }
178
+     *
179
+     * $request['myvar'] = 'something'; // This throws an exception.
180
+     *
181
+     * @param string $offset The key to lookup
182
+     * @return boolean
183
+     */
184
+    public function offsetExists($offset): bool {
185
+        return isset($this->items['parameters'][$offset]);
186
+    }
187
+
188
+    /**
189
+     * @see offsetExists
190
+     * @param string $offset
191
+     * @return mixed
192
+     */
193
+    #[\ReturnTypeWillChange]
194
+    public function offsetGet($offset) {
195
+        return isset($this->items['parameters'][$offset])
196
+            ? $this->items['parameters'][$offset]
197
+            : null;
198
+    }
199
+
200
+    /**
201
+     * @see offsetExists
202
+     * @param string $offset
203
+     * @param mixed $value
204
+     */
205
+    public function offsetSet($offset, $value): void {
206
+        throw new \RuntimeException('You cannot change the contents of the request object');
207
+    }
208
+
209
+    /**
210
+     * @see offsetExists
211
+     * @param string $offset
212
+     */
213
+    public function offsetUnset($offset): void {
214
+        throw new \RuntimeException('You cannot change the contents of the request object');
215
+    }
216
+
217
+    /**
218
+     * Magic property accessors
219
+     * @param string $name
220
+     * @param mixed $value
221
+     */
222
+    public function __set($name, $value) {
223
+        throw new \RuntimeException('You cannot change the contents of the request object');
224
+    }
225
+
226
+    /**
227
+     * Access request variables by method and name.
228
+     * Examples:
229
+     *
230
+     * $request->post['myvar']; // Only look for POST variables
231
+     * $request->myvar; or $request->{'myvar'}; or $request->{$myvar}
232
+     * Looks in the combined GET, POST and urlParams array.
233
+     *
234
+     * If you access e.g. ->post but the current HTTP request method
235
+     * is GET a \LogicException will be thrown.
236
+     *
237
+     * @param string $name The key to look for.
238
+     * @throws \LogicException
239
+     * @return mixed|null
240
+     */
241
+    public function __get($name) {
242
+        switch ($name) {
243
+            case 'put':
244
+            case 'patch':
245
+            case 'get':
246
+            case 'post':
247
+                if ($this->method !== strtoupper($name)) {
248
+                    throw new \LogicException(sprintf('%s cannot be accessed in a %s request.', $name, $this->method));
249
+                }
250
+                return $this->getContent();
251
+            case 'files':
252
+            case 'server':
253
+            case 'env':
254
+            case 'cookies':
255
+            case 'urlParams':
256
+            case 'method':
257
+                return isset($this->items[$name])
258
+                    ? $this->items[$name]
259
+                    : null;
260
+            case 'parameters':
261
+            case 'params':
262
+                return $this->getContent();
263
+            default:
264
+                return isset($this[$name])
265
+                    ? $this[$name]
266
+                    : null;
267
+        }
268
+    }
269
+
270
+    /**
271
+     * @param string $name
272
+     * @return bool
273
+     */
274
+    public function __isset($name) {
275
+        if (\in_array($name, $this->allowedKeys, true)) {
276
+            return true;
277
+        }
278
+        return isset($this->items['parameters'][$name]);
279
+    }
280
+
281
+    /**
282
+     * @param string $id
283
+     */
284
+    public function __unset($id) {
285
+        throw new \RuntimeException('You cannot change the contents of the request object');
286
+    }
287
+
288
+    /**
289
+     * Returns the value for a specific http header.
290
+     *
291
+     * This method returns an empty string if the header did not exist.
292
+     *
293
+     * @param string $name
294
+     * @return string
295
+     */
296
+    public function getHeader(string $name): string {
297
+        $name = strtoupper(str_replace('-', '_', $name));
298
+        if (isset($this->server['HTTP_' . $name])) {
299
+            return $this->server['HTTP_' . $name];
300
+        }
301
+
302
+        // There's a few headers that seem to end up in the top-level
303
+        // server array.
304
+        switch ($name) {
305
+            case 'CONTENT_TYPE':
306
+            case 'CONTENT_LENGTH':
307
+            case 'REMOTE_ADDR':
308
+                if (isset($this->server[$name])) {
309
+                    return $this->server[$name];
310
+                }
311
+                break;
312
+        }
313
+
314
+        return '';
315
+    }
316
+
317
+    /**
318
+     * Lets you access post and get parameters by the index
319
+     * In case of json requests the encoded json body is accessed
320
+     *
321
+     * @param string $key the key which you want to access in the URL Parameter
322
+     *                     placeholder, $_POST or $_GET array.
323
+     *                     The priority how they're returned is the following:
324
+     *                     1. URL parameters
325
+     *                     2. POST parameters
326
+     *                     3. GET parameters
327
+     * @param mixed $default If the key is not found, this value will be returned
328
+     * @return mixed the content of the array
329
+     */
330
+    public function getParam(string $key, $default = null) {
331
+        return isset($this->parameters[$key])
332
+            ? $this->parameters[$key]
333
+            : $default;
334
+    }
335
+
336
+    /**
337
+     * Returns all params that were received, be it from the request
338
+     * (as GET or POST) or through the URL by the route
339
+     * @return array the array with all parameters
340
+     */
341
+    public function getParams(): array {
342
+        return is_array($this->parameters) ? $this->parameters : [];
343
+    }
344
+
345
+    /**
346
+     * Returns the method of the request
347
+     * @return string the method of the request (POST, GET, etc)
348
+     */
349
+    public function getMethod(): string {
350
+        return $this->method;
351
+    }
352
+
353
+    /**
354
+     * Shortcut for accessing an uploaded file through the $_FILES array
355
+     * @param string $key the key that will be taken from the $_FILES array
356
+     * @return array the file in the $_FILES element
357
+     */
358
+    public function getUploadedFile(string $key) {
359
+        return isset($this->files[$key]) ? $this->files[$key] : null;
360
+    }
361
+
362
+    /**
363
+     * Shortcut for getting env variables
364
+     * @param string $key the key that will be taken from the $_ENV array
365
+     * @return array the value in the $_ENV element
366
+     */
367
+    public function getEnv(string $key) {
368
+        return isset($this->env[$key]) ? $this->env[$key] : null;
369
+    }
370
+
371
+    /**
372
+     * Shortcut for getting cookie variables
373
+     * @param string $key the key that will be taken from the $_COOKIE array
374
+     * @return string the value in the $_COOKIE element
375
+     */
376
+    public function getCookie(string $key) {
377
+        return isset($this->cookies[$key]) ? $this->cookies[$key] : null;
378
+    }
379
+
380
+    /**
381
+     * Returns the request body content.
382
+     *
383
+     * If the HTTP request method is PUT and the body
384
+     * not application/x-www-form-urlencoded or application/json a stream
385
+     * resource is returned, otherwise an array.
386
+     *
387
+     * @return array|string|resource The request body content or a resource to read the body stream.
388
+     *
389
+     * @throws \LogicException
390
+     */
391
+    protected function getContent() {
392
+        // If the content can't be parsed into an array then return a stream resource.
393
+        if ($this->method === 'PUT'
394
+            && $this->getHeader('Content-Length') !== '0'
395
+            && $this->getHeader('Content-Length') !== ''
396
+            && strpos($this->getHeader('Content-Type'), 'application/x-www-form-urlencoded') === false
397
+            && strpos($this->getHeader('Content-Type'), 'application/json') === false
398
+        ) {
399
+            if ($this->content === false) {
400
+                throw new \LogicException(
401
+                    '"put" can only be accessed once if not '
402
+                    . 'application/x-www-form-urlencoded or application/json.'
403
+                );
404
+            }
405
+            $this->content = false;
406
+            return fopen($this->inputStream, 'rb');
407
+        } else {
408
+            $this->decodeContent();
409
+            return $this->items['parameters'];
410
+        }
411
+    }
412
+
413
+    /**
414
+     * Attempt to decode the content and populate parameters
415
+     */
416
+    protected function decodeContent() {
417
+        if ($this->contentDecoded) {
418
+            return;
419
+        }
420
+        $params = [];
421
+
422
+        // 'application/json' must be decoded manually.
423
+        if (strpos($this->getHeader('Content-Type'), 'application/json') !== false) {
424
+            $params = json_decode(file_get_contents($this->inputStream), true);
425
+            if (\is_array($params) && \count($params) > 0) {
426
+                $this->items['params'] = $params;
427
+                if ($this->method === 'POST') {
428
+                    $this->items['post'] = $params;
429
+                }
430
+            }
431
+            // Handle application/x-www-form-urlencoded for methods other than GET
432
+        // or post correctly
433
+        } elseif ($this->method !== 'GET'
434
+                && $this->method !== 'POST'
435
+                && strpos($this->getHeader('Content-Type'), 'application/x-www-form-urlencoded') !== false) {
436
+            parse_str(file_get_contents($this->inputStream), $params);
437
+            if (\is_array($params)) {
438
+                $this->items['params'] = $params;
439
+            }
440
+        }
441
+
442
+        if (\is_array($params)) {
443
+            $this->items['parameters'] = array_merge($this->items['parameters'], $params);
444
+        }
445
+        $this->contentDecoded = true;
446
+    }
447
+
448
+
449
+    /**
450
+     * Checks if the CSRF check was correct
451
+     * @return bool true if CSRF check passed
452
+     */
453
+    public function passesCSRFCheck(): bool {
454
+        if ($this->csrfTokenManager === null) {
455
+            return false;
456
+        }
457
+
458
+        if (!$this->passesStrictCookieCheck()) {
459
+            return false;
460
+        }
461
+
462
+        if (isset($this->items['get']['requesttoken'])) {
463
+            $token = $this->items['get']['requesttoken'];
464
+        } elseif (isset($this->items['post']['requesttoken'])) {
465
+            $token = $this->items['post']['requesttoken'];
466
+        } elseif (isset($this->items['server']['HTTP_REQUESTTOKEN'])) {
467
+            $token = $this->items['server']['HTTP_REQUESTTOKEN'];
468
+        } else {
469
+            //no token found.
470
+            return false;
471
+        }
472
+        $token = new CsrfToken($token);
473
+
474
+        return $this->csrfTokenManager->isTokenValid($token);
475
+    }
476
+
477
+    /**
478
+     * Whether the cookie checks are required
479
+     *
480
+     * @return bool
481
+     */
482
+    private function cookieCheckRequired(): bool {
483
+        if ($this->getHeader('OCS-APIREQUEST')) {
484
+            return false;
485
+        }
486
+        if ($this->getCookie(session_name()) === null && $this->getCookie('nc_token') === null) {
487
+            return false;
488
+        }
489
+
490
+        return true;
491
+    }
492
+
493
+    /**
494
+     * Wrapper around session_get_cookie_params
495
+     *
496
+     * @return array
497
+     */
498
+    public function getCookieParams(): array {
499
+        return session_get_cookie_params();
500
+    }
501
+
502
+    /**
503
+     * Appends the __Host- prefix to the cookie if applicable
504
+     *
505
+     * @param string $name
506
+     * @return string
507
+     */
508
+    protected function getProtectedCookieName(string $name): string {
509
+        $cookieParams = $this->getCookieParams();
510
+        $prefix = '';
511
+        if ($cookieParams['secure'] === true && $cookieParams['path'] === '/') {
512
+            $prefix = '__Host-';
513
+        }
514
+
515
+        return $prefix.$name;
516
+    }
517
+
518
+    /**
519
+     * Checks if the strict cookie has been sent with the request if the request
520
+     * is including any cookies.
521
+     *
522
+     * @return bool
523
+     * @since 9.1.0
524
+     */
525
+    public function passesStrictCookieCheck(): bool {
526
+        if (!$this->cookieCheckRequired()) {
527
+            return true;
528
+        }
529
+
530
+        $cookieName = $this->getProtectedCookieName('nc_sameSiteCookiestrict');
531
+        if ($this->getCookie($cookieName) === 'true'
532
+            && $this->passesLaxCookieCheck()) {
533
+            return true;
534
+        }
535
+        return false;
536
+    }
537
+
538
+    /**
539
+     * Checks if the lax cookie has been sent with the request if the request
540
+     * is including any cookies.
541
+     *
542
+     * @return bool
543
+     * @since 9.1.0
544
+     */
545
+    public function passesLaxCookieCheck(): bool {
546
+        if (!$this->cookieCheckRequired()) {
547
+            return true;
548
+        }
549
+
550
+        $cookieName = $this->getProtectedCookieName('nc_sameSiteCookielax');
551
+        if ($this->getCookie($cookieName) === 'true') {
552
+            return true;
553
+        }
554
+        return false;
555
+    }
556
+
557
+
558
+    /**
559
+     * Returns an ID for the request, value is not guaranteed to be unique and is mostly meant for logging
560
+     * If `mod_unique_id` is installed this value will be taken.
561
+     * @return string
562
+     */
563
+    public function getId(): string {
564
+        return $this->requestId->getId();
565
+    }
566
+
567
+    /**
568
+     * Checks if given $remoteAddress matches any entry in the given array $trustedProxies.
569
+     * For details regarding what "match" means, refer to `matchesTrustedProxy`.
570
+     * @return boolean true if $remoteAddress matches any entry in $trustedProxies, false otherwise
571
+     */
572
+    protected function isTrustedProxy($trustedProxies, $remoteAddress) {
573
+        return IpUtils::checkIp($remoteAddress, $trustedProxies);
574
+    }
575
+
576
+    /**
577
+     * Returns the remote address, if the connection came from a trusted proxy
578
+     * and `forwarded_for_headers` has been configured then the IP address
579
+     * specified in this header will be returned instead.
580
+     * Do always use this instead of $_SERVER['REMOTE_ADDR']
581
+     * @return string IP address
582
+     */
583
+    public function getRemoteAddress(): string {
584
+        $remoteAddress = isset($this->server['REMOTE_ADDR']) ? $this->server['REMOTE_ADDR'] : '';
585
+        $trustedProxies = $this->config->getSystemValue('trusted_proxies', []);
586
+
587
+        if (\is_array($trustedProxies) && $this->isTrustedProxy($trustedProxies, $remoteAddress)) {
588
+            $forwardedForHeaders = $this->config->getSystemValue('forwarded_for_headers', [
589
+                'HTTP_X_FORWARDED_FOR'
590
+                // only have one default, so we cannot ship an insecure product out of the box
591
+            ]);
592
+
593
+            foreach ($forwardedForHeaders as $header) {
594
+                if (isset($this->server[$header])) {
595
+                    foreach (explode(',', $this->server[$header]) as $IP) {
596
+                        $IP = trim($IP);
597
+
598
+                        // remove brackets from IPv6 addresses
599
+                        if (strpos($IP, '[') === 0 && substr($IP, -1) === ']') {
600
+                            $IP = substr($IP, 1, -1);
601
+                        }
602
+
603
+                        if (filter_var($IP, FILTER_VALIDATE_IP) !== false) {
604
+                            return $IP;
605
+                        }
606
+                    }
607
+                }
608
+            }
609
+        }
610
+
611
+        return $remoteAddress;
612
+    }
613
+
614
+    /**
615
+     * Check overwrite condition
616
+     * @param string $type
617
+     * @return bool
618
+     */
619
+    private function isOverwriteCondition(string $type = ''): bool {
620
+        $regex = '/' . $this->config->getSystemValue('overwritecondaddr', '')  . '/';
621
+        $remoteAddr = isset($this->server['REMOTE_ADDR']) ? $this->server['REMOTE_ADDR'] : '';
622
+        return $regex === '//' || preg_match($regex, $remoteAddr) === 1
623
+        || $type !== 'protocol';
624
+    }
625
+
626
+    /**
627
+     * Returns the server protocol. It respects one or more reverse proxies servers
628
+     * and load balancers
629
+     * @return string Server protocol (http or https)
630
+     */
631
+    public function getServerProtocol(): string {
632
+        if ($this->config->getSystemValue('overwriteprotocol') !== ''
633
+            && $this->isOverwriteCondition('protocol')) {
634
+            return $this->config->getSystemValue('overwriteprotocol');
635
+        }
636
+
637
+        if ($this->fromTrustedProxy() && isset($this->server['HTTP_X_FORWARDED_PROTO'])) {
638
+            if (strpos($this->server['HTTP_X_FORWARDED_PROTO'], ',') !== false) {
639
+                $parts = explode(',', $this->server['HTTP_X_FORWARDED_PROTO']);
640
+                $proto = strtolower(trim($parts[0]));
641
+            } else {
642
+                $proto = strtolower($this->server['HTTP_X_FORWARDED_PROTO']);
643
+            }
644
+
645
+            // Verify that the protocol is always HTTP or HTTPS
646
+            // default to http if an invalid value is provided
647
+            return $proto === 'https' ? 'https' : 'http';
648
+        }
649
+
650
+        if (isset($this->server['HTTPS'])
651
+            && $this->server['HTTPS'] !== null
652
+            && $this->server['HTTPS'] !== 'off'
653
+            && $this->server['HTTPS'] !== '') {
654
+            return 'https';
655
+        }
656
+
657
+        return 'http';
658
+    }
659
+
660
+    /**
661
+     * Returns the used HTTP protocol.
662
+     *
663
+     * @return string HTTP protocol. HTTP/2, HTTP/1.1 or HTTP/1.0.
664
+     */
665
+    public function getHttpProtocol(): string {
666
+        $claimedProtocol = $this->server['SERVER_PROTOCOL'];
667
+
668
+        if (\is_string($claimedProtocol)) {
669
+            $claimedProtocol = strtoupper($claimedProtocol);
670
+        }
671
+
672
+        $validProtocols = [
673
+            'HTTP/1.0',
674
+            'HTTP/1.1',
675
+            'HTTP/2',
676
+        ];
677
+
678
+        if (\in_array($claimedProtocol, $validProtocols, true)) {
679
+            return $claimedProtocol;
680
+        }
681
+
682
+        return 'HTTP/1.1';
683
+    }
684
+
685
+    /**
686
+     * Returns the request uri, even if the website uses one or more
687
+     * reverse proxies
688
+     * @return string
689
+     */
690
+    public function getRequestUri(): string {
691
+        $uri = isset($this->server['REQUEST_URI']) ? $this->server['REQUEST_URI'] : '';
692
+        if ($this->config->getSystemValue('overwritewebroot') !== '' && $this->isOverwriteCondition()) {
693
+            $uri = $this->getScriptName() . substr($uri, \strlen($this->server['SCRIPT_NAME']));
694
+        }
695
+        return $uri;
696
+    }
697
+
698
+    /**
699
+     * Get raw PathInfo from request (not urldecoded)
700
+     * @throws \Exception
701
+     * @return string Path info
702
+     */
703
+    public function getRawPathInfo(): string {
704
+        $requestUri = isset($this->server['REQUEST_URI']) ? $this->server['REQUEST_URI'] : '';
705
+        // remove too many slashes - can be caused by reverse proxy configuration
706
+        $requestUri = preg_replace('%/{2,}%', '/', $requestUri);
707
+
708
+        // Remove the query string from REQUEST_URI
709
+        if ($pos = strpos($requestUri, '?')) {
710
+            $requestUri = substr($requestUri, 0, $pos);
711
+        }
712
+
713
+        $scriptName = $this->server['SCRIPT_NAME'];
714
+        $pathInfo = $requestUri;
715
+
716
+        // strip off the script name's dir and file name
717
+        // FIXME: Sabre does not really belong here
718
+        [$path, $name] = \Sabre\Uri\split($scriptName);
719
+        if (!empty($path)) {
720
+            if ($path === $pathInfo || strpos($pathInfo, $path.'/') === 0) {
721
+                $pathInfo = substr($pathInfo, \strlen($path));
722
+            } else {
723
+                throw new \Exception("The requested uri($requestUri) cannot be processed by the script '$scriptName')");
724
+            }
725
+        }
726
+        if ($name === null) {
727
+            $name = '';
728
+        }
729
+
730
+        if (strpos($pathInfo, '/'.$name) === 0) {
731
+            $pathInfo = substr($pathInfo, \strlen($name) + 1);
732
+        }
733
+        if ($name !== '' && strpos($pathInfo, $name) === 0) {
734
+            $pathInfo = substr($pathInfo, \strlen($name));
735
+        }
736
+        if ($pathInfo === false || $pathInfo === '/') {
737
+            return '';
738
+        } else {
739
+            return $pathInfo;
740
+        }
741
+    }
742
+
743
+    /**
744
+     * Get PathInfo from request
745
+     * @throws \Exception
746
+     * @return string|false Path info or false when not found
747
+     */
748
+    public function getPathInfo() {
749
+        $pathInfo = $this->getRawPathInfo();
750
+        return \Sabre\HTTP\decodePath($pathInfo);
751
+    }
752
+
753
+    /**
754
+     * Returns the script name, even if the website uses one or more
755
+     * reverse proxies
756
+     * @return string the script name
757
+     */
758
+    public function getScriptName(): string {
759
+        $name = $this->server['SCRIPT_NAME'];
760
+        $overwriteWebRoot = $this->config->getSystemValue('overwritewebroot');
761
+        if ($overwriteWebRoot !== '' && $this->isOverwriteCondition()) {
762
+            // FIXME: This code is untestable due to __DIR__, also that hardcoded path is really dangerous
763
+            $serverRoot = str_replace('\\', '/', substr(__DIR__, 0, -\strlen('lib/private/appframework/http/')));
764
+            $suburi = str_replace('\\', '/', substr(realpath($this->server['SCRIPT_FILENAME']), \strlen($serverRoot)));
765
+            $name = '/' . ltrim($overwriteWebRoot . $suburi, '/');
766
+        }
767
+        return $name;
768
+    }
769
+
770
+    /**
771
+     * Checks whether the user agent matches a given regex
772
+     * @param array $agent array of agent names
773
+     * @return bool true if at least one of the given agent matches, false otherwise
774
+     */
775
+    public function isUserAgent(array $agent): bool {
776
+        if (!isset($this->server['HTTP_USER_AGENT'])) {
777
+            return false;
778
+        }
779
+        foreach ($agent as $regex) {
780
+            if (preg_match($regex, $this->server['HTTP_USER_AGENT'])) {
781
+                return true;
782
+            }
783
+        }
784
+        return false;
785
+    }
786
+
787
+    /**
788
+     * Returns the unverified server host from the headers without checking
789
+     * whether it is a trusted domain
790
+     * @return string Server host
791
+     */
792
+    public function getInsecureServerHost(): string {
793
+        if ($this->fromTrustedProxy() && $this->getOverwriteHost() !== null) {
794
+            return $this->getOverwriteHost();
795
+        }
796
+
797
+        $host = 'localhost';
798
+        if ($this->fromTrustedProxy() && isset($this->server['HTTP_X_FORWARDED_HOST'])) {
799
+            if (strpos($this->server['HTTP_X_FORWARDED_HOST'], ',') !== false) {
800
+                $parts = explode(',', $this->server['HTTP_X_FORWARDED_HOST']);
801
+                $host = trim(current($parts));
802
+            } else {
803
+                $host = $this->server['HTTP_X_FORWARDED_HOST'];
804
+            }
805
+        } else {
806
+            if (isset($this->server['HTTP_HOST'])) {
807
+                $host = $this->server['HTTP_HOST'];
808
+            } elseif (isset($this->server['SERVER_NAME'])) {
809
+                $host = $this->server['SERVER_NAME'];
810
+            }
811
+        }
812
+
813
+        return $host;
814
+    }
815
+
816
+
817
+    /**
818
+     * Returns the server host from the headers, or the first configured
819
+     * trusted domain if the host isn't in the trusted list
820
+     * @return string Server host
821
+     */
822
+    public function getServerHost(): string {
823
+        // overwritehost is always trusted
824
+        $host = $this->getOverwriteHost();
825
+        if ($host !== null) {
826
+            return $host;
827
+        }
828
+
829
+        // get the host from the headers
830
+        $host = $this->getInsecureServerHost();
831
+
832
+        // Verify that the host is a trusted domain if the trusted domains
833
+        // are defined
834
+        // If no trusted domain is provided the first trusted domain is returned
835
+        $trustedDomainHelper = new TrustedDomainHelper($this->config);
836
+        if ($trustedDomainHelper->isTrustedDomain($host)) {
837
+            return $host;
838
+        }
839
+
840
+        $trustedList = (array)$this->config->getSystemValue('trusted_domains', []);
841
+        if (count($trustedList) > 0) {
842
+            return reset($trustedList);
843
+        }
844
+
845
+        return '';
846
+    }
847
+
848
+    /**
849
+     * Returns the overwritehost setting from the config if set and
850
+     * if the overwrite condition is met
851
+     * @return string|null overwritehost value or null if not defined or the defined condition
852
+     * isn't met
853
+     */
854
+    private function getOverwriteHost() {
855
+        if ($this->config->getSystemValue('overwritehost') !== '' && $this->isOverwriteCondition()) {
856
+            return $this->config->getSystemValue('overwritehost');
857
+        }
858
+        return null;
859
+    }
860
+
861
+    private function fromTrustedProxy(): bool {
862
+        $remoteAddress = isset($this->server['REMOTE_ADDR']) ? $this->server['REMOTE_ADDR'] : '';
863
+        $trustedProxies = $this->config->getSystemValue('trusted_proxies', []);
864
+
865
+        return \is_array($trustedProxies) && $this->isTrustedProxy($trustedProxies, $remoteAddress);
866
+    }
867 867
 }
Please login to merge, or discard this patch.