Completed
Push — stable9 ( 485cb1...e094cf )
by Lukas
26:41 queued 26:23
created

lib/base.php (15 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * @copyright Copyright (c) 2016, ownCloud, Inc.
4
 *
5
 * @author Adam Williamson <[email protected]>
6
 * @author Andreas Fischer <[email protected]>
7
 * @author Arthur Schiwon <[email protected]>
8
 * @author Bart Visscher <[email protected]>
9
 * @author Bernhard Posselt <[email protected]>
10
 * @author Björn Schießle <[email protected]>
11
 * @author Christopher Schäpers <[email protected]>
12
 * @author davidgumberg <[email protected]>
13
 * @author Florian Scholz <[email protected]>
14
 * @author Florin Peter <[email protected]>
15
 * @author Frank Karlitschek <[email protected]>
16
 * @author Georg Ehrke <[email protected]>
17
 * @author Hugo Gonzalez Labrador <[email protected]>
18
 * @author Individual IT Services <[email protected]>
19
 * @author Jakob Sack <[email protected]>
20
 * @author Joachim Bauch <[email protected]>
21
 * @author Joachim Sokolowski <[email protected]>
22
 * @author Joas Schilling <[email protected]>
23
 * @author Jörn Friedrich Dreyer <[email protected]>
24
 * @author Lukas Reschke <[email protected]>
25
 * @author marc0s <[email protected]>
26
 * @author Martin Mattel <[email protected]>
27
 * @author Michael Gapczynski <[email protected]>
28
 * @author Morris Jobke <[email protected]>
29
 * @author Owen Winkler <[email protected]>
30
 * @author Phil Davis <[email protected]>
31
 * @author Ramiro Aparicio <[email protected]>
32
 * @author Robin Appelman <[email protected]>
33
 * @author Robin McCorkell <[email protected]>
34
 * @author Roeland Jago Douma <[email protected]>
35
 * @author scolebrook <[email protected]>
36
 * @author Thomas Müller <[email protected]>
37
 * @author Thomas Tanghus <[email protected]>
38
 * @author Victor Dubiniuk <[email protected]>
39
 * @author Vincent Petry <[email protected]>
40
 * @author Volkan Gezer <[email protected]>
41
 *
42
 * @license AGPL-3.0
43
 *
44
 * This code is free software: you can redistribute it and/or modify
45
 * it under the terms of the GNU Affero General Public License, version 3,
46
 * as published by the Free Software Foundation.
47
 *
48
 * This program is distributed in the hope that it will be useful,
49
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
50
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
51
 * GNU Affero General Public License for more details.
52
 *
53
 * You should have received a copy of the GNU Affero General Public License, version 3,
54
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
55
 *
56
 */
57
58
require_once 'public/constants.php';
59
60
/**
61
 * Class that is a namespace for all global OC variables
62
 * No, we can not put this class in its own file because it is used by
63
 * OC_autoload!
64
 */
65
class OC {
66
	/**
67
	 * Associative array for autoloading. classname => filename
68
	 */
69
	public static $CLASSPATH = array();
70
	/**
71
	 * The installation path for Nextcloud  on the server (e.g. /srv/http/nextcloud)
72
	 */
73
	public static $SERVERROOT = '';
74
	/**
75
	 * the current request path relative to the Nextcloud root (e.g. files/index.php)
76
	 */
77
	private static $SUBURI = '';
78
	/**
79
	 * the Nextcloud root path for http requests (e.g. nextcloud/)
80
	 */
81
	public static $WEBROOT = '';
82
	/**
83
	 * The installation path of the 3rdparty folder on the server (e.g. /srv/http/nextcloud/3rdparty)
84
	 */
85
	public static $THIRDPARTYROOT = '';
86
	/**
87
	 * the root path of the 3rdparty folder for http requests (e.g. nextcloud/3rdparty)
88
	 */
89
	public static $THIRDPARTYWEBROOT = '';
90
	/**
91
	 * The installation path array of the apps folder on the server (e.g. /srv/http/nextcloud) 'path' and
92
	 * web path in 'url'
93
	 */
94
	public static $APPSROOTS = array();
95
96
	public static $configDir;
97
98
	/**
99
	 * requested app
100
	 */
101
	public static $REQUESTEDAPP = '';
102
103
	/**
104
	 * check if Nextcloud runs in cli mode
105
	 */
106
	public static $CLI = false;
107
108
	/**
109
	 * @var \OC\Autoloader $loader
110
	 */
111
	public static $loader = null;
112
113
	/**
114
	 * @var \OC\Server
115
	 */
116
	public static $server = null;
117
118
	/**
119
	 * @var \OC\Config
120
	 */
121
	private static $config = null;
122
123
	/**
124
	 * @throws \RuntimeException when the 3rdparty directory is missing or
125
	 * the app path list is empty or contains an invalid path
126
	 */
127
	public static function initPaths() {
128
		if(defined('PHPUNIT_CONFIG_DIR')) {
129
			self::$configDir = OC::$SERVERROOT . '/' . PHPUNIT_CONFIG_DIR . '/';
130
		} elseif(defined('PHPUNIT_RUN') and PHPUNIT_RUN and is_dir(OC::$SERVERROOT . '/tests/config/')) {
131
			self::$configDir = OC::$SERVERROOT . '/tests/config/';
132
		} else {
133
			self::$configDir = OC::$SERVERROOT . '/config/';
134
		}
135
		self::$config = new \OC\Config(self::$configDir);
136
137
		OC::$SUBURI = str_replace("\\", "/", substr(realpath($_SERVER["SCRIPT_FILENAME"]), strlen(OC::$SERVERROOT)));
138
		/**
139
		 * FIXME: The following lines are required because we can't yet instantiiate
140
		 *        \OC::$server->getRequest() since \OC::$server does not yet exist.
141
		 */
142
		$params = [
143
			'server' => [
144
				'SCRIPT_NAME' => $_SERVER['SCRIPT_NAME'],
145
				'SCRIPT_FILENAME' => $_SERVER['SCRIPT_FILENAME'],
146
			],
147
		];
148
		$fakeRequest = new \OC\AppFramework\Http\Request($params, null, new \OC\AllConfig(new \OC\SystemConfig(self::$config)));
149
		$scriptName = $fakeRequest->getScriptName();
150
		if (substr($scriptName, -1) == '/') {
151
			$scriptName .= 'index.php';
152
			//make sure suburi follows the same rules as scriptName
153
			if (substr(OC::$SUBURI, -9) != 'index.php') {
154
				if (substr(OC::$SUBURI, -1) != '/') {
155
					OC::$SUBURI = OC::$SUBURI . '/';
156
				}
157
				OC::$SUBURI = OC::$SUBURI . 'index.php';
158
			}
159
		}
160
161
162
		if (OC::$CLI) {
163
			OC::$WEBROOT = self::$config->getValue('overwritewebroot', '');
164
		} else {
165
			if (substr($scriptName, 0 - strlen(OC::$SUBURI)) === OC::$SUBURI) {
166
				OC::$WEBROOT = substr($scriptName, 0, 0 - strlen(OC::$SUBURI));
167
168
				if (OC::$WEBROOT != '' && OC::$WEBROOT[0] !== '/') {
169
					OC::$WEBROOT = '/' . OC::$WEBROOT;
170
				}
171
			} else {
172
				// The scriptName is not ending with OC::$SUBURI
173
				// This most likely means that we are calling from CLI.
174
				// However some cron jobs still need to generate
175
				// a web URL, so we use overwritewebroot as a fallback.
176
				OC::$WEBROOT = self::$config->getValue('overwritewebroot', '');
177
			}
178
179
			// Resolve /nextcloud to /nextcloud/ to ensure to always have a trailing
180
			// slash which is required by URL generation.
181
			if($_SERVER['REQUEST_URI'] === \OC::$WEBROOT &&
182
					substr($_SERVER['REQUEST_URI'], -1) !== '/') {
183
				header('Location: '.\OC::$WEBROOT.'/');
184
				exit();
185
			}
186
		}
187
188
		// search the 3rdparty folder
189
		OC::$THIRDPARTYROOT = self::$config->getValue('3rdpartyroot', null);
190
		OC::$THIRDPARTYWEBROOT = self::$config->getValue('3rdpartyurl', null);
191
192
		if (empty(OC::$THIRDPARTYROOT) && empty(OC::$THIRDPARTYWEBROOT)) {
193
			if (file_exists(OC::$SERVERROOT . '/3rdparty')) {
194
				OC::$THIRDPARTYROOT = OC::$SERVERROOT;
195
				OC::$THIRDPARTYWEBROOT = OC::$WEBROOT;
196
			} elseif (file_exists(OC::$SERVERROOT . '/../3rdparty')) {
197
				OC::$THIRDPARTYWEBROOT = rtrim(dirname(OC::$WEBROOT), '/');
198
				OC::$THIRDPARTYROOT = rtrim(dirname(OC::$SERVERROOT), '/');
199
			}
200
		}
201
		if (empty(OC::$THIRDPARTYROOT) || !file_exists(OC::$THIRDPARTYROOT)) {
202
			throw new \RuntimeException('3rdparty directory not found! Please put the Nextcloud 3rdparty'
203
				. ' folder in the Nextcloud folder or the folder above.'
204
				. ' You can also configure the location in the config.php file.');
205
		}
206
207
		// search the apps folder
208
		$config_paths = self::$config->getValue('apps_paths', array());
209
		if (!empty($config_paths)) {
210
			foreach ($config_paths as $paths) {
211
				if (isset($paths['url']) && isset($paths['path'])) {
212
					$paths['url'] = rtrim($paths['url'], '/');
213
					$paths['path'] = rtrim($paths['path'], '/');
214
					OC::$APPSROOTS[] = $paths;
215
				}
216
			}
217
		} elseif (file_exists(OC::$SERVERROOT . '/apps')) {
218
			OC::$APPSROOTS[] = array('path' => OC::$SERVERROOT . '/apps', 'url' => '/apps', 'writable' => true);
219
		} elseif (file_exists(OC::$SERVERROOT . '/../apps')) {
220
			OC::$APPSROOTS[] = array(
221
				'path' => rtrim(dirname(OC::$SERVERROOT), '/') . '/apps',
222
				'url' => '/apps',
223
				'writable' => true
224
			);
225
		}
226
227
		if (empty(OC::$APPSROOTS)) {
228
			throw new \RuntimeException('apps directory not found! Please put the Nextcloud apps folder in the Nextcloud folder'
229
				. ' or the folder above. You can also configure the location in the config.php file.');
230
		}
231
		$paths = array();
232
		foreach (OC::$APPSROOTS as $path) {
233
			$paths[] = $path['path'];
234
			if (!is_dir($path['path'])) {
235
				throw new \RuntimeException(sprintf('App directory "%s" not found! Please put the Nextcloud apps folder in the'
236
					. ' Nextcloud folder or the folder above. You can also configure the location in the'
237
					. ' config.php file.', $path['path']));
238
			}
239
		}
240
241
		// set the right include path
242
		set_include_path(
243
			OC::$SERVERROOT . '/lib/private' . PATH_SEPARATOR .
244
			OC::$SERVERROOT . '/config' . PATH_SEPARATOR .
245
			OC::$THIRDPARTYROOT . '/3rdparty' . PATH_SEPARATOR .
246
			implode(PATH_SEPARATOR, $paths) . PATH_SEPARATOR .
247
			get_include_path() . PATH_SEPARATOR .
248
			OC::$SERVERROOT
249
		);
250
	}
251
252
	public static function checkConfig() {
253
		$l = \OC::$server->getL10N('lib');
254
255
		// Create config if it does not already exist
256
		$configFilePath = self::$configDir .'/config.php';
257
		if(!file_exists($configFilePath)) {
258
			@touch($configFilePath);
259
		}
260
261
		// Check if config is writable
262
		$configFileWritable = is_writable($configFilePath);
263
		if (!$configFileWritable && !OC_Helper::isReadOnlyConfigEnabled()
264
			|| !$configFileWritable && self::checkUpgrade(false)) {
265
266
			$urlGenerator = \OC::$server->getURLGenerator();
267
268
			if (self::$CLI) {
269
				echo $l->t('Cannot write into "config" directory!')."\n";
270
				echo $l->t('This can usually be fixed by giving the webserver write access to the config directory')."\n";
271
				echo "\n";
272
				echo $l->t('See %s', [ $urlGenerator->linkToDocs('admin-dir_permissions') ])."\n";
273
				exit;
274
			} else {
275
				OC_Template::printErrorPage(
276
					$l->t('Cannot write into "config" directory!'),
277
					$l->t('This can usually be fixed by '
278
					. '%sgiving the webserver write access to the config directory%s.',
279
					 array('<a href="' . $urlGenerator->linkToDocs('admin-dir_permissions') . '" target="_blank">', '</a>'))
280
				);
281
			}
282
		}
283
	}
284
285
	public static function checkInstalled() {
286
		if (defined('OC_CONSOLE')) {
287
			return;
288
		}
289
		// Redirect to installer if not installed
290
		if (!\OC::$server->getSystemConfig()->getValue('installed', false) && OC::$SUBURI !== '/index.php' && OC::$SUBURI !== '/status.php') {
291
			if (OC::$CLI) {
292
				throw new Exception('Not installed');
293
			} else {
294
				$url = 'http://' . $_SERVER['SERVER_NAME'] . OC::$WEBROOT . '/index.php';
295
				header('Location: ' . $url);
296
			}
297
			exit();
298
		}
299
	}
300
301
	public static function checkMaintenanceMode() {
302
		// Allow ajax update script to execute without being stopped
303
		if (\OC::$server->getSystemConfig()->getValue('maintenance', false) && OC::$SUBURI != '/core/ajax/update.php') {
304
			// send http status 503
305
			header('HTTP/1.1 503 Service Temporarily Unavailable');
306
			header('Status: 503 Service Temporarily Unavailable');
307
			header('Retry-After: 120');
308
309
			// render error page
310
			$template = new OC_Template('', 'update.user', 'guest');
311
			OC_Util::addscript('maintenance-check');
312
			$template->printPage();
313
			die();
314
		}
315
	}
316
317
	public static function checkSingleUserMode($lockIfNoUserLoggedIn = false) {
318
		if (!\OC::$server->getSystemConfig()->getValue('singleuser', false)) {
319
			return;
320
		}
321
		$user = OC_User::getUserSession()->getUser();
322
		if ($user) {
323
			$group = \OC::$server->getGroupManager()->get('admin');
324
			if ($group->inGroup($user)) {
325
				return;
326
			}
327
		} else {
328
			if(!$lockIfNoUserLoggedIn) {
329
				return;
330
			}
331
		}
332
		// send http status 503
333
		header('HTTP/1.1 503 Service Temporarily Unavailable');
334
		header('Status: 503 Service Temporarily Unavailable');
335
		header('Retry-After: 120');
336
337
		// render error page
338
		$template = new OC_Template('', 'singleuser.user', 'guest');
339
		$template->printPage();
340
		die();
341
	}
342
343
	/**
344
	 * check if the instance needs to perform an upgrade
345
	 *
346
	 * @return bool
347
	 * @deprecated use \OCP\Util::needUpgrade() instead
348
	 */
349
	public static function needUpgrade() {
350
		return \OCP\Util::needUpgrade();
351
	}
352
353
	/**
354
	 * Checks if the version requires an update and shows
355
	 * @param bool $showTemplate Whether an update screen should get shown
356
	 * @return bool|void
357
	 */
358
	public static function checkUpgrade($showTemplate = true) {
359
		if (\OCP\Util::needUpgrade()) {
360
			$systemConfig = \OC::$server->getSystemConfig();
361
			if ($showTemplate && !$systemConfig->getValue('maintenance', false)) {
362
				self::printUpgradePage();
363
				exit();
364
			} else {
365
				return true;
366
			}
367
		}
368
		return false;
369
	}
370
371
	/**
372
	 * Prints the upgrade page
373
	 */
374
	private static function printUpgradePage() {
375
		$systemConfig = \OC::$server->getSystemConfig();
376
		$oldTheme = $systemConfig->getValue('theme');
377
		$systemConfig->setValue('theme', '');
378
		\OCP\Util::addScript('config'); // needed for web root
379
		\OCP\Util::addScript('update');
380
381
		// check whether this is a core update or apps update
382
		$installedVersion = $systemConfig->getValue('version', '0.0.0');
383
		$currentVersion = implode('.', \OCP\Util::getVersion());
384
385
		$appManager = \OC::$server->getAppManager();
386
387
		$tmpl = new OC_Template('', 'update.admin', 'guest');
388
		$tmpl->assign('version', OC_Util::getVersionString());
389
390
		// if not a core upgrade, then it's apps upgrade
391
		if (version_compare($currentVersion, $installedVersion, '=')) {
392
			$tmpl->assign('isAppsOnlyUpgrade', true);
393
		} else {
394
			$tmpl->assign('isAppsOnlyUpgrade', false);
395
		}
396
397
		$releaseNotes = new \OC\ReleaseNotes(\OC::$server->getDatabaseConnection());
398
399
		// get third party apps
400
		$ocVersion = \OCP\Util::getVersion();
401
		$tmpl->assign('appsToUpgrade', $appManager->getAppsNeedingUpgrade($ocVersion));
402
		$tmpl->assign('incompatibleAppsList', $appManager->getIncompatibleApps($ocVersion));
403
		$tmpl->assign('productName', 'Nextcloud'); // for now
404
		$tmpl->assign('oldTheme', $oldTheme);
405
		$tmpl->assign('releaseNotes', $releaseNotes->getReleaseNotes($installedVersion, $currentVersion));
406
		$tmpl->printPage();
407
	}
408
409
	public static function initSession() {
410
		// prevents javascript from accessing php session cookies
411
		ini_set('session.cookie_httponly', true);
412
413
		// set the cookie path to the Nextcloud directory
414
		$cookie_path = OC::$WEBROOT ? : '/';
415
		ini_set('session.cookie_path', $cookie_path);
416
417
		// Let the session name be changed in the initSession Hook
418
		$sessionName = OC_Util::getInstanceId();
419
420
		try {
421
			// Allow session apps to create a custom session object
422
			$useCustomSession = false;
423
			$session = self::$server->getSession();
424
			OC_Hook::emit('OC', 'initSession', array('session' => &$session, 'sessionName' => &$sessionName, 'useCustomSession' => &$useCustomSession));
425
			if (!$useCustomSession) {
426
				// set the session name to the instance id - which is unique
427
				$session = new \OC\Session\Internal($sessionName);
428
			}
429
430
			$cryptoWrapper = \OC::$server->getSessionCryptoWrapper();
431
			$session = $cryptoWrapper->wrapSession($session);
432
			self::$server->setSession($session);
433
434
			// if session cant be started break with http 500 error
435
		} catch (Exception $e) {
436
			\OCP\Util::logException('base', $e);
437
			//show the user a detailed error page
438
			OC_Response::setStatus(OC_Response::STATUS_INTERNAL_SERVER_ERROR);
439
			OC_Template::printExceptionErrorPage($e);
440
			die();
441
		}
442
443
		$sessionLifeTime = self::getSessionLifeTime();
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(), null, -1, self::$WEBROOT ? : '/');
449
			}
450
			$session->clear();
451
		}
452
453
		$session->set('LAST_ACTIVITY', time());
454
	}
455
456
	/**
457
	 * @return string
458
	 */
459
	private static function getSessionLifeTime() {
460
		return \OC::$server->getConfig()->getSystemValue('session_lifetime', 60 * 60 * 24);
461
	}
462
463
	public static function loadAppClassPaths() {
464 View Code Duplication
		foreach (OC_APP::getEnabledApps() as $app) {
465
			$appPath = OC_App::getAppPath($app);
466
			if ($appPath === false) {
467
				continue;
468
			}
469
470
			$file = $appPath . '/appinfo/classpath.php';
471
			if (file_exists($file)) {
472
				require_once $file;
473
			}
474
		}
475
	}
476
477
	/**
478
	 * Try to set some values to the required Nextcloud default
479
	 */
480
	public static function setRequiredIniValues() {
481
		@ini_set('default_charset', 'UTF-8');
482
		@ini_set('gd.jpeg_ignore_warning', 1);
483
	}
484
485
	/**
486
	 * Send the same site cookies
487
	 */
488
	private static function sendSameSiteCookies() {
489
		$cookieParams = session_get_cookie_params();
490
		$secureCookie = ($cookieParams['secure'] === true) ? 'secure; ' : '';
491
		$policies = [
492
			'lax',
493
			'strict',
494
		];
495
		foreach($policies as $policy) {
496
			header(
497
				sprintf(
498
					'Set-Cookie: nc_sameSiteCookie%s=true; path=%s; httponly;' . $secureCookie . 'expires=Fri, 31-Dec-2100 23:59:59 GMT; SameSite=%s',
499
					$policy,
500
					$cookieParams['path'],
501
					$policy
502
				),
503
				false
504
			);
505
		}
506
	}
507
508
	/**
509
	 * Same Site cookie to further mitigate CSRF attacks. This cookie has to
510
	 * be set in every request if cookies are sent to add a second level of
511
	 * defense against CSRF.
512
	 *
513
	 * If the cookie is not sent this will set the cookie and reload the page.
514
	 * We use an additional cookie since we want to protect logout CSRF and
515
	 * also we can't directly interfere with PHP's session mechanism.
516
	 */
517
	private static function performSameSiteCookieProtection() {
518
		if(count($_COOKIE) > 0) {
519
			$request = \OC::$server->getRequest();
520
			$requestUri = $request->getScriptName();
521
			$processingScript = explode('/', $requestUri);
522
			$processingScript = $processingScript[count($processingScript)-1];
523
524
			// FIXME: In a SAML scenario we don't get any strict or lax cookie
525
			// send for the ACS endpoint. Since we have some legacy code in Nextcloud
526
			// (direct PHP files) the enforcement of lax cookies is performed here
527
			// instead of the middleware.
528
			//
529
			// This means we cannot exclude some routes from the cookie validation,
530
			// which normally is not a problem but is a little bit cumbersome for
531
			// this use-case.
532
			// Once the old legacy PHP endpoints have been removed we can move
533
			// the verification into a middleware and also adds some exemptions.
534
			//
535
			// Questions about this code? Ask Lukas ;-)
536
			$currentUrl = substr(explode('?',$request->getRequestUri(), 2)[0], strlen(\OC::$WEBROOT));
537
			if($currentUrl === '/index.php/apps/user_saml/saml/acs') {
538
				return;
539
			}
540
541
			// For the "index.php" endpoint only a lax cookie is required.
542
			if($processingScript === 'index.php') {
543
				if(!$request->passesLaxCookieCheck()) {
544
					self::sendSameSiteCookies();
545
					header('Location: '.$_SERVER['REQUEST_URI']);
546
					exit();
547
				}
548
			} else {
549
				// All other endpoints require the lax and the strict cookie
550
				if(!$request->passesStrictCookieCheck()) {
551
					self::sendSameSiteCookies();
552
					// Debug mode gets access to the resources without strict cookie
553
					// due to the fact that the SabreDAV browser also lives there.
554
					if(!\OC::$server->getConfig()->getSystemValue('debug', false)) {
555
						http_response_code(\OCP\AppFramework\Http::STATUS_SERVICE_UNAVAILABLE);
556
						exit();
557
					}
558
				}
559
			}
560
		} elseif(!isset($_COOKIE['nc_sameSiteCookielax']) || !isset($_COOKIE['nc_sameSiteCookiestrict'])) {
561
			self::sendSameSiteCookies();
562
		}
563
	}
564
565
566
	public static function init() {
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',
575
			OC::$SERVERROOT . '/core',
576
			OC::$SERVERROOT . '/settings',
577
			OC::$SERVERROOT . '/ocs',
578
			OC::$SERVERROOT . '/ocs-provider',
579
		]);
580
		if (defined('PHPUNIT_RUN')) {
581
			self::$loader->addValidRoot(OC::$SERVERROOT . '/tests');
582
		}
583
		spl_autoload_register(array(self::$loader, 'load'));
584
		$loaderEnd = microtime(true);
585
586
		self::$CLI = (php_sapi_name() == 'cli');
587
588
		try {
589
			self::initPaths();
590
			// setup 3rdparty autoloader
591
			$vendorAutoLoad = OC::$THIRDPARTYROOT . '/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
597
		} catch (\RuntimeException $e) {
598
			if (!self::$CLI) {
599
				$claimedProtocol = strtoupper($_SERVER['SERVER_PROTOCOL']);
600
				$protocol = in_array($claimedProtocol, ['HTTP/1.0', 'HTTP/1.1', 'HTTP/2']) ? $claimedProtocol : 'HTTP/1.1';
601
				header($protocol . ' ' . OC_Response::STATUS_SERVICE_UNAVAILABLE);
602
			}
603
			// we can't use the template error page here, because this needs the
604
			// DI container which isn't available yet
605
			print($e->getMessage());
606
			exit();
607
		}
608
609
		// setup the basic server
610
		self::$server = new \OC\Server(\OC::$WEBROOT, self::$config);
611
		\OC::$server->getEventLogger()->log('autoloader', 'Autoloader', $loaderStart, $loaderEnd);
612
		\OC::$server->getEventLogger()->start('boot', 'Initialize');
613
614
		// Don't display errors and log them
615
		error_reporting(E_ALL | E_STRICT);
616
		@ini_set('display_errors', 0);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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

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

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
617
		@ini_set('log_errors', 1);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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

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

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
618
619
		date_default_timezone_set('UTC');
620
621
		//try to configure php to enable big file uploads.
622
		//this doesn´t work always depending on the webserver and php configuration.
623
		//Let´s try to overwrite some defaults anyway
624
625
		//try to set the maximum execution time to 60min
626
		@set_time_limit(3600);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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

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

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
627
		@ini_set('max_execution_time', 3600);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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

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

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
628
		@ini_set('max_input_time', 3600);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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

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

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
629
630
		//try to set the maximum filesize to 10G
631
		@ini_set('upload_max_filesize', '10G');
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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

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

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
632
		@ini_set('post_max_size', '10G');
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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

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

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
633
		@ini_set('file_uploads', '50');
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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

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

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
634
635
		self::setRequiredIniValues();
636
		self::handleAuthHeaders();
637
		self::registerAutoloaderCache();
638
639
		// initialize intl fallback is necessary
640
		\Patchwork\Utf8\Bootup::initIntl();
641
		OC_Util::isSetLocaleWorking();
642
643
		if (!defined('PHPUNIT_RUN')) {
644
			$logger = \OC::$server->getLogger();
645
			OC\Log\ErrorHandler::setLogger($logger);
646
			if (\OC::$server->getConfig()->getSystemValue('debug', false)) {
647
				OC\Log\ErrorHandler::register(true);
648
				set_exception_handler(array('OC_Template', 'printExceptionErrorPage'));
649
			} else {
650
				OC\Log\ErrorHandler::register();
651
			}
652
		}
653
654
		// register the stream wrappers
655
		stream_wrapper_register('fakedir', 'OC\Files\Stream\Dir');
656
		stream_wrapper_register('static', 'OC\Files\Stream\StaticStream');
657
		stream_wrapper_register('close', 'OC\Files\Stream\Close');
658
		stream_wrapper_register('quota', 'OC\Files\Stream\Quota');
659
		stream_wrapper_register('oc', 'OC\Files\Stream\OC');
660
661
		\OC::$server->getEventLogger()->start('init_session', 'Initialize session');
662
		OC_App::loadApps(array('session'));
663
		if (!self::$CLI) {
664
			self::initSession();
665
		}
666
		\OC::$server->getEventLogger()->end('init_session');
667
		self::checkConfig();
668
		self::checkInstalled();
669
670
		OC_Response::addSecurityHeaders();
671
		if(self::$server->getRequest()->getServerProtocol() === 'https') {
672
			ini_set('session.cookie_secure', true);
673
		}
674
675
		self::performSameSiteCookieProtection();
676
677
		if (!defined('OC_CONSOLE')) {
678
			$errors = OC_Util::checkServer(\OC::$server->getConfig());
679
			if (count($errors) > 0) {
680
				if (self::$CLI) {
681
					// Convert l10n string into regular string for usage in database
682
					$staticErrors = [];
683
					foreach ($errors as $error) {
684
						echo $error['error'] . "\n";
685
						echo $error['hint'] . "\n\n";
686
						$staticErrors[] = [
687
							'error' => (string)$error['error'],
688
							'hint' => (string)$error['hint'],
689
						];
690
					}
691
692
					try {
693
						\OC::$server->getConfig()->setAppValue('core', 'cronErrors', json_encode($staticErrors));
694
					} catch (\Exception $e) {
695
						echo('Writing to database failed');
696
					}
697
					exit(1);
698
				} else {
699
					OC_Response::setStatus(OC_Response::STATUS_SERVICE_UNAVAILABLE);
700
					OC_Template::printGuestPage('', 'error', array('errors' => $errors));
701
					exit;
702
				}
703 View Code Duplication
			} elseif (self::$CLI && \OC::$server->getConfig()->getSystemValue('installed', false)) {
704
				\OC::$server->getConfig()->deleteAppValue('core', 'cronErrors');
705
			}
706
		}
707
		//try to set the session lifetime
708
		$sessionLifeTime = self::getSessionLifeTime();
709
		@ini_set('gc_maxlifetime', (string)$sessionLifeTime);
710
711
		$systemConfig = \OC::$server->getSystemConfig();
712
713
		// User and Groups
714
		if (!$systemConfig->getValue("installed", false)) {
715
			self::$server->getSession()->set('user_id', '');
716
		}
717
718
		OC_User::useBackend(new OC_User_Database());
719
		OC_Group::useBackend(new OC_Group_Database());
720
721
		// Subscribe to the hook
722
		\OCP\Util::connectHook(
723
			'\OCA\Files_Sharing\API\Server2Server',
724
			'preLoginNameUsedAsUserName',
725
			'\OC_User_Database',
726
			'preLoginNameUsedAsUserName'
727
		);
728
729
		//setup extra user backends
730
		if (!self::checkUpgrade(false)) {
731
			OC_User::setupBackends();
732
		}
733
734
		self::registerCacheHooks();
735
		self::registerFilesystemHooks();
736
		if ($systemConfig->getValue('enable_previews', true)) {
737
			self::registerPreviewHooks();
738
		}
739
		self::registerShareHooks();
740
		self::registerLogRotate();
741
		self::registerEncryptionWrapper();
742
		self::registerEncryptionHooks();
743
744
		//make sure temporary files are cleaned up
745
		$tmpManager = \OC::$server->getTempManager();
746
		register_shutdown_function(array($tmpManager, 'clean'));
747
		$lockProvider = \OC::$server->getLockingProvider();
748
		register_shutdown_function(array($lockProvider, 'releaseAll'));
749
750
		// Check whether the sample configuration has been copied
751
		if($systemConfig->getValue('copied_sample_config', false)) {
752
			$l = \OC::$server->getL10N('lib');
753
			header('HTTP/1.1 503 Service Temporarily Unavailable');
754
			header('Status: 503 Service Temporarily Unavailable');
755
			OC_Template::printErrorPage(
756
				$l->t('Sample configuration detected'),
757
				$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')
758
			);
759
			return;
760
		}
761
762
		$request = \OC::$server->getRequest();
763
		$host = $request->getInsecureServerHost();
764
		/**
765
		 * if the host passed in headers isn't trusted
766
		 * FIXME: Should not be in here at all :see_no_evil:
767
		 */
768
		if (!OC::$CLI
769
			// overwritehost is always trusted, workaround to not have to make
770
			// \OC\AppFramework\Http\Request::getOverwriteHost public
771
			&& self::$server->getConfig()->getSystemValue('overwritehost') === ''
772
			&& !\OC::$server->getTrustedDomainHelper()->isTrustedDomain($host)
773
			&& self::$server->getConfig()->getSystemValue('installed', false)
774
		) {
775
			header('HTTP/1.1 400 Bad Request');
776
			header('Status: 400 Bad Request');
777
778
			\OC::$server->getLogger()->warning(
779
					'Trusted domain error. "{remoteAddress}" tried to access using "{host}" as host.',
780
					[
781
						'app' => 'core',
782
						'remoteAddress' => $request->getRemoteAddress(),
783
						'host' => $host,
784
					]
785
			);
786
787
			$tmpl = new OCP\Template('core', 'untrustedDomain', 'guest');
788
			$tmpl->assign('domain', $host);
789
			$tmpl->printPage();
790
791
			exit();
792
		}
793
		\OC::$server->getEventLogger()->end('boot');
794
	}
795
796
	/**
797
	 * register hooks for the cache
798
	 */
799
	public static function registerCacheHooks() {
800
		//don't try to do this before we are properly setup
801
		if (\OC::$server->getSystemConfig()->getValue('installed', false) && !self::checkUpgrade(false)) {
802
803
			// NOTE: This will be replaced to use OCP
804
			$userSession = self::$server->getUserSession();
805
			$userSession->listen('\OC\User', 'postLogin', function () {
806
				try {
807
					$cache = new \OC\Cache\File();
808
					$cache->gc();
809
				} catch (\OC\ServerNotAvailableException $e) {
810
					// not a GC exception, pass it on
811
					throw $e;
812
				} catch (\Exception $e) {
813
					// a GC exception should not prevent users from using OC,
814
					// so log the exception
815
					\OC::$server->getLogger()->warning('Exception when running cache gc: ' . $e->getMessage(), array('app' => 'core'));
816
				}
817
			});
818
		}
819
	}
820
821
	private static function registerEncryptionWrapper() {
822
		$manager = self::$server->getEncryptionManager();
823
		\OCP\Util::connectHook('OC_Filesystem', 'preSetup', $manager, 'setupStorage');
824
	}
825
826
	private static function registerEncryptionHooks() {
827
		$enabled = self::$server->getEncryptionManager()->isEnabled();
828
		if ($enabled) {
829
			\OCP\Util::connectHook('OCP\Share', 'post_shared', 'OC\Encryption\HookManager', 'postShared');
830
			\OCP\Util::connectHook('OCP\Share', 'post_unshare', 'OC\Encryption\HookManager', 'postUnshared');
831
			\OCP\Util::connectHook('OC_Filesystem', 'post_rename', 'OC\Encryption\HookManager', 'postRename');
832
			\OCP\Util::connectHook('\OCA\Files_Trashbin\Trashbin', 'post_restore', 'OC\Encryption\HookManager', 'postRestore');
833
		}
834
	}
835
836
	/**
837
	 * register hooks for the cache
838
	 */
839
	public static function registerLogRotate() {
840
		$systemConfig = \OC::$server->getSystemConfig();
841
		if ($systemConfig->getValue('installed', false) && $systemConfig->getValue('log_rotate_size', false) && !self::checkUpgrade(false)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression self::checkUpgrade(false) of type null|boolean is loosely compared to false; this is ambiguous if the boolean can be false. You might want to explicitly use !== null instead.

If an expression can have both false, and null as possible values. It is generally a good practice to always use strict comparison to clearly distinguish between those two values.

$a = canBeFalseAndNull();

// Instead of
if ( ! $a) { }

// Better use one of the explicit versions:
if ($a !== null) { }
if ($a !== false) { }
if ($a !== null && $a !== false) { }
Loading history...
842
			//don't try to do this before we are properly setup
843
			//use custom logfile path if defined, otherwise use default of owncloud.log in data directory
844
			\OCP\BackgroundJob::registerJob('OC\Log\Rotate', $systemConfig->getValue('logfile', $systemConfig->getValue('datadirectory', OC::$SERVERROOT . '/data') . '/owncloud.log'));
845
		}
846
	}
847
848
	/**
849
	 * register hooks for the filesystem
850
	 */
851
	public static function registerFilesystemHooks() {
852
		// Check for blacklisted files
853
		OC_Hook::connect('OC_Filesystem', 'write', 'OC\Files\Filesystem', 'isBlacklisted');
854
		OC_Hook::connect('OC_Filesystem', 'rename', 'OC\Files\Filesystem', 'isBlacklisted');
855
	}
856
857
	/**
858
	 * register hooks for previews
859
	 */
860
	public static function registerPreviewHooks() {
861
		OC_Hook::connect('OC_Filesystem', 'post_write', 'OC\Preview', 'post_write');
862
		OC_Hook::connect('OC_Filesystem', 'delete', 'OC\Preview', 'prepare_delete_files');
863
		OC_Hook::connect('\OCP\Versions', 'preDelete', 'OC\Preview', 'prepare_delete');
864
		OC_Hook::connect('\OCP\Trashbin', 'preDelete', 'OC\Preview', 'prepare_delete');
865
		OC_Hook::connect('OC_Filesystem', 'post_delete', 'OC\Preview', 'post_delete_files');
866
		OC_Hook::connect('\OCP\Versions', 'delete', 'OC\Preview', 'post_delete_versions');
867
		OC_Hook::connect('\OCP\Trashbin', 'delete', 'OC\Preview', 'post_delete');
868
		OC_Hook::connect('\OCP\Versions', 'rollback', 'OC\Preview', 'post_delete_versions');
869
	}
870
871
	/**
872
	 * register hooks for sharing
873
	 */
874
	public static function registerShareHooks() {
875
		if (\OC::$server->getSystemConfig()->getValue('installed')) {
876
			OC_Hook::connect('OC_User', 'post_deleteUser', 'OC\Share\Hooks', 'post_deleteUser');
877
			OC_Hook::connect('OC_User', 'post_addToGroup', 'OC\Share\Hooks', 'post_addToGroup');
878
			OC_Hook::connect('OC_Group', 'pre_addToGroup', 'OC\Share\Hooks', 'pre_addToGroup');
879
			OC_Hook::connect('OC_User', 'post_removeFromGroup', 'OC\Share\Hooks', 'post_removeFromGroup');
880
			OC_Hook::connect('OC_User', 'post_deleteGroup', 'OC\Share\Hooks', 'post_deleteGroup');
881
		}
882
	}
883
884
	protected static function registerAutoloaderCache() {
885
		// The class loader takes an optional low-latency cache, which MUST be
886
		// namespaced. The instanceid is used for namespacing, but might be
887
		// unavailable at this point. Futhermore, it might not be possible to
888
		// generate an instanceid via \OC_Util::getInstanceId() because the
889
		// config file may not be writable. As such, we only register a class
890
		// loader cache if instanceid is available without trying to create one.
891
		$instanceId = \OC::$server->getSystemConfig()->getValue('instanceid', null);
892
		if ($instanceId) {
893
			try {
894
				$memcacheFactory = \OC::$server->getMemCacheFactory();
895
				self::$loader->setMemoryCache($memcacheFactory->createLocal('Autoloader'));
896
			} catch (\Exception $ex) {
897
			}
898
		}
899
	}
900
901
	/**
902
	 * Handle the request
903
	 */
904
	public static function handleRequest() {
905
906
		\OC::$server->getEventLogger()->start('handle_request', 'Handle request');
907
		$systemConfig = \OC::$server->getSystemConfig();
908
		// load all the classpaths from the enabled apps so they are available
909
		// in the routing files of each app
910
		OC::loadAppClassPaths();
911
912
		// Check if Nextcloud is installed or in maintenance (update) mode
913
		if (!$systemConfig->getValue('installed', false)) {
914
			\OC::$server->getSession()->clear();
915
			$setupHelper = new OC\Setup(\OC::$server->getConfig(), \OC::$server->getIniWrapper(),
916
				\OC::$server->getL10N('lib'), \OC::$server->getThemingDefaults(), \OC::$server->getLogger(),
917
				\OC::$server->getSecureRandom());
918
			$controller = new OC\Core\Controller\SetupController($setupHelper);
919
			$controller->run($_POST);
920
			exit();
921
		}
922
923
		$request = \OC::$server->getRequest();
924
		$requestPath = $request->getRawPathInfo();
925
		if (substr($requestPath, -3) !== '.js') { // we need these files during the upgrade
926
			self::checkMaintenanceMode();
927
			self::checkUpgrade();
928
		}
929
930
		// emergency app disabling
931
		if ($requestPath === '/disableapp'
932
			&& $request->getMethod() === 'POST'
933
			&& ((string)$request->getParam('appid')) !== ''
934
		) {
935
			\OCP\JSON::callCheck();
0 ignored issues
show
Deprecated Code introduced by
The method OCP\JSON::callCheck() has been deprecated with message: 8.1.0 Use annotation based CSRF checks from the AppFramework instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
936
			\OCP\JSON::checkAdminUser();
0 ignored issues
show
Deprecated Code introduced by
The method OCP\JSON::checkAdminUser() has been deprecated with message: 8.1.0 Use annotation based ACLs from the AppFramework instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
937
			$appId = (string)$request->getParam('appid');
938
			$appId = \OC_App::cleanAppId($appId);
939
940
			\OC_App::disable($appId);
941
			\OC_JSON::success();
0 ignored issues
show
Deprecated Code introduced by
The method OC_JSON::success() has been deprecated with message: Use a AppFramework JSONResponse instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
942
			exit();
943
		}
944
945
		// Always load authentication apps
946
		OC_App::loadApps(['authentication']);
947
948
		// Load minimum set of apps
949
		if (!self::checkUpgrade(false)
0 ignored issues
show
Bug Best Practice introduced by
The expression self::checkUpgrade(false) of type null|boolean is loosely compared to false; this is ambiguous if the boolean can be false. You might want to explicitly use !== null instead.

If an expression can have both false, and null as possible values. It is generally a good practice to always use strict comparison to clearly distinguish between those two values.

$a = canBeFalseAndNull();

// Instead of
if ( ! $a) { }

// Better use one of the explicit versions:
if ($a !== null) { }
if ($a !== false) { }
if ($a !== null && $a !== false) { }
Loading history...
950
			&& !$systemConfig->getValue('maintenance', false)) {
951
			// For logged-in users: Load everything
952
			if(OC_User::isLoggedIn()) {
953
				OC_App::loadApps();
954
			} else {
955
				// For guests: Load only filesystem and logging
956
				OC_App::loadApps(array('filesystem', 'logging'));
957
				\OC_User::tryBasicAuthLogin();
958
			}
959
		}
960
961
		if (!self::$CLI and (!isset($_GET["logout"]) or ($_GET["logout"] !== 'true'))) {
962
			try {
963
				if (!$systemConfig->getValue('maintenance', false) && !self::checkUpgrade(false)) {
964
					OC_App::loadApps(array('filesystem', 'logging'));
965
					OC_App::loadApps();
966
				}
967
				self::checkSingleUserMode();
968
				OC_Util::setupFS();
969
				OC::$server->getRouter()->match(\OC::$server->getRequest()->getRawPathInfo());
970
				return;
971
			} catch (Symfony\Component\Routing\Exception\ResourceNotFoundException $e) {
0 ignored issues
show
The class Symfony\Component\Routin...sourceNotFoundException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
972
				//header('HTTP/1.0 404 Not Found');
973
			} catch (Symfony\Component\Routing\Exception\MethodNotAllowedException $e) {
0 ignored issues
show
The class Symfony\Component\Routin...thodNotAllowedException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
974
				OC_Response::setStatus(405);
975
				return;
976
			}
977
		}
978
979
		// Handle redirect URL for logged in users
980
		if (isset($_REQUEST['redirect_url']) && OC_User::isLoggedIn()) {
981
			$location = \OC::$server->getURLGenerator()->getAbsoluteURL(urldecode($_REQUEST['redirect_url']));
982
983
			// Deny the redirect if the URL contains a @
984
			// This prevents unvalidated redirects like ?redirect_url=:[email protected]
985
			if (strpos($location, '@') === false) {
986
				header('Location: ' . $location);
987
				return;
988
			}
989
		}
990
		// Handle WebDAV
991
		if ($_SERVER['REQUEST_METHOD'] == 'PROPFIND') {
992
			// not allowed any more to prevent people
993
			// mounting this root directly.
994
			// Users need to mount remote.php/webdav instead.
995
			header('HTTP/1.1 405 Method Not Allowed');
996
			header('Status: 405 Method Not Allowed');
997
			return;
998
		}
999
1000
		// Redirect to index if the logout link is accessed without valid session
1001
		// this is needed to prevent "Token expired" messages while login if a session is expired
1002
		// @see https://github.com/owncloud/core/pull/8443#issuecomment-42425583
1003
		if(isset($_GET['logout']) && !OC_User::isLoggedIn()) {
1004
			header("Location: " . \OC::$server->getURLGenerator()->getAbsoluteURL('/'));
1005
			return;
1006
		}
1007
1008
		// Someone is logged in
1009
		if (OC_User::isLoggedIn()) {
1010
			OC_App::loadApps();
1011
			OC_User::setupBackends();
1012
			OC_Util::setupFS();
1013
			if (isset($_GET["logout"]) and ($_GET["logout"])) {
1014
				OC_JSON::callCheck();
1015
				if (isset($_COOKIE['oc_token'])) {
1016
					\OC::$server->getConfig()->deleteUserValue(OC_User::getUser(), 'login_token', $_COOKIE['oc_token']);
1017
				}
1018
				OC_User::logout();
1019
				// redirect to webroot and add slash if webroot is empty
1020
				header("Location: " . \OC::$server->getURLGenerator()->getAbsoluteURL('/'));
1021
			} else {
1022
				// Redirect to default application
1023
				OC_Util::redirectToDefaultPage();
1024
			}
1025
		} else {
1026
			// Not handled and not logged in
1027
			self::handleLogin();
1028
		}
1029
	}
1030
1031
	protected static function handleAuthHeaders() {
1032
		//copy http auth headers for apache+php-fcgid work around
1033
		if (isset($_SERVER['HTTP_XAUTHORIZATION']) && !isset($_SERVER['HTTP_AUTHORIZATION'])) {
1034
			$_SERVER['HTTP_AUTHORIZATION'] = $_SERVER['HTTP_XAUTHORIZATION'];
1035
		}
1036
1037
		// Extract PHP_AUTH_USER/PHP_AUTH_PW from other headers if necessary.
1038
		$vars = array(
1039
			'HTTP_AUTHORIZATION', // apache+php-cgi work around
1040
			'REDIRECT_HTTP_AUTHORIZATION', // apache+php-cgi alternative
1041
		);
1042
		foreach ($vars as $var) {
1043
			if (isset($_SERVER[$var]) && preg_match('/Basic\s+(.*)$/i', $_SERVER[$var], $matches)) {
1044
				list($name, $password) = explode(':', base64_decode($matches[1]), 2);
1045
				$_SERVER['PHP_AUTH_USER'] = $name;
1046
				$_SERVER['PHP_AUTH_PW'] = $password;
1047
				break;
1048
			}
1049
		}
1050
	}
1051
1052
	protected static function handleLogin() {
1053
		OC_App::loadApps(array('prelogin'));
1054
		$error = array();
1055
		$messages = [];
1056
1057
		try {
1058
			// auth possible via apache module?
1059
			if (OC::tryApacheAuth()) {
1060
				$error[] = 'apacheauthfailed';
1061
			} // remember was checked after last login
1062
			elseif (OC::tryRememberLogin()) {
1063
				$error[] = 'invalidcookie';
1064
			} // logon via web form
1065
			elseif (OC::tryFormLogin()) {
1066
				$error[] = 'invalidpassword';
1067
			}
1068
		} catch (\OC\User\LoginException $e) {
1069
			$messages[] = $e->getMessage();
1070
		} catch (\Exception $ex) {
1071
			\OCP\Util::logException('handleLogin', $ex);
1072
			// do not disclose information. show generic error
1073
			$error[] = 'internalexception';
1074
		}
1075
1076
		OC_Util::displayLoginPage(array_unique($error), $messages);
1077
	}
1078
1079
	/**
1080
	 * Remove outdated and therefore invalid tokens for a user
1081
	 * @param string $user
1082
	 */
1083
	protected static function cleanupLoginTokens($user) {
1084
		$config = \OC::$server->getConfig();
1085
		$cutoff = time() - $config->getSystemValue('remember_login_cookie_lifetime', 60 * 60 * 24 * 15);
1086
		$tokens = $config->getUserKeys($user, 'login_token');
1087
		foreach ($tokens as $token) {
1088
			$time = $config->getUserValue($user, 'login_token', $token);
1089
			if ($time < $cutoff) {
1090
				$config->deleteUserValue($user, 'login_token', $token);
1091
			}
1092
		}
1093
	}
1094
1095
	/**
1096
	 * Try to login a user via HTTP authentication
1097
	 * @return bool|void
1098
	 */
1099
	protected static function tryApacheAuth() {
1100
		$return = OC_User::handleApacheAuth();
1101
1102
		// if return is true we are logged in -> redirect to the default page
1103
		if ($return === true) {
1104
			$_REQUEST['redirect_url'] = \OC::$server->getRequest()->getRequestUri();
1105
			OC_Util::redirectToDefaultPage();
1106
			exit;
1107
		}
1108
1109
		// in case $return is null apache based auth is not enabled
1110
		return is_null($return) ? false : true;
1111
	}
1112
1113
	/**
1114
	 * Try to login a user using the remember me cookie.
1115
	 * @return bool Whether the provided cookie was valid
1116
	 */
1117
	protected static function tryRememberLogin() {
1118
		if (!isset($_COOKIE["oc_remember_login"])
1119
			|| !isset($_COOKIE["oc_token"])
1120
			|| !isset($_COOKIE["oc_username"])
1121
			|| !$_COOKIE["oc_remember_login"]
1122
			|| !OC_Util::rememberLoginAllowed()
1123
		) {
1124
			return false;
1125
		}
1126
1127
		if (\OC::$server->getConfig()->getSystemValue('debug', false)) {
1128
			\OCP\Util::writeLog('core', 'Trying to login from cookie', \OCP\Util::DEBUG);
1129
		}
1130
1131
		if(OC_User::userExists($_COOKIE['oc_username'])) {
1132
			self::cleanupLoginTokens($_COOKIE['oc_username']);
1133
			// verify whether the supplied "remember me" token was valid
1134
			$granted = OC_User::loginWithCookie(
1135
				$_COOKIE['oc_username'], $_COOKIE['oc_token']);
1136
			if($granted === true) {
1137
				OC_Util::redirectToDefaultPage();
1138
				// doesn't return
1139
			}
1140
			\OCP\Util::writeLog('core', 'Authentication cookie rejected for user ' .
1141
				$_COOKIE['oc_username'], \OCP\Util::WARN);
1142
			// if you reach this point you have changed your password
1143
			// or you are an attacker
1144
			// we can not delete tokens here because users may reach
1145
			// this point multiple times after a password change
1146
		}
1147
1148
		OC_User::unsetMagicInCookie();
1149
		return true;
1150
	}
1151
1152
	/**
1153
	 * Tries to login a user using the form based authentication
1154
	 * @return bool|void
1155
	 */
1156
	protected static function tryFormLogin() {
1157
		if (!isset($_POST["user"]) || !isset($_POST['password'])) {
1158
			return false;
1159
		}
1160
1161
		if(!(\OC::$server->getRequest()->passesCSRFCheck())) {
1162
			return false;
1163
		}
1164
		OC_App::loadApps();
1165
1166
		//setup extra user backends
1167
		OC_User::setupBackends();
1168
1169
		if (OC_User::login((string)$_POST["user"], (string)$_POST["password"])) {
1170
			$userId = OC_User::getUser();
1171
1172
			// setting up the time zone
1173
			if (isset($_POST['timezone-offset'])) {
1174
				self::$server->getSession()->set('timezone', (string)$_POST['timezone-offset']);
1175
				self::$server->getConfig()->setUserValue($userId, 'core', 'timezone', (string)$_POST['timezone']);
1176
			}
1177
1178
			self::cleanupLoginTokens($userId);
1179
			if (!empty($_POST["remember_login"])) {
1180
				$config = self::$server->getConfig();
1181
				if ($config->getSystemValue('debug', false)) {
1182
					self::$server->getLogger()->debug('Setting remember login to cookie', array('app' => 'core'));
1183
				}
1184
				$token = \OC::$server->getSecureRandom()->generate(32);
1185
				$config->setUserValue($userId, 'login_token', $token, time());
1186
				OC_User::setMagicInCookie($userId, $token);
1187
			} else {
1188
				OC_User::unsetMagicInCookie();
1189
			}
1190
			OC_Util::redirectToDefaultPage();
1191
			exit();
1192
		}
1193
		return true;
1194
	}
1195
1196
}
1197
1198
1199
OC::init();
1200