Completed
Push — master ( d0de8e...846b0d )
by Morris
65:39 queued 44:01
created
settings/ajax/disableapp.php 2 patches
Indentation   +7 added lines, -7 removed lines patch added patch discarded remove patch
@@ -26,19 +26,19 @@
 block discarded – undo
26 26
 
27 27
 $lastConfirm = (int) \OC::$server->getSession()->get('last-password-confirm');
28 28
 if ($lastConfirm < (time() - 30 * 60 + 15)) { // allow 15 seconds delay
29
-	$l = \OC::$server->getL10N('core');
30
-	OC_JSON::error(array( 'data' => array( 'message' => $l->t('Password confirmation is required'))));
31
-	exit();
29
+    $l = \OC::$server->getL10N('core');
30
+    OC_JSON::error(array( 'data' => array( 'message' => $l->t('Password confirmation is required'))));
31
+    exit();
32 32
 }
33 33
 
34 34
 if (!array_key_exists('appid', $_POST)) {
35
-	OC_JSON::error();
36
-	exit;
35
+    OC_JSON::error();
36
+    exit;
37 37
 }
38 38
 
39 39
 $appIds = (array)$_POST['appid'];
40 40
 foreach($appIds as $appId) {
41
-	$appId = OC_App::cleanAppId($appId);
42
-	\OC::$server->getAppManager()->disableApp($appId);
41
+    $appId = OC_App::cleanAppId($appId);
42
+    \OC::$server->getAppManager()->disableApp($appId);
43 43
 }
44 44
 OC_JSON::success();
Please login to merge, or discard this patch.
Spacing   +3 added lines, -3 removed lines patch added patch discarded remove patch
@@ -27,7 +27,7 @@  discard block
 block discarded – undo
27 27
 $lastConfirm = (int) \OC::$server->getSession()->get('last-password-confirm');
28 28
 if ($lastConfirm < (time() - 30 * 60 + 15)) { // allow 15 seconds delay
29 29
 	$l = \OC::$server->getL10N('core');
30
-	OC_JSON::error(array( 'data' => array( 'message' => $l->t('Password confirmation is required'))));
30
+	OC_JSON::error(array('data' => array('message' => $l->t('Password confirmation is required'))));
31 31
 	exit();
32 32
 }
33 33
 
@@ -36,8 +36,8 @@  discard block
 block discarded – undo
36 36
 	exit;
37 37
 }
38 38
 
39
-$appIds = (array)$_POST['appid'];
40
-foreach($appIds as $appId) {
39
+$appIds = (array) $_POST['appid'];
40
+foreach ($appIds as $appId) {
41 41
 	$appId = OC_App::cleanAppId($appId);
42 42
 	\OC::$server->getAppManager()->disableApp($appId);
43 43
 }
Please login to merge, or discard this patch.
lib/base.php 2 patches
Spacing   +56 added lines, -56 removed lines patch added patch discarded remove patch
@@ -127,14 +127,14 @@  discard block
 block discarded – undo
127 127
 	 * the app path list is empty or contains an invalid path
128 128
 	 */
129 129
 	public static function initPaths() {
130
-		if(defined('PHPUNIT_CONFIG_DIR')) {
131
-			self::$configDir = OC::$SERVERROOT . '/' . PHPUNIT_CONFIG_DIR . '/';
132
-		} elseif(defined('PHPUNIT_RUN') and PHPUNIT_RUN and is_dir(OC::$SERVERROOT . '/tests/config/')) {
133
-			self::$configDir = OC::$SERVERROOT . '/tests/config/';
134
-		} elseif($dir = getenv('NEXTCLOUD_CONFIG_DIR')) {
135
-			self::$configDir = rtrim($dir, '/') . '/';
130
+		if (defined('PHPUNIT_CONFIG_DIR')) {
131
+			self::$configDir = OC::$SERVERROOT.'/'.PHPUNIT_CONFIG_DIR.'/';
132
+		} elseif (defined('PHPUNIT_RUN') and PHPUNIT_RUN and is_dir(OC::$SERVERROOT.'/tests/config/')) {
133
+			self::$configDir = OC::$SERVERROOT.'/tests/config/';
134
+		} elseif ($dir = getenv('NEXTCLOUD_CONFIG_DIR')) {
135
+			self::$configDir = rtrim($dir, '/').'/';
136 136
 		} else {
137
-			self::$configDir = OC::$SERVERROOT . '/config/';
137
+			self::$configDir = OC::$SERVERROOT.'/config/';
138 138
 		}
139 139
 		self::$config = new \OC\Config(self::$configDir);
140 140
 
@@ -156,9 +156,9 @@  discard block
 block discarded – undo
156 156
 			//make sure suburi follows the same rules as scriptName
157 157
 			if (substr(OC::$SUBURI, -9) != 'index.php') {
158 158
 				if (substr(OC::$SUBURI, -1) != '/') {
159
-					OC::$SUBURI = OC::$SUBURI . '/';
159
+					OC::$SUBURI = OC::$SUBURI.'/';
160 160
 				}
161
-				OC::$SUBURI = OC::$SUBURI . 'index.php';
161
+				OC::$SUBURI = OC::$SUBURI.'index.php';
162 162
 			}
163 163
 		}
164 164
 
@@ -170,7 +170,7 @@  discard block
 block discarded – undo
170 170
 				OC::$WEBROOT = substr($scriptName, 0, 0 - strlen(OC::$SUBURI));
171 171
 
172 172
 				if (OC::$WEBROOT != '' && OC::$WEBROOT[0] !== '/') {
173
-					OC::$WEBROOT = '/' . OC::$WEBROOT;
173
+					OC::$WEBROOT = '/'.OC::$WEBROOT;
174 174
 				}
175 175
 			} else {
176 176
 				// The scriptName is not ending with OC::$SUBURI
@@ -199,11 +199,11 @@  discard block
 block discarded – undo
199 199
 					OC::$APPSROOTS[] = $paths;
200 200
 				}
201 201
 			}
202
-		} elseif (file_exists(OC::$SERVERROOT . '/apps')) {
203
-			OC::$APPSROOTS[] = array('path' => OC::$SERVERROOT . '/apps', 'url' => '/apps', 'writable' => true);
204
-		} elseif (file_exists(OC::$SERVERROOT . '/../apps')) {
202
+		} elseif (file_exists(OC::$SERVERROOT.'/apps')) {
203
+			OC::$APPSROOTS[] = array('path' => OC::$SERVERROOT.'/apps', 'url' => '/apps', 'writable' => true);
204
+		} elseif (file_exists(OC::$SERVERROOT.'/../apps')) {
205 205
 			OC::$APPSROOTS[] = array(
206
-				'path' => rtrim(dirname(OC::$SERVERROOT), '/') . '/apps',
206
+				'path' => rtrim(dirname(OC::$SERVERROOT), '/').'/apps',
207 207
 				'url' => '/apps',
208 208
 				'writable' => true
209 209
 			);
@@ -233,8 +233,8 @@  discard block
 block discarded – undo
233 233
 		$l = \OC::$server->getL10N('lib');
234 234
 
235 235
 		// Create config if it does not already exist
236
-		$configFilePath = self::$configDir .'/config.php';
237
-		if(!file_exists($configFilePath)) {
236
+		$configFilePath = self::$configDir.'/config.php';
237
+		if (!file_exists($configFilePath)) {
238 238
 			@touch($configFilePath);
239 239
 		}
240 240
 
@@ -249,13 +249,13 @@  discard block
 block discarded – undo
249 249
 				echo $l->t('Cannot write into "config" directory!')."\n";
250 250
 				echo $l->t('This can usually be fixed by giving the webserver write access to the config directory')."\n";
251 251
 				echo "\n";
252
-				echo $l->t('See %s', [ $urlGenerator->linkToDocs('admin-dir_permissions') ])."\n";
252
+				echo $l->t('See %s', [$urlGenerator->linkToDocs('admin-dir_permissions')])."\n";
253 253
 				exit;
254 254
 			} else {
255 255
 				OC_Template::printErrorPage(
256 256
 					$l->t('Cannot write into "config" directory!'),
257 257
 					$l->t('This can usually be fixed by giving the webserver write access to the config directory. See %s',
258
-					 [ $urlGenerator->linkToDocs('admin-dir_permissions') ])
258
+					 [$urlGenerator->linkToDocs('admin-dir_permissions')])
259 259
 				);
260 260
 			}
261 261
 		}
@@ -270,8 +270,8 @@  discard block
 block discarded – undo
270 270
 			if (OC::$CLI) {
271 271
 				throw new Exception('Not installed');
272 272
 			} else {
273
-				$url = OC::$WEBROOT . '/index.php';
274
-				header('Location: ' . $url);
273
+				$url = OC::$WEBROOT.'/index.php';
274
+				header('Location: '.$url);
275 275
 			}
276 276
 			exit();
277 277
 		}
@@ -377,14 +377,14 @@  discard block
 block discarded – undo
377 377
 		$incompatibleShippedApps = [];
378 378
 		foreach ($incompatibleApps as $appInfo) {
379 379
 			if ($appManager->isShipped($appInfo['id'])) {
380
-				$incompatibleShippedApps[] = $appInfo['name'] . ' (' . $appInfo['id'] . ')';
380
+				$incompatibleShippedApps[] = $appInfo['name'].' ('.$appInfo['id'].')';
381 381
 			}
382 382
 		}
383 383
 
384 384
 		if (!empty($incompatibleShippedApps)) {
385 385
 			$l = \OC::$server->getL10N('core');
386 386
 			$hint = $l->t('The files of the app %$1s were not replaced correctly. Make sure it is a version compatible with the server.', [implode(', ', $incompatibleShippedApps)]);
387
-			throw new \OC\HintException('The files of the app ' . implode(', ', $incompatibleShippedApps) . ' were not replaced correctly. Make sure it is a version compatible with the server.', $hint);
387
+			throw new \OC\HintException('The files of the app '.implode(', ', $incompatibleShippedApps).' were not replaced correctly. Make sure it is a version compatible with the server.', $hint);
388 388
 		}
389 389
 
390 390
 		$tmpl->assign('appsToUpgrade', $appManager->getAppsNeedingUpgrade($ocVersion));
@@ -395,7 +395,7 @@  discard block
 block discarded – undo
395 395
 	}
396 396
 
397 397
 	public static function initSession() {
398
-		if(self::$server->getRequest()->getServerProtocol() === 'https') {
398
+		if (self::$server->getRequest()->getServerProtocol() === 'https') {
399 399
 			ini_set('session.cookie_secure', true);
400 400
 		}
401 401
 
@@ -403,7 +403,7 @@  discard block
 block discarded – undo
403 403
 		ini_set('session.cookie_httponly', 'true');
404 404
 
405 405
 		// set the cookie path to the Nextcloud directory
406
-		$cookie_path = OC::$WEBROOT ? : '/';
406
+		$cookie_path = OC::$WEBROOT ?: '/';
407 407
 		ini_set('session.cookie_path', $cookie_path);
408 408
 
409 409
 		// Let the session name be changed in the initSession Hook
@@ -437,7 +437,7 @@  discard block
 block discarded – undo
437 437
 		// session timeout
438 438
 		if ($session->exists('LAST_ACTIVITY') && (time() - $session->get('LAST_ACTIVITY') > $sessionLifeTime)) {
439 439
 			if (isset($_COOKIE[session_name()])) {
440
-				setcookie(session_name(), null, -1, self::$WEBROOT ? : '/');
440
+				setcookie(session_name(), null, -1, self::$WEBROOT ?: '/');
441 441
 			}
442 442
 			\OC::$server->getUserSession()->logout();
443 443
 		}
@@ -459,7 +459,7 @@  discard block
 block discarded – undo
459 459
 				continue;
460 460
 			}
461 461
 
462
-			$file = $appPath . '/appinfo/classpath.php';
462
+			$file = $appPath.'/appinfo/classpath.php';
463 463
 			if (file_exists($file)) {
464 464
 				require_once $file;
465 465
 			}
@@ -487,14 +487,14 @@  discard block
 block discarded – undo
487 487
 
488 488
 		// Append __Host to the cookie if it meets the requirements
489 489
 		$cookiePrefix = '';
490
-		if($cookieParams['secure'] === true && $cookieParams['path'] === '/') {
490
+		if ($cookieParams['secure'] === true && $cookieParams['path'] === '/') {
491 491
 			$cookiePrefix = '__Host-';
492 492
 		}
493 493
 
494
-		foreach($policies as $policy) {
494
+		foreach ($policies as $policy) {
495 495
 			header(
496 496
 				sprintf(
497
-					'Set-Cookie: %snc_sameSiteCookie%s=true; path=%s; httponly;' . $secureCookie . 'expires=Fri, 31-Dec-2100 23:59:59 GMT; SameSite=%s',
497
+					'Set-Cookie: %snc_sameSiteCookie%s=true; path=%s; httponly;'.$secureCookie.'expires=Fri, 31-Dec-2100 23:59:59 GMT; SameSite=%s',
498 498
 					$cookiePrefix,
499 499
 					$policy,
500 500
 					$cookieParams['path'],
@@ -525,31 +525,31 @@  discard block
 block discarded – undo
525 525
 			// OS X Finder
526 526
 			'/^WebDAVFS/',
527 527
 		];
528
-		if($request->isUserAgent($incompatibleUserAgents)) {
528
+		if ($request->isUserAgent($incompatibleUserAgents)) {
529 529
 			return;
530 530
 		}
531 531
 
532
-		if(count($_COOKIE) > 0) {
532
+		if (count($_COOKIE) > 0) {
533 533
 			$requestUri = $request->getScriptName();
534 534
 			$processingScript = explode('/', $requestUri);
535
-			$processingScript = $processingScript[count($processingScript)-1];
535
+			$processingScript = $processingScript[count($processingScript) - 1];
536 536
 
537 537
 			// index.php routes are handled in the middleware
538
-			if($processingScript === 'index.php') {
538
+			if ($processingScript === 'index.php') {
539 539
 				return;
540 540
 			}
541 541
 
542 542
 			// All other endpoints require the lax and the strict cookie
543
-			if(!$request->passesStrictCookieCheck()) {
543
+			if (!$request->passesStrictCookieCheck()) {
544 544
 				self::sendSameSiteCookies();
545 545
 				// Debug mode gets access to the resources without strict cookie
546 546
 				// due to the fact that the SabreDAV browser also lives there.
547
-				if(!\OC::$server->getConfig()->getSystemValue('debug', false)) {
547
+				if (!\OC::$server->getConfig()->getSystemValue('debug', false)) {
548 548
 					http_response_code(\OCP\AppFramework\Http::STATUS_SERVICE_UNAVAILABLE);
549 549
 					exit();
550 550
 				}
551 551
 			}
552
-		} elseif(!isset($_COOKIE['nc_sameSiteCookielax']) || !isset($_COOKIE['nc_sameSiteCookiestrict'])) {
552
+		} elseif (!isset($_COOKIE['nc_sameSiteCookielax']) || !isset($_COOKIE['nc_sameSiteCookiestrict'])) {
553 553
 			self::sendSameSiteCookies();
554 554
 		}
555 555
 	}
@@ -560,12 +560,12 @@  discard block
 block discarded – undo
560 560
 
561 561
 		// register autoloader
562 562
 		$loaderStart = microtime(true);
563
-		require_once __DIR__ . '/autoloader.php';
563
+		require_once __DIR__.'/autoloader.php';
564 564
 		self::$loader = new \OC\Autoloader([
565
-			OC::$SERVERROOT . '/lib/private/legacy',
565
+			OC::$SERVERROOT.'/lib/private/legacy',
566 566
 		]);
567 567
 		if (defined('PHPUNIT_RUN')) {
568
-			self::$loader->addValidRoot(OC::$SERVERROOT . '/tests');
568
+			self::$loader->addValidRoot(OC::$SERVERROOT.'/tests');
569 569
 		}
570 570
 		spl_autoload_register(array(self::$loader, 'load'));
571 571
 		$loaderEnd = microtime(true);
@@ -573,12 +573,12 @@  discard block
 block discarded – undo
573 573
 		self::$CLI = (php_sapi_name() == 'cli');
574 574
 
575 575
 		// Add default composer PSR-4 autoloader
576
-		self::$composerAutoloader = require_once OC::$SERVERROOT . '/lib/composer/autoload.php';
576
+		self::$composerAutoloader = require_once OC::$SERVERROOT.'/lib/composer/autoload.php';
577 577
 
578 578
 		try {
579 579
 			self::initPaths();
580 580
 			// setup 3rdparty autoloader
581
-			$vendorAutoLoad = OC::$SERVERROOT. '/3rdparty/autoload.php';
581
+			$vendorAutoLoad = OC::$SERVERROOT.'/3rdparty/autoload.php';
582 582
 			if (!file_exists($vendorAutoLoad)) {
583 583
 				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".');
584 584
 			}
@@ -588,7 +588,7 @@  discard block
 block discarded – undo
588 588
 			if (!self::$CLI) {
589 589
 				$claimedProtocol = strtoupper($_SERVER['SERVER_PROTOCOL']);
590 590
 				$protocol = in_array($claimedProtocol, ['HTTP/1.0', 'HTTP/1.1', 'HTTP/2']) ? $claimedProtocol : 'HTTP/1.1';
591
-				header($protocol . ' ' . OC_Response::STATUS_SERVICE_UNAVAILABLE);
591
+				header($protocol.' '.OC_Response::STATUS_SERVICE_UNAVAILABLE);
592 592
 			}
593 593
 			// we can't use the template error page here, because this needs the
594 594
 			// DI container which isn't available yet
@@ -606,7 +606,7 @@  discard block
 block discarded – undo
606 606
 		@ini_set('display_errors', '0');
607 607
 		@ini_set('log_errors', '1');
608 608
 
609
-		if(!date_default_timezone_set('UTC')) {
609
+		if (!date_default_timezone_set('UTC')) {
610 610
 			throw new \RuntimeException('Could not set timezone to UTC');
611 611
 		}
612 612
 
@@ -660,11 +660,11 @@  discard block
 block discarded – undo
660 660
 					// Convert l10n string into regular string for usage in database
661 661
 					$staticErrors = [];
662 662
 					foreach ($errors as $error) {
663
-						echo $error['error'] . "\n";
664
-						echo $error['hint'] . "\n\n";
663
+						echo $error['error']."\n";
664
+						echo $error['hint']."\n\n";
665 665
 						$staticErrors[] = [
666
-							'error' => (string)$error['error'],
667
-							'hint' => (string)$error['hint'],
666
+							'error' => (string) $error['error'],
667
+							'hint' => (string) $error['hint'],
668 668
 						];
669 669
 					}
670 670
 
@@ -686,7 +686,7 @@  discard block
 block discarded – undo
686 686
 		}
687 687
 		//try to set the session lifetime
688 688
 		$sessionLifeTime = self::getSessionLifeTime();
689
-		@ini_set('gc_maxlifetime', (string)$sessionLifeTime);
689
+		@ini_set('gc_maxlifetime', (string) $sessionLifeTime);
690 690
 
691 691
 		$systemConfig = \OC::$server->getSystemConfig();
692 692
 
@@ -734,7 +734,7 @@  discard block
 block discarded – undo
734 734
 		register_shutdown_function(array($lockProvider, 'releaseAll'));
735 735
 
736 736
 		// Check whether the sample configuration has been copied
737
-		if($systemConfig->getValue('copied_sample_config', false)) {
737
+		if ($systemConfig->getValue('copied_sample_config', false)) {
738 738
 			$l = \OC::$server->getL10N('lib');
739 739
 			header('HTTP/1.1 503 Service Temporarily Unavailable');
740 740
 			header('Status: 503 Service Temporarily Unavailable');
@@ -760,11 +760,11 @@  discard block
 block discarded – undo
760 760
 		) {
761 761
 			// Allow access to CSS resources
762 762
 			$isScssRequest = false;
763
-			if(strpos($request->getPathInfo(), '/css/') === 0) {
763
+			if (strpos($request->getPathInfo(), '/css/') === 0) {
764 764
 				$isScssRequest = true;
765 765
 			}
766 766
 
767
-			if(substr($request->getRequestUri(), -11) === '/status.php') {
767
+			if (substr($request->getRequestUri(), -11) === '/status.php') {
768 768
 				OC_Response::setStatus(\OC_Response::STATUS_BAD_REQUEST);
769 769
 				header('Status: 400 Bad Request');
770 770
 				header('Content-Type: application/json');
@@ -804,7 +804,7 @@  discard block
 block discarded – undo
804 804
 
805 805
 			// NOTE: This will be replaced to use OCP
806 806
 			$userSession = self::$server->getUserSession();
807
-			$userSession->listen('\OC\User', 'postLogin', function () use ($userSession) {
807
+			$userSession->listen('\OC\User', 'postLogin', function() use ($userSession) {
808 808
 				if (!defined('PHPUNIT_RUN')) {
809 809
 					// reset brute force delay for this IP address and username
810 810
 					$uid = \OC::$server->getUserSession()->getUser()->getUID();
@@ -941,12 +941,12 @@  discard block
 block discarded – undo
941 941
 		// emergency app disabling
942 942
 		if ($requestPath === '/disableapp'
943 943
 			&& $request->getMethod() === 'POST'
944
-			&& ((array)$request->getParam('appid')) !== ''
944
+			&& ((array) $request->getParam('appid')) !== ''
945 945
 		) {
946 946
 			\OCP\JSON::callCheck();
947 947
 			\OCP\JSON::checkAdminUser();
948
-			$appIds = (array)$request->getParam('appid');
949
-			foreach($appIds as $appId) {
948
+			$appIds = (array) $request->getParam('appid');
949
+			foreach ($appIds as $appId) {
950 950
 				$appId = \OC_App::cleanAppId($appId);
951 951
 				\OC::$server->getAppManager()->disableApp($appId);
952 952
 			}
@@ -961,7 +961,7 @@  discard block
 block discarded – undo
961 961
 		if (!\OCP\Util::needUpgrade()
962 962
 			&& !$systemConfig->getValue('maintenance', false)) {
963 963
 			// For logged-in users: Load everything
964
-			if(\OC::$server->getUserSession()->isLoggedIn()) {
964
+			if (\OC::$server->getUserSession()->isLoggedIn()) {
965 965
 				OC_App::loadApps();
966 966
 			} else {
967 967
 				// For guests: Load only filesystem and logging
Please login to merge, or discard this patch.
Indentation   +991 added lines, -991 removed lines patch added patch discarded remove patch
@@ -67,997 +67,997 @@
 block discarded – undo
67 67
  * OC_autoload!
68 68
  */
69 69
 class OC {
70
-	/**
71
-	 * Associative array for autoloading. classname => filename
72
-	 */
73
-	public static $CLASSPATH = array();
74
-	/**
75
-	 * The installation path for Nextcloud  on the server (e.g. /srv/http/nextcloud)
76
-	 */
77
-	public static $SERVERROOT = '';
78
-	/**
79
-	 * the current request path relative to the Nextcloud root (e.g. files/index.php)
80
-	 */
81
-	private static $SUBURI = '';
82
-	/**
83
-	 * the Nextcloud root path for http requests (e.g. nextcloud/)
84
-	 */
85
-	public static $WEBROOT = '';
86
-	/**
87
-	 * The installation path array of the apps folder on the server (e.g. /srv/http/nextcloud) 'path' and
88
-	 * web path in 'url'
89
-	 */
90
-	public static $APPSROOTS = array();
91
-
92
-	/**
93
-	 * @var string
94
-	 */
95
-	public static $configDir;
96
-
97
-	/**
98
-	 * requested app
99
-	 */
100
-	public static $REQUESTEDAPP = '';
101
-
102
-	/**
103
-	 * check if Nextcloud runs in cli mode
104
-	 */
105
-	public static $CLI = false;
106
-
107
-	/**
108
-	 * @var \OC\Autoloader $loader
109
-	 */
110
-	public static $loader = null;
111
-
112
-	/** @var \Composer\Autoload\ClassLoader $composerAutoloader */
113
-	public static $composerAutoloader = null;
114
-
115
-	/**
116
-	 * @var \OC\Server
117
-	 */
118
-	public static $server = null;
119
-
120
-	/**
121
-	 * @var \OC\Config
122
-	 */
123
-	private static $config = null;
124
-
125
-	/**
126
-	 * @throws \RuntimeException when the 3rdparty directory is missing or
127
-	 * the app path list is empty or contains an invalid path
128
-	 */
129
-	public static function initPaths() {
130
-		if(defined('PHPUNIT_CONFIG_DIR')) {
131
-			self::$configDir = OC::$SERVERROOT . '/' . PHPUNIT_CONFIG_DIR . '/';
132
-		} elseif(defined('PHPUNIT_RUN') and PHPUNIT_RUN and is_dir(OC::$SERVERROOT . '/tests/config/')) {
133
-			self::$configDir = OC::$SERVERROOT . '/tests/config/';
134
-		} elseif($dir = getenv('NEXTCLOUD_CONFIG_DIR')) {
135
-			self::$configDir = rtrim($dir, '/') . '/';
136
-		} else {
137
-			self::$configDir = OC::$SERVERROOT . '/config/';
138
-		}
139
-		self::$config = new \OC\Config(self::$configDir);
140
-
141
-		OC::$SUBURI = str_replace("\\", "/", substr(realpath($_SERVER["SCRIPT_FILENAME"]), strlen(OC::$SERVERROOT)));
142
-		/**
143
-		 * FIXME: The following lines are required because we can't yet instantiate
144
-		 *        \OC::$server->getRequest() since \OC::$server does not yet exist.
145
-		 */
146
-		$params = [
147
-			'server' => [
148
-				'SCRIPT_NAME' => $_SERVER['SCRIPT_NAME'],
149
-				'SCRIPT_FILENAME' => $_SERVER['SCRIPT_FILENAME'],
150
-			],
151
-		];
152
-		$fakeRequest = new \OC\AppFramework\Http\Request($params, null, new \OC\AllConfig(new \OC\SystemConfig(self::$config)));
153
-		$scriptName = $fakeRequest->getScriptName();
154
-		if (substr($scriptName, -1) == '/') {
155
-			$scriptName .= 'index.php';
156
-			//make sure suburi follows the same rules as scriptName
157
-			if (substr(OC::$SUBURI, -9) != 'index.php') {
158
-				if (substr(OC::$SUBURI, -1) != '/') {
159
-					OC::$SUBURI = OC::$SUBURI . '/';
160
-				}
161
-				OC::$SUBURI = OC::$SUBURI . 'index.php';
162
-			}
163
-		}
164
-
165
-
166
-		if (OC::$CLI) {
167
-			OC::$WEBROOT = self::$config->getValue('overwritewebroot', '');
168
-		} else {
169
-			if (substr($scriptName, 0 - strlen(OC::$SUBURI)) === OC::$SUBURI) {
170
-				OC::$WEBROOT = substr($scriptName, 0, 0 - strlen(OC::$SUBURI));
171
-
172
-				if (OC::$WEBROOT != '' && OC::$WEBROOT[0] !== '/') {
173
-					OC::$WEBROOT = '/' . OC::$WEBROOT;
174
-				}
175
-			} else {
176
-				// The scriptName is not ending with OC::$SUBURI
177
-				// This most likely means that we are calling from CLI.
178
-				// However some cron jobs still need to generate
179
-				// a web URL, so we use overwritewebroot as a fallback.
180
-				OC::$WEBROOT = self::$config->getValue('overwritewebroot', '');
181
-			}
182
-
183
-			// Resolve /nextcloud to /nextcloud/ to ensure to always have a trailing
184
-			// slash which is required by URL generation.
185
-			if (isset($_SERVER['REQUEST_URI']) && $_SERVER['REQUEST_URI'] === \OC::$WEBROOT &&
186
-					substr($_SERVER['REQUEST_URI'], -1) !== '/') {
187
-				header('Location: '.\OC::$WEBROOT.'/');
188
-				exit();
189
-			}
190
-		}
191
-
192
-		// search the apps folder
193
-		$config_paths = self::$config->getValue('apps_paths', array());
194
-		if (!empty($config_paths)) {
195
-			foreach ($config_paths as $paths) {
196
-				if (isset($paths['url']) && isset($paths['path'])) {
197
-					$paths['url'] = rtrim($paths['url'], '/');
198
-					$paths['path'] = rtrim($paths['path'], '/');
199
-					OC::$APPSROOTS[] = $paths;
200
-				}
201
-			}
202
-		} elseif (file_exists(OC::$SERVERROOT . '/apps')) {
203
-			OC::$APPSROOTS[] = array('path' => OC::$SERVERROOT . '/apps', 'url' => '/apps', 'writable' => true);
204
-		} elseif (file_exists(OC::$SERVERROOT . '/../apps')) {
205
-			OC::$APPSROOTS[] = array(
206
-				'path' => rtrim(dirname(OC::$SERVERROOT), '/') . '/apps',
207
-				'url' => '/apps',
208
-				'writable' => true
209
-			);
210
-		}
211
-
212
-		if (empty(OC::$APPSROOTS)) {
213
-			throw new \RuntimeException('apps directory not found! Please put the Nextcloud apps folder in the Nextcloud folder'
214
-				. ' or the folder above. You can also configure the location in the config.php file.');
215
-		}
216
-		$paths = array();
217
-		foreach (OC::$APPSROOTS as $path) {
218
-			$paths[] = $path['path'];
219
-			if (!is_dir($path['path'])) {
220
-				throw new \RuntimeException(sprintf('App directory "%s" not found! Please put the Nextcloud apps folder in the'
221
-					. ' Nextcloud folder or the folder above. You can also configure the location in the'
222
-					. ' config.php file.', $path['path']));
223
-			}
224
-		}
225
-
226
-		// set the right include path
227
-		set_include_path(
228
-			implode(PATH_SEPARATOR, $paths)
229
-		);
230
-	}
231
-
232
-	public static function checkConfig() {
233
-		$l = \OC::$server->getL10N('lib');
234
-
235
-		// Create config if it does not already exist
236
-		$configFilePath = self::$configDir .'/config.php';
237
-		if(!file_exists($configFilePath)) {
238
-			@touch($configFilePath);
239
-		}
240
-
241
-		// Check if config is writable
242
-		$configFileWritable = is_writable($configFilePath);
243
-		if (!$configFileWritable && !OC_Helper::isReadOnlyConfigEnabled()
244
-			|| !$configFileWritable && \OCP\Util::needUpgrade()) {
245
-
246
-			$urlGenerator = \OC::$server->getURLGenerator();
247
-
248
-			if (self::$CLI) {
249
-				echo $l->t('Cannot write into "config" directory!')."\n";
250
-				echo $l->t('This can usually be fixed by giving the webserver write access to the config directory')."\n";
251
-				echo "\n";
252
-				echo $l->t('See %s', [ $urlGenerator->linkToDocs('admin-dir_permissions') ])."\n";
253
-				exit;
254
-			} else {
255
-				OC_Template::printErrorPage(
256
-					$l->t('Cannot write into "config" directory!'),
257
-					$l->t('This can usually be fixed by giving the webserver write access to the config directory. See %s',
258
-					 [ $urlGenerator->linkToDocs('admin-dir_permissions') ])
259
-				);
260
-			}
261
-		}
262
-	}
263
-
264
-	public static function checkInstalled() {
265
-		if (defined('OC_CONSOLE')) {
266
-			return;
267
-		}
268
-		// Redirect to installer if not installed
269
-		if (!\OC::$server->getSystemConfig()->getValue('installed', false) && OC::$SUBURI !== '/index.php' && OC::$SUBURI !== '/status.php') {
270
-			if (OC::$CLI) {
271
-				throw new Exception('Not installed');
272
-			} else {
273
-				$url = OC::$WEBROOT . '/index.php';
274
-				header('Location: ' . $url);
275
-			}
276
-			exit();
277
-		}
278
-	}
279
-
280
-	public static function checkMaintenanceMode() {
281
-		// Allow ajax update script to execute without being stopped
282
-		if (\OC::$server->getSystemConfig()->getValue('maintenance', false) && OC::$SUBURI != '/core/ajax/update.php') {
283
-			// send http status 503
284
-			header('HTTP/1.1 503 Service Temporarily Unavailable');
285
-			header('Status: 503 Service Temporarily Unavailable');
286
-			header('Retry-After: 120');
287
-
288
-			// render error page
289
-			$template = new OC_Template('', 'update.user', 'guest');
290
-			OC_Util::addScript('maintenance-check');
291
-			OC_Util::addStyle('core', 'guest');
292
-			$template->printPage();
293
-			die();
294
-		}
295
-	}
296
-
297
-	/**
298
-	 * Prints the upgrade page
299
-	 *
300
-	 * @param \OC\SystemConfig $systemConfig
301
-	 */
302
-	private static function printUpgradePage(\OC\SystemConfig $systemConfig) {
303
-		$disableWebUpdater = $systemConfig->getValue('upgrade.disable-web', false);
304
-		$tooBig = false;
305
-		if (!$disableWebUpdater) {
306
-			$apps = \OC::$server->getAppManager();
307
-			if ($apps->isInstalled('user_ldap')) {
308
-				$qb = \OC::$server->getDatabaseConnection()->getQueryBuilder();
309
-
310
-				$result = $qb->selectAlias($qb->createFunction('COUNT(*)'), 'user_count')
311
-					->from('ldap_user_mapping')
312
-					->execute();
313
-				$row = $result->fetch();
314
-				$result->closeCursor();
315
-
316
-				$tooBig = ($row['user_count'] > 50);
317
-			}
318
-			if (!$tooBig && $apps->isInstalled('user_saml')) {
319
-				$qb = \OC::$server->getDatabaseConnection()->getQueryBuilder();
320
-
321
-				$result = $qb->selectAlias($qb->createFunction('COUNT(*)'), 'user_count')
322
-					->from('user_saml_users')
323
-					->execute();
324
-				$row = $result->fetch();
325
-				$result->closeCursor();
326
-
327
-				$tooBig = ($row['user_count'] > 50);
328
-			}
329
-			if (!$tooBig) {
330
-				// count users
331
-				$stats = \OC::$server->getUserManager()->countUsers();
332
-				$totalUsers = array_sum($stats);
333
-				$tooBig = ($totalUsers > 50);
334
-			}
335
-		}
336
-		$ignoreTooBigWarning = isset($_GET['IKnowThatThisIsABigInstanceAndTheUpdateRequestCouldRunIntoATimeoutAndHowToRestoreABackup']) &&
337
-			$_GET['IKnowThatThisIsABigInstanceAndTheUpdateRequestCouldRunIntoATimeoutAndHowToRestoreABackup'] === 'IAmSuperSureToDoThis';
338
-
339
-		if ($disableWebUpdater || ($tooBig && !$ignoreTooBigWarning)) {
340
-			// send http status 503
341
-			header('HTTP/1.1 503 Service Temporarily Unavailable');
342
-			header('Status: 503 Service Temporarily Unavailable');
343
-			header('Retry-After: 120');
344
-
345
-			// render error page
346
-			$template = new OC_Template('', 'update.use-cli', 'guest');
347
-			$template->assign('productName', 'nextcloud'); // for now
348
-			$template->assign('version', OC_Util::getVersionString());
349
-			$template->assign('tooBig', $tooBig);
350
-
351
-			$template->printPage();
352
-			die();
353
-		}
354
-
355
-		// check whether this is a core update or apps update
356
-		$installedVersion = $systemConfig->getValue('version', '0.0.0');
357
-		$currentVersion = implode('.', \OCP\Util::getVersion());
358
-
359
-		// if not a core upgrade, then it's apps upgrade
360
-		$isAppsOnlyUpgrade = version_compare($currentVersion, $installedVersion, '=');
361
-
362
-		$oldTheme = $systemConfig->getValue('theme');
363
-		$systemConfig->setValue('theme', '');
364
-		OC_Util::addScript('config'); // needed for web root
365
-		OC_Util::addScript('update');
366
-
367
-		/** @var \OC\App\AppManager $appManager */
368
-		$appManager = \OC::$server->getAppManager();
369
-
370
-		$tmpl = new OC_Template('', 'update.admin', 'guest');
371
-		$tmpl->assign('version', OC_Util::getVersionString());
372
-		$tmpl->assign('isAppsOnlyUpgrade', $isAppsOnlyUpgrade);
373
-
374
-		// get third party apps
375
-		$ocVersion = \OCP\Util::getVersion();
376
-		$ocVersion = implode('.', $ocVersion);
377
-		$incompatibleApps = $appManager->getIncompatibleApps($ocVersion);
378
-		$incompatibleShippedApps = [];
379
-		foreach ($incompatibleApps as $appInfo) {
380
-			if ($appManager->isShipped($appInfo['id'])) {
381
-				$incompatibleShippedApps[] = $appInfo['name'] . ' (' . $appInfo['id'] . ')';
382
-			}
383
-		}
384
-
385
-		if (!empty($incompatibleShippedApps)) {
386
-			$l = \OC::$server->getL10N('core');
387
-			$hint = $l->t('The files of the app %$1s were not replaced correctly. Make sure it is a version compatible with the server.', [implode(', ', $incompatibleShippedApps)]);
388
-			throw new \OC\HintException('The files of the app ' . implode(', ', $incompatibleShippedApps) . ' were not replaced correctly. Make sure it is a version compatible with the server.', $hint);
389
-		}
390
-
391
-		$tmpl->assign('appsToUpgrade', $appManager->getAppsNeedingUpgrade($ocVersion));
392
-		$tmpl->assign('incompatibleAppsList', $incompatibleApps);
393
-		$tmpl->assign('productName', 'Nextcloud'); // for now
394
-		$tmpl->assign('oldTheme', $oldTheme);
395
-		$tmpl->printPage();
396
-	}
397
-
398
-	public static function initSession() {
399
-		if(self::$server->getRequest()->getServerProtocol() === 'https') {
400
-			ini_set('session.cookie_secure', true);
401
-		}
402
-
403
-		// prevents javascript from accessing php session cookies
404
-		ini_set('session.cookie_httponly', 'true');
405
-
406
-		// set the cookie path to the Nextcloud directory
407
-		$cookie_path = OC::$WEBROOT ? : '/';
408
-		ini_set('session.cookie_path', $cookie_path);
409
-
410
-		// Let the session name be changed in the initSession Hook
411
-		$sessionName = OC_Util::getInstanceId();
412
-
413
-		try {
414
-			// Allow session apps to create a custom session object
415
-			$useCustomSession = false;
416
-			$session = self::$server->getSession();
417
-			OC_Hook::emit('OC', 'initSession', array('session' => &$session, 'sessionName' => &$sessionName, 'useCustomSession' => &$useCustomSession));
418
-			if (!$useCustomSession) {
419
-				// set the session name to the instance id - which is unique
420
-				$session = new \OC\Session\Internal($sessionName);
421
-			}
422
-
423
-			$cryptoWrapper = \OC::$server->getSessionCryptoWrapper();
424
-			$session = $cryptoWrapper->wrapSession($session);
425
-			self::$server->setSession($session);
426
-
427
-			// if session can't be started break with http 500 error
428
-		} catch (Exception $e) {
429
-			\OCP\Util::logException('base', $e);
430
-			//show the user a detailed error page
431
-			OC_Response::setStatus(OC_Response::STATUS_INTERNAL_SERVER_ERROR);
432
-			OC_Template::printExceptionErrorPage($e);
433
-			die();
434
-		}
435
-
436
-		$sessionLifeTime = self::getSessionLifeTime();
437
-
438
-		// session timeout
439
-		if ($session->exists('LAST_ACTIVITY') && (time() - $session->get('LAST_ACTIVITY') > $sessionLifeTime)) {
440
-			if (isset($_COOKIE[session_name()])) {
441
-				setcookie(session_name(), null, -1, self::$WEBROOT ? : '/');
442
-			}
443
-			\OC::$server->getUserSession()->logout();
444
-		}
445
-
446
-		$session->set('LAST_ACTIVITY', time());
447
-	}
448
-
449
-	/**
450
-	 * @return string
451
-	 */
452
-	private static function getSessionLifeTime() {
453
-		return \OC::$server->getConfig()->getSystemValue('session_lifetime', 60 * 60 * 24);
454
-	}
455
-
456
-	public static function loadAppClassPaths() {
457
-		foreach (OC_App::getEnabledApps() as $app) {
458
-			$appPath = OC_App::getAppPath($app);
459
-			if ($appPath === false) {
460
-				continue;
461
-			}
462
-
463
-			$file = $appPath . '/appinfo/classpath.php';
464
-			if (file_exists($file)) {
465
-				require_once $file;
466
-			}
467
-		}
468
-	}
469
-
470
-	/**
471
-	 * Try to set some values to the required Nextcloud default
472
-	 */
473
-	public static function setRequiredIniValues() {
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() {
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() {
519
-		$request = \OC::$server->getRequest();
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 = [
526
-			// OS X Finder
527
-			'/^WebDAVFS/',
528
-		];
529
-		if($request->isUserAgent($incompatibleUserAgents)) {
530
-			return;
531
-		}
532
-
533
-		if(count($_COOKIE) > 0) {
534
-			$requestUri = $request->getScriptName();
535
-			$processingScript = explode('/', $requestUri);
536
-			$processingScript = $processingScript[count($processingScript)-1];
537
-
538
-			// index.php routes are handled in the middleware
539
-			if($processingScript === 'index.php') {
540
-				return;
541
-			}
542
-
543
-			// All other endpoints require the lax and the strict cookie
544
-			if(!$request->passesStrictCookieCheck()) {
545
-				self::sendSameSiteCookies();
546
-				// Debug mode gets access to the resources without strict cookie
547
-				// due to the fact that the SabreDAV browser also lives there.
548
-				if(!\OC::$server->getConfig()->getSystemValue('debug', false)) {
549
-					http_response_code(\OCP\AppFramework\Http::STATUS_SERVICE_UNAVAILABLE);
550
-					exit();
551
-				}
552
-			}
553
-		} elseif(!isset($_COOKIE['nc_sameSiteCookielax']) || !isset($_COOKIE['nc_sameSiteCookiestrict'])) {
554
-			self::sendSameSiteCookies();
555
-		}
556
-	}
557
-
558
-	public static function init() {
559
-		// calculate the root directories
560
-		OC::$SERVERROOT = str_replace("\\", '/', substr(__DIR__, 0, -4));
561
-
562
-		// register autoloader
563
-		$loaderStart = microtime(true);
564
-		require_once __DIR__ . '/autoloader.php';
565
-		self::$loader = new \OC\Autoloader([
566
-			OC::$SERVERROOT . '/lib/private/legacy',
567
-		]);
568
-		if (defined('PHPUNIT_RUN')) {
569
-			self::$loader->addValidRoot(OC::$SERVERROOT . '/tests');
570
-		}
571
-		spl_autoload_register(array(self::$loader, 'load'));
572
-		$loaderEnd = microtime(true);
573
-
574
-		self::$CLI = (php_sapi_name() == 'cli');
575
-
576
-		// Add default composer PSR-4 autoloader
577
-		self::$composerAutoloader = require_once OC::$SERVERROOT . '/lib/composer/autoload.php';
578
-
579
-		try {
580
-			self::initPaths();
581
-			// setup 3rdparty autoloader
582
-			$vendorAutoLoad = OC::$SERVERROOT. '/3rdparty/autoload.php';
583
-			if (!file_exists($vendorAutoLoad)) {
584
-				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".');
585
-			}
586
-			require_once $vendorAutoLoad;
587
-
588
-		} catch (\RuntimeException $e) {
589
-			if (!self::$CLI) {
590
-				$claimedProtocol = strtoupper($_SERVER['SERVER_PROTOCOL']);
591
-				$protocol = in_array($claimedProtocol, ['HTTP/1.0', 'HTTP/1.1', 'HTTP/2']) ? $claimedProtocol : 'HTTP/1.1';
592
-				header($protocol . ' ' . OC_Response::STATUS_SERVICE_UNAVAILABLE);
593
-			}
594
-			// we can't use the template error page here, because this needs the
595
-			// DI container which isn't available yet
596
-			print($e->getMessage());
597
-			exit();
598
-		}
599
-
600
-		// setup the basic server
601
-		self::$server = new \OC\Server(\OC::$WEBROOT, self::$config);
602
-		\OC::$server->getEventLogger()->log('autoloader', 'Autoloader', $loaderStart, $loaderEnd);
603
-		\OC::$server->getEventLogger()->start('boot', 'Initialize');
604
-
605
-		// Don't display errors and log them
606
-		error_reporting(E_ALL | E_STRICT);
607
-		@ini_set('display_errors', '0');
608
-		@ini_set('log_errors', '1');
609
-
610
-		if(!date_default_timezone_set('UTC')) {
611
-			throw new \RuntimeException('Could not set timezone to UTC');
612
-		}
613
-
614
-		//try to configure php to enable big file uploads.
615
-		//this doesn´t work always depending on the webserver and php configuration.
616
-		//Let´s try to overwrite some defaults anyway
617
-
618
-		//try to set the maximum execution time to 60min
619
-		if (strpos(@ini_get('disable_functions'), 'set_time_limit') === false) {
620
-			@set_time_limit(3600);
621
-		}
622
-		@ini_set('max_execution_time', '3600');
623
-		@ini_set('max_input_time', '3600');
624
-
625
-		//try to set the maximum filesize to 10G
626
-		@ini_set('upload_max_filesize', '10G');
627
-		@ini_set('post_max_size', '10G');
628
-		@ini_set('file_uploads', '50');
629
-
630
-		self::setRequiredIniValues();
631
-		self::handleAuthHeaders();
632
-		self::registerAutoloaderCache();
633
-
634
-		// initialize intl fallback is necessary
635
-		\Patchwork\Utf8\Bootup::initIntl();
636
-		OC_Util::isSetLocaleWorking();
637
-
638
-		if (!defined('PHPUNIT_RUN')) {
639
-			OC\Log\ErrorHandler::setLogger(\OC::$server->getLogger());
640
-			$debug = \OC::$server->getConfig()->getSystemValue('debug', false);
641
-			OC\Log\ErrorHandler::register($debug);
642
-		}
643
-
644
-		\OC::$server->getEventLogger()->start('init_session', 'Initialize session');
645
-		OC_App::loadApps(array('session'));
646
-		if (!self::$CLI) {
647
-			self::initSession();
648
-		}
649
-		\OC::$server->getEventLogger()->end('init_session');
650
-		self::checkConfig();
651
-		self::checkInstalled();
652
-
653
-		OC_Response::addSecurityHeaders();
654
-
655
-		self::performSameSiteCookieProtection();
656
-
657
-		if (!defined('OC_CONSOLE')) {
658
-			$errors = OC_Util::checkServer(\OC::$server->getSystemConfig());
659
-			if (count($errors) > 0) {
660
-				if (self::$CLI) {
661
-					// Convert l10n string into regular string for usage in database
662
-					$staticErrors = [];
663
-					foreach ($errors as $error) {
664
-						echo $error['error'] . "\n";
665
-						echo $error['hint'] . "\n\n";
666
-						$staticErrors[] = [
667
-							'error' => (string)$error['error'],
668
-							'hint' => (string)$error['hint'],
669
-						];
670
-					}
671
-
672
-					try {
673
-						\OC::$server->getConfig()->setAppValue('core', 'cronErrors', json_encode($staticErrors));
674
-					} catch (\Exception $e) {
675
-						echo('Writing to database failed');
676
-					}
677
-					exit(1);
678
-				} else {
679
-					OC_Response::setStatus(OC_Response::STATUS_SERVICE_UNAVAILABLE);
680
-					OC_Util::addStyle('guest');
681
-					OC_Template::printGuestPage('', 'error', array('errors' => $errors));
682
-					exit;
683
-				}
684
-			} elseif (self::$CLI && \OC::$server->getConfig()->getSystemValue('installed', false)) {
685
-				\OC::$server->getConfig()->deleteAppValue('core', 'cronErrors');
686
-			}
687
-		}
688
-		//try to set the session lifetime
689
-		$sessionLifeTime = self::getSessionLifeTime();
690
-		@ini_set('gc_maxlifetime', (string)$sessionLifeTime);
691
-
692
-		$systemConfig = \OC::$server->getSystemConfig();
693
-
694
-		// User and Groups
695
-		if (!$systemConfig->getValue("installed", false)) {
696
-			self::$server->getSession()->set('user_id', '');
697
-		}
698
-
699
-		OC_User::useBackend(new \OC\User\Database());
700
-		\OC::$server->getGroupManager()->addBackend(new \OC\Group\Database());
701
-
702
-		// Subscribe to the hook
703
-		\OCP\Util::connectHook(
704
-			'\OCA\Files_Sharing\API\Server2Server',
705
-			'preLoginNameUsedAsUserName',
706
-			'\OC\User\Database',
707
-			'preLoginNameUsedAsUserName'
708
-		);
709
-
710
-		//setup extra user backends
711
-		if (!\OCP\Util::needUpgrade()) {
712
-			OC_User::setupBackends();
713
-		} else {
714
-			// Run upgrades in incognito mode
715
-			OC_User::setIncognitoMode(true);
716
-		}
717
-
718
-		self::registerCleanupHooks();
719
-		self::registerFilesystemHooks();
720
-		self::registerShareHooks();
721
-		self::registerEncryptionWrapper();
722
-		self::registerEncryptionHooks();
723
-		self::registerAccountHooks();
724
-
725
-		// Make sure that the application class is not loaded before the database is setup
726
-		if ($systemConfig->getValue("installed", false)) {
727
-			$settings = new \OC\Settings\Application();
728
-			$settings->register();
729
-		}
730
-
731
-		//make sure temporary files are cleaned up
732
-		$tmpManager = \OC::$server->getTempManager();
733
-		register_shutdown_function(array($tmpManager, 'clean'));
734
-		$lockProvider = \OC::$server->getLockingProvider();
735
-		register_shutdown_function(array($lockProvider, 'releaseAll'));
736
-
737
-		// Check whether the sample configuration has been copied
738
-		if($systemConfig->getValue('copied_sample_config', false)) {
739
-			$l = \OC::$server->getL10N('lib');
740
-			header('HTTP/1.1 503 Service Temporarily Unavailable');
741
-			header('Status: 503 Service Temporarily Unavailable');
742
-			OC_Template::printErrorPage(
743
-				$l->t('Sample configuration detected'),
744
-				$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')
745
-			);
746
-			return;
747
-		}
748
-
749
-		$request = \OC::$server->getRequest();
750
-		$host = $request->getInsecureServerHost();
751
-		/**
752
-		 * if the host passed in headers isn't trusted
753
-		 * FIXME: Should not be in here at all :see_no_evil:
754
-		 */
755
-		if (!OC::$CLI
756
-			// overwritehost is always trusted, workaround to not have to make
757
-			// \OC\AppFramework\Http\Request::getOverwriteHost public
758
-			&& self::$server->getConfig()->getSystemValue('overwritehost') === ''
759
-			&& !\OC::$server->getTrustedDomainHelper()->isTrustedDomain($host)
760
-			&& self::$server->getConfig()->getSystemValue('installed', false)
761
-		) {
762
-			// Allow access to CSS resources
763
-			$isScssRequest = false;
764
-			if(strpos($request->getPathInfo(), '/css/') === 0) {
765
-				$isScssRequest = true;
766
-			}
767
-
768
-			if(substr($request->getRequestUri(), -11) === '/status.php') {
769
-				OC_Response::setStatus(\OC_Response::STATUS_BAD_REQUEST);
770
-				header('Status: 400 Bad Request');
771
-				header('Content-Type: application/json');
772
-				echo '{"error": "Trusted domain error.", "code": 15}';
773
-				exit();
774
-			}
775
-
776
-			if (!$isScssRequest) {
777
-				OC_Response::setStatus(\OC_Response::STATUS_BAD_REQUEST);
778
-				header('Status: 400 Bad Request');
779
-
780
-				\OC::$server->getLogger()->warning(
781
-					'Trusted domain error. "{remoteAddress}" tried to access using "{host}" as host.',
782
-					[
783
-						'app' => 'core',
784
-						'remoteAddress' => $request->getRemoteAddress(),
785
-						'host' => $host,
786
-					]
787
-				);
788
-
789
-				$tmpl = new OCP\Template('core', 'untrustedDomain', 'guest');
790
-				$tmpl->assign('domain', $host);
791
-				$tmpl->printPage();
792
-
793
-				exit();
794
-			}
795
-		}
796
-		\OC::$server->getEventLogger()->end('boot');
797
-	}
798
-
799
-	/**
800
-	 * register hooks for the cleanup of cache and bruteforce protection
801
-	 */
802
-	public static function registerCleanupHooks() {
803
-		//don't try to do this before we are properly setup
804
-		if (\OC::$server->getSystemConfig()->getValue('installed', false) && !\OCP\Util::needUpgrade()) {
805
-
806
-			// NOTE: This will be replaced to use OCP
807
-			$userSession = self::$server->getUserSession();
808
-			$userSession->listen('\OC\User', 'postLogin', function () use ($userSession) {
809
-				if (!defined('PHPUNIT_RUN')) {
810
-					// reset brute force delay for this IP address and username
811
-					$uid = \OC::$server->getUserSession()->getUser()->getUID();
812
-					$request = \OC::$server->getRequest();
813
-					$throttler = \OC::$server->getBruteForceThrottler();
814
-					$throttler->resetDelay($request->getRemoteAddress(), 'login', ['user' => $uid]);
815
-				}
816
-
817
-				try {
818
-					$cache = new \OC\Cache\File();
819
-					$cache->gc();
820
-				} catch (\OC\ServerNotAvailableException $e) {
821
-					// not a GC exception, pass it on
822
-					throw $e;
823
-				} catch (\OC\ForbiddenException $e) {
824
-					// filesystem blocked for this request, ignore
825
-				} catch (\Exception $e) {
826
-					// a GC exception should not prevent users from using OC,
827
-					// so log the exception
828
-					\OC::$server->getLogger()->logException($e, [
829
-						'message' => 'Exception when running cache gc.',
830
-						'level' => \OCP\Util::WARN,
831
-						'app' => 'core',
832
-					]);
833
-				}
834
-			});
835
-		}
836
-	}
837
-
838
-	private static function registerEncryptionWrapper() {
839
-		$manager = self::$server->getEncryptionManager();
840
-		\OCP\Util::connectHook('OC_Filesystem', 'preSetup', $manager, 'setupStorage');
841
-	}
842
-
843
-	private static function registerEncryptionHooks() {
844
-		$enabled = self::$server->getEncryptionManager()->isEnabled();
845
-		if ($enabled) {
846
-			\OCP\Util::connectHook(Share::class, 'post_shared', HookManager::class, 'postShared');
847
-			\OCP\Util::connectHook(Share::class, 'post_unshare', HookManager::class, 'postUnshared');
848
-			\OCP\Util::connectHook('OC_Filesystem', 'post_rename', HookManager::class, 'postRename');
849
-			\OCP\Util::connectHook('\OCA\Files_Trashbin\Trashbin', 'post_restore', HookManager::class, 'postRestore');
850
-		}
851
-	}
852
-
853
-	private static function registerAccountHooks() {
854
-		$hookHandler = new \OC\Accounts\Hooks(\OC::$server->getLogger());
855
-		\OCP\Util::connectHook('OC_User', 'changeUser', $hookHandler, 'changeUserHook');
856
-	}
857
-
858
-	/**
859
-	 * register hooks for the filesystem
860
-	 */
861
-	public static function registerFilesystemHooks() {
862
-		// Check for blacklisted files
863
-		OC_Hook::connect('OC_Filesystem', 'write', Filesystem::class, 'isBlacklisted');
864
-		OC_Hook::connect('OC_Filesystem', 'rename', Filesystem::class, 'isBlacklisted');
865
-	}
866
-
867
-	/**
868
-	 * register hooks for sharing
869
-	 */
870
-	public static function registerShareHooks() {
871
-		if (\OC::$server->getSystemConfig()->getValue('installed')) {
872
-			OC_Hook::connect('OC_User', 'post_deleteUser', Hooks::class, 'post_deleteUser');
873
-			OC_Hook::connect('OC_User', 'post_removeFromGroup', Hooks::class, 'post_removeFromGroup');
874
-			OC_Hook::connect('OC_User', 'post_deleteGroup', Hooks::class, 'post_deleteGroup');
875
-		}
876
-	}
877
-
878
-	protected static function registerAutoloaderCache() {
879
-		// The class loader takes an optional low-latency cache, which MUST be
880
-		// namespaced. The instanceid is used for namespacing, but might be
881
-		// unavailable at this point. Furthermore, it might not be possible to
882
-		// generate an instanceid via \OC_Util::getInstanceId() because the
883
-		// config file may not be writable. As such, we only register a class
884
-		// loader cache if instanceid is available without trying to create one.
885
-		$instanceId = \OC::$server->getSystemConfig()->getValue('instanceid', null);
886
-		if ($instanceId) {
887
-			try {
888
-				$memcacheFactory = \OC::$server->getMemCacheFactory();
889
-				self::$loader->setMemoryCache($memcacheFactory->createLocal('Autoloader'));
890
-			} catch (\Exception $ex) {
891
-			}
892
-		}
893
-	}
894
-
895
-	/**
896
-	 * Handle the request
897
-	 */
898
-	public static function handleRequest() {
899
-
900
-		\OC::$server->getEventLogger()->start('handle_request', 'Handle request');
901
-		$systemConfig = \OC::$server->getSystemConfig();
902
-		// load all the classpaths from the enabled apps so they are available
903
-		// in the routing files of each app
904
-		OC::loadAppClassPaths();
905
-
906
-		// Check if Nextcloud is installed or in maintenance (update) mode
907
-		if (!$systemConfig->getValue('installed', false)) {
908
-			\OC::$server->getSession()->clear();
909
-			$setupHelper = new OC\Setup(
910
-				$systemConfig,
911
-				\OC::$server->getIniWrapper(),
912
-				\OC::$server->getL10N('lib'),
913
-				\OC::$server->query(\OCP\Defaults::class),
914
-				\OC::$server->getLogger(),
915
-				\OC::$server->getSecureRandom(),
916
-				\OC::$server->query(\OC\Installer::class)
917
-			);
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 ($requestPath === '/heartbeat') {
926
-			return;
927
-		}
928
-		if (substr($requestPath, -3) !== '.js') { // we need these files during the upgrade
929
-			self::checkMaintenanceMode();
930
-
931
-			if (\OCP\Util::needUpgrade()) {
932
-				if (function_exists('opcache_reset')) {
933
-					opcache_reset();
934
-				}
935
-				if (!$systemConfig->getValue('maintenance', false)) {
936
-					self::printUpgradePage($systemConfig);
937
-					exit();
938
-				}
939
-			}
940
-		}
941
-
942
-		// emergency app disabling
943
-		if ($requestPath === '/disableapp'
944
-			&& $request->getMethod() === 'POST'
945
-			&& ((array)$request->getParam('appid')) !== ''
946
-		) {
947
-			\OCP\JSON::callCheck();
948
-			\OCP\JSON::checkAdminUser();
949
-			$appIds = (array)$request->getParam('appid');
950
-			foreach($appIds as $appId) {
951
-				$appId = \OC_App::cleanAppId($appId);
952
-				\OC::$server->getAppManager()->disableApp($appId);
953
-			}
954
-			\OC_JSON::success();
955
-			exit();
956
-		}
957
-
958
-		// Always load authentication apps
959
-		OC_App::loadApps(['authentication']);
960
-
961
-		// Load minimum set of apps
962
-		if (!\OCP\Util::needUpgrade()
963
-			&& !$systemConfig->getValue('maintenance', false)) {
964
-			// For logged-in users: Load everything
965
-			if(\OC::$server->getUserSession()->isLoggedIn()) {
966
-				OC_App::loadApps();
967
-			} else {
968
-				// For guests: Load only filesystem and logging
969
-				OC_App::loadApps(array('filesystem', 'logging'));
970
-				self::handleLogin($request);
971
-			}
972
-		}
973
-
974
-		if (!self::$CLI) {
975
-			try {
976
-				if (!$systemConfig->getValue('maintenance', false) && !\OCP\Util::needUpgrade()) {
977
-					OC_App::loadApps(array('filesystem', 'logging'));
978
-					OC_App::loadApps();
979
-				}
980
-				OC_Util::setupFS();
981
-				OC::$server->getRouter()->match(\OC::$server->getRequest()->getRawPathInfo());
982
-				return;
983
-			} catch (Symfony\Component\Routing\Exception\ResourceNotFoundException $e) {
984
-				//header('HTTP/1.0 404 Not Found');
985
-			} catch (Symfony\Component\Routing\Exception\MethodNotAllowedException $e) {
986
-				OC_Response::setStatus(405);
987
-				return;
988
-			}
989
-		}
990
-
991
-		// Handle WebDAV
992
-		if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'PROPFIND') {
993
-			// not allowed any more to prevent people
994
-			// mounting this root directly.
995
-			// Users need to mount remote.php/webdav instead.
996
-			header('HTTP/1.1 405 Method Not Allowed');
997
-			header('Status: 405 Method Not Allowed');
998
-			return;
999
-		}
1000
-
1001
-		// Someone is logged in
1002
-		if (\OC::$server->getUserSession()->isLoggedIn()) {
1003
-			OC_App::loadApps();
1004
-			OC_User::setupBackends();
1005
-			OC_Util::setupFS();
1006
-			// FIXME
1007
-			// Redirect to default application
1008
-			OC_Util::redirectToDefaultPage();
1009
-		} else {
1010
-			// Not handled and not logged in
1011
-			header('Location: '.\OC::$server->getURLGenerator()->linkToRouteAbsolute('core.login.showLoginForm'));
1012
-		}
1013
-	}
1014
-
1015
-	/**
1016
-	 * Check login: apache auth, auth token, basic auth
1017
-	 *
1018
-	 * @param OCP\IRequest $request
1019
-	 * @return boolean
1020
-	 */
1021
-	static function handleLogin(OCP\IRequest $request) {
1022
-		$userSession = self::$server->getUserSession();
1023
-		if (OC_User::handleApacheAuth()) {
1024
-			return true;
1025
-		}
1026
-		if ($userSession->tryTokenLogin($request)) {
1027
-			return true;
1028
-		}
1029
-		if (isset($_COOKIE['nc_username'])
1030
-			&& isset($_COOKIE['nc_token'])
1031
-			&& isset($_COOKIE['nc_session_id'])
1032
-			&& $userSession->loginWithCookie($_COOKIE['nc_username'], $_COOKIE['nc_token'], $_COOKIE['nc_session_id'])) {
1033
-			return true;
1034
-		}
1035
-		if ($userSession->tryBasicAuthLogin($request, \OC::$server->getBruteForceThrottler())) {
1036
-			return true;
1037
-		}
1038
-		return false;
1039
-	}
1040
-
1041
-	protected static function handleAuthHeaders() {
1042
-		//copy http auth headers for apache+php-fcgid work around
1043
-		if (isset($_SERVER['HTTP_XAUTHORIZATION']) && !isset($_SERVER['HTTP_AUTHORIZATION'])) {
1044
-			$_SERVER['HTTP_AUTHORIZATION'] = $_SERVER['HTTP_XAUTHORIZATION'];
1045
-		}
1046
-
1047
-		// Extract PHP_AUTH_USER/PHP_AUTH_PW from other headers if necessary.
1048
-		$vars = array(
1049
-			'HTTP_AUTHORIZATION', // apache+php-cgi work around
1050
-			'REDIRECT_HTTP_AUTHORIZATION', // apache+php-cgi alternative
1051
-		);
1052
-		foreach ($vars as $var) {
1053
-			if (isset($_SERVER[$var]) && preg_match('/Basic\s+(.*)$/i', $_SERVER[$var], $matches)) {
1054
-				list($name, $password) = explode(':', base64_decode($matches[1]), 2);
1055
-				$_SERVER['PHP_AUTH_USER'] = $name;
1056
-				$_SERVER['PHP_AUTH_PW'] = $password;
1057
-				break;
1058
-			}
1059
-		}
1060
-	}
70
+    /**
71
+     * Associative array for autoloading. classname => filename
72
+     */
73
+    public static $CLASSPATH = array();
74
+    /**
75
+     * The installation path for Nextcloud  on the server (e.g. /srv/http/nextcloud)
76
+     */
77
+    public static $SERVERROOT = '';
78
+    /**
79
+     * the current request path relative to the Nextcloud root (e.g. files/index.php)
80
+     */
81
+    private static $SUBURI = '';
82
+    /**
83
+     * the Nextcloud root path for http requests (e.g. nextcloud/)
84
+     */
85
+    public static $WEBROOT = '';
86
+    /**
87
+     * The installation path array of the apps folder on the server (e.g. /srv/http/nextcloud) 'path' and
88
+     * web path in 'url'
89
+     */
90
+    public static $APPSROOTS = array();
91
+
92
+    /**
93
+     * @var string
94
+     */
95
+    public static $configDir;
96
+
97
+    /**
98
+     * requested app
99
+     */
100
+    public static $REQUESTEDAPP = '';
101
+
102
+    /**
103
+     * check if Nextcloud runs in cli mode
104
+     */
105
+    public static $CLI = false;
106
+
107
+    /**
108
+     * @var \OC\Autoloader $loader
109
+     */
110
+    public static $loader = null;
111
+
112
+    /** @var \Composer\Autoload\ClassLoader $composerAutoloader */
113
+    public static $composerAutoloader = null;
114
+
115
+    /**
116
+     * @var \OC\Server
117
+     */
118
+    public static $server = null;
119
+
120
+    /**
121
+     * @var \OC\Config
122
+     */
123
+    private static $config = null;
124
+
125
+    /**
126
+     * @throws \RuntimeException when the 3rdparty directory is missing or
127
+     * the app path list is empty or contains an invalid path
128
+     */
129
+    public static function initPaths() {
130
+        if(defined('PHPUNIT_CONFIG_DIR')) {
131
+            self::$configDir = OC::$SERVERROOT . '/' . PHPUNIT_CONFIG_DIR . '/';
132
+        } elseif(defined('PHPUNIT_RUN') and PHPUNIT_RUN and is_dir(OC::$SERVERROOT . '/tests/config/')) {
133
+            self::$configDir = OC::$SERVERROOT . '/tests/config/';
134
+        } elseif($dir = getenv('NEXTCLOUD_CONFIG_DIR')) {
135
+            self::$configDir = rtrim($dir, '/') . '/';
136
+        } else {
137
+            self::$configDir = OC::$SERVERROOT . '/config/';
138
+        }
139
+        self::$config = new \OC\Config(self::$configDir);
140
+
141
+        OC::$SUBURI = str_replace("\\", "/", substr(realpath($_SERVER["SCRIPT_FILENAME"]), strlen(OC::$SERVERROOT)));
142
+        /**
143
+         * FIXME: The following lines are required because we can't yet instantiate
144
+         *        \OC::$server->getRequest() since \OC::$server does not yet exist.
145
+         */
146
+        $params = [
147
+            'server' => [
148
+                'SCRIPT_NAME' => $_SERVER['SCRIPT_NAME'],
149
+                'SCRIPT_FILENAME' => $_SERVER['SCRIPT_FILENAME'],
150
+            ],
151
+        ];
152
+        $fakeRequest = new \OC\AppFramework\Http\Request($params, null, new \OC\AllConfig(new \OC\SystemConfig(self::$config)));
153
+        $scriptName = $fakeRequest->getScriptName();
154
+        if (substr($scriptName, -1) == '/') {
155
+            $scriptName .= 'index.php';
156
+            //make sure suburi follows the same rules as scriptName
157
+            if (substr(OC::$SUBURI, -9) != 'index.php') {
158
+                if (substr(OC::$SUBURI, -1) != '/') {
159
+                    OC::$SUBURI = OC::$SUBURI . '/';
160
+                }
161
+                OC::$SUBURI = OC::$SUBURI . 'index.php';
162
+            }
163
+        }
164
+
165
+
166
+        if (OC::$CLI) {
167
+            OC::$WEBROOT = self::$config->getValue('overwritewebroot', '');
168
+        } else {
169
+            if (substr($scriptName, 0 - strlen(OC::$SUBURI)) === OC::$SUBURI) {
170
+                OC::$WEBROOT = substr($scriptName, 0, 0 - strlen(OC::$SUBURI));
171
+
172
+                if (OC::$WEBROOT != '' && OC::$WEBROOT[0] !== '/') {
173
+                    OC::$WEBROOT = '/' . OC::$WEBROOT;
174
+                }
175
+            } else {
176
+                // The scriptName is not ending with OC::$SUBURI
177
+                // This most likely means that we are calling from CLI.
178
+                // However some cron jobs still need to generate
179
+                // a web URL, so we use overwritewebroot as a fallback.
180
+                OC::$WEBROOT = self::$config->getValue('overwritewebroot', '');
181
+            }
182
+
183
+            // Resolve /nextcloud to /nextcloud/ to ensure to always have a trailing
184
+            // slash which is required by URL generation.
185
+            if (isset($_SERVER['REQUEST_URI']) && $_SERVER['REQUEST_URI'] === \OC::$WEBROOT &&
186
+                    substr($_SERVER['REQUEST_URI'], -1) !== '/') {
187
+                header('Location: '.\OC::$WEBROOT.'/');
188
+                exit();
189
+            }
190
+        }
191
+
192
+        // search the apps folder
193
+        $config_paths = self::$config->getValue('apps_paths', array());
194
+        if (!empty($config_paths)) {
195
+            foreach ($config_paths as $paths) {
196
+                if (isset($paths['url']) && isset($paths['path'])) {
197
+                    $paths['url'] = rtrim($paths['url'], '/');
198
+                    $paths['path'] = rtrim($paths['path'], '/');
199
+                    OC::$APPSROOTS[] = $paths;
200
+                }
201
+            }
202
+        } elseif (file_exists(OC::$SERVERROOT . '/apps')) {
203
+            OC::$APPSROOTS[] = array('path' => OC::$SERVERROOT . '/apps', 'url' => '/apps', 'writable' => true);
204
+        } elseif (file_exists(OC::$SERVERROOT . '/../apps')) {
205
+            OC::$APPSROOTS[] = array(
206
+                'path' => rtrim(dirname(OC::$SERVERROOT), '/') . '/apps',
207
+                'url' => '/apps',
208
+                'writable' => true
209
+            );
210
+        }
211
+
212
+        if (empty(OC::$APPSROOTS)) {
213
+            throw new \RuntimeException('apps directory not found! Please put the Nextcloud apps folder in the Nextcloud folder'
214
+                . ' or the folder above. You can also configure the location in the config.php file.');
215
+        }
216
+        $paths = array();
217
+        foreach (OC::$APPSROOTS as $path) {
218
+            $paths[] = $path['path'];
219
+            if (!is_dir($path['path'])) {
220
+                throw new \RuntimeException(sprintf('App directory "%s" not found! Please put the Nextcloud apps folder in the'
221
+                    . ' Nextcloud folder or the folder above. You can also configure the location in the'
222
+                    . ' config.php file.', $path['path']));
223
+            }
224
+        }
225
+
226
+        // set the right include path
227
+        set_include_path(
228
+            implode(PATH_SEPARATOR, $paths)
229
+        );
230
+    }
231
+
232
+    public static function checkConfig() {
233
+        $l = \OC::$server->getL10N('lib');
234
+
235
+        // Create config if it does not already exist
236
+        $configFilePath = self::$configDir .'/config.php';
237
+        if(!file_exists($configFilePath)) {
238
+            @touch($configFilePath);
239
+        }
240
+
241
+        // Check if config is writable
242
+        $configFileWritable = is_writable($configFilePath);
243
+        if (!$configFileWritable && !OC_Helper::isReadOnlyConfigEnabled()
244
+            || !$configFileWritable && \OCP\Util::needUpgrade()) {
245
+
246
+            $urlGenerator = \OC::$server->getURLGenerator();
247
+
248
+            if (self::$CLI) {
249
+                echo $l->t('Cannot write into "config" directory!')."\n";
250
+                echo $l->t('This can usually be fixed by giving the webserver write access to the config directory')."\n";
251
+                echo "\n";
252
+                echo $l->t('See %s', [ $urlGenerator->linkToDocs('admin-dir_permissions') ])."\n";
253
+                exit;
254
+            } else {
255
+                OC_Template::printErrorPage(
256
+                    $l->t('Cannot write into "config" directory!'),
257
+                    $l->t('This can usually be fixed by giving the webserver write access to the config directory. See %s',
258
+                        [ $urlGenerator->linkToDocs('admin-dir_permissions') ])
259
+                );
260
+            }
261
+        }
262
+    }
263
+
264
+    public static function checkInstalled() {
265
+        if (defined('OC_CONSOLE')) {
266
+            return;
267
+        }
268
+        // Redirect to installer if not installed
269
+        if (!\OC::$server->getSystemConfig()->getValue('installed', false) && OC::$SUBURI !== '/index.php' && OC::$SUBURI !== '/status.php') {
270
+            if (OC::$CLI) {
271
+                throw new Exception('Not installed');
272
+            } else {
273
+                $url = OC::$WEBROOT . '/index.php';
274
+                header('Location: ' . $url);
275
+            }
276
+            exit();
277
+        }
278
+    }
279
+
280
+    public static function checkMaintenanceMode() {
281
+        // Allow ajax update script to execute without being stopped
282
+        if (\OC::$server->getSystemConfig()->getValue('maintenance', false) && OC::$SUBURI != '/core/ajax/update.php') {
283
+            // send http status 503
284
+            header('HTTP/1.1 503 Service Temporarily Unavailable');
285
+            header('Status: 503 Service Temporarily Unavailable');
286
+            header('Retry-After: 120');
287
+
288
+            // render error page
289
+            $template = new OC_Template('', 'update.user', 'guest');
290
+            OC_Util::addScript('maintenance-check');
291
+            OC_Util::addStyle('core', 'guest');
292
+            $template->printPage();
293
+            die();
294
+        }
295
+    }
296
+
297
+    /**
298
+     * Prints the upgrade page
299
+     *
300
+     * @param \OC\SystemConfig $systemConfig
301
+     */
302
+    private static function printUpgradePage(\OC\SystemConfig $systemConfig) {
303
+        $disableWebUpdater = $systemConfig->getValue('upgrade.disable-web', false);
304
+        $tooBig = false;
305
+        if (!$disableWebUpdater) {
306
+            $apps = \OC::$server->getAppManager();
307
+            if ($apps->isInstalled('user_ldap')) {
308
+                $qb = \OC::$server->getDatabaseConnection()->getQueryBuilder();
309
+
310
+                $result = $qb->selectAlias($qb->createFunction('COUNT(*)'), 'user_count')
311
+                    ->from('ldap_user_mapping')
312
+                    ->execute();
313
+                $row = $result->fetch();
314
+                $result->closeCursor();
315
+
316
+                $tooBig = ($row['user_count'] > 50);
317
+            }
318
+            if (!$tooBig && $apps->isInstalled('user_saml')) {
319
+                $qb = \OC::$server->getDatabaseConnection()->getQueryBuilder();
320
+
321
+                $result = $qb->selectAlias($qb->createFunction('COUNT(*)'), 'user_count')
322
+                    ->from('user_saml_users')
323
+                    ->execute();
324
+                $row = $result->fetch();
325
+                $result->closeCursor();
326
+
327
+                $tooBig = ($row['user_count'] > 50);
328
+            }
329
+            if (!$tooBig) {
330
+                // count users
331
+                $stats = \OC::$server->getUserManager()->countUsers();
332
+                $totalUsers = array_sum($stats);
333
+                $tooBig = ($totalUsers > 50);
334
+            }
335
+        }
336
+        $ignoreTooBigWarning = isset($_GET['IKnowThatThisIsABigInstanceAndTheUpdateRequestCouldRunIntoATimeoutAndHowToRestoreABackup']) &&
337
+            $_GET['IKnowThatThisIsABigInstanceAndTheUpdateRequestCouldRunIntoATimeoutAndHowToRestoreABackup'] === 'IAmSuperSureToDoThis';
338
+
339
+        if ($disableWebUpdater || ($tooBig && !$ignoreTooBigWarning)) {
340
+            // send http status 503
341
+            header('HTTP/1.1 503 Service Temporarily Unavailable');
342
+            header('Status: 503 Service Temporarily Unavailable');
343
+            header('Retry-After: 120');
344
+
345
+            // render error page
346
+            $template = new OC_Template('', 'update.use-cli', 'guest');
347
+            $template->assign('productName', 'nextcloud'); // for now
348
+            $template->assign('version', OC_Util::getVersionString());
349
+            $template->assign('tooBig', $tooBig);
350
+
351
+            $template->printPage();
352
+            die();
353
+        }
354
+
355
+        // check whether this is a core update or apps update
356
+        $installedVersion = $systemConfig->getValue('version', '0.0.0');
357
+        $currentVersion = implode('.', \OCP\Util::getVersion());
358
+
359
+        // if not a core upgrade, then it's apps upgrade
360
+        $isAppsOnlyUpgrade = version_compare($currentVersion, $installedVersion, '=');
361
+
362
+        $oldTheme = $systemConfig->getValue('theme');
363
+        $systemConfig->setValue('theme', '');
364
+        OC_Util::addScript('config'); // needed for web root
365
+        OC_Util::addScript('update');
366
+
367
+        /** @var \OC\App\AppManager $appManager */
368
+        $appManager = \OC::$server->getAppManager();
369
+
370
+        $tmpl = new OC_Template('', 'update.admin', 'guest');
371
+        $tmpl->assign('version', OC_Util::getVersionString());
372
+        $tmpl->assign('isAppsOnlyUpgrade', $isAppsOnlyUpgrade);
373
+
374
+        // get third party apps
375
+        $ocVersion = \OCP\Util::getVersion();
376
+        $ocVersion = implode('.', $ocVersion);
377
+        $incompatibleApps = $appManager->getIncompatibleApps($ocVersion);
378
+        $incompatibleShippedApps = [];
379
+        foreach ($incompatibleApps as $appInfo) {
380
+            if ($appManager->isShipped($appInfo['id'])) {
381
+                $incompatibleShippedApps[] = $appInfo['name'] . ' (' . $appInfo['id'] . ')';
382
+            }
383
+        }
384
+
385
+        if (!empty($incompatibleShippedApps)) {
386
+            $l = \OC::$server->getL10N('core');
387
+            $hint = $l->t('The files of the app %$1s were not replaced correctly. Make sure it is a version compatible with the server.', [implode(', ', $incompatibleShippedApps)]);
388
+            throw new \OC\HintException('The files of the app ' . implode(', ', $incompatibleShippedApps) . ' were not replaced correctly. Make sure it is a version compatible with the server.', $hint);
389
+        }
390
+
391
+        $tmpl->assign('appsToUpgrade', $appManager->getAppsNeedingUpgrade($ocVersion));
392
+        $tmpl->assign('incompatibleAppsList', $incompatibleApps);
393
+        $tmpl->assign('productName', 'Nextcloud'); // for now
394
+        $tmpl->assign('oldTheme', $oldTheme);
395
+        $tmpl->printPage();
396
+    }
397
+
398
+    public static function initSession() {
399
+        if(self::$server->getRequest()->getServerProtocol() === 'https') {
400
+            ini_set('session.cookie_secure', true);
401
+        }
402
+
403
+        // prevents javascript from accessing php session cookies
404
+        ini_set('session.cookie_httponly', 'true');
405
+
406
+        // set the cookie path to the Nextcloud directory
407
+        $cookie_path = OC::$WEBROOT ? : '/';
408
+        ini_set('session.cookie_path', $cookie_path);
409
+
410
+        // Let the session name be changed in the initSession Hook
411
+        $sessionName = OC_Util::getInstanceId();
412
+
413
+        try {
414
+            // Allow session apps to create a custom session object
415
+            $useCustomSession = false;
416
+            $session = self::$server->getSession();
417
+            OC_Hook::emit('OC', 'initSession', array('session' => &$session, 'sessionName' => &$sessionName, 'useCustomSession' => &$useCustomSession));
418
+            if (!$useCustomSession) {
419
+                // set the session name to the instance id - which is unique
420
+                $session = new \OC\Session\Internal($sessionName);
421
+            }
422
+
423
+            $cryptoWrapper = \OC::$server->getSessionCryptoWrapper();
424
+            $session = $cryptoWrapper->wrapSession($session);
425
+            self::$server->setSession($session);
426
+
427
+            // if session can't be started break with http 500 error
428
+        } catch (Exception $e) {
429
+            \OCP\Util::logException('base', $e);
430
+            //show the user a detailed error page
431
+            OC_Response::setStatus(OC_Response::STATUS_INTERNAL_SERVER_ERROR);
432
+            OC_Template::printExceptionErrorPage($e);
433
+            die();
434
+        }
435
+
436
+        $sessionLifeTime = self::getSessionLifeTime();
437
+
438
+        // session timeout
439
+        if ($session->exists('LAST_ACTIVITY') && (time() - $session->get('LAST_ACTIVITY') > $sessionLifeTime)) {
440
+            if (isset($_COOKIE[session_name()])) {
441
+                setcookie(session_name(), null, -1, self::$WEBROOT ? : '/');
442
+            }
443
+            \OC::$server->getUserSession()->logout();
444
+        }
445
+
446
+        $session->set('LAST_ACTIVITY', time());
447
+    }
448
+
449
+    /**
450
+     * @return string
451
+     */
452
+    private static function getSessionLifeTime() {
453
+        return \OC::$server->getConfig()->getSystemValue('session_lifetime', 60 * 60 * 24);
454
+    }
455
+
456
+    public static function loadAppClassPaths() {
457
+        foreach (OC_App::getEnabledApps() as $app) {
458
+            $appPath = OC_App::getAppPath($app);
459
+            if ($appPath === false) {
460
+                continue;
461
+            }
462
+
463
+            $file = $appPath . '/appinfo/classpath.php';
464
+            if (file_exists($file)) {
465
+                require_once $file;
466
+            }
467
+        }
468
+    }
469
+
470
+    /**
471
+     * Try to set some values to the required Nextcloud default
472
+     */
473
+    public static function setRequiredIniValues() {
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() {
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() {
519
+        $request = \OC::$server->getRequest();
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 = [
526
+            // OS X Finder
527
+            '/^WebDAVFS/',
528
+        ];
529
+        if($request->isUserAgent($incompatibleUserAgents)) {
530
+            return;
531
+        }
532
+
533
+        if(count($_COOKIE) > 0) {
534
+            $requestUri = $request->getScriptName();
535
+            $processingScript = explode('/', $requestUri);
536
+            $processingScript = $processingScript[count($processingScript)-1];
537
+
538
+            // index.php routes are handled in the middleware
539
+            if($processingScript === 'index.php') {
540
+                return;
541
+            }
542
+
543
+            // All other endpoints require the lax and the strict cookie
544
+            if(!$request->passesStrictCookieCheck()) {
545
+                self::sendSameSiteCookies();
546
+                // Debug mode gets access to the resources without strict cookie
547
+                // due to the fact that the SabreDAV browser also lives there.
548
+                if(!\OC::$server->getConfig()->getSystemValue('debug', false)) {
549
+                    http_response_code(\OCP\AppFramework\Http::STATUS_SERVICE_UNAVAILABLE);
550
+                    exit();
551
+                }
552
+            }
553
+        } elseif(!isset($_COOKIE['nc_sameSiteCookielax']) || !isset($_COOKIE['nc_sameSiteCookiestrict'])) {
554
+            self::sendSameSiteCookies();
555
+        }
556
+    }
557
+
558
+    public static function init() {
559
+        // calculate the root directories
560
+        OC::$SERVERROOT = str_replace("\\", '/', substr(__DIR__, 0, -4));
561
+
562
+        // register autoloader
563
+        $loaderStart = microtime(true);
564
+        require_once __DIR__ . '/autoloader.php';
565
+        self::$loader = new \OC\Autoloader([
566
+            OC::$SERVERROOT . '/lib/private/legacy',
567
+        ]);
568
+        if (defined('PHPUNIT_RUN')) {
569
+            self::$loader->addValidRoot(OC::$SERVERROOT . '/tests');
570
+        }
571
+        spl_autoload_register(array(self::$loader, 'load'));
572
+        $loaderEnd = microtime(true);
573
+
574
+        self::$CLI = (php_sapi_name() == 'cli');
575
+
576
+        // Add default composer PSR-4 autoloader
577
+        self::$composerAutoloader = require_once OC::$SERVERROOT . '/lib/composer/autoload.php';
578
+
579
+        try {
580
+            self::initPaths();
581
+            // setup 3rdparty autoloader
582
+            $vendorAutoLoad = OC::$SERVERROOT. '/3rdparty/autoload.php';
583
+            if (!file_exists($vendorAutoLoad)) {
584
+                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".');
585
+            }
586
+            require_once $vendorAutoLoad;
587
+
588
+        } catch (\RuntimeException $e) {
589
+            if (!self::$CLI) {
590
+                $claimedProtocol = strtoupper($_SERVER['SERVER_PROTOCOL']);
591
+                $protocol = in_array($claimedProtocol, ['HTTP/1.0', 'HTTP/1.1', 'HTTP/2']) ? $claimedProtocol : 'HTTP/1.1';
592
+                header($protocol . ' ' . OC_Response::STATUS_SERVICE_UNAVAILABLE);
593
+            }
594
+            // we can't use the template error page here, because this needs the
595
+            // DI container which isn't available yet
596
+            print($e->getMessage());
597
+            exit();
598
+        }
599
+
600
+        // setup the basic server
601
+        self::$server = new \OC\Server(\OC::$WEBROOT, self::$config);
602
+        \OC::$server->getEventLogger()->log('autoloader', 'Autoloader', $loaderStart, $loaderEnd);
603
+        \OC::$server->getEventLogger()->start('boot', 'Initialize');
604
+
605
+        // Don't display errors and log them
606
+        error_reporting(E_ALL | E_STRICT);
607
+        @ini_set('display_errors', '0');
608
+        @ini_set('log_errors', '1');
609
+
610
+        if(!date_default_timezone_set('UTC')) {
611
+            throw new \RuntimeException('Could not set timezone to UTC');
612
+        }
613
+
614
+        //try to configure php to enable big file uploads.
615
+        //this doesn´t work always depending on the webserver and php configuration.
616
+        //Let´s try to overwrite some defaults anyway
617
+
618
+        //try to set the maximum execution time to 60min
619
+        if (strpos(@ini_get('disable_functions'), 'set_time_limit') === false) {
620
+            @set_time_limit(3600);
621
+        }
622
+        @ini_set('max_execution_time', '3600');
623
+        @ini_set('max_input_time', '3600');
624
+
625
+        //try to set the maximum filesize to 10G
626
+        @ini_set('upload_max_filesize', '10G');
627
+        @ini_set('post_max_size', '10G');
628
+        @ini_set('file_uploads', '50');
629
+
630
+        self::setRequiredIniValues();
631
+        self::handleAuthHeaders();
632
+        self::registerAutoloaderCache();
633
+
634
+        // initialize intl fallback is necessary
635
+        \Patchwork\Utf8\Bootup::initIntl();
636
+        OC_Util::isSetLocaleWorking();
637
+
638
+        if (!defined('PHPUNIT_RUN')) {
639
+            OC\Log\ErrorHandler::setLogger(\OC::$server->getLogger());
640
+            $debug = \OC::$server->getConfig()->getSystemValue('debug', false);
641
+            OC\Log\ErrorHandler::register($debug);
642
+        }
643
+
644
+        \OC::$server->getEventLogger()->start('init_session', 'Initialize session');
645
+        OC_App::loadApps(array('session'));
646
+        if (!self::$CLI) {
647
+            self::initSession();
648
+        }
649
+        \OC::$server->getEventLogger()->end('init_session');
650
+        self::checkConfig();
651
+        self::checkInstalled();
652
+
653
+        OC_Response::addSecurityHeaders();
654
+
655
+        self::performSameSiteCookieProtection();
656
+
657
+        if (!defined('OC_CONSOLE')) {
658
+            $errors = OC_Util::checkServer(\OC::$server->getSystemConfig());
659
+            if (count($errors) > 0) {
660
+                if (self::$CLI) {
661
+                    // Convert l10n string into regular string for usage in database
662
+                    $staticErrors = [];
663
+                    foreach ($errors as $error) {
664
+                        echo $error['error'] . "\n";
665
+                        echo $error['hint'] . "\n\n";
666
+                        $staticErrors[] = [
667
+                            'error' => (string)$error['error'],
668
+                            'hint' => (string)$error['hint'],
669
+                        ];
670
+                    }
671
+
672
+                    try {
673
+                        \OC::$server->getConfig()->setAppValue('core', 'cronErrors', json_encode($staticErrors));
674
+                    } catch (\Exception $e) {
675
+                        echo('Writing to database failed');
676
+                    }
677
+                    exit(1);
678
+                } else {
679
+                    OC_Response::setStatus(OC_Response::STATUS_SERVICE_UNAVAILABLE);
680
+                    OC_Util::addStyle('guest');
681
+                    OC_Template::printGuestPage('', 'error', array('errors' => $errors));
682
+                    exit;
683
+                }
684
+            } elseif (self::$CLI && \OC::$server->getConfig()->getSystemValue('installed', false)) {
685
+                \OC::$server->getConfig()->deleteAppValue('core', 'cronErrors');
686
+            }
687
+        }
688
+        //try to set the session lifetime
689
+        $sessionLifeTime = self::getSessionLifeTime();
690
+        @ini_set('gc_maxlifetime', (string)$sessionLifeTime);
691
+
692
+        $systemConfig = \OC::$server->getSystemConfig();
693
+
694
+        // User and Groups
695
+        if (!$systemConfig->getValue("installed", false)) {
696
+            self::$server->getSession()->set('user_id', '');
697
+        }
698
+
699
+        OC_User::useBackend(new \OC\User\Database());
700
+        \OC::$server->getGroupManager()->addBackend(new \OC\Group\Database());
701
+
702
+        // Subscribe to the hook
703
+        \OCP\Util::connectHook(
704
+            '\OCA\Files_Sharing\API\Server2Server',
705
+            'preLoginNameUsedAsUserName',
706
+            '\OC\User\Database',
707
+            'preLoginNameUsedAsUserName'
708
+        );
709
+
710
+        //setup extra user backends
711
+        if (!\OCP\Util::needUpgrade()) {
712
+            OC_User::setupBackends();
713
+        } else {
714
+            // Run upgrades in incognito mode
715
+            OC_User::setIncognitoMode(true);
716
+        }
717
+
718
+        self::registerCleanupHooks();
719
+        self::registerFilesystemHooks();
720
+        self::registerShareHooks();
721
+        self::registerEncryptionWrapper();
722
+        self::registerEncryptionHooks();
723
+        self::registerAccountHooks();
724
+
725
+        // Make sure that the application class is not loaded before the database is setup
726
+        if ($systemConfig->getValue("installed", false)) {
727
+            $settings = new \OC\Settings\Application();
728
+            $settings->register();
729
+        }
730
+
731
+        //make sure temporary files are cleaned up
732
+        $tmpManager = \OC::$server->getTempManager();
733
+        register_shutdown_function(array($tmpManager, 'clean'));
734
+        $lockProvider = \OC::$server->getLockingProvider();
735
+        register_shutdown_function(array($lockProvider, 'releaseAll'));
736
+
737
+        // Check whether the sample configuration has been copied
738
+        if($systemConfig->getValue('copied_sample_config', false)) {
739
+            $l = \OC::$server->getL10N('lib');
740
+            header('HTTP/1.1 503 Service Temporarily Unavailable');
741
+            header('Status: 503 Service Temporarily Unavailable');
742
+            OC_Template::printErrorPage(
743
+                $l->t('Sample configuration detected'),
744
+                $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')
745
+            );
746
+            return;
747
+        }
748
+
749
+        $request = \OC::$server->getRequest();
750
+        $host = $request->getInsecureServerHost();
751
+        /**
752
+         * if the host passed in headers isn't trusted
753
+         * FIXME: Should not be in here at all :see_no_evil:
754
+         */
755
+        if (!OC::$CLI
756
+            // overwritehost is always trusted, workaround to not have to make
757
+            // \OC\AppFramework\Http\Request::getOverwriteHost public
758
+            && self::$server->getConfig()->getSystemValue('overwritehost') === ''
759
+            && !\OC::$server->getTrustedDomainHelper()->isTrustedDomain($host)
760
+            && self::$server->getConfig()->getSystemValue('installed', false)
761
+        ) {
762
+            // Allow access to CSS resources
763
+            $isScssRequest = false;
764
+            if(strpos($request->getPathInfo(), '/css/') === 0) {
765
+                $isScssRequest = true;
766
+            }
767
+
768
+            if(substr($request->getRequestUri(), -11) === '/status.php') {
769
+                OC_Response::setStatus(\OC_Response::STATUS_BAD_REQUEST);
770
+                header('Status: 400 Bad Request');
771
+                header('Content-Type: application/json');
772
+                echo '{"error": "Trusted domain error.", "code": 15}';
773
+                exit();
774
+            }
775
+
776
+            if (!$isScssRequest) {
777
+                OC_Response::setStatus(\OC_Response::STATUS_BAD_REQUEST);
778
+                header('Status: 400 Bad Request');
779
+
780
+                \OC::$server->getLogger()->warning(
781
+                    'Trusted domain error. "{remoteAddress}" tried to access using "{host}" as host.',
782
+                    [
783
+                        'app' => 'core',
784
+                        'remoteAddress' => $request->getRemoteAddress(),
785
+                        'host' => $host,
786
+                    ]
787
+                );
788
+
789
+                $tmpl = new OCP\Template('core', 'untrustedDomain', 'guest');
790
+                $tmpl->assign('domain', $host);
791
+                $tmpl->printPage();
792
+
793
+                exit();
794
+            }
795
+        }
796
+        \OC::$server->getEventLogger()->end('boot');
797
+    }
798
+
799
+    /**
800
+     * register hooks for the cleanup of cache and bruteforce protection
801
+     */
802
+    public static function registerCleanupHooks() {
803
+        //don't try to do this before we are properly setup
804
+        if (\OC::$server->getSystemConfig()->getValue('installed', false) && !\OCP\Util::needUpgrade()) {
805
+
806
+            // NOTE: This will be replaced to use OCP
807
+            $userSession = self::$server->getUserSession();
808
+            $userSession->listen('\OC\User', 'postLogin', function () use ($userSession) {
809
+                if (!defined('PHPUNIT_RUN')) {
810
+                    // reset brute force delay for this IP address and username
811
+                    $uid = \OC::$server->getUserSession()->getUser()->getUID();
812
+                    $request = \OC::$server->getRequest();
813
+                    $throttler = \OC::$server->getBruteForceThrottler();
814
+                    $throttler->resetDelay($request->getRemoteAddress(), 'login', ['user' => $uid]);
815
+                }
816
+
817
+                try {
818
+                    $cache = new \OC\Cache\File();
819
+                    $cache->gc();
820
+                } catch (\OC\ServerNotAvailableException $e) {
821
+                    // not a GC exception, pass it on
822
+                    throw $e;
823
+                } catch (\OC\ForbiddenException $e) {
824
+                    // filesystem blocked for this request, ignore
825
+                } catch (\Exception $e) {
826
+                    // a GC exception should not prevent users from using OC,
827
+                    // so log the exception
828
+                    \OC::$server->getLogger()->logException($e, [
829
+                        'message' => 'Exception when running cache gc.',
830
+                        'level' => \OCP\Util::WARN,
831
+                        'app' => 'core',
832
+                    ]);
833
+                }
834
+            });
835
+        }
836
+    }
837
+
838
+    private static function registerEncryptionWrapper() {
839
+        $manager = self::$server->getEncryptionManager();
840
+        \OCP\Util::connectHook('OC_Filesystem', 'preSetup', $manager, 'setupStorage');
841
+    }
842
+
843
+    private static function registerEncryptionHooks() {
844
+        $enabled = self::$server->getEncryptionManager()->isEnabled();
845
+        if ($enabled) {
846
+            \OCP\Util::connectHook(Share::class, 'post_shared', HookManager::class, 'postShared');
847
+            \OCP\Util::connectHook(Share::class, 'post_unshare', HookManager::class, 'postUnshared');
848
+            \OCP\Util::connectHook('OC_Filesystem', 'post_rename', HookManager::class, 'postRename');
849
+            \OCP\Util::connectHook('\OCA\Files_Trashbin\Trashbin', 'post_restore', HookManager::class, 'postRestore');
850
+        }
851
+    }
852
+
853
+    private static function registerAccountHooks() {
854
+        $hookHandler = new \OC\Accounts\Hooks(\OC::$server->getLogger());
855
+        \OCP\Util::connectHook('OC_User', 'changeUser', $hookHandler, 'changeUserHook');
856
+    }
857
+
858
+    /**
859
+     * register hooks for the filesystem
860
+     */
861
+    public static function registerFilesystemHooks() {
862
+        // Check for blacklisted files
863
+        OC_Hook::connect('OC_Filesystem', 'write', Filesystem::class, 'isBlacklisted');
864
+        OC_Hook::connect('OC_Filesystem', 'rename', Filesystem::class, 'isBlacklisted');
865
+    }
866
+
867
+    /**
868
+     * register hooks for sharing
869
+     */
870
+    public static function registerShareHooks() {
871
+        if (\OC::$server->getSystemConfig()->getValue('installed')) {
872
+            OC_Hook::connect('OC_User', 'post_deleteUser', Hooks::class, 'post_deleteUser');
873
+            OC_Hook::connect('OC_User', 'post_removeFromGroup', Hooks::class, 'post_removeFromGroup');
874
+            OC_Hook::connect('OC_User', 'post_deleteGroup', Hooks::class, 'post_deleteGroup');
875
+        }
876
+    }
877
+
878
+    protected static function registerAutoloaderCache() {
879
+        // The class loader takes an optional low-latency cache, which MUST be
880
+        // namespaced. The instanceid is used for namespacing, but might be
881
+        // unavailable at this point. Furthermore, it might not be possible to
882
+        // generate an instanceid via \OC_Util::getInstanceId() because the
883
+        // config file may not be writable. As such, we only register a class
884
+        // loader cache if instanceid is available without trying to create one.
885
+        $instanceId = \OC::$server->getSystemConfig()->getValue('instanceid', null);
886
+        if ($instanceId) {
887
+            try {
888
+                $memcacheFactory = \OC::$server->getMemCacheFactory();
889
+                self::$loader->setMemoryCache($memcacheFactory->createLocal('Autoloader'));
890
+            } catch (\Exception $ex) {
891
+            }
892
+        }
893
+    }
894
+
895
+    /**
896
+     * Handle the request
897
+     */
898
+    public static function handleRequest() {
899
+
900
+        \OC::$server->getEventLogger()->start('handle_request', 'Handle request');
901
+        $systemConfig = \OC::$server->getSystemConfig();
902
+        // load all the classpaths from the enabled apps so they are available
903
+        // in the routing files of each app
904
+        OC::loadAppClassPaths();
905
+
906
+        // Check if Nextcloud is installed or in maintenance (update) mode
907
+        if (!$systemConfig->getValue('installed', false)) {
908
+            \OC::$server->getSession()->clear();
909
+            $setupHelper = new OC\Setup(
910
+                $systemConfig,
911
+                \OC::$server->getIniWrapper(),
912
+                \OC::$server->getL10N('lib'),
913
+                \OC::$server->query(\OCP\Defaults::class),
914
+                \OC::$server->getLogger(),
915
+                \OC::$server->getSecureRandom(),
916
+                \OC::$server->query(\OC\Installer::class)
917
+            );
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 ($requestPath === '/heartbeat') {
926
+            return;
927
+        }
928
+        if (substr($requestPath, -3) !== '.js') { // we need these files during the upgrade
929
+            self::checkMaintenanceMode();
930
+
931
+            if (\OCP\Util::needUpgrade()) {
932
+                if (function_exists('opcache_reset')) {
933
+                    opcache_reset();
934
+                }
935
+                if (!$systemConfig->getValue('maintenance', false)) {
936
+                    self::printUpgradePage($systemConfig);
937
+                    exit();
938
+                }
939
+            }
940
+        }
941
+
942
+        // emergency app disabling
943
+        if ($requestPath === '/disableapp'
944
+            && $request->getMethod() === 'POST'
945
+            && ((array)$request->getParam('appid')) !== ''
946
+        ) {
947
+            \OCP\JSON::callCheck();
948
+            \OCP\JSON::checkAdminUser();
949
+            $appIds = (array)$request->getParam('appid');
950
+            foreach($appIds as $appId) {
951
+                $appId = \OC_App::cleanAppId($appId);
952
+                \OC::$server->getAppManager()->disableApp($appId);
953
+            }
954
+            \OC_JSON::success();
955
+            exit();
956
+        }
957
+
958
+        // Always load authentication apps
959
+        OC_App::loadApps(['authentication']);
960
+
961
+        // Load minimum set of apps
962
+        if (!\OCP\Util::needUpgrade()
963
+            && !$systemConfig->getValue('maintenance', false)) {
964
+            // For logged-in users: Load everything
965
+            if(\OC::$server->getUserSession()->isLoggedIn()) {
966
+                OC_App::loadApps();
967
+            } else {
968
+                // For guests: Load only filesystem and logging
969
+                OC_App::loadApps(array('filesystem', 'logging'));
970
+                self::handleLogin($request);
971
+            }
972
+        }
973
+
974
+        if (!self::$CLI) {
975
+            try {
976
+                if (!$systemConfig->getValue('maintenance', false) && !\OCP\Util::needUpgrade()) {
977
+                    OC_App::loadApps(array('filesystem', 'logging'));
978
+                    OC_App::loadApps();
979
+                }
980
+                OC_Util::setupFS();
981
+                OC::$server->getRouter()->match(\OC::$server->getRequest()->getRawPathInfo());
982
+                return;
983
+            } catch (Symfony\Component\Routing\Exception\ResourceNotFoundException $e) {
984
+                //header('HTTP/1.0 404 Not Found');
985
+            } catch (Symfony\Component\Routing\Exception\MethodNotAllowedException $e) {
986
+                OC_Response::setStatus(405);
987
+                return;
988
+            }
989
+        }
990
+
991
+        // Handle WebDAV
992
+        if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'PROPFIND') {
993
+            // not allowed any more to prevent people
994
+            // mounting this root directly.
995
+            // Users need to mount remote.php/webdav instead.
996
+            header('HTTP/1.1 405 Method Not Allowed');
997
+            header('Status: 405 Method Not Allowed');
998
+            return;
999
+        }
1000
+
1001
+        // Someone is logged in
1002
+        if (\OC::$server->getUserSession()->isLoggedIn()) {
1003
+            OC_App::loadApps();
1004
+            OC_User::setupBackends();
1005
+            OC_Util::setupFS();
1006
+            // FIXME
1007
+            // Redirect to default application
1008
+            OC_Util::redirectToDefaultPage();
1009
+        } else {
1010
+            // Not handled and not logged in
1011
+            header('Location: '.\OC::$server->getURLGenerator()->linkToRouteAbsolute('core.login.showLoginForm'));
1012
+        }
1013
+    }
1014
+
1015
+    /**
1016
+     * Check login: apache auth, auth token, basic auth
1017
+     *
1018
+     * @param OCP\IRequest $request
1019
+     * @return boolean
1020
+     */
1021
+    static function handleLogin(OCP\IRequest $request) {
1022
+        $userSession = self::$server->getUserSession();
1023
+        if (OC_User::handleApacheAuth()) {
1024
+            return true;
1025
+        }
1026
+        if ($userSession->tryTokenLogin($request)) {
1027
+            return true;
1028
+        }
1029
+        if (isset($_COOKIE['nc_username'])
1030
+            && isset($_COOKIE['nc_token'])
1031
+            && isset($_COOKIE['nc_session_id'])
1032
+            && $userSession->loginWithCookie($_COOKIE['nc_username'], $_COOKIE['nc_token'], $_COOKIE['nc_session_id'])) {
1033
+            return true;
1034
+        }
1035
+        if ($userSession->tryBasicAuthLogin($request, \OC::$server->getBruteForceThrottler())) {
1036
+            return true;
1037
+        }
1038
+        return false;
1039
+    }
1040
+
1041
+    protected static function handleAuthHeaders() {
1042
+        //copy http auth headers for apache+php-fcgid work around
1043
+        if (isset($_SERVER['HTTP_XAUTHORIZATION']) && !isset($_SERVER['HTTP_AUTHORIZATION'])) {
1044
+            $_SERVER['HTTP_AUTHORIZATION'] = $_SERVER['HTTP_XAUTHORIZATION'];
1045
+        }
1046
+
1047
+        // Extract PHP_AUTH_USER/PHP_AUTH_PW from other headers if necessary.
1048
+        $vars = array(
1049
+            'HTTP_AUTHORIZATION', // apache+php-cgi work around
1050
+            'REDIRECT_HTTP_AUTHORIZATION', // apache+php-cgi alternative
1051
+        );
1052
+        foreach ($vars as $var) {
1053
+            if (isset($_SERVER[$var]) && preg_match('/Basic\s+(.*)$/i', $_SERVER[$var], $matches)) {
1054
+                list($name, $password) = explode(':', base64_decode($matches[1]), 2);
1055
+                $_SERVER['PHP_AUTH_USER'] = $name;
1056
+                $_SERVER['PHP_AUTH_PW'] = $password;
1057
+                break;
1058
+            }
1059
+        }
1060
+    }
1061 1061
 }
1062 1062
 
1063 1063
 OC::init();
Please login to merge, or discard this patch.
lib/private/legacy/app.php 2 patches
Indentation   +1028 added lines, -1028 removed lines patch added patch discarded remove patch
@@ -63,1032 +63,1032 @@
 block discarded – undo
63 63
  * upgrading and removing apps.
64 64
  */
65 65
 class OC_App {
66
-	static private $adminForms = [];
67
-	static private $personalForms = [];
68
-	static private $appTypes = [];
69
-	static private $loadedApps = [];
70
-	static private $altLogin = [];
71
-	static private $alreadyRegistered = [];
72
-	const officialApp = 200;
73
-
74
-	/**
75
-	 * clean the appId
76
-	 *
77
-	 * @param string $app AppId that needs to be cleaned
78
-	 * @return string
79
-	 */
80
-	public static function cleanAppId(string $app): string {
81
-		return str_replace(array('\0', '/', '\\', '..'), '', $app);
82
-	}
83
-
84
-	/**
85
-	 * Check if an app is loaded
86
-	 *
87
-	 * @param string $app
88
-	 * @return bool
89
-	 */
90
-	public static function isAppLoaded(string $app): bool {
91
-		return in_array($app, self::$loadedApps, true);
92
-	}
93
-
94
-	/**
95
-	 * loads all apps
96
-	 *
97
-	 * @param string[] $types
98
-	 * @return bool
99
-	 *
100
-	 * This function walks through the ownCloud directory and loads all apps
101
-	 * it can find. A directory contains an app if the file /appinfo/info.xml
102
-	 * exists.
103
-	 *
104
-	 * if $types is set to non-empty array, only apps of those types will be loaded
105
-	 */
106
-	public static function loadApps(array $types = []): bool {
107
-		if (\OC::$server->getSystemConfig()->getValue('maintenance', false)) {
108
-			return false;
109
-		}
110
-		// Load the enabled apps here
111
-		$apps = self::getEnabledApps();
112
-
113
-		// Add each apps' folder as allowed class path
114
-		foreach($apps as $app) {
115
-			$path = self::getAppPath($app);
116
-			if($path !== false) {
117
-				self::registerAutoloading($app, $path);
118
-			}
119
-		}
120
-
121
-		// prevent app.php from printing output
122
-		ob_start();
123
-		foreach ($apps as $app) {
124
-			if (($types === [] or self::isType($app, $types)) && !in_array($app, self::$loadedApps)) {
125
-				self::loadApp($app);
126
-			}
127
-		}
128
-		ob_end_clean();
129
-
130
-		return true;
131
-	}
132
-
133
-	/**
134
-	 * load a single app
135
-	 *
136
-	 * @param string $app
137
-	 * @throws Exception
138
-	 */
139
-	public static function loadApp(string $app) {
140
-		self::$loadedApps[] = $app;
141
-		$appPath = self::getAppPath($app);
142
-		if($appPath === false) {
143
-			return;
144
-		}
145
-
146
-		// in case someone calls loadApp() directly
147
-		self::registerAutoloading($app, $appPath);
148
-
149
-		if (is_file($appPath . '/appinfo/app.php')) {
150
-			\OC::$server->getEventLogger()->start('load_app_' . $app, 'Load app: ' . $app);
151
-			try {
152
-				self::requireAppFile($app);
153
-			} catch (Error $ex) {
154
-				\OC::$server->getLogger()->logException($ex);
155
-				if (!\OC::$server->getAppManager()->isShipped($app)) {
156
-					// Only disable apps which are not shipped
157
-					\OC::$server->getAppManager()->disableApp($app);
158
-				}
159
-			}
160
-			\OC::$server->getEventLogger()->end('load_app_' . $app);
161
-		}
162
-
163
-		$info = self::getAppInfo($app);
164
-		if (!empty($info['activity']['filters'])) {
165
-			foreach ($info['activity']['filters'] as $filter) {
166
-				\OC::$server->getActivityManager()->registerFilter($filter);
167
-			}
168
-		}
169
-		if (!empty($info['activity']['settings'])) {
170
-			foreach ($info['activity']['settings'] as $setting) {
171
-				\OC::$server->getActivityManager()->registerSetting($setting);
172
-			}
173
-		}
174
-		if (!empty($info['activity']['providers'])) {
175
-			foreach ($info['activity']['providers'] as $provider) {
176
-				\OC::$server->getActivityManager()->registerProvider($provider);
177
-			}
178
-		}
179
-
180
-		if (!empty($info['settings']['admin'])) {
181
-			foreach ($info['settings']['admin'] as $setting) {
182
-				\OC::$server->getSettingsManager()->registerSetting('admin', $setting);
183
-			}
184
-		}
185
-		if (!empty($info['settings']['admin-section'])) {
186
-			foreach ($info['settings']['admin-section'] as $section) {
187
-				\OC::$server->getSettingsManager()->registerSection('admin', $section);
188
-			}
189
-		}
190
-		if (!empty($info['settings']['personal'])) {
191
-			foreach ($info['settings']['personal'] as $setting) {
192
-				\OC::$server->getSettingsManager()->registerSetting('personal', $setting);
193
-			}
194
-		}
195
-		if (!empty($info['settings']['personal-section'])) {
196
-			foreach ($info['settings']['personal-section'] as $section) {
197
-				\OC::$server->getSettingsManager()->registerSection('personal', $section);
198
-			}
199
-		}
200
-
201
-		if (!empty($info['collaboration']['plugins'])) {
202
-			// deal with one or many plugin entries
203
-			$plugins = isset($info['collaboration']['plugins']['plugin']['@value']) ?
204
-				[$info['collaboration']['plugins']['plugin']] : $info['collaboration']['plugins']['plugin'];
205
-			foreach ($plugins as $plugin) {
206
-				if($plugin['@attributes']['type'] === 'collaborator-search') {
207
-					$pluginInfo = [
208
-						'shareType' => $plugin['@attributes']['share-type'],
209
-						'class' => $plugin['@value'],
210
-					];
211
-					\OC::$server->getCollaboratorSearch()->registerPlugin($pluginInfo);
212
-				} else if ($plugin['@attributes']['type'] === 'autocomplete-sort') {
213
-					\OC::$server->getAutoCompleteManager()->registerSorter($plugin['@value']);
214
-				}
215
-			}
216
-		}
217
-	}
218
-
219
-	/**
220
-	 * @internal
221
-	 * @param string $app
222
-	 * @param string $path
223
-	 */
224
-	public static function registerAutoloading(string $app, string $path) {
225
-		$key = $app . '-' . $path;
226
-		if(isset(self::$alreadyRegistered[$key])) {
227
-			return;
228
-		}
229
-
230
-		self::$alreadyRegistered[$key] = true;
231
-
232
-		// Register on PSR-4 composer autoloader
233
-		$appNamespace = \OC\AppFramework\App::buildAppNamespace($app);
234
-		\OC::$server->registerNamespace($app, $appNamespace);
235
-
236
-		if (file_exists($path . '/composer/autoload.php')) {
237
-			require_once $path . '/composer/autoload.php';
238
-		} else {
239
-			\OC::$composerAutoloader->addPsr4($appNamespace . '\\', $path . '/lib/', true);
240
-			// Register on legacy autoloader
241
-			\OC::$loader->addValidRoot($path);
242
-		}
243
-
244
-		// Register Test namespace only when testing
245
-		if (defined('PHPUNIT_RUN') || defined('CLI_TEST_RUN')) {
246
-			\OC::$composerAutoloader->addPsr4($appNamespace . '\\Tests\\', $path . '/tests/', true);
247
-		}
248
-	}
249
-
250
-	/**
251
-	 * Load app.php from the given app
252
-	 *
253
-	 * @param string $app app name
254
-	 * @throws Error
255
-	 */
256
-	private static function requireAppFile(string $app) {
257
-		// encapsulated here to avoid variable scope conflicts
258
-		require_once $app . '/appinfo/app.php';
259
-	}
260
-
261
-	/**
262
-	 * check if an app is of a specific type
263
-	 *
264
-	 * @param string $app
265
-	 * @param array $types
266
-	 * @return bool
267
-	 */
268
-	public static function isType(string $app, array $types): bool {
269
-		$appTypes = self::getAppTypes($app);
270
-		foreach ($types as $type) {
271
-			if (array_search($type, $appTypes) !== false) {
272
-				return true;
273
-			}
274
-		}
275
-		return false;
276
-	}
277
-
278
-	/**
279
-	 * get the types of an app
280
-	 *
281
-	 * @param string $app
282
-	 * @return array
283
-	 */
284
-	private static function getAppTypes(string $app): array {
285
-		//load the cache
286
-		if (count(self::$appTypes) == 0) {
287
-			self::$appTypes = \OC::$server->getAppConfig()->getValues(false, 'types');
288
-		}
289
-
290
-		if (isset(self::$appTypes[$app])) {
291
-			return explode(',', self::$appTypes[$app]);
292
-		}
293
-
294
-		return [];
295
-	}
296
-
297
-	/**
298
-	 * read app types from info.xml and cache them in the database
299
-	 */
300
-	public static function setAppTypes(string $app) {
301
-		$appManager = \OC::$server->getAppManager();
302
-		$appData = $appManager->getAppInfo($app);
303
-		if(!is_array($appData)) {
304
-			return;
305
-		}
306
-
307
-		if (isset($appData['types'])) {
308
-			$appTypes = implode(',', $appData['types']);
309
-		} else {
310
-			$appTypes = '';
311
-			$appData['types'] = [];
312
-		}
313
-
314
-		$config = \OC::$server->getConfig();
315
-		$config->setAppValue($app, 'types', $appTypes);
316
-
317
-		if ($appManager->hasProtectedAppType($appData['types'])) {
318
-			$enabled = $config->getAppValue($app, 'enabled', 'yes');
319
-			if ($enabled !== 'yes' && $enabled !== 'no') {
320
-				$config->setAppValue($app, 'enabled', 'yes');
321
-			}
322
-		}
323
-	}
324
-
325
-	/**
326
-	 * Returns apps enabled for the current user.
327
-	 *
328
-	 * @param bool $forceRefresh whether to refresh the cache
329
-	 * @param bool $all whether to return apps for all users, not only the
330
-	 * currently logged in one
331
-	 * @return string[]
332
-	 */
333
-	public static function getEnabledApps(bool $forceRefresh = false, bool $all = false): array {
334
-		if (!\OC::$server->getSystemConfig()->getValue('installed', false)) {
335
-			return [];
336
-		}
337
-		// in incognito mode or when logged out, $user will be false,
338
-		// which is also the case during an upgrade
339
-		$appManager = \OC::$server->getAppManager();
340
-		if ($all) {
341
-			$user = null;
342
-		} else {
343
-			$user = \OC::$server->getUserSession()->getUser();
344
-		}
345
-
346
-		if (is_null($user)) {
347
-			$apps = $appManager->getInstalledApps();
348
-		} else {
349
-			$apps = $appManager->getEnabledAppsForUser($user);
350
-		}
351
-		$apps = array_filter($apps, function ($app) {
352
-			return $app !== 'files';//we add this manually
353
-		});
354
-		sort($apps);
355
-		array_unshift($apps, 'files');
356
-		return $apps;
357
-	}
358
-
359
-	/**
360
-	 * checks whether or not an app is enabled
361
-	 *
362
-	 * @param string $app app
363
-	 * @return bool
364
-	 * @deprecated 13.0.0 use \OC::$server->getAppManager()->isEnabledForUser($appId)
365
-	 *
366
-	 * This function checks whether or not an app is enabled.
367
-	 */
368
-	public static function isEnabled(string $app): bool {
369
-		return \OC::$server->getAppManager()->isEnabledForUser($app);
370
-	}
371
-
372
-	/**
373
-	 * enables an app
374
-	 *
375
-	 * @param string $appId
376
-	 * @param array $groups (optional) when set, only these groups will have access to the app
377
-	 * @throws \Exception
378
-	 * @return void
379
-	 *
380
-	 * This function set an app as enabled in appconfig.
381
-	 */
382
-	public function enable(string $appId,
383
-						   array $groups = []) {
384
-
385
-		// Check if app is already downloaded
386
-		$installer = \OC::$server->query(Installer::class);
387
-		$isDownloaded = $installer->isDownloaded($appId);
388
-
389
-		if(!$isDownloaded) {
390
-			$installer->downloadApp($appId);
391
-		}
392
-
393
-		$installer->installApp($appId);
394
-
395
-		$appManager = \OC::$server->getAppManager();
396
-		if ($groups !== []) {
397
-			$groupManager = \OC::$server->getGroupManager();
398
-			$groupsList = [];
399
-			foreach ($groups as $group) {
400
-				$groupItem = $groupManager->get($group);
401
-				if ($groupItem instanceof \OCP\IGroup) {
402
-					$groupsList[] = $groupManager->get($group);
403
-				}
404
-			}
405
-			$appManager->enableAppForGroups($appId, $groupsList);
406
-		} else {
407
-			$appManager->enableApp($appId);
408
-		}
409
-	}
410
-
411
-	/**
412
-	 * Get the path where to install apps
413
-	 *
414
-	 * @return string|false
415
-	 */
416
-	public static function getInstallPath() {
417
-		if (\OC::$server->getSystemConfig()->getValue('appstoreenabled', true) == false) {
418
-			return false;
419
-		}
420
-
421
-		foreach (OC::$APPSROOTS as $dir) {
422
-			if (isset($dir['writable']) && $dir['writable'] === true) {
423
-				return $dir['path'];
424
-			}
425
-		}
426
-
427
-		\OCP\Util::writeLog('core', 'No application directories are marked as writable.', \OCP\Util::ERROR);
428
-		return null;
429
-	}
430
-
431
-
432
-	/**
433
-	 * search for an app in all app-directories
434
-	 *
435
-	 * @param string $appId
436
-	 * @return false|string
437
-	 */
438
-	public static function findAppInDirectories(string $appId) {
439
-		$sanitizedAppId = self::cleanAppId($appId);
440
-		if($sanitizedAppId !== $appId) {
441
-			return false;
442
-		}
443
-		static $app_dir = [];
444
-
445
-		if (isset($app_dir[$appId])) {
446
-			return $app_dir[$appId];
447
-		}
448
-
449
-		$possibleApps = [];
450
-		foreach (OC::$APPSROOTS as $dir) {
451
-			if (file_exists($dir['path'] . '/' . $appId)) {
452
-				$possibleApps[] = $dir;
453
-			}
454
-		}
455
-
456
-		if (empty($possibleApps)) {
457
-			return false;
458
-		} elseif (count($possibleApps) === 1) {
459
-			$dir = array_shift($possibleApps);
460
-			$app_dir[$appId] = $dir;
461
-			return $dir;
462
-		} else {
463
-			$versionToLoad = [];
464
-			foreach ($possibleApps as $possibleApp) {
465
-				$version = self::getAppVersionByPath($possibleApp['path']);
466
-				if (empty($versionToLoad) || version_compare($version, $versionToLoad['version'], '>')) {
467
-					$versionToLoad = array(
468
-						'dir' => $possibleApp,
469
-						'version' => $version,
470
-					);
471
-				}
472
-			}
473
-			$app_dir[$appId] = $versionToLoad['dir'];
474
-			return $versionToLoad['dir'];
475
-			//TODO - write test
476
-		}
477
-	}
478
-
479
-	/**
480
-	 * Get the directory for the given app.
481
-	 * If the app is defined in multiple directories, the first one is taken. (false if not found)
482
-	 *
483
-	 * @param string $appId
484
-	 * @return string|false
485
-	 */
486
-	public static function getAppPath(string $appId) {
487
-		if ($appId === null || trim($appId) === '') {
488
-			return false;
489
-		}
490
-
491
-		if (($dir = self::findAppInDirectories($appId)) != false) {
492
-			return $dir['path'] . '/' . $appId;
493
-		}
494
-		return false;
495
-	}
496
-
497
-	/**
498
-	 * Get the path for the given app on the access
499
-	 * If the app is defined in multiple directories, the first one is taken. (false if not found)
500
-	 *
501
-	 * @param string $appId
502
-	 * @return string|false
503
-	 */
504
-	public static function getAppWebPath(string $appId) {
505
-		if (($dir = self::findAppInDirectories($appId)) != false) {
506
-			return OC::$WEBROOT . $dir['url'] . '/' . $appId;
507
-		}
508
-		return false;
509
-	}
510
-
511
-	/**
512
-	 * get the last version of the app from appinfo/info.xml
513
-	 *
514
-	 * @param string $appId
515
-	 * @param bool $useCache
516
-	 * @return string
517
-	 * @deprecated 14.0.0 use \OC::$server->getAppManager()->getAppVersion()
518
-	 */
519
-	public static function getAppVersion(string $appId, bool $useCache = true): string {
520
-		return \OC::$server->getAppManager()->getAppVersion($appId, $useCache);
521
-	}
522
-
523
-	/**
524
-	 * get app's version based on it's path
525
-	 *
526
-	 * @param string $path
527
-	 * @return string
528
-	 */
529
-	public static function getAppVersionByPath(string $path): string {
530
-		$infoFile = $path . '/appinfo/info.xml';
531
-		$appData = \OC::$server->getAppManager()->getAppInfo($infoFile, true);
532
-		return isset($appData['version']) ? $appData['version'] : '';
533
-	}
534
-
535
-
536
-	/**
537
-	 * Read all app metadata from the info.xml file
538
-	 *
539
-	 * @param string $appId id of the app or the path of the info.xml file
540
-	 * @param bool $path
541
-	 * @param string $lang
542
-	 * @return array|null
543
-	 * @note all data is read from info.xml, not just pre-defined fields
544
-	 * @deprecated 14.0.0 use \OC::$server->getAppManager()->getAppInfo()
545
-	 */
546
-	public static function getAppInfo(string $appId, bool $path = false, string $lang = null) {
547
-		return \OC::$server->getAppManager()->getAppInfo($appId, $path, $lang);
548
-	}
549
-
550
-	/**
551
-	 * Returns the navigation
552
-	 *
553
-	 * @return array
554
-	 * @deprecated 14.0.0 use \OC::$server->getNavigationManager()->getAll()
555
-	 *
556
-	 * This function returns an array containing all entries added. The
557
-	 * entries are sorted by the key 'order' ascending. Additional to the keys
558
-	 * given for each app the following keys exist:
559
-	 *   - active: boolean, signals if the user is on this navigation entry
560
-	 */
561
-	public static function getNavigation(): array {
562
-		return OC::$server->getNavigationManager()->getAll();
563
-	}
564
-
565
-	/**
566
-	 * Returns the Settings Navigation
567
-	 *
568
-	 * @return string[]
569
-	 * @deprecated 14.0.0 use \OC::$server->getNavigationManager()->getAll('settings')
570
-	 *
571
-	 * This function returns an array containing all settings pages added. The
572
-	 * entries are sorted by the key 'order' ascending.
573
-	 */
574
-	public static function getSettingsNavigation(): array {
575
-		return OC::$server->getNavigationManager()->getAll('settings');
576
-	}
577
-
578
-	/**
579
-	 * get the id of loaded app
580
-	 *
581
-	 * @return string
582
-	 */
583
-	public static function getCurrentApp(): string {
584
-		$request = \OC::$server->getRequest();
585
-		$script = substr($request->getScriptName(), strlen(OC::$WEBROOT) + 1);
586
-		$topFolder = substr($script, 0, strpos($script, '/') ?: 0);
587
-		if (empty($topFolder)) {
588
-			$path_info = $request->getPathInfo();
589
-			if ($path_info) {
590
-				$topFolder = substr($path_info, 1, strpos($path_info, '/', 1) - 1);
591
-			}
592
-		}
593
-		if ($topFolder == 'apps') {
594
-			$length = strlen($topFolder);
595
-			return substr($script, $length + 1, strpos($script, '/', $length + 1) - $length - 1) ?: '';
596
-		} else {
597
-			return $topFolder;
598
-		}
599
-	}
600
-
601
-	/**
602
-	 * @param string $type
603
-	 * @return array
604
-	 */
605
-	public static function getForms(string $type): array {
606
-		$forms = [];
607
-		switch ($type) {
608
-			case 'admin':
609
-				$source = self::$adminForms;
610
-				break;
611
-			case 'personal':
612
-				$source = self::$personalForms;
613
-				break;
614
-			default:
615
-				return [];
616
-		}
617
-		foreach ($source as $form) {
618
-			$forms[] = include $form;
619
-		}
620
-		return $forms;
621
-	}
622
-
623
-	/**
624
-	 * register an admin form to be shown
625
-	 *
626
-	 * @param string $app
627
-	 * @param string $page
628
-	 */
629
-	public static function registerAdmin(string $app, string $page) {
630
-		self::$adminForms[] = $app . '/' . $page . '.php';
631
-	}
632
-
633
-	/**
634
-	 * register a personal form to be shown
635
-	 * @param string $app
636
-	 * @param string $page
637
-	 */
638
-	public static function registerPersonal(string $app, string $page) {
639
-		self::$personalForms[] = $app . '/' . $page . '.php';
640
-	}
641
-
642
-	/**
643
-	 * @param array $entry
644
-	 */
645
-	public static function registerLogIn(array $entry) {
646
-		self::$altLogin[] = $entry;
647
-	}
648
-
649
-	/**
650
-	 * @return array
651
-	 */
652
-	public static function getAlternativeLogIns(): array {
653
-		return self::$altLogin;
654
-	}
655
-
656
-	/**
657
-	 * get a list of all apps in the apps folder
658
-	 *
659
-	 * @return array an array of app names (string IDs)
660
-	 * @todo: change the name of this method to getInstalledApps, which is more accurate
661
-	 */
662
-	public static function getAllApps(): array {
663
-
664
-		$apps = [];
665
-
666
-		foreach (OC::$APPSROOTS as $apps_dir) {
667
-			if (!is_readable($apps_dir['path'])) {
668
-				\OCP\Util::writeLog('core', 'unable to read app folder : ' . $apps_dir['path'], \OCP\Util::WARN);
669
-				continue;
670
-			}
671
-			$dh = opendir($apps_dir['path']);
672
-
673
-			if (is_resource($dh)) {
674
-				while (($file = readdir($dh)) !== false) {
675
-
676
-					if ($file[0] != '.' and is_dir($apps_dir['path'] . '/' . $file) and is_file($apps_dir['path'] . '/' . $file . '/appinfo/info.xml')) {
677
-
678
-						$apps[] = $file;
679
-					}
680
-				}
681
-			}
682
-		}
683
-
684
-		$apps = array_unique($apps);
685
-
686
-		return $apps;
687
-	}
688
-
689
-	/**
690
-	 * List all apps, this is used in apps.php
691
-	 *
692
-	 * @return array
693
-	 */
694
-	public function listAllApps(): array {
695
-		$installedApps = OC_App::getAllApps();
696
-
697
-		$appManager = \OC::$server->getAppManager();
698
-		//we don't want to show configuration for these
699
-		$blacklist = $appManager->getAlwaysEnabledApps();
700
-		$appList = [];
701
-		$langCode = \OC::$server->getL10N('core')->getLanguageCode();
702
-		$urlGenerator = \OC::$server->getURLGenerator();
703
-
704
-		foreach ($installedApps as $app) {
705
-			if (array_search($app, $blacklist) === false) {
706
-
707
-				$info = OC_App::getAppInfo($app, false, $langCode);
708
-				if (!is_array($info)) {
709
-					\OCP\Util::writeLog('core', 'Could not read app info file for app "' . $app . '"', \OCP\Util::ERROR);
710
-					continue;
711
-				}
712
-
713
-				if (!isset($info['name'])) {
714
-					\OCP\Util::writeLog('core', 'App id "' . $app . '" has no name in appinfo', \OCP\Util::ERROR);
715
-					continue;
716
-				}
717
-
718
-				$enabled = \OC::$server->getConfig()->getAppValue($app, 'enabled', 'no');
719
-				$info['groups'] = null;
720
-				if ($enabled === 'yes') {
721
-					$active = true;
722
-				} else if ($enabled === 'no') {
723
-					$active = false;
724
-				} else {
725
-					$active = true;
726
-					$info['groups'] = $enabled;
727
-				}
728
-
729
-				$info['active'] = $active;
730
-
731
-				if ($appManager->isShipped($app)) {
732
-					$info['internal'] = true;
733
-					$info['level'] = self::officialApp;
734
-					$info['removable'] = false;
735
-				} else {
736
-					$info['internal'] = false;
737
-					$info['removable'] = true;
738
-				}
739
-
740
-				$appPath = self::getAppPath($app);
741
-				if($appPath !== false) {
742
-					$appIcon = $appPath . '/img/' . $app . '.svg';
743
-					if (file_exists($appIcon)) {
744
-						$info['preview'] = $urlGenerator->imagePath($app, $app . '.svg');
745
-						$info['previewAsIcon'] = true;
746
-					} else {
747
-						$appIcon = $appPath . '/img/app.svg';
748
-						if (file_exists($appIcon)) {
749
-							$info['preview'] = $urlGenerator->imagePath($app, 'app.svg');
750
-							$info['previewAsIcon'] = true;
751
-						}
752
-					}
753
-				}
754
-				// fix documentation
755
-				if (isset($info['documentation']) && is_array($info['documentation'])) {
756
-					foreach ($info['documentation'] as $key => $url) {
757
-						// If it is not an absolute URL we assume it is a key
758
-						// i.e. admin-ldap will get converted to go.php?to=admin-ldap
759
-						if (stripos($url, 'https://') !== 0 && stripos($url, 'http://') !== 0) {
760
-							$url = $urlGenerator->linkToDocs($url);
761
-						}
762
-
763
-						$info['documentation'][$key] = $url;
764
-					}
765
-				}
766
-
767
-				$info['version'] = OC_App::getAppVersion($app);
768
-				$appList[] = $info;
769
-			}
770
-		}
771
-
772
-		return $appList;
773
-	}
774
-
775
-	public static function shouldUpgrade(string $app): bool {
776
-		$versions = self::getAppVersions();
777
-		$currentVersion = OC_App::getAppVersion($app);
778
-		if ($currentVersion && isset($versions[$app])) {
779
-			$installedVersion = $versions[$app];
780
-			if (!version_compare($currentVersion, $installedVersion, '=')) {
781
-				return true;
782
-			}
783
-		}
784
-		return false;
785
-	}
786
-
787
-	/**
788
-	 * Adjust the number of version parts of $version1 to match
789
-	 * the number of version parts of $version2.
790
-	 *
791
-	 * @param string $version1 version to adjust
792
-	 * @param string $version2 version to take the number of parts from
793
-	 * @return string shortened $version1
794
-	 */
795
-	private static function adjustVersionParts(string $version1, string $version2): string {
796
-		$version1 = explode('.', $version1);
797
-		$version2 = explode('.', $version2);
798
-		// reduce $version1 to match the number of parts in $version2
799
-		while (count($version1) > count($version2)) {
800
-			array_pop($version1);
801
-		}
802
-		// if $version1 does not have enough parts, add some
803
-		while (count($version1) < count($version2)) {
804
-			$version1[] = '0';
805
-		}
806
-		return implode('.', $version1);
807
-	}
808
-
809
-	/**
810
-	 * Check whether the current ownCloud version matches the given
811
-	 * application's version requirements.
812
-	 *
813
-	 * The comparison is made based on the number of parts that the
814
-	 * app info version has. For example for ownCloud 6.0.3 if the
815
-	 * app info version is expecting version 6.0, the comparison is
816
-	 * made on the first two parts of the ownCloud version.
817
-	 * This means that it's possible to specify "requiremin" => 6
818
-	 * and "requiremax" => 6 and it will still match ownCloud 6.0.3.
819
-	 *
820
-	 * @param string $ocVersion ownCloud version to check against
821
-	 * @param array $appInfo app info (from xml)
822
-	 *
823
-	 * @return boolean true if compatible, otherwise false
824
-	 */
825
-	public static function isAppCompatible(string $ocVersion, array $appInfo): bool {
826
-		$requireMin = '';
827
-		$requireMax = '';
828
-		if (isset($appInfo['dependencies']['nextcloud']['@attributes']['min-version'])) {
829
-			$requireMin = $appInfo['dependencies']['nextcloud']['@attributes']['min-version'];
830
-		} elseif (isset($appInfo['dependencies']['owncloud']['@attributes']['min-version'])) {
831
-			$requireMin = $appInfo['dependencies']['owncloud']['@attributes']['min-version'];
832
-		} else if (isset($appInfo['requiremin'])) {
833
-			$requireMin = $appInfo['requiremin'];
834
-		} else if (isset($appInfo['require'])) {
835
-			$requireMin = $appInfo['require'];
836
-		}
837
-
838
-		if (isset($appInfo['dependencies']['nextcloud']['@attributes']['max-version'])) {
839
-			$requireMax = $appInfo['dependencies']['nextcloud']['@attributes']['max-version'];
840
-		} elseif (isset($appInfo['dependencies']['owncloud']['@attributes']['max-version'])) {
841
-			$requireMax = $appInfo['dependencies']['owncloud']['@attributes']['max-version'];
842
-		} else if (isset($appInfo['requiremax'])) {
843
-			$requireMax = $appInfo['requiremax'];
844
-		}
845
-
846
-		if (!empty($requireMin)
847
-			&& version_compare(self::adjustVersionParts($ocVersion, $requireMin), $requireMin, '<')
848
-		) {
849
-
850
-			return false;
851
-		}
852
-
853
-		if (!empty($requireMax)
854
-			&& version_compare(self::adjustVersionParts($ocVersion, $requireMax), $requireMax, '>')
855
-		) {
856
-			return false;
857
-		}
858
-
859
-		return true;
860
-	}
861
-
862
-	/**
863
-	 * get the installed version of all apps
864
-	 */
865
-	public static function getAppVersions() {
866
-		static $versions;
867
-
868
-		if(!$versions) {
869
-			$appConfig = \OC::$server->getAppConfig();
870
-			$versions = $appConfig->getValues(false, 'installed_version');
871
-		}
872
-		return $versions;
873
-	}
874
-
875
-	/**
876
-	 * update the database for the app and call the update script
877
-	 *
878
-	 * @param string $appId
879
-	 * @return bool
880
-	 */
881
-	public static function updateApp(string $appId): bool {
882
-		$appPath = self::getAppPath($appId);
883
-		if($appPath === false) {
884
-			return false;
885
-		}
886
-		self::registerAutoloading($appId, $appPath);
887
-
888
-		$appData = self::getAppInfo($appId);
889
-		self::executeRepairSteps($appId, $appData['repair-steps']['pre-migration']);
890
-
891
-		if (file_exists($appPath . '/appinfo/database.xml')) {
892
-			OC_DB::updateDbFromStructure($appPath . '/appinfo/database.xml');
893
-		} else {
894
-			$ms = new MigrationService($appId, \OC::$server->getDatabaseConnection());
895
-			$ms->migrate();
896
-		}
897
-
898
-		self::executeRepairSteps($appId, $appData['repair-steps']['post-migration']);
899
-		self::setupLiveMigrations($appId, $appData['repair-steps']['live-migration']);
900
-		// update appversion in app manager
901
-		\OC::$server->getAppManager()->getAppVersion($appId, false);
902
-
903
-		// run upgrade code
904
-		if (file_exists($appPath . '/appinfo/update.php')) {
905
-			self::loadApp($appId);
906
-			include $appPath . '/appinfo/update.php';
907
-		}
908
-		self::setupBackgroundJobs($appData['background-jobs']);
909
-
910
-		//set remote/public handlers
911
-		if (array_key_exists('ocsid', $appData)) {
912
-			\OC::$server->getConfig()->setAppValue($appId, 'ocsid', $appData['ocsid']);
913
-		} elseif(\OC::$server->getConfig()->getAppValue($appId, 'ocsid', null) !== null) {
914
-			\OC::$server->getConfig()->deleteAppValue($appId, 'ocsid');
915
-		}
916
-		foreach ($appData['remote'] as $name => $path) {
917
-			\OC::$server->getConfig()->setAppValue('core', 'remote_' . $name, $appId . '/' . $path);
918
-		}
919
-		foreach ($appData['public'] as $name => $path) {
920
-			\OC::$server->getConfig()->setAppValue('core', 'public_' . $name, $appId . '/' . $path);
921
-		}
922
-
923
-		self::setAppTypes($appId);
924
-
925
-		$version = \OC_App::getAppVersion($appId);
926
-		\OC::$server->getConfig()->setAppValue($appId, 'installed_version', $version);
927
-
928
-		\OC::$server->getEventDispatcher()->dispatch(ManagerEvent::EVENT_APP_UPDATE, new ManagerEvent(
929
-			ManagerEvent::EVENT_APP_UPDATE, $appId
930
-		));
931
-
932
-		return true;
933
-	}
934
-
935
-	/**
936
-	 * @param string $appId
937
-	 * @param string[] $steps
938
-	 * @throws \OC\NeedsUpdateException
939
-	 */
940
-	public static function executeRepairSteps(string $appId, array $steps) {
941
-		if (empty($steps)) {
942
-			return;
943
-		}
944
-		// load the app
945
-		self::loadApp($appId);
946
-
947
-		$dispatcher = OC::$server->getEventDispatcher();
948
-
949
-		// load the steps
950
-		$r = new Repair([], $dispatcher);
951
-		foreach ($steps as $step) {
952
-			try {
953
-				$r->addStep($step);
954
-			} catch (Exception $ex) {
955
-				$r->emit('\OC\Repair', 'error', [$ex->getMessage()]);
956
-				\OC::$server->getLogger()->logException($ex);
957
-			}
958
-		}
959
-		// run the steps
960
-		$r->run();
961
-	}
962
-
963
-	public static function setupBackgroundJobs(array $jobs) {
964
-		$queue = \OC::$server->getJobList();
965
-		foreach ($jobs as $job) {
966
-			$queue->add($job);
967
-		}
968
-	}
969
-
970
-	/**
971
-	 * @param string $appId
972
-	 * @param string[] $steps
973
-	 */
974
-	private static function setupLiveMigrations(string $appId, array $steps) {
975
-		$queue = \OC::$server->getJobList();
976
-		foreach ($steps as $step) {
977
-			$queue->add('OC\Migration\BackgroundRepair', [
978
-				'app' => $appId,
979
-				'step' => $step]);
980
-		}
981
-	}
982
-
983
-	/**
984
-	 * @param string $appId
985
-	 * @return \OC\Files\View|false
986
-	 */
987
-	public static function getStorage(string $appId) {
988
-		if (\OC::$server->getAppManager()->isEnabledForUser($appId)) { //sanity check
989
-			if (\OC::$server->getUserSession()->isLoggedIn()) {
990
-				$view = new \OC\Files\View('/' . OC_User::getUser());
991
-				if (!$view->file_exists($appId)) {
992
-					$view->mkdir($appId);
993
-				}
994
-				return new \OC\Files\View('/' . OC_User::getUser() . '/' . $appId);
995
-			} else {
996
-				\OCP\Util::writeLog('core', 'Can\'t get app storage, app ' . $appId . ', user not logged in', \OCP\Util::ERROR);
997
-				return false;
998
-			}
999
-		} else {
1000
-			\OCP\Util::writeLog('core', 'Can\'t get app storage, app ' . $appId . ' not enabled', \OCP\Util::ERROR);
1001
-			return false;
1002
-		}
1003
-	}
1004
-
1005
-	protected static function findBestL10NOption(array $options, string $lang): string {
1006
-		$fallback = $similarLangFallback = $englishFallback = false;
1007
-
1008
-		$lang = strtolower($lang);
1009
-		$similarLang = $lang;
1010
-		if (strpos($similarLang, '_')) {
1011
-			// For "de_DE" we want to find "de" and the other way around
1012
-			$similarLang = substr($lang, 0, strpos($lang, '_'));
1013
-		}
1014
-
1015
-		foreach ($options as $option) {
1016
-			if (is_array($option)) {
1017
-				if ($fallback === false) {
1018
-					$fallback = $option['@value'];
1019
-				}
1020
-
1021
-				if (!isset($option['@attributes']['lang'])) {
1022
-					continue;
1023
-				}
1024
-
1025
-				$attributeLang = strtolower($option['@attributes']['lang']);
1026
-				if ($attributeLang === $lang) {
1027
-					return $option['@value'];
1028
-				}
1029
-
1030
-				if ($attributeLang === $similarLang) {
1031
-					$similarLangFallback = $option['@value'];
1032
-				} else if (strpos($attributeLang, $similarLang . '_') === 0) {
1033
-					if ($similarLangFallback === false) {
1034
-						$similarLangFallback =  $option['@value'];
1035
-					}
1036
-				}
1037
-			} else {
1038
-				$englishFallback = $option;
1039
-			}
1040
-		}
1041
-
1042
-		if ($similarLangFallback !== false) {
1043
-			return $similarLangFallback;
1044
-		} else if ($englishFallback !== false) {
1045
-			return $englishFallback;
1046
-		}
1047
-		return (string) $fallback;
1048
-	}
1049
-
1050
-	/**
1051
-	 * parses the app data array and enhanced the 'description' value
1052
-	 *
1053
-	 * @param array $data the app data
1054
-	 * @param string $lang
1055
-	 * @return array improved app data
1056
-	 */
1057
-	public static function parseAppInfo(array $data, $lang = null): array {
1058
-
1059
-		if ($lang && isset($data['name']) && is_array($data['name'])) {
1060
-			$data['name'] = self::findBestL10NOption($data['name'], $lang);
1061
-		}
1062
-		if ($lang && isset($data['summary']) && is_array($data['summary'])) {
1063
-			$data['summary'] = self::findBestL10NOption($data['summary'], $lang);
1064
-		}
1065
-		if ($lang && isset($data['description']) && is_array($data['description'])) {
1066
-			$data['description'] = trim(self::findBestL10NOption($data['description'], $lang));
1067
-		} else if (isset($data['description']) && is_string($data['description'])) {
1068
-			$data['description'] = trim($data['description']);
1069
-		} else  {
1070
-			$data['description'] = '';
1071
-		}
1072
-
1073
-		return $data;
1074
-	}
1075
-
1076
-	/**
1077
-	 * @param \OCP\IConfig $config
1078
-	 * @param \OCP\IL10N $l
1079
-	 * @param array $info
1080
-	 * @throws \Exception
1081
-	 */
1082
-	public static function checkAppDependencies(\OCP\IConfig $config, \OCP\IL10N $l, array $info) {
1083
-		$dependencyAnalyzer = new DependencyAnalyzer(new Platform($config), $l);
1084
-		$missing = $dependencyAnalyzer->analyze($info);
1085
-		if (!empty($missing)) {
1086
-			$missingMsg = implode(PHP_EOL, $missing);
1087
-			throw new \Exception(
1088
-				$l->t('App "%s" cannot be installed because the following dependencies are not fulfilled: %s',
1089
-					[$info['name'], $missingMsg]
1090
-				)
1091
-			);
1092
-		}
1093
-	}
66
+    static private $adminForms = [];
67
+    static private $personalForms = [];
68
+    static private $appTypes = [];
69
+    static private $loadedApps = [];
70
+    static private $altLogin = [];
71
+    static private $alreadyRegistered = [];
72
+    const officialApp = 200;
73
+
74
+    /**
75
+     * clean the appId
76
+     *
77
+     * @param string $app AppId that needs to be cleaned
78
+     * @return string
79
+     */
80
+    public static function cleanAppId(string $app): string {
81
+        return str_replace(array('\0', '/', '\\', '..'), '', $app);
82
+    }
83
+
84
+    /**
85
+     * Check if an app is loaded
86
+     *
87
+     * @param string $app
88
+     * @return bool
89
+     */
90
+    public static function isAppLoaded(string $app): bool {
91
+        return in_array($app, self::$loadedApps, true);
92
+    }
93
+
94
+    /**
95
+     * loads all apps
96
+     *
97
+     * @param string[] $types
98
+     * @return bool
99
+     *
100
+     * This function walks through the ownCloud directory and loads all apps
101
+     * it can find. A directory contains an app if the file /appinfo/info.xml
102
+     * exists.
103
+     *
104
+     * if $types is set to non-empty array, only apps of those types will be loaded
105
+     */
106
+    public static function loadApps(array $types = []): bool {
107
+        if (\OC::$server->getSystemConfig()->getValue('maintenance', false)) {
108
+            return false;
109
+        }
110
+        // Load the enabled apps here
111
+        $apps = self::getEnabledApps();
112
+
113
+        // Add each apps' folder as allowed class path
114
+        foreach($apps as $app) {
115
+            $path = self::getAppPath($app);
116
+            if($path !== false) {
117
+                self::registerAutoloading($app, $path);
118
+            }
119
+        }
120
+
121
+        // prevent app.php from printing output
122
+        ob_start();
123
+        foreach ($apps as $app) {
124
+            if (($types === [] or self::isType($app, $types)) && !in_array($app, self::$loadedApps)) {
125
+                self::loadApp($app);
126
+            }
127
+        }
128
+        ob_end_clean();
129
+
130
+        return true;
131
+    }
132
+
133
+    /**
134
+     * load a single app
135
+     *
136
+     * @param string $app
137
+     * @throws Exception
138
+     */
139
+    public static function loadApp(string $app) {
140
+        self::$loadedApps[] = $app;
141
+        $appPath = self::getAppPath($app);
142
+        if($appPath === false) {
143
+            return;
144
+        }
145
+
146
+        // in case someone calls loadApp() directly
147
+        self::registerAutoloading($app, $appPath);
148
+
149
+        if (is_file($appPath . '/appinfo/app.php')) {
150
+            \OC::$server->getEventLogger()->start('load_app_' . $app, 'Load app: ' . $app);
151
+            try {
152
+                self::requireAppFile($app);
153
+            } catch (Error $ex) {
154
+                \OC::$server->getLogger()->logException($ex);
155
+                if (!\OC::$server->getAppManager()->isShipped($app)) {
156
+                    // Only disable apps which are not shipped
157
+                    \OC::$server->getAppManager()->disableApp($app);
158
+                }
159
+            }
160
+            \OC::$server->getEventLogger()->end('load_app_' . $app);
161
+        }
162
+
163
+        $info = self::getAppInfo($app);
164
+        if (!empty($info['activity']['filters'])) {
165
+            foreach ($info['activity']['filters'] as $filter) {
166
+                \OC::$server->getActivityManager()->registerFilter($filter);
167
+            }
168
+        }
169
+        if (!empty($info['activity']['settings'])) {
170
+            foreach ($info['activity']['settings'] as $setting) {
171
+                \OC::$server->getActivityManager()->registerSetting($setting);
172
+            }
173
+        }
174
+        if (!empty($info['activity']['providers'])) {
175
+            foreach ($info['activity']['providers'] as $provider) {
176
+                \OC::$server->getActivityManager()->registerProvider($provider);
177
+            }
178
+        }
179
+
180
+        if (!empty($info['settings']['admin'])) {
181
+            foreach ($info['settings']['admin'] as $setting) {
182
+                \OC::$server->getSettingsManager()->registerSetting('admin', $setting);
183
+            }
184
+        }
185
+        if (!empty($info['settings']['admin-section'])) {
186
+            foreach ($info['settings']['admin-section'] as $section) {
187
+                \OC::$server->getSettingsManager()->registerSection('admin', $section);
188
+            }
189
+        }
190
+        if (!empty($info['settings']['personal'])) {
191
+            foreach ($info['settings']['personal'] as $setting) {
192
+                \OC::$server->getSettingsManager()->registerSetting('personal', $setting);
193
+            }
194
+        }
195
+        if (!empty($info['settings']['personal-section'])) {
196
+            foreach ($info['settings']['personal-section'] as $section) {
197
+                \OC::$server->getSettingsManager()->registerSection('personal', $section);
198
+            }
199
+        }
200
+
201
+        if (!empty($info['collaboration']['plugins'])) {
202
+            // deal with one or many plugin entries
203
+            $plugins = isset($info['collaboration']['plugins']['plugin']['@value']) ?
204
+                [$info['collaboration']['plugins']['plugin']] : $info['collaboration']['plugins']['plugin'];
205
+            foreach ($plugins as $plugin) {
206
+                if($plugin['@attributes']['type'] === 'collaborator-search') {
207
+                    $pluginInfo = [
208
+                        'shareType' => $plugin['@attributes']['share-type'],
209
+                        'class' => $plugin['@value'],
210
+                    ];
211
+                    \OC::$server->getCollaboratorSearch()->registerPlugin($pluginInfo);
212
+                } else if ($plugin['@attributes']['type'] === 'autocomplete-sort') {
213
+                    \OC::$server->getAutoCompleteManager()->registerSorter($plugin['@value']);
214
+                }
215
+            }
216
+        }
217
+    }
218
+
219
+    /**
220
+     * @internal
221
+     * @param string $app
222
+     * @param string $path
223
+     */
224
+    public static function registerAutoloading(string $app, string $path) {
225
+        $key = $app . '-' . $path;
226
+        if(isset(self::$alreadyRegistered[$key])) {
227
+            return;
228
+        }
229
+
230
+        self::$alreadyRegistered[$key] = true;
231
+
232
+        // Register on PSR-4 composer autoloader
233
+        $appNamespace = \OC\AppFramework\App::buildAppNamespace($app);
234
+        \OC::$server->registerNamespace($app, $appNamespace);
235
+
236
+        if (file_exists($path . '/composer/autoload.php')) {
237
+            require_once $path . '/composer/autoload.php';
238
+        } else {
239
+            \OC::$composerAutoloader->addPsr4($appNamespace . '\\', $path . '/lib/', true);
240
+            // Register on legacy autoloader
241
+            \OC::$loader->addValidRoot($path);
242
+        }
243
+
244
+        // Register Test namespace only when testing
245
+        if (defined('PHPUNIT_RUN') || defined('CLI_TEST_RUN')) {
246
+            \OC::$composerAutoloader->addPsr4($appNamespace . '\\Tests\\', $path . '/tests/', true);
247
+        }
248
+    }
249
+
250
+    /**
251
+     * Load app.php from the given app
252
+     *
253
+     * @param string $app app name
254
+     * @throws Error
255
+     */
256
+    private static function requireAppFile(string $app) {
257
+        // encapsulated here to avoid variable scope conflicts
258
+        require_once $app . '/appinfo/app.php';
259
+    }
260
+
261
+    /**
262
+     * check if an app is of a specific type
263
+     *
264
+     * @param string $app
265
+     * @param array $types
266
+     * @return bool
267
+     */
268
+    public static function isType(string $app, array $types): bool {
269
+        $appTypes = self::getAppTypes($app);
270
+        foreach ($types as $type) {
271
+            if (array_search($type, $appTypes) !== false) {
272
+                return true;
273
+            }
274
+        }
275
+        return false;
276
+    }
277
+
278
+    /**
279
+     * get the types of an app
280
+     *
281
+     * @param string $app
282
+     * @return array
283
+     */
284
+    private static function getAppTypes(string $app): array {
285
+        //load the cache
286
+        if (count(self::$appTypes) == 0) {
287
+            self::$appTypes = \OC::$server->getAppConfig()->getValues(false, 'types');
288
+        }
289
+
290
+        if (isset(self::$appTypes[$app])) {
291
+            return explode(',', self::$appTypes[$app]);
292
+        }
293
+
294
+        return [];
295
+    }
296
+
297
+    /**
298
+     * read app types from info.xml and cache them in the database
299
+     */
300
+    public static function setAppTypes(string $app) {
301
+        $appManager = \OC::$server->getAppManager();
302
+        $appData = $appManager->getAppInfo($app);
303
+        if(!is_array($appData)) {
304
+            return;
305
+        }
306
+
307
+        if (isset($appData['types'])) {
308
+            $appTypes = implode(',', $appData['types']);
309
+        } else {
310
+            $appTypes = '';
311
+            $appData['types'] = [];
312
+        }
313
+
314
+        $config = \OC::$server->getConfig();
315
+        $config->setAppValue($app, 'types', $appTypes);
316
+
317
+        if ($appManager->hasProtectedAppType($appData['types'])) {
318
+            $enabled = $config->getAppValue($app, 'enabled', 'yes');
319
+            if ($enabled !== 'yes' && $enabled !== 'no') {
320
+                $config->setAppValue($app, 'enabled', 'yes');
321
+            }
322
+        }
323
+    }
324
+
325
+    /**
326
+     * Returns apps enabled for the current user.
327
+     *
328
+     * @param bool $forceRefresh whether to refresh the cache
329
+     * @param bool $all whether to return apps for all users, not only the
330
+     * currently logged in one
331
+     * @return string[]
332
+     */
333
+    public static function getEnabledApps(bool $forceRefresh = false, bool $all = false): array {
334
+        if (!\OC::$server->getSystemConfig()->getValue('installed', false)) {
335
+            return [];
336
+        }
337
+        // in incognito mode or when logged out, $user will be false,
338
+        // which is also the case during an upgrade
339
+        $appManager = \OC::$server->getAppManager();
340
+        if ($all) {
341
+            $user = null;
342
+        } else {
343
+            $user = \OC::$server->getUserSession()->getUser();
344
+        }
345
+
346
+        if (is_null($user)) {
347
+            $apps = $appManager->getInstalledApps();
348
+        } else {
349
+            $apps = $appManager->getEnabledAppsForUser($user);
350
+        }
351
+        $apps = array_filter($apps, function ($app) {
352
+            return $app !== 'files';//we add this manually
353
+        });
354
+        sort($apps);
355
+        array_unshift($apps, 'files');
356
+        return $apps;
357
+    }
358
+
359
+    /**
360
+     * checks whether or not an app is enabled
361
+     *
362
+     * @param string $app app
363
+     * @return bool
364
+     * @deprecated 13.0.0 use \OC::$server->getAppManager()->isEnabledForUser($appId)
365
+     *
366
+     * This function checks whether or not an app is enabled.
367
+     */
368
+    public static function isEnabled(string $app): bool {
369
+        return \OC::$server->getAppManager()->isEnabledForUser($app);
370
+    }
371
+
372
+    /**
373
+     * enables an app
374
+     *
375
+     * @param string $appId
376
+     * @param array $groups (optional) when set, only these groups will have access to the app
377
+     * @throws \Exception
378
+     * @return void
379
+     *
380
+     * This function set an app as enabled in appconfig.
381
+     */
382
+    public function enable(string $appId,
383
+                            array $groups = []) {
384
+
385
+        // Check if app is already downloaded
386
+        $installer = \OC::$server->query(Installer::class);
387
+        $isDownloaded = $installer->isDownloaded($appId);
388
+
389
+        if(!$isDownloaded) {
390
+            $installer->downloadApp($appId);
391
+        }
392
+
393
+        $installer->installApp($appId);
394
+
395
+        $appManager = \OC::$server->getAppManager();
396
+        if ($groups !== []) {
397
+            $groupManager = \OC::$server->getGroupManager();
398
+            $groupsList = [];
399
+            foreach ($groups as $group) {
400
+                $groupItem = $groupManager->get($group);
401
+                if ($groupItem instanceof \OCP\IGroup) {
402
+                    $groupsList[] = $groupManager->get($group);
403
+                }
404
+            }
405
+            $appManager->enableAppForGroups($appId, $groupsList);
406
+        } else {
407
+            $appManager->enableApp($appId);
408
+        }
409
+    }
410
+
411
+    /**
412
+     * Get the path where to install apps
413
+     *
414
+     * @return string|false
415
+     */
416
+    public static function getInstallPath() {
417
+        if (\OC::$server->getSystemConfig()->getValue('appstoreenabled', true) == false) {
418
+            return false;
419
+        }
420
+
421
+        foreach (OC::$APPSROOTS as $dir) {
422
+            if (isset($dir['writable']) && $dir['writable'] === true) {
423
+                return $dir['path'];
424
+            }
425
+        }
426
+
427
+        \OCP\Util::writeLog('core', 'No application directories are marked as writable.', \OCP\Util::ERROR);
428
+        return null;
429
+    }
430
+
431
+
432
+    /**
433
+     * search for an app in all app-directories
434
+     *
435
+     * @param string $appId
436
+     * @return false|string
437
+     */
438
+    public static function findAppInDirectories(string $appId) {
439
+        $sanitizedAppId = self::cleanAppId($appId);
440
+        if($sanitizedAppId !== $appId) {
441
+            return false;
442
+        }
443
+        static $app_dir = [];
444
+
445
+        if (isset($app_dir[$appId])) {
446
+            return $app_dir[$appId];
447
+        }
448
+
449
+        $possibleApps = [];
450
+        foreach (OC::$APPSROOTS as $dir) {
451
+            if (file_exists($dir['path'] . '/' . $appId)) {
452
+                $possibleApps[] = $dir;
453
+            }
454
+        }
455
+
456
+        if (empty($possibleApps)) {
457
+            return false;
458
+        } elseif (count($possibleApps) === 1) {
459
+            $dir = array_shift($possibleApps);
460
+            $app_dir[$appId] = $dir;
461
+            return $dir;
462
+        } else {
463
+            $versionToLoad = [];
464
+            foreach ($possibleApps as $possibleApp) {
465
+                $version = self::getAppVersionByPath($possibleApp['path']);
466
+                if (empty($versionToLoad) || version_compare($version, $versionToLoad['version'], '>')) {
467
+                    $versionToLoad = array(
468
+                        'dir' => $possibleApp,
469
+                        'version' => $version,
470
+                    );
471
+                }
472
+            }
473
+            $app_dir[$appId] = $versionToLoad['dir'];
474
+            return $versionToLoad['dir'];
475
+            //TODO - write test
476
+        }
477
+    }
478
+
479
+    /**
480
+     * Get the directory for the given app.
481
+     * If the app is defined in multiple directories, the first one is taken. (false if not found)
482
+     *
483
+     * @param string $appId
484
+     * @return string|false
485
+     */
486
+    public static function getAppPath(string $appId) {
487
+        if ($appId === null || trim($appId) === '') {
488
+            return false;
489
+        }
490
+
491
+        if (($dir = self::findAppInDirectories($appId)) != false) {
492
+            return $dir['path'] . '/' . $appId;
493
+        }
494
+        return false;
495
+    }
496
+
497
+    /**
498
+     * Get the path for the given app on the access
499
+     * If the app is defined in multiple directories, the first one is taken. (false if not found)
500
+     *
501
+     * @param string $appId
502
+     * @return string|false
503
+     */
504
+    public static function getAppWebPath(string $appId) {
505
+        if (($dir = self::findAppInDirectories($appId)) != false) {
506
+            return OC::$WEBROOT . $dir['url'] . '/' . $appId;
507
+        }
508
+        return false;
509
+    }
510
+
511
+    /**
512
+     * get the last version of the app from appinfo/info.xml
513
+     *
514
+     * @param string $appId
515
+     * @param bool $useCache
516
+     * @return string
517
+     * @deprecated 14.0.0 use \OC::$server->getAppManager()->getAppVersion()
518
+     */
519
+    public static function getAppVersion(string $appId, bool $useCache = true): string {
520
+        return \OC::$server->getAppManager()->getAppVersion($appId, $useCache);
521
+    }
522
+
523
+    /**
524
+     * get app's version based on it's path
525
+     *
526
+     * @param string $path
527
+     * @return string
528
+     */
529
+    public static function getAppVersionByPath(string $path): string {
530
+        $infoFile = $path . '/appinfo/info.xml';
531
+        $appData = \OC::$server->getAppManager()->getAppInfo($infoFile, true);
532
+        return isset($appData['version']) ? $appData['version'] : '';
533
+    }
534
+
535
+
536
+    /**
537
+     * Read all app metadata from the info.xml file
538
+     *
539
+     * @param string $appId id of the app or the path of the info.xml file
540
+     * @param bool $path
541
+     * @param string $lang
542
+     * @return array|null
543
+     * @note all data is read from info.xml, not just pre-defined fields
544
+     * @deprecated 14.0.0 use \OC::$server->getAppManager()->getAppInfo()
545
+     */
546
+    public static function getAppInfo(string $appId, bool $path = false, string $lang = null) {
547
+        return \OC::$server->getAppManager()->getAppInfo($appId, $path, $lang);
548
+    }
549
+
550
+    /**
551
+     * Returns the navigation
552
+     *
553
+     * @return array
554
+     * @deprecated 14.0.0 use \OC::$server->getNavigationManager()->getAll()
555
+     *
556
+     * This function returns an array containing all entries added. The
557
+     * entries are sorted by the key 'order' ascending. Additional to the keys
558
+     * given for each app the following keys exist:
559
+     *   - active: boolean, signals if the user is on this navigation entry
560
+     */
561
+    public static function getNavigation(): array {
562
+        return OC::$server->getNavigationManager()->getAll();
563
+    }
564
+
565
+    /**
566
+     * Returns the Settings Navigation
567
+     *
568
+     * @return string[]
569
+     * @deprecated 14.0.0 use \OC::$server->getNavigationManager()->getAll('settings')
570
+     *
571
+     * This function returns an array containing all settings pages added. The
572
+     * entries are sorted by the key 'order' ascending.
573
+     */
574
+    public static function getSettingsNavigation(): array {
575
+        return OC::$server->getNavigationManager()->getAll('settings');
576
+    }
577
+
578
+    /**
579
+     * get the id of loaded app
580
+     *
581
+     * @return string
582
+     */
583
+    public static function getCurrentApp(): string {
584
+        $request = \OC::$server->getRequest();
585
+        $script = substr($request->getScriptName(), strlen(OC::$WEBROOT) + 1);
586
+        $topFolder = substr($script, 0, strpos($script, '/') ?: 0);
587
+        if (empty($topFolder)) {
588
+            $path_info = $request->getPathInfo();
589
+            if ($path_info) {
590
+                $topFolder = substr($path_info, 1, strpos($path_info, '/', 1) - 1);
591
+            }
592
+        }
593
+        if ($topFolder == 'apps') {
594
+            $length = strlen($topFolder);
595
+            return substr($script, $length + 1, strpos($script, '/', $length + 1) - $length - 1) ?: '';
596
+        } else {
597
+            return $topFolder;
598
+        }
599
+    }
600
+
601
+    /**
602
+     * @param string $type
603
+     * @return array
604
+     */
605
+    public static function getForms(string $type): array {
606
+        $forms = [];
607
+        switch ($type) {
608
+            case 'admin':
609
+                $source = self::$adminForms;
610
+                break;
611
+            case 'personal':
612
+                $source = self::$personalForms;
613
+                break;
614
+            default:
615
+                return [];
616
+        }
617
+        foreach ($source as $form) {
618
+            $forms[] = include $form;
619
+        }
620
+        return $forms;
621
+    }
622
+
623
+    /**
624
+     * register an admin form to be shown
625
+     *
626
+     * @param string $app
627
+     * @param string $page
628
+     */
629
+    public static function registerAdmin(string $app, string $page) {
630
+        self::$adminForms[] = $app . '/' . $page . '.php';
631
+    }
632
+
633
+    /**
634
+     * register a personal form to be shown
635
+     * @param string $app
636
+     * @param string $page
637
+     */
638
+    public static function registerPersonal(string $app, string $page) {
639
+        self::$personalForms[] = $app . '/' . $page . '.php';
640
+    }
641
+
642
+    /**
643
+     * @param array $entry
644
+     */
645
+    public static function registerLogIn(array $entry) {
646
+        self::$altLogin[] = $entry;
647
+    }
648
+
649
+    /**
650
+     * @return array
651
+     */
652
+    public static function getAlternativeLogIns(): array {
653
+        return self::$altLogin;
654
+    }
655
+
656
+    /**
657
+     * get a list of all apps in the apps folder
658
+     *
659
+     * @return array an array of app names (string IDs)
660
+     * @todo: change the name of this method to getInstalledApps, which is more accurate
661
+     */
662
+    public static function getAllApps(): array {
663
+
664
+        $apps = [];
665
+
666
+        foreach (OC::$APPSROOTS as $apps_dir) {
667
+            if (!is_readable($apps_dir['path'])) {
668
+                \OCP\Util::writeLog('core', 'unable to read app folder : ' . $apps_dir['path'], \OCP\Util::WARN);
669
+                continue;
670
+            }
671
+            $dh = opendir($apps_dir['path']);
672
+
673
+            if (is_resource($dh)) {
674
+                while (($file = readdir($dh)) !== false) {
675
+
676
+                    if ($file[0] != '.' and is_dir($apps_dir['path'] . '/' . $file) and is_file($apps_dir['path'] . '/' . $file . '/appinfo/info.xml')) {
677
+
678
+                        $apps[] = $file;
679
+                    }
680
+                }
681
+            }
682
+        }
683
+
684
+        $apps = array_unique($apps);
685
+
686
+        return $apps;
687
+    }
688
+
689
+    /**
690
+     * List all apps, this is used in apps.php
691
+     *
692
+     * @return array
693
+     */
694
+    public function listAllApps(): array {
695
+        $installedApps = OC_App::getAllApps();
696
+
697
+        $appManager = \OC::$server->getAppManager();
698
+        //we don't want to show configuration for these
699
+        $blacklist = $appManager->getAlwaysEnabledApps();
700
+        $appList = [];
701
+        $langCode = \OC::$server->getL10N('core')->getLanguageCode();
702
+        $urlGenerator = \OC::$server->getURLGenerator();
703
+
704
+        foreach ($installedApps as $app) {
705
+            if (array_search($app, $blacklist) === false) {
706
+
707
+                $info = OC_App::getAppInfo($app, false, $langCode);
708
+                if (!is_array($info)) {
709
+                    \OCP\Util::writeLog('core', 'Could not read app info file for app "' . $app . '"', \OCP\Util::ERROR);
710
+                    continue;
711
+                }
712
+
713
+                if (!isset($info['name'])) {
714
+                    \OCP\Util::writeLog('core', 'App id "' . $app . '" has no name in appinfo', \OCP\Util::ERROR);
715
+                    continue;
716
+                }
717
+
718
+                $enabled = \OC::$server->getConfig()->getAppValue($app, 'enabled', 'no');
719
+                $info['groups'] = null;
720
+                if ($enabled === 'yes') {
721
+                    $active = true;
722
+                } else if ($enabled === 'no') {
723
+                    $active = false;
724
+                } else {
725
+                    $active = true;
726
+                    $info['groups'] = $enabled;
727
+                }
728
+
729
+                $info['active'] = $active;
730
+
731
+                if ($appManager->isShipped($app)) {
732
+                    $info['internal'] = true;
733
+                    $info['level'] = self::officialApp;
734
+                    $info['removable'] = false;
735
+                } else {
736
+                    $info['internal'] = false;
737
+                    $info['removable'] = true;
738
+                }
739
+
740
+                $appPath = self::getAppPath($app);
741
+                if($appPath !== false) {
742
+                    $appIcon = $appPath . '/img/' . $app . '.svg';
743
+                    if (file_exists($appIcon)) {
744
+                        $info['preview'] = $urlGenerator->imagePath($app, $app . '.svg');
745
+                        $info['previewAsIcon'] = true;
746
+                    } else {
747
+                        $appIcon = $appPath . '/img/app.svg';
748
+                        if (file_exists($appIcon)) {
749
+                            $info['preview'] = $urlGenerator->imagePath($app, 'app.svg');
750
+                            $info['previewAsIcon'] = true;
751
+                        }
752
+                    }
753
+                }
754
+                // fix documentation
755
+                if (isset($info['documentation']) && is_array($info['documentation'])) {
756
+                    foreach ($info['documentation'] as $key => $url) {
757
+                        // If it is not an absolute URL we assume it is a key
758
+                        // i.e. admin-ldap will get converted to go.php?to=admin-ldap
759
+                        if (stripos($url, 'https://') !== 0 && stripos($url, 'http://') !== 0) {
760
+                            $url = $urlGenerator->linkToDocs($url);
761
+                        }
762
+
763
+                        $info['documentation'][$key] = $url;
764
+                    }
765
+                }
766
+
767
+                $info['version'] = OC_App::getAppVersion($app);
768
+                $appList[] = $info;
769
+            }
770
+        }
771
+
772
+        return $appList;
773
+    }
774
+
775
+    public static function shouldUpgrade(string $app): bool {
776
+        $versions = self::getAppVersions();
777
+        $currentVersion = OC_App::getAppVersion($app);
778
+        if ($currentVersion && isset($versions[$app])) {
779
+            $installedVersion = $versions[$app];
780
+            if (!version_compare($currentVersion, $installedVersion, '=')) {
781
+                return true;
782
+            }
783
+        }
784
+        return false;
785
+    }
786
+
787
+    /**
788
+     * Adjust the number of version parts of $version1 to match
789
+     * the number of version parts of $version2.
790
+     *
791
+     * @param string $version1 version to adjust
792
+     * @param string $version2 version to take the number of parts from
793
+     * @return string shortened $version1
794
+     */
795
+    private static function adjustVersionParts(string $version1, string $version2): string {
796
+        $version1 = explode('.', $version1);
797
+        $version2 = explode('.', $version2);
798
+        // reduce $version1 to match the number of parts in $version2
799
+        while (count($version1) > count($version2)) {
800
+            array_pop($version1);
801
+        }
802
+        // if $version1 does not have enough parts, add some
803
+        while (count($version1) < count($version2)) {
804
+            $version1[] = '0';
805
+        }
806
+        return implode('.', $version1);
807
+    }
808
+
809
+    /**
810
+     * Check whether the current ownCloud version matches the given
811
+     * application's version requirements.
812
+     *
813
+     * The comparison is made based on the number of parts that the
814
+     * app info version has. For example for ownCloud 6.0.3 if the
815
+     * app info version is expecting version 6.0, the comparison is
816
+     * made on the first two parts of the ownCloud version.
817
+     * This means that it's possible to specify "requiremin" => 6
818
+     * and "requiremax" => 6 and it will still match ownCloud 6.0.3.
819
+     *
820
+     * @param string $ocVersion ownCloud version to check against
821
+     * @param array $appInfo app info (from xml)
822
+     *
823
+     * @return boolean true if compatible, otherwise false
824
+     */
825
+    public static function isAppCompatible(string $ocVersion, array $appInfo): bool {
826
+        $requireMin = '';
827
+        $requireMax = '';
828
+        if (isset($appInfo['dependencies']['nextcloud']['@attributes']['min-version'])) {
829
+            $requireMin = $appInfo['dependencies']['nextcloud']['@attributes']['min-version'];
830
+        } elseif (isset($appInfo['dependencies']['owncloud']['@attributes']['min-version'])) {
831
+            $requireMin = $appInfo['dependencies']['owncloud']['@attributes']['min-version'];
832
+        } else if (isset($appInfo['requiremin'])) {
833
+            $requireMin = $appInfo['requiremin'];
834
+        } else if (isset($appInfo['require'])) {
835
+            $requireMin = $appInfo['require'];
836
+        }
837
+
838
+        if (isset($appInfo['dependencies']['nextcloud']['@attributes']['max-version'])) {
839
+            $requireMax = $appInfo['dependencies']['nextcloud']['@attributes']['max-version'];
840
+        } elseif (isset($appInfo['dependencies']['owncloud']['@attributes']['max-version'])) {
841
+            $requireMax = $appInfo['dependencies']['owncloud']['@attributes']['max-version'];
842
+        } else if (isset($appInfo['requiremax'])) {
843
+            $requireMax = $appInfo['requiremax'];
844
+        }
845
+
846
+        if (!empty($requireMin)
847
+            && version_compare(self::adjustVersionParts($ocVersion, $requireMin), $requireMin, '<')
848
+        ) {
849
+
850
+            return false;
851
+        }
852
+
853
+        if (!empty($requireMax)
854
+            && version_compare(self::adjustVersionParts($ocVersion, $requireMax), $requireMax, '>')
855
+        ) {
856
+            return false;
857
+        }
858
+
859
+        return true;
860
+    }
861
+
862
+    /**
863
+     * get the installed version of all apps
864
+     */
865
+    public static function getAppVersions() {
866
+        static $versions;
867
+
868
+        if(!$versions) {
869
+            $appConfig = \OC::$server->getAppConfig();
870
+            $versions = $appConfig->getValues(false, 'installed_version');
871
+        }
872
+        return $versions;
873
+    }
874
+
875
+    /**
876
+     * update the database for the app and call the update script
877
+     *
878
+     * @param string $appId
879
+     * @return bool
880
+     */
881
+    public static function updateApp(string $appId): bool {
882
+        $appPath = self::getAppPath($appId);
883
+        if($appPath === false) {
884
+            return false;
885
+        }
886
+        self::registerAutoloading($appId, $appPath);
887
+
888
+        $appData = self::getAppInfo($appId);
889
+        self::executeRepairSteps($appId, $appData['repair-steps']['pre-migration']);
890
+
891
+        if (file_exists($appPath . '/appinfo/database.xml')) {
892
+            OC_DB::updateDbFromStructure($appPath . '/appinfo/database.xml');
893
+        } else {
894
+            $ms = new MigrationService($appId, \OC::$server->getDatabaseConnection());
895
+            $ms->migrate();
896
+        }
897
+
898
+        self::executeRepairSteps($appId, $appData['repair-steps']['post-migration']);
899
+        self::setupLiveMigrations($appId, $appData['repair-steps']['live-migration']);
900
+        // update appversion in app manager
901
+        \OC::$server->getAppManager()->getAppVersion($appId, false);
902
+
903
+        // run upgrade code
904
+        if (file_exists($appPath . '/appinfo/update.php')) {
905
+            self::loadApp($appId);
906
+            include $appPath . '/appinfo/update.php';
907
+        }
908
+        self::setupBackgroundJobs($appData['background-jobs']);
909
+
910
+        //set remote/public handlers
911
+        if (array_key_exists('ocsid', $appData)) {
912
+            \OC::$server->getConfig()->setAppValue($appId, 'ocsid', $appData['ocsid']);
913
+        } elseif(\OC::$server->getConfig()->getAppValue($appId, 'ocsid', null) !== null) {
914
+            \OC::$server->getConfig()->deleteAppValue($appId, 'ocsid');
915
+        }
916
+        foreach ($appData['remote'] as $name => $path) {
917
+            \OC::$server->getConfig()->setAppValue('core', 'remote_' . $name, $appId . '/' . $path);
918
+        }
919
+        foreach ($appData['public'] as $name => $path) {
920
+            \OC::$server->getConfig()->setAppValue('core', 'public_' . $name, $appId . '/' . $path);
921
+        }
922
+
923
+        self::setAppTypes($appId);
924
+
925
+        $version = \OC_App::getAppVersion($appId);
926
+        \OC::$server->getConfig()->setAppValue($appId, 'installed_version', $version);
927
+
928
+        \OC::$server->getEventDispatcher()->dispatch(ManagerEvent::EVENT_APP_UPDATE, new ManagerEvent(
929
+            ManagerEvent::EVENT_APP_UPDATE, $appId
930
+        ));
931
+
932
+        return true;
933
+    }
934
+
935
+    /**
936
+     * @param string $appId
937
+     * @param string[] $steps
938
+     * @throws \OC\NeedsUpdateException
939
+     */
940
+    public static function executeRepairSteps(string $appId, array $steps) {
941
+        if (empty($steps)) {
942
+            return;
943
+        }
944
+        // load the app
945
+        self::loadApp($appId);
946
+
947
+        $dispatcher = OC::$server->getEventDispatcher();
948
+
949
+        // load the steps
950
+        $r = new Repair([], $dispatcher);
951
+        foreach ($steps as $step) {
952
+            try {
953
+                $r->addStep($step);
954
+            } catch (Exception $ex) {
955
+                $r->emit('\OC\Repair', 'error', [$ex->getMessage()]);
956
+                \OC::$server->getLogger()->logException($ex);
957
+            }
958
+        }
959
+        // run the steps
960
+        $r->run();
961
+    }
962
+
963
+    public static function setupBackgroundJobs(array $jobs) {
964
+        $queue = \OC::$server->getJobList();
965
+        foreach ($jobs as $job) {
966
+            $queue->add($job);
967
+        }
968
+    }
969
+
970
+    /**
971
+     * @param string $appId
972
+     * @param string[] $steps
973
+     */
974
+    private static function setupLiveMigrations(string $appId, array $steps) {
975
+        $queue = \OC::$server->getJobList();
976
+        foreach ($steps as $step) {
977
+            $queue->add('OC\Migration\BackgroundRepair', [
978
+                'app' => $appId,
979
+                'step' => $step]);
980
+        }
981
+    }
982
+
983
+    /**
984
+     * @param string $appId
985
+     * @return \OC\Files\View|false
986
+     */
987
+    public static function getStorage(string $appId) {
988
+        if (\OC::$server->getAppManager()->isEnabledForUser($appId)) { //sanity check
989
+            if (\OC::$server->getUserSession()->isLoggedIn()) {
990
+                $view = new \OC\Files\View('/' . OC_User::getUser());
991
+                if (!$view->file_exists($appId)) {
992
+                    $view->mkdir($appId);
993
+                }
994
+                return new \OC\Files\View('/' . OC_User::getUser() . '/' . $appId);
995
+            } else {
996
+                \OCP\Util::writeLog('core', 'Can\'t get app storage, app ' . $appId . ', user not logged in', \OCP\Util::ERROR);
997
+                return false;
998
+            }
999
+        } else {
1000
+            \OCP\Util::writeLog('core', 'Can\'t get app storage, app ' . $appId . ' not enabled', \OCP\Util::ERROR);
1001
+            return false;
1002
+        }
1003
+    }
1004
+
1005
+    protected static function findBestL10NOption(array $options, string $lang): string {
1006
+        $fallback = $similarLangFallback = $englishFallback = false;
1007
+
1008
+        $lang = strtolower($lang);
1009
+        $similarLang = $lang;
1010
+        if (strpos($similarLang, '_')) {
1011
+            // For "de_DE" we want to find "de" and the other way around
1012
+            $similarLang = substr($lang, 0, strpos($lang, '_'));
1013
+        }
1014
+
1015
+        foreach ($options as $option) {
1016
+            if (is_array($option)) {
1017
+                if ($fallback === false) {
1018
+                    $fallback = $option['@value'];
1019
+                }
1020
+
1021
+                if (!isset($option['@attributes']['lang'])) {
1022
+                    continue;
1023
+                }
1024
+
1025
+                $attributeLang = strtolower($option['@attributes']['lang']);
1026
+                if ($attributeLang === $lang) {
1027
+                    return $option['@value'];
1028
+                }
1029
+
1030
+                if ($attributeLang === $similarLang) {
1031
+                    $similarLangFallback = $option['@value'];
1032
+                } else if (strpos($attributeLang, $similarLang . '_') === 0) {
1033
+                    if ($similarLangFallback === false) {
1034
+                        $similarLangFallback =  $option['@value'];
1035
+                    }
1036
+                }
1037
+            } else {
1038
+                $englishFallback = $option;
1039
+            }
1040
+        }
1041
+
1042
+        if ($similarLangFallback !== false) {
1043
+            return $similarLangFallback;
1044
+        } else if ($englishFallback !== false) {
1045
+            return $englishFallback;
1046
+        }
1047
+        return (string) $fallback;
1048
+    }
1049
+
1050
+    /**
1051
+     * parses the app data array and enhanced the 'description' value
1052
+     *
1053
+     * @param array $data the app data
1054
+     * @param string $lang
1055
+     * @return array improved app data
1056
+     */
1057
+    public static function parseAppInfo(array $data, $lang = null): array {
1058
+
1059
+        if ($lang && isset($data['name']) && is_array($data['name'])) {
1060
+            $data['name'] = self::findBestL10NOption($data['name'], $lang);
1061
+        }
1062
+        if ($lang && isset($data['summary']) && is_array($data['summary'])) {
1063
+            $data['summary'] = self::findBestL10NOption($data['summary'], $lang);
1064
+        }
1065
+        if ($lang && isset($data['description']) && is_array($data['description'])) {
1066
+            $data['description'] = trim(self::findBestL10NOption($data['description'], $lang));
1067
+        } else if (isset($data['description']) && is_string($data['description'])) {
1068
+            $data['description'] = trim($data['description']);
1069
+        } else  {
1070
+            $data['description'] = '';
1071
+        }
1072
+
1073
+        return $data;
1074
+    }
1075
+
1076
+    /**
1077
+     * @param \OCP\IConfig $config
1078
+     * @param \OCP\IL10N $l
1079
+     * @param array $info
1080
+     * @throws \Exception
1081
+     */
1082
+    public static function checkAppDependencies(\OCP\IConfig $config, \OCP\IL10N $l, array $info) {
1083
+        $dependencyAnalyzer = new DependencyAnalyzer(new Platform($config), $l);
1084
+        $missing = $dependencyAnalyzer->analyze($info);
1085
+        if (!empty($missing)) {
1086
+            $missingMsg = implode(PHP_EOL, $missing);
1087
+            throw new \Exception(
1088
+                $l->t('App "%s" cannot be installed because the following dependencies are not fulfilled: %s',
1089
+                    [$info['name'], $missingMsg]
1090
+                )
1091
+            );
1092
+        }
1093
+    }
1094 1094
 }
Please login to merge, or discard this patch.
Spacing   +50 added lines, -50 removed lines patch added patch discarded remove patch
@@ -1,5 +1,5 @@  discard block
 block discarded – undo
1 1
 <?php
2
-declare(strict_types=1);
2
+declare(strict_types = 1);
3 3
 /**
4 4
  * @copyright Copyright (c) 2016, ownCloud, Inc.
5 5
  * @copyright Copyright (c) 2016, Lukas Reschke <[email protected]>
@@ -111,9 +111,9 @@  discard block
 block discarded – undo
111 111
 		$apps = self::getEnabledApps();
112 112
 
113 113
 		// Add each apps' folder as allowed class path
114
-		foreach($apps as $app) {
114
+		foreach ($apps as $app) {
115 115
 			$path = self::getAppPath($app);
116
-			if($path !== false) {
116
+			if ($path !== false) {
117 117
 				self::registerAutoloading($app, $path);
118 118
 			}
119 119
 		}
@@ -139,15 +139,15 @@  discard block
 block discarded – undo
139 139
 	public static function loadApp(string $app) {
140 140
 		self::$loadedApps[] = $app;
141 141
 		$appPath = self::getAppPath($app);
142
-		if($appPath === false) {
142
+		if ($appPath === false) {
143 143
 			return;
144 144
 		}
145 145
 
146 146
 		// in case someone calls loadApp() directly
147 147
 		self::registerAutoloading($app, $appPath);
148 148
 
149
-		if (is_file($appPath . '/appinfo/app.php')) {
150
-			\OC::$server->getEventLogger()->start('load_app_' . $app, 'Load app: ' . $app);
149
+		if (is_file($appPath.'/appinfo/app.php')) {
150
+			\OC::$server->getEventLogger()->start('load_app_'.$app, 'Load app: '.$app);
151 151
 			try {
152 152
 				self::requireAppFile($app);
153 153
 			} catch (Error $ex) {
@@ -157,7 +157,7 @@  discard block
 block discarded – undo
157 157
 					\OC::$server->getAppManager()->disableApp($app);
158 158
 				}
159 159
 			}
160
-			\OC::$server->getEventLogger()->end('load_app_' . $app);
160
+			\OC::$server->getEventLogger()->end('load_app_'.$app);
161 161
 		}
162 162
 
163 163
 		$info = self::getAppInfo($app);
@@ -203,7 +203,7 @@  discard block
 block discarded – undo
203 203
 			$plugins = isset($info['collaboration']['plugins']['plugin']['@value']) ?
204 204
 				[$info['collaboration']['plugins']['plugin']] : $info['collaboration']['plugins']['plugin'];
205 205
 			foreach ($plugins as $plugin) {
206
-				if($plugin['@attributes']['type'] === 'collaborator-search') {
206
+				if ($plugin['@attributes']['type'] === 'collaborator-search') {
207 207
 					$pluginInfo = [
208 208
 						'shareType' => $plugin['@attributes']['share-type'],
209 209
 						'class' => $plugin['@value'],
@@ -222,8 +222,8 @@  discard block
 block discarded – undo
222 222
 	 * @param string $path
223 223
 	 */
224 224
 	public static function registerAutoloading(string $app, string $path) {
225
-		$key = $app . '-' . $path;
226
-		if(isset(self::$alreadyRegistered[$key])) {
225
+		$key = $app.'-'.$path;
226
+		if (isset(self::$alreadyRegistered[$key])) {
227 227
 			return;
228 228
 		}
229 229
 
@@ -233,17 +233,17 @@  discard block
 block discarded – undo
233 233
 		$appNamespace = \OC\AppFramework\App::buildAppNamespace($app);
234 234
 		\OC::$server->registerNamespace($app, $appNamespace);
235 235
 
236
-		if (file_exists($path . '/composer/autoload.php')) {
237
-			require_once $path . '/composer/autoload.php';
236
+		if (file_exists($path.'/composer/autoload.php')) {
237
+			require_once $path.'/composer/autoload.php';
238 238
 		} else {
239
-			\OC::$composerAutoloader->addPsr4($appNamespace . '\\', $path . '/lib/', true);
239
+			\OC::$composerAutoloader->addPsr4($appNamespace.'\\', $path.'/lib/', true);
240 240
 			// Register on legacy autoloader
241 241
 			\OC::$loader->addValidRoot($path);
242 242
 		}
243 243
 
244 244
 		// Register Test namespace only when testing
245 245
 		if (defined('PHPUNIT_RUN') || defined('CLI_TEST_RUN')) {
246
-			\OC::$composerAutoloader->addPsr4($appNamespace . '\\Tests\\', $path . '/tests/', true);
246
+			\OC::$composerAutoloader->addPsr4($appNamespace.'\\Tests\\', $path.'/tests/', true);
247 247
 		}
248 248
 	}
249 249
 
@@ -255,7 +255,7 @@  discard block
 block discarded – undo
255 255
 	 */
256 256
 	private static function requireAppFile(string $app) {
257 257
 		// encapsulated here to avoid variable scope conflicts
258
-		require_once $app . '/appinfo/app.php';
258
+		require_once $app.'/appinfo/app.php';
259 259
 	}
260 260
 
261 261
 	/**
@@ -300,7 +300,7 @@  discard block
 block discarded – undo
300 300
 	public static function setAppTypes(string $app) {
301 301
 		$appManager = \OC::$server->getAppManager();
302 302
 		$appData = $appManager->getAppInfo($app);
303
-		if(!is_array($appData)) {
303
+		if (!is_array($appData)) {
304 304
 			return;
305 305
 		}
306 306
 
@@ -348,8 +348,8 @@  discard block
 block discarded – undo
348 348
 		} else {
349 349
 			$apps = $appManager->getEnabledAppsForUser($user);
350 350
 		}
351
-		$apps = array_filter($apps, function ($app) {
352
-			return $app !== 'files';//we add this manually
351
+		$apps = array_filter($apps, function($app) {
352
+			return $app !== 'files'; //we add this manually
353 353
 		});
354 354
 		sort($apps);
355 355
 		array_unshift($apps, 'files');
@@ -386,7 +386,7 @@  discard block
 block discarded – undo
386 386
 		$installer = \OC::$server->query(Installer::class);
387 387
 		$isDownloaded = $installer->isDownloaded($appId);
388 388
 
389
-		if(!$isDownloaded) {
389
+		if (!$isDownloaded) {
390 390
 			$installer->downloadApp($appId);
391 391
 		}
392 392
 
@@ -437,7 +437,7 @@  discard block
 block discarded – undo
437 437
 	 */
438 438
 	public static function findAppInDirectories(string $appId) {
439 439
 		$sanitizedAppId = self::cleanAppId($appId);
440
-		if($sanitizedAppId !== $appId) {
440
+		if ($sanitizedAppId !== $appId) {
441 441
 			return false;
442 442
 		}
443 443
 		static $app_dir = [];
@@ -448,7 +448,7 @@  discard block
 block discarded – undo
448 448
 
449 449
 		$possibleApps = [];
450 450
 		foreach (OC::$APPSROOTS as $dir) {
451
-			if (file_exists($dir['path'] . '/' . $appId)) {
451
+			if (file_exists($dir['path'].'/'.$appId)) {
452 452
 				$possibleApps[] = $dir;
453 453
 			}
454 454
 		}
@@ -489,7 +489,7 @@  discard block
 block discarded – undo
489 489
 		}
490 490
 
491 491
 		if (($dir = self::findAppInDirectories($appId)) != false) {
492
-			return $dir['path'] . '/' . $appId;
492
+			return $dir['path'].'/'.$appId;
493 493
 		}
494 494
 		return false;
495 495
 	}
@@ -503,7 +503,7 @@  discard block
 block discarded – undo
503 503
 	 */
504 504
 	public static function getAppWebPath(string $appId) {
505 505
 		if (($dir = self::findAppInDirectories($appId)) != false) {
506
-			return OC::$WEBROOT . $dir['url'] . '/' . $appId;
506
+			return OC::$WEBROOT.$dir['url'].'/'.$appId;
507 507
 		}
508 508
 		return false;
509 509
 	}
@@ -527,7 +527,7 @@  discard block
 block discarded – undo
527 527
 	 * @return string
528 528
 	 */
529 529
 	public static function getAppVersionByPath(string $path): string {
530
-		$infoFile = $path . '/appinfo/info.xml';
530
+		$infoFile = $path.'/appinfo/info.xml';
531 531
 		$appData = \OC::$server->getAppManager()->getAppInfo($infoFile, true);
532 532
 		return isset($appData['version']) ? $appData['version'] : '';
533 533
 	}
@@ -627,7 +627,7 @@  discard block
 block discarded – undo
627 627
 	 * @param string $page
628 628
 	 */
629 629
 	public static function registerAdmin(string $app, string $page) {
630
-		self::$adminForms[] = $app . '/' . $page . '.php';
630
+		self::$adminForms[] = $app.'/'.$page.'.php';
631 631
 	}
632 632
 
633 633
 	/**
@@ -636,7 +636,7 @@  discard block
 block discarded – undo
636 636
 	 * @param string $page
637 637
 	 */
638 638
 	public static function registerPersonal(string $app, string $page) {
639
-		self::$personalForms[] = $app . '/' . $page . '.php';
639
+		self::$personalForms[] = $app.'/'.$page.'.php';
640 640
 	}
641 641
 
642 642
 	/**
@@ -665,7 +665,7 @@  discard block
 block discarded – undo
665 665
 
666 666
 		foreach (OC::$APPSROOTS as $apps_dir) {
667 667
 			if (!is_readable($apps_dir['path'])) {
668
-				\OCP\Util::writeLog('core', 'unable to read app folder : ' . $apps_dir['path'], \OCP\Util::WARN);
668
+				\OCP\Util::writeLog('core', 'unable to read app folder : '.$apps_dir['path'], \OCP\Util::WARN);
669 669
 				continue;
670 670
 			}
671 671
 			$dh = opendir($apps_dir['path']);
@@ -673,7 +673,7 @@  discard block
 block discarded – undo
673 673
 			if (is_resource($dh)) {
674 674
 				while (($file = readdir($dh)) !== false) {
675 675
 
676
-					if ($file[0] != '.' and is_dir($apps_dir['path'] . '/' . $file) and is_file($apps_dir['path'] . '/' . $file . '/appinfo/info.xml')) {
676
+					if ($file[0] != '.' and is_dir($apps_dir['path'].'/'.$file) and is_file($apps_dir['path'].'/'.$file.'/appinfo/info.xml')) {
677 677
 
678 678
 						$apps[] = $file;
679 679
 					}
@@ -706,12 +706,12 @@  discard block
 block discarded – undo
706 706
 
707 707
 				$info = OC_App::getAppInfo($app, false, $langCode);
708 708
 				if (!is_array($info)) {
709
-					\OCP\Util::writeLog('core', 'Could not read app info file for app "' . $app . '"', \OCP\Util::ERROR);
709
+					\OCP\Util::writeLog('core', 'Could not read app info file for app "'.$app.'"', \OCP\Util::ERROR);
710 710
 					continue;
711 711
 				}
712 712
 
713 713
 				if (!isset($info['name'])) {
714
-					\OCP\Util::writeLog('core', 'App id "' . $app . '" has no name in appinfo', \OCP\Util::ERROR);
714
+					\OCP\Util::writeLog('core', 'App id "'.$app.'" has no name in appinfo', \OCP\Util::ERROR);
715 715
 					continue;
716 716
 				}
717 717
 
@@ -738,13 +738,13 @@  discard block
 block discarded – undo
738 738
 				}
739 739
 
740 740
 				$appPath = self::getAppPath($app);
741
-				if($appPath !== false) {
742
-					$appIcon = $appPath . '/img/' . $app . '.svg';
741
+				if ($appPath !== false) {
742
+					$appIcon = $appPath.'/img/'.$app.'.svg';
743 743
 					if (file_exists($appIcon)) {
744
-						$info['preview'] = $urlGenerator->imagePath($app, $app . '.svg');
744
+						$info['preview'] = $urlGenerator->imagePath($app, $app.'.svg');
745 745
 						$info['previewAsIcon'] = true;
746 746
 					} else {
747
-						$appIcon = $appPath . '/img/app.svg';
747
+						$appIcon = $appPath.'/img/app.svg';
748 748
 						if (file_exists($appIcon)) {
749 749
 							$info['preview'] = $urlGenerator->imagePath($app, 'app.svg');
750 750
 							$info['previewAsIcon'] = true;
@@ -865,7 +865,7 @@  discard block
 block discarded – undo
865 865
 	public static function getAppVersions() {
866 866
 		static $versions;
867 867
 
868
-		if(!$versions) {
868
+		if (!$versions) {
869 869
 			$appConfig = \OC::$server->getAppConfig();
870 870
 			$versions = $appConfig->getValues(false, 'installed_version');
871 871
 		}
@@ -880,7 +880,7 @@  discard block
 block discarded – undo
880 880
 	 */
881 881
 	public static function updateApp(string $appId): bool {
882 882
 		$appPath = self::getAppPath($appId);
883
-		if($appPath === false) {
883
+		if ($appPath === false) {
884 884
 			return false;
885 885
 		}
886 886
 		self::registerAutoloading($appId, $appPath);
@@ -888,8 +888,8 @@  discard block
 block discarded – undo
888 888
 		$appData = self::getAppInfo($appId);
889 889
 		self::executeRepairSteps($appId, $appData['repair-steps']['pre-migration']);
890 890
 
891
-		if (file_exists($appPath . '/appinfo/database.xml')) {
892
-			OC_DB::updateDbFromStructure($appPath . '/appinfo/database.xml');
891
+		if (file_exists($appPath.'/appinfo/database.xml')) {
892
+			OC_DB::updateDbFromStructure($appPath.'/appinfo/database.xml');
893 893
 		} else {
894 894
 			$ms = new MigrationService($appId, \OC::$server->getDatabaseConnection());
895 895
 			$ms->migrate();
@@ -901,23 +901,23 @@  discard block
 block discarded – undo
901 901
 		\OC::$server->getAppManager()->getAppVersion($appId, false);
902 902
 
903 903
 		// run upgrade code
904
-		if (file_exists($appPath . '/appinfo/update.php')) {
904
+		if (file_exists($appPath.'/appinfo/update.php')) {
905 905
 			self::loadApp($appId);
906
-			include $appPath . '/appinfo/update.php';
906
+			include $appPath.'/appinfo/update.php';
907 907
 		}
908 908
 		self::setupBackgroundJobs($appData['background-jobs']);
909 909
 
910 910
 		//set remote/public handlers
911 911
 		if (array_key_exists('ocsid', $appData)) {
912 912
 			\OC::$server->getConfig()->setAppValue($appId, 'ocsid', $appData['ocsid']);
913
-		} elseif(\OC::$server->getConfig()->getAppValue($appId, 'ocsid', null) !== null) {
913
+		} elseif (\OC::$server->getConfig()->getAppValue($appId, 'ocsid', null) !== null) {
914 914
 			\OC::$server->getConfig()->deleteAppValue($appId, 'ocsid');
915 915
 		}
916 916
 		foreach ($appData['remote'] as $name => $path) {
917
-			\OC::$server->getConfig()->setAppValue('core', 'remote_' . $name, $appId . '/' . $path);
917
+			\OC::$server->getConfig()->setAppValue('core', 'remote_'.$name, $appId.'/'.$path);
918 918
 		}
919 919
 		foreach ($appData['public'] as $name => $path) {
920
-			\OC::$server->getConfig()->setAppValue('core', 'public_' . $name, $appId . '/' . $path);
920
+			\OC::$server->getConfig()->setAppValue('core', 'public_'.$name, $appId.'/'.$path);
921 921
 		}
922 922
 
923 923
 		self::setAppTypes($appId);
@@ -987,17 +987,17 @@  discard block
 block discarded – undo
987 987
 	public static function getStorage(string $appId) {
988 988
 		if (\OC::$server->getAppManager()->isEnabledForUser($appId)) { //sanity check
989 989
 			if (\OC::$server->getUserSession()->isLoggedIn()) {
990
-				$view = new \OC\Files\View('/' . OC_User::getUser());
990
+				$view = new \OC\Files\View('/'.OC_User::getUser());
991 991
 				if (!$view->file_exists($appId)) {
992 992
 					$view->mkdir($appId);
993 993
 				}
994
-				return new \OC\Files\View('/' . OC_User::getUser() . '/' . $appId);
994
+				return new \OC\Files\View('/'.OC_User::getUser().'/'.$appId);
995 995
 			} else {
996
-				\OCP\Util::writeLog('core', 'Can\'t get app storage, app ' . $appId . ', user not logged in', \OCP\Util::ERROR);
996
+				\OCP\Util::writeLog('core', 'Can\'t get app storage, app '.$appId.', user not logged in', \OCP\Util::ERROR);
997 997
 				return false;
998 998
 			}
999 999
 		} else {
1000
-			\OCP\Util::writeLog('core', 'Can\'t get app storage, app ' . $appId . ' not enabled', \OCP\Util::ERROR);
1000
+			\OCP\Util::writeLog('core', 'Can\'t get app storage, app '.$appId.' not enabled', \OCP\Util::ERROR);
1001 1001
 			return false;
1002 1002
 		}
1003 1003
 	}
@@ -1029,9 +1029,9 @@  discard block
 block discarded – undo
1029 1029
 
1030 1030
 				if ($attributeLang === $similarLang) {
1031 1031
 					$similarLangFallback = $option['@value'];
1032
-				} else if (strpos($attributeLang, $similarLang . '_') === 0) {
1032
+				} else if (strpos($attributeLang, $similarLang.'_') === 0) {
1033 1033
 					if ($similarLangFallback === false) {
1034
-						$similarLangFallback =  $option['@value'];
1034
+						$similarLangFallback = $option['@value'];
1035 1035
 					}
1036 1036
 				}
1037 1037
 			} else {
@@ -1066,7 +1066,7 @@  discard block
 block discarded – undo
1066 1066
 			$data['description'] = trim(self::findBestL10NOption($data['description'], $lang));
1067 1067
 		} else if (isset($data['description']) && is_string($data['description'])) {
1068 1068
 			$data['description'] = trim($data['description']);
1069
-		} else  {
1069
+		} else {
1070 1070
 			$data['description'] = '';
1071 1071
 		}
1072 1072
 
Please login to merge, or discard this patch.
lib/private/Updater.php 2 patches
Indentation   +550 added lines, -550 removed lines patch added patch discarded remove patch
@@ -54,556 +54,556 @@
 block discarded – undo
54 54
  */
55 55
 class Updater extends BasicEmitter {
56 56
 
57
-	/** @var ILogger $log */
58
-	private $log;
59
-
60
-	/** @var IConfig */
61
-	private $config;
62
-
63
-	/** @var Checker */
64
-	private $checker;
65
-
66
-	/** @var Installer */
67
-	private $installer;
68
-
69
-	private $logLevelNames = [
70
-		0 => 'Debug',
71
-		1 => 'Info',
72
-		2 => 'Warning',
73
-		3 => 'Error',
74
-		4 => 'Fatal',
75
-	];
76
-
77
-	/**
78
-	 * @param IConfig $config
79
-	 * @param Checker $checker
80
-	 * @param ILogger $log
81
-	 * @param Installer $installer
82
-	 */
83
-	public function __construct(IConfig $config,
84
-								Checker $checker,
85
-								ILogger $log = null,
86
-								Installer $installer) {
87
-		$this->log = $log;
88
-		$this->config = $config;
89
-		$this->checker = $checker;
90
-		$this->installer = $installer;
91
-	}
92
-
93
-	/**
94
-	 * runs the update actions in maintenance mode, does not upgrade the source files
95
-	 * except the main .htaccess file
96
-	 *
97
-	 * @return bool true if the operation succeeded, false otherwise
98
-	 */
99
-	public function upgrade() {
100
-		$this->emitRepairEvents();
101
-		$this->logAllEvents();
102
-
103
-		$logLevel = $this->config->getSystemValue('loglevel', Util::WARN);
104
-		$this->emit('\OC\Updater', 'setDebugLogLevel', [ $logLevel, $this->logLevelNames[$logLevel] ]);
105
-		$this->config->setSystemValue('loglevel', Util::DEBUG);
106
-
107
-		$wasMaintenanceModeEnabled = $this->config->getSystemValue('maintenance', false);
108
-
109
-		if(!$wasMaintenanceModeEnabled) {
110
-			$this->config->setSystemValue('maintenance', true);
111
-			$this->emit('\OC\Updater', 'maintenanceEnabled');
112
-		}
113
-
114
-		$installedVersion = $this->config->getSystemValue('version', '0.0.0');
115
-		$currentVersion = implode('.', \OCP\Util::getVersion());
116
-		$this->log->debug('starting upgrade from ' . $installedVersion . ' to ' . $currentVersion, array('app' => 'core'));
117
-
118
-		$success = true;
119
-		try {
120
-			$this->doUpgrade($currentVersion, $installedVersion);
121
-		} catch (HintException $exception) {
122
-			$this->log->logException($exception, ['app' => 'core']);
123
-			$this->emit('\OC\Updater', 'failure', array($exception->getMessage() . ': ' .$exception->getHint()));
124
-			$success = false;
125
-		} catch (\Exception $exception) {
126
-			$this->log->logException($exception, ['app' => 'core']);
127
-			$this->emit('\OC\Updater', 'failure', array(get_class($exception) . ': ' .$exception->getMessage()));
128
-			$success = false;
129
-		}
130
-
131
-		$this->emit('\OC\Updater', 'updateEnd', array($success));
132
-
133
-		if(!$wasMaintenanceModeEnabled && $success) {
134
-			$this->config->setSystemValue('maintenance', false);
135
-			$this->emit('\OC\Updater', 'maintenanceDisabled');
136
-		} else {
137
-			$this->emit('\OC\Updater', 'maintenanceActive');
138
-		}
139
-
140
-		$this->emit('\OC\Updater', 'resetLogLevel', [ $logLevel, $this->logLevelNames[$logLevel] ]);
141
-		$this->config->setSystemValue('loglevel', $logLevel);
142
-		$this->config->setSystemValue('installed', true);
143
-
144
-		return $success;
145
-	}
146
-
147
-	/**
148
-	 * Return version from which this version is allowed to upgrade from
149
-	 *
150
-	 * @return array allowed previous versions per vendor
151
-	 */
152
-	private function getAllowedPreviousVersions() {
153
-		// this should really be a JSON file
154
-		require \OC::$SERVERROOT . '/version.php';
155
-		/** @var array $OC_VersionCanBeUpgradedFrom */
156
-		return $OC_VersionCanBeUpgradedFrom;
157
-	}
158
-
159
-	/**
160
-	 * Return vendor from which this version was published
161
-	 *
162
-	 * @return string Get the vendor
163
-	 */
164
-	private function getVendor() {
165
-		// this should really be a JSON file
166
-		require \OC::$SERVERROOT . '/version.php';
167
-		/** @var string $vendor */
168
-		return (string) $vendor;
169
-	}
170
-
171
-	/**
172
-	 * Whether an upgrade to a specified version is possible
173
-	 * @param string $oldVersion
174
-	 * @param string $newVersion
175
-	 * @param array $allowedPreviousVersions
176
-	 * @return bool
177
-	 */
178
-	public function isUpgradePossible($oldVersion, $newVersion, array $allowedPreviousVersions) {
179
-		$version = explode('.', $oldVersion);
180
-		$majorMinor = $version[0] . '.' . $version[1];
181
-
182
-		$currentVendor = $this->config->getAppValue('core', 'vendor', '');
183
-
184
-		// Vendor was not set correctly on install, so we have to white-list known versions
185
-		if ($currentVendor === '' && isset($allowedPreviousVersions['owncloud'][$oldVersion])) {
186
-			$currentVendor = 'owncloud';
187
-		}
188
-
189
-		if ($currentVendor === 'nextcloud') {
190
-			return isset($allowedPreviousVersions[$currentVendor][$majorMinor])
191
-				&& (version_compare($oldVersion, $newVersion, '<=') ||
192
-					$this->config->getSystemValue('debug', false));
193
-		}
194
-
195
-		// Check if the instance can be migrated
196
-		return isset($allowedPreviousVersions[$currentVendor][$majorMinor]) ||
197
-			isset($allowedPreviousVersions[$currentVendor][$oldVersion]);
198
-	}
199
-
200
-	/**
201
-	 * runs the update actions in maintenance mode, does not upgrade the source files
202
-	 * except the main .htaccess file
203
-	 *
204
-	 * @param string $currentVersion current version to upgrade to
205
-	 * @param string $installedVersion previous version from which to upgrade from
206
-	 *
207
-	 * @throws \Exception
208
-	 */
209
-	private function doUpgrade($currentVersion, $installedVersion) {
210
-		// Stop update if the update is over several major versions
211
-		$allowedPreviousVersions = $this->getAllowedPreviousVersions();
212
-		if (!$this->isUpgradePossible($installedVersion, $currentVersion, $allowedPreviousVersions)) {
213
-			throw new \Exception('Updates between multiple major versions and downgrades are unsupported.');
214
-		}
215
-
216
-		// Update .htaccess files
217
-		try {
218
-			Setup::updateHtaccess();
219
-			Setup::protectDataDirectory();
220
-		} catch (\Exception $e) {
221
-			throw new \Exception($e->getMessage());
222
-		}
223
-
224
-		// create empty file in data dir, so we can later find
225
-		// out that this is indeed an ownCloud data directory
226
-		// (in case it didn't exist before)
227
-		file_put_contents($this->config->getSystemValue('datadirectory', \OC::$SERVERROOT . '/data') . '/.ocdata', '');
228
-
229
-		// pre-upgrade repairs
230
-		$repair = new Repair(Repair::getBeforeUpgradeRepairSteps(), \OC::$server->getEventDispatcher());
231
-		$repair->run();
232
-
233
-		$this->doCoreUpgrade();
234
-
235
-		try {
236
-			// TODO: replace with the new repair step mechanism https://github.com/owncloud/core/pull/24378
237
-			Setup::installBackgroundJobs();
238
-		} catch (\Exception $e) {
239
-			throw new \Exception($e->getMessage());
240
-		}
241
-
242
-		// update all shipped apps
243
-		$this->checkAppsRequirements();
244
-		$this->doAppUpgrade();
245
-
246
-		// Update the appfetchers version so it downloads the correct list from the appstore
247
-		\OC::$server->getAppFetcher()->setVersion($currentVersion);
248
-
249
-		// upgrade appstore apps
250
-		$this->upgradeAppStoreApps(\OC::$server->getAppManager()->getInstalledApps());
251
-
252
-		// install new shipped apps on upgrade
253
-		OC_App::loadApps(['authentication']);
254
-		$errors = Installer::installShippedApps(true);
255
-		foreach ($errors as $appId => $exception) {
256
-			/** @var \Exception $exception */
257
-			$this->log->logException($exception, ['app' => $appId]);
258
-			$this->emit('\OC\Updater', 'failure', [$appId . ': ' . $exception->getMessage()]);
259
-		}
260
-
261
-		// post-upgrade repairs
262
-		$repair = new Repair(Repair::getRepairSteps(), \OC::$server->getEventDispatcher());
263
-		$repair->run();
264
-
265
-		//Invalidate update feed
266
-		$this->config->setAppValue('core', 'lastupdatedat', 0);
267
-
268
-		// Check for code integrity if not disabled
269
-		if(\OC::$server->getIntegrityCodeChecker()->isCodeCheckEnforced()) {
270
-			$this->emit('\OC\Updater', 'startCheckCodeIntegrity');
271
-			$this->checker->runInstanceVerification();
272
-			$this->emit('\OC\Updater', 'finishedCheckCodeIntegrity');
273
-		}
274
-
275
-		// only set the final version if everything went well
276
-		$this->config->setSystemValue('version', implode('.', Util::getVersion()));
277
-		$this->config->setAppValue('core', 'vendor', $this->getVendor());
278
-	}
279
-
280
-	protected function doCoreUpgrade() {
281
-		$this->emit('\OC\Updater', 'dbUpgradeBefore');
282
-
283
-		// execute core migrations
284
-		$ms = new MigrationService('core', \OC::$server->getDatabaseConnection());
285
-		$ms->migrate();
286
-
287
-		$this->emit('\OC\Updater', 'dbUpgrade');
288
-	}
289
-
290
-	/**
291
-	 * @param string $version the oc version to check app compatibility with
292
-	 */
293
-	protected function checkAppUpgrade($version) {
294
-		$apps = \OC_App::getEnabledApps();
295
-		$this->emit('\OC\Updater', 'appUpgradeCheckBefore');
296
-
297
-		$appManager = \OC::$server->getAppManager();
298
-		foreach ($apps as $appId) {
299
-			$info = \OC_App::getAppInfo($appId);
300
-			$compatible = \OC_App::isAppCompatible($version, $info);
301
-			$isShipped = $appManager->isShipped($appId);
302
-
303
-			if ($compatible && $isShipped && \OC_App::shouldUpgrade($appId)) {
304
-				/**
305
-				 * FIXME: The preupdate check is performed before the database migration, otherwise database changes
306
-				 * are not possible anymore within it. - Consider this when touching the code.
307
-				 * @link https://github.com/owncloud/core/issues/10980
308
-				 * @see \OC_App::updateApp
309
-				 */
310
-				if (file_exists(\OC_App::getAppPath($appId) . '/appinfo/preupdate.php')) {
311
-					$this->includePreUpdate($appId);
312
-				}
313
-				if (file_exists(\OC_App::getAppPath($appId) . '/appinfo/database.xml')) {
314
-					$this->emit('\OC\Updater', 'appSimulateUpdate', array($appId));
315
-					\OC_DB::simulateUpdateDbFromStructure(\OC_App::getAppPath($appId) . '/appinfo/database.xml');
316
-				}
317
-			}
318
-		}
319
-
320
-		$this->emit('\OC\Updater', 'appUpgradeCheck');
321
-	}
322
-
323
-	/**
324
-	 * Includes the pre-update file. Done here to prevent namespace mixups.
325
-	 * @param string $appId
326
-	 */
327
-	private function includePreUpdate($appId) {
328
-		include \OC_App::getAppPath($appId) . '/appinfo/preupdate.php';
329
-	}
330
-
331
-	/**
332
-	 * upgrades all apps within a major ownCloud upgrade. Also loads "priority"
333
-	 * (types authentication, filesystem, logging, in that order) afterwards.
334
-	 *
335
-	 * @throws NeedsUpdateException
336
-	 */
337
-	protected function doAppUpgrade() {
338
-		$apps = \OC_App::getEnabledApps();
339
-		$priorityTypes = array('authentication', 'filesystem', 'logging');
340
-		$pseudoOtherType = 'other';
341
-		$stacks = array($pseudoOtherType => array());
342
-
343
-		foreach ($apps as $appId) {
344
-			$priorityType = false;
345
-			foreach ($priorityTypes as $type) {
346
-				if(!isset($stacks[$type])) {
347
-					$stacks[$type] = array();
348
-				}
349
-				if (\OC_App::isType($appId, [$type])) {
350
-					$stacks[$type][] = $appId;
351
-					$priorityType = true;
352
-					break;
353
-				}
354
-			}
355
-			if (!$priorityType) {
356
-				$stacks[$pseudoOtherType][] = $appId;
357
-			}
358
-		}
359
-		foreach ($stacks as $type => $stack) {
360
-			foreach ($stack as $appId) {
361
-				if (\OC_App::shouldUpgrade($appId)) {
362
-					$this->emit('\OC\Updater', 'appUpgradeStarted', [$appId, \OC_App::getAppVersion($appId)]);
363
-					\OC_App::updateApp($appId);
364
-					$this->emit('\OC\Updater', 'appUpgrade', [$appId, \OC_App::getAppVersion($appId)]);
365
-				}
366
-				if($type !== $pseudoOtherType) {
367
-					// load authentication, filesystem and logging apps after
368
-					// upgrading them. Other apps my need to rely on modifying
369
-					// user and/or filesystem aspects.
370
-					\OC_App::loadApp($appId);
371
-				}
372
-			}
373
-		}
374
-	}
375
-
376
-	/**
377
-	 * check if the current enabled apps are compatible with the current
378
-	 * ownCloud version. disable them if not.
379
-	 * This is important if you upgrade ownCloud and have non ported 3rd
380
-	 * party apps installed.
381
-	 *
382
-	 * @return array
383
-	 * @throws \Exception
384
-	 */
385
-	private function checkAppsRequirements() {
386
-		$isCoreUpgrade = $this->isCodeUpgrade();
387
-		$apps = OC_App::getEnabledApps();
388
-		$version = implode('.', Util::getVersion());
389
-		$disabledApps = [];
390
-		$appManager = \OC::$server->getAppManager();
391
-		foreach ($apps as $app) {
392
-			// check if the app is compatible with this version of ownCloud
393
-			$info = OC_App::getAppInfo($app);
394
-			if(!OC_App::isAppCompatible($version, $info)) {
395
-				if ($appManager->isShipped($app)) {
396
-					throw new \UnexpectedValueException('The files of the app "' . $app . '" were not correctly replaced before running the update');
397
-				}
398
-				\OC::$server->getAppManager()->disableApp($app);
399
-				$this->emit('\OC\Updater', 'incompatibleAppDisabled', array($app));
400
-			}
401
-			// no need to disable any app in case this is a non-core upgrade
402
-			if (!$isCoreUpgrade) {
403
-				continue;
404
-			}
405
-			// shipped apps will remain enabled
406
-			if ($appManager->isShipped($app)) {
407
-				continue;
408
-			}
409
-			// authentication and session apps will remain enabled as well
410
-			if (OC_App::isType($app, ['session', 'authentication'])) {
411
-				continue;
412
-			}
413
-		}
414
-		return $disabledApps;
415
-	}
416
-
417
-	/**
418
-	 * @return bool
419
-	 */
420
-	private function isCodeUpgrade() {
421
-		$installedVersion = $this->config->getSystemValue('version', '0.0.0');
422
-		$currentVersion = implode('.', Util::getVersion());
423
-		if (version_compare($currentVersion, $installedVersion, '>')) {
424
-			return true;
425
-		}
426
-		return false;
427
-	}
428
-
429
-	/**
430
-	 * @param array $disabledApps
431
-	 * @throws \Exception
432
-	 */
433
-	private function upgradeAppStoreApps(array $disabledApps) {
434
-		foreach($disabledApps as $app) {
435
-			try {
436
-				$this->emit('\OC\Updater', 'checkAppStoreAppBefore', [$app]);
437
-				if ($this->installer->isUpdateAvailable($app)) {
438
-					$this->emit('\OC\Updater', 'upgradeAppStoreApp', [$app]);
439
-					$this->installer->updateAppstoreApp($app);
440
-				}
441
-				$this->emit('\OC\Updater', 'checkAppStoreApp', [$app]);
442
-			} catch (\Exception $ex) {
443
-				$this->log->logException($ex, ['app' => 'core']);
444
-			}
445
-		}
446
-	}
447
-
448
-	/**
449
-	 * Forward messages emitted by the repair routine
450
-	 */
451
-	private function emitRepairEvents() {
452
-		$dispatcher = \OC::$server->getEventDispatcher();
453
-		$dispatcher->addListener('\OC\Repair::warning', function ($event) {
454
-			if ($event instanceof GenericEvent) {
455
-				$this->emit('\OC\Updater', 'repairWarning', $event->getArguments());
456
-			}
457
-		});
458
-		$dispatcher->addListener('\OC\Repair::error', function ($event) {
459
-			if ($event instanceof GenericEvent) {
460
-				$this->emit('\OC\Updater', 'repairError', $event->getArguments());
461
-			}
462
-		});
463
-		$dispatcher->addListener('\OC\Repair::info', function ($event) {
464
-			if ($event instanceof GenericEvent) {
465
-				$this->emit('\OC\Updater', 'repairInfo', $event->getArguments());
466
-			}
467
-		});
468
-		$dispatcher->addListener('\OC\Repair::step', function ($event) {
469
-			if ($event instanceof GenericEvent) {
470
-				$this->emit('\OC\Updater', 'repairStep', $event->getArguments());
471
-			}
472
-		});
473
-	}
474
-
475
-	private function logAllEvents() {
476
-		$log = $this->log;
477
-
478
-		$dispatcher = \OC::$server->getEventDispatcher();
479
-		$dispatcher->addListener('\OC\DB\Migrator::executeSql', function($event) use ($log) {
480
-			if (!$event instanceof GenericEvent) {
481
-				return;
482
-			}
483
-			$log->info('\OC\DB\Migrator::executeSql: ' . $event->getSubject() . ' (' . $event->getArgument(0) . ' of ' . $event->getArgument(1) . ')', ['app' => 'updater']);
484
-		});
485
-		$dispatcher->addListener('\OC\DB\Migrator::checkTable', function($event) use ($log) {
486
-			if (!$event instanceof GenericEvent) {
487
-				return;
488
-			}
489
-			$log->info('\OC\DB\Migrator::checkTable: ' . $event->getSubject() . ' (' . $event->getArgument(0) . ' of ' . $event->getArgument(1) . ')', ['app' => 'updater']);
490
-		});
491
-
492
-		$repairListener = function($event) use ($log) {
493
-			if (!$event instanceof GenericEvent) {
494
-				return;
495
-			}
496
-			switch ($event->getSubject()) {
497
-				case '\OC\Repair::startProgress':
498
-					$log->info('\OC\Repair::startProgress: Starting ... ' . $event->getArgument(1) .  ' (' . $event->getArgument(0) . ')', ['app' => 'updater']);
499
-					break;
500
-				case '\OC\Repair::advance':
501
-					$desc = $event->getArgument(1);
502
-					if (empty($desc)) {
503
-						$desc = '';
504
-					}
505
-					$log->info('\OC\Repair::advance: ' . $desc . ' (' . $event->getArgument(0) . ')', ['app' => 'updater']);
506
-
507
-					break;
508
-				case '\OC\Repair::finishProgress':
509
-					$log->info('\OC\Repair::finishProgress', ['app' => 'updater']);
510
-					break;
511
-				case '\OC\Repair::step':
512
-					$log->info('\OC\Repair::step: Repair step: ' . $event->getArgument(0), ['app' => 'updater']);
513
-					break;
514
-				case '\OC\Repair::info':
515
-					$log->info('\OC\Repair::info: Repair info: ' . $event->getArgument(0), ['app' => 'updater']);
516
-					break;
517
-				case '\OC\Repair::warning':
518
-					$log->warning('\OC\Repair::warning: Repair warning: ' . $event->getArgument(0), ['app' => 'updater']);
519
-					break;
520
-				case '\OC\Repair::error':
521
-					$log->error('\OC\Repair::error: Repair error: ' . $event->getArgument(0), ['app' => 'updater']);
522
-					break;
523
-			}
524
-		};
525
-
526
-		$dispatcher->addListener('\OC\Repair::startProgress', $repairListener);
527
-		$dispatcher->addListener('\OC\Repair::advance', $repairListener);
528
-		$dispatcher->addListener('\OC\Repair::finishProgress', $repairListener);
529
-		$dispatcher->addListener('\OC\Repair::step', $repairListener);
530
-		$dispatcher->addListener('\OC\Repair::info', $repairListener);
531
-		$dispatcher->addListener('\OC\Repair::warning', $repairListener);
532
-		$dispatcher->addListener('\OC\Repair::error', $repairListener);
533
-
534
-
535
-		$this->listen('\OC\Updater', 'maintenanceEnabled', function () use($log) {
536
-			$log->info('\OC\Updater::maintenanceEnabled: Turned on maintenance mode', ['app' => 'updater']);
537
-		});
538
-		$this->listen('\OC\Updater', 'maintenanceDisabled', function () use($log) {
539
-			$log->info('\OC\Updater::maintenanceDisabled: Turned off maintenance mode', ['app' => 'updater']);
540
-		});
541
-		$this->listen('\OC\Updater', 'maintenanceActive', function () use($log) {
542
-			$log->info('\OC\Updater::maintenanceActive: Maintenance mode is kept active', ['app' => 'updater']);
543
-		});
544
-		$this->listen('\OC\Updater', 'updateEnd', function ($success) use($log) {
545
-			if ($success) {
546
-				$log->info('\OC\Updater::updateEnd: Update successful', ['app' => 'updater']);
547
-			} else {
548
-				$log->error('\OC\Updater::updateEnd: Update failed', ['app' => 'updater']);
549
-			}
550
-		});
551
-		$this->listen('\OC\Updater', 'dbUpgradeBefore', function () use($log) {
552
-			$log->info('\OC\Updater::dbUpgradeBefore: Updating database schema', ['app' => 'updater']);
553
-		});
554
-		$this->listen('\OC\Updater', 'dbUpgrade', function () use($log) {
555
-			$log->info('\OC\Updater::dbUpgrade: Updated database', ['app' => 'updater']);
556
-		});
557
-		$this->listen('\OC\Updater', 'dbSimulateUpgradeBefore', function () use($log) {
558
-			$log->info('\OC\Updater::dbSimulateUpgradeBefore: Checking whether the database schema can be updated (this can take a long time depending on the database size)', ['app' => 'updater']);
559
-		});
560
-		$this->listen('\OC\Updater', 'dbSimulateUpgrade', function () use($log) {
561
-			$log->info('\OC\Updater::dbSimulateUpgrade: Checked database schema update', ['app' => 'updater']);
562
-		});
563
-		$this->listen('\OC\Updater', 'incompatibleAppDisabled', function ($app) use($log) {
564
-			$log->info('\OC\Updater::incompatibleAppDisabled: Disabled incompatible app: ' . $app, ['app' => 'updater']);
565
-		});
566
-		$this->listen('\OC\Updater', 'checkAppStoreAppBefore', function ($app) use($log) {
567
-			$log->info('\OC\Updater::checkAppStoreAppBefore: Checking for update of app "' . $app . '" in appstore', ['app' => 'updater']);
568
-		});
569
-		$this->listen('\OC\Updater', 'upgradeAppStoreApp', function ($app) use($log) {
570
-			$log->info('\OC\Updater::upgradeAppStoreApp: Update app "' . $app . '" from appstore', ['app' => 'updater']);
571
-		});
572
-		$this->listen('\OC\Updater', 'checkAppStoreApp', function ($app) use($log) {
573
-			$log->info('\OC\Updater::checkAppStoreApp: Checked for update of app "' . $app . '" in appstore', ['app' => 'updater']);
574
-		});
575
-		$this->listen('\OC\Updater', 'appUpgradeCheckBefore', function () use ($log) {
576
-			$log->info('\OC\Updater::appUpgradeCheckBefore: Checking updates of apps', ['app' => 'updater']);
577
-		});
578
-		$this->listen('\OC\Updater', 'appSimulateUpdate', function ($app) use ($log) {
579
-			$log->info('\OC\Updater::appSimulateUpdate: Checking whether the database schema for <' . $app . '> can be updated (this can take a long time depending on the database size)', ['app' => 'updater']);
580
-		});
581
-		$this->listen('\OC\Updater', 'appUpgradeCheck', function () use ($log) {
582
-			$log->info('\OC\Updater::appUpgradeCheck: Checked database schema update for apps', ['app' => 'updater']);
583
-		});
584
-		$this->listen('\OC\Updater', 'appUpgradeStarted', function ($app) use ($log) {
585
-			$log->info('\OC\Updater::appUpgradeStarted: Updating <' . $app . '> ...', ['app' => 'updater']);
586
-		});
587
-		$this->listen('\OC\Updater', 'appUpgrade', function ($app, $version) use ($log) {
588
-			$log->info('\OC\Updater::appUpgrade: Updated <' . $app . '> to ' . $version, ['app' => 'updater']);
589
-		});
590
-		$this->listen('\OC\Updater', 'failure', function ($message) use($log) {
591
-			$log->error('\OC\Updater::failure: ' . $message, ['app' => 'updater']);
592
-		});
593
-		$this->listen('\OC\Updater', 'setDebugLogLevel', function () use($log) {
594
-			$log->info('\OC\Updater::setDebugLogLevel: Set log level to debug', ['app' => 'updater']);
595
-		});
596
-		$this->listen('\OC\Updater', 'resetLogLevel', function ($logLevel, $logLevelName) use($log) {
597
-			$log->info('\OC\Updater::resetLogLevel: Reset log level to ' . $logLevelName . '(' . $logLevel . ')', ['app' => 'updater']);
598
-		});
599
-		$this->listen('\OC\Updater', 'startCheckCodeIntegrity', function () use($log) {
600
-			$log->info('\OC\Updater::startCheckCodeIntegrity: Starting code integrity check...', ['app' => 'updater']);
601
-		});
602
-		$this->listen('\OC\Updater', 'finishedCheckCodeIntegrity', function () use($log) {
603
-			$log->info('\OC\Updater::finishedCheckCodeIntegrity: Finished code integrity check', ['app' => 'updater']);
604
-		});
605
-
606
-	}
57
+    /** @var ILogger $log */
58
+    private $log;
59
+
60
+    /** @var IConfig */
61
+    private $config;
62
+
63
+    /** @var Checker */
64
+    private $checker;
65
+
66
+    /** @var Installer */
67
+    private $installer;
68
+
69
+    private $logLevelNames = [
70
+        0 => 'Debug',
71
+        1 => 'Info',
72
+        2 => 'Warning',
73
+        3 => 'Error',
74
+        4 => 'Fatal',
75
+    ];
76
+
77
+    /**
78
+     * @param IConfig $config
79
+     * @param Checker $checker
80
+     * @param ILogger $log
81
+     * @param Installer $installer
82
+     */
83
+    public function __construct(IConfig $config,
84
+                                Checker $checker,
85
+                                ILogger $log = null,
86
+                                Installer $installer) {
87
+        $this->log = $log;
88
+        $this->config = $config;
89
+        $this->checker = $checker;
90
+        $this->installer = $installer;
91
+    }
92
+
93
+    /**
94
+     * runs the update actions in maintenance mode, does not upgrade the source files
95
+     * except the main .htaccess file
96
+     *
97
+     * @return bool true if the operation succeeded, false otherwise
98
+     */
99
+    public function upgrade() {
100
+        $this->emitRepairEvents();
101
+        $this->logAllEvents();
102
+
103
+        $logLevel = $this->config->getSystemValue('loglevel', Util::WARN);
104
+        $this->emit('\OC\Updater', 'setDebugLogLevel', [ $logLevel, $this->logLevelNames[$logLevel] ]);
105
+        $this->config->setSystemValue('loglevel', Util::DEBUG);
106
+
107
+        $wasMaintenanceModeEnabled = $this->config->getSystemValue('maintenance', false);
108
+
109
+        if(!$wasMaintenanceModeEnabled) {
110
+            $this->config->setSystemValue('maintenance', true);
111
+            $this->emit('\OC\Updater', 'maintenanceEnabled');
112
+        }
113
+
114
+        $installedVersion = $this->config->getSystemValue('version', '0.0.0');
115
+        $currentVersion = implode('.', \OCP\Util::getVersion());
116
+        $this->log->debug('starting upgrade from ' . $installedVersion . ' to ' . $currentVersion, array('app' => 'core'));
117
+
118
+        $success = true;
119
+        try {
120
+            $this->doUpgrade($currentVersion, $installedVersion);
121
+        } catch (HintException $exception) {
122
+            $this->log->logException($exception, ['app' => 'core']);
123
+            $this->emit('\OC\Updater', 'failure', array($exception->getMessage() . ': ' .$exception->getHint()));
124
+            $success = false;
125
+        } catch (\Exception $exception) {
126
+            $this->log->logException($exception, ['app' => 'core']);
127
+            $this->emit('\OC\Updater', 'failure', array(get_class($exception) . ': ' .$exception->getMessage()));
128
+            $success = false;
129
+        }
130
+
131
+        $this->emit('\OC\Updater', 'updateEnd', array($success));
132
+
133
+        if(!$wasMaintenanceModeEnabled && $success) {
134
+            $this->config->setSystemValue('maintenance', false);
135
+            $this->emit('\OC\Updater', 'maintenanceDisabled');
136
+        } else {
137
+            $this->emit('\OC\Updater', 'maintenanceActive');
138
+        }
139
+
140
+        $this->emit('\OC\Updater', 'resetLogLevel', [ $logLevel, $this->logLevelNames[$logLevel] ]);
141
+        $this->config->setSystemValue('loglevel', $logLevel);
142
+        $this->config->setSystemValue('installed', true);
143
+
144
+        return $success;
145
+    }
146
+
147
+    /**
148
+     * Return version from which this version is allowed to upgrade from
149
+     *
150
+     * @return array allowed previous versions per vendor
151
+     */
152
+    private function getAllowedPreviousVersions() {
153
+        // this should really be a JSON file
154
+        require \OC::$SERVERROOT . '/version.php';
155
+        /** @var array $OC_VersionCanBeUpgradedFrom */
156
+        return $OC_VersionCanBeUpgradedFrom;
157
+    }
158
+
159
+    /**
160
+     * Return vendor from which this version was published
161
+     *
162
+     * @return string Get the vendor
163
+     */
164
+    private function getVendor() {
165
+        // this should really be a JSON file
166
+        require \OC::$SERVERROOT . '/version.php';
167
+        /** @var string $vendor */
168
+        return (string) $vendor;
169
+    }
170
+
171
+    /**
172
+     * Whether an upgrade to a specified version is possible
173
+     * @param string $oldVersion
174
+     * @param string $newVersion
175
+     * @param array $allowedPreviousVersions
176
+     * @return bool
177
+     */
178
+    public function isUpgradePossible($oldVersion, $newVersion, array $allowedPreviousVersions) {
179
+        $version = explode('.', $oldVersion);
180
+        $majorMinor = $version[0] . '.' . $version[1];
181
+
182
+        $currentVendor = $this->config->getAppValue('core', 'vendor', '');
183
+
184
+        // Vendor was not set correctly on install, so we have to white-list known versions
185
+        if ($currentVendor === '' && isset($allowedPreviousVersions['owncloud'][$oldVersion])) {
186
+            $currentVendor = 'owncloud';
187
+        }
188
+
189
+        if ($currentVendor === 'nextcloud') {
190
+            return isset($allowedPreviousVersions[$currentVendor][$majorMinor])
191
+                && (version_compare($oldVersion, $newVersion, '<=') ||
192
+                    $this->config->getSystemValue('debug', false));
193
+        }
194
+
195
+        // Check if the instance can be migrated
196
+        return isset($allowedPreviousVersions[$currentVendor][$majorMinor]) ||
197
+            isset($allowedPreviousVersions[$currentVendor][$oldVersion]);
198
+    }
199
+
200
+    /**
201
+     * runs the update actions in maintenance mode, does not upgrade the source files
202
+     * except the main .htaccess file
203
+     *
204
+     * @param string $currentVersion current version to upgrade to
205
+     * @param string $installedVersion previous version from which to upgrade from
206
+     *
207
+     * @throws \Exception
208
+     */
209
+    private function doUpgrade($currentVersion, $installedVersion) {
210
+        // Stop update if the update is over several major versions
211
+        $allowedPreviousVersions = $this->getAllowedPreviousVersions();
212
+        if (!$this->isUpgradePossible($installedVersion, $currentVersion, $allowedPreviousVersions)) {
213
+            throw new \Exception('Updates between multiple major versions and downgrades are unsupported.');
214
+        }
215
+
216
+        // Update .htaccess files
217
+        try {
218
+            Setup::updateHtaccess();
219
+            Setup::protectDataDirectory();
220
+        } catch (\Exception $e) {
221
+            throw new \Exception($e->getMessage());
222
+        }
223
+
224
+        // create empty file in data dir, so we can later find
225
+        // out that this is indeed an ownCloud data directory
226
+        // (in case it didn't exist before)
227
+        file_put_contents($this->config->getSystemValue('datadirectory', \OC::$SERVERROOT . '/data') . '/.ocdata', '');
228
+
229
+        // pre-upgrade repairs
230
+        $repair = new Repair(Repair::getBeforeUpgradeRepairSteps(), \OC::$server->getEventDispatcher());
231
+        $repair->run();
232
+
233
+        $this->doCoreUpgrade();
234
+
235
+        try {
236
+            // TODO: replace with the new repair step mechanism https://github.com/owncloud/core/pull/24378
237
+            Setup::installBackgroundJobs();
238
+        } catch (\Exception $e) {
239
+            throw new \Exception($e->getMessage());
240
+        }
241
+
242
+        // update all shipped apps
243
+        $this->checkAppsRequirements();
244
+        $this->doAppUpgrade();
245
+
246
+        // Update the appfetchers version so it downloads the correct list from the appstore
247
+        \OC::$server->getAppFetcher()->setVersion($currentVersion);
248
+
249
+        // upgrade appstore apps
250
+        $this->upgradeAppStoreApps(\OC::$server->getAppManager()->getInstalledApps());
251
+
252
+        // install new shipped apps on upgrade
253
+        OC_App::loadApps(['authentication']);
254
+        $errors = Installer::installShippedApps(true);
255
+        foreach ($errors as $appId => $exception) {
256
+            /** @var \Exception $exception */
257
+            $this->log->logException($exception, ['app' => $appId]);
258
+            $this->emit('\OC\Updater', 'failure', [$appId . ': ' . $exception->getMessage()]);
259
+        }
260
+
261
+        // post-upgrade repairs
262
+        $repair = new Repair(Repair::getRepairSteps(), \OC::$server->getEventDispatcher());
263
+        $repair->run();
264
+
265
+        //Invalidate update feed
266
+        $this->config->setAppValue('core', 'lastupdatedat', 0);
267
+
268
+        // Check for code integrity if not disabled
269
+        if(\OC::$server->getIntegrityCodeChecker()->isCodeCheckEnforced()) {
270
+            $this->emit('\OC\Updater', 'startCheckCodeIntegrity');
271
+            $this->checker->runInstanceVerification();
272
+            $this->emit('\OC\Updater', 'finishedCheckCodeIntegrity');
273
+        }
274
+
275
+        // only set the final version if everything went well
276
+        $this->config->setSystemValue('version', implode('.', Util::getVersion()));
277
+        $this->config->setAppValue('core', 'vendor', $this->getVendor());
278
+    }
279
+
280
+    protected function doCoreUpgrade() {
281
+        $this->emit('\OC\Updater', 'dbUpgradeBefore');
282
+
283
+        // execute core migrations
284
+        $ms = new MigrationService('core', \OC::$server->getDatabaseConnection());
285
+        $ms->migrate();
286
+
287
+        $this->emit('\OC\Updater', 'dbUpgrade');
288
+    }
289
+
290
+    /**
291
+     * @param string $version the oc version to check app compatibility with
292
+     */
293
+    protected function checkAppUpgrade($version) {
294
+        $apps = \OC_App::getEnabledApps();
295
+        $this->emit('\OC\Updater', 'appUpgradeCheckBefore');
296
+
297
+        $appManager = \OC::$server->getAppManager();
298
+        foreach ($apps as $appId) {
299
+            $info = \OC_App::getAppInfo($appId);
300
+            $compatible = \OC_App::isAppCompatible($version, $info);
301
+            $isShipped = $appManager->isShipped($appId);
302
+
303
+            if ($compatible && $isShipped && \OC_App::shouldUpgrade($appId)) {
304
+                /**
305
+                 * FIXME: The preupdate check is performed before the database migration, otherwise database changes
306
+                 * are not possible anymore within it. - Consider this when touching the code.
307
+                 * @link https://github.com/owncloud/core/issues/10980
308
+                 * @see \OC_App::updateApp
309
+                 */
310
+                if (file_exists(\OC_App::getAppPath($appId) . '/appinfo/preupdate.php')) {
311
+                    $this->includePreUpdate($appId);
312
+                }
313
+                if (file_exists(\OC_App::getAppPath($appId) . '/appinfo/database.xml')) {
314
+                    $this->emit('\OC\Updater', 'appSimulateUpdate', array($appId));
315
+                    \OC_DB::simulateUpdateDbFromStructure(\OC_App::getAppPath($appId) . '/appinfo/database.xml');
316
+                }
317
+            }
318
+        }
319
+
320
+        $this->emit('\OC\Updater', 'appUpgradeCheck');
321
+    }
322
+
323
+    /**
324
+     * Includes the pre-update file. Done here to prevent namespace mixups.
325
+     * @param string $appId
326
+     */
327
+    private function includePreUpdate($appId) {
328
+        include \OC_App::getAppPath($appId) . '/appinfo/preupdate.php';
329
+    }
330
+
331
+    /**
332
+     * upgrades all apps within a major ownCloud upgrade. Also loads "priority"
333
+     * (types authentication, filesystem, logging, in that order) afterwards.
334
+     *
335
+     * @throws NeedsUpdateException
336
+     */
337
+    protected function doAppUpgrade() {
338
+        $apps = \OC_App::getEnabledApps();
339
+        $priorityTypes = array('authentication', 'filesystem', 'logging');
340
+        $pseudoOtherType = 'other';
341
+        $stacks = array($pseudoOtherType => array());
342
+
343
+        foreach ($apps as $appId) {
344
+            $priorityType = false;
345
+            foreach ($priorityTypes as $type) {
346
+                if(!isset($stacks[$type])) {
347
+                    $stacks[$type] = array();
348
+                }
349
+                if (\OC_App::isType($appId, [$type])) {
350
+                    $stacks[$type][] = $appId;
351
+                    $priorityType = true;
352
+                    break;
353
+                }
354
+            }
355
+            if (!$priorityType) {
356
+                $stacks[$pseudoOtherType][] = $appId;
357
+            }
358
+        }
359
+        foreach ($stacks as $type => $stack) {
360
+            foreach ($stack as $appId) {
361
+                if (\OC_App::shouldUpgrade($appId)) {
362
+                    $this->emit('\OC\Updater', 'appUpgradeStarted', [$appId, \OC_App::getAppVersion($appId)]);
363
+                    \OC_App::updateApp($appId);
364
+                    $this->emit('\OC\Updater', 'appUpgrade', [$appId, \OC_App::getAppVersion($appId)]);
365
+                }
366
+                if($type !== $pseudoOtherType) {
367
+                    // load authentication, filesystem and logging apps after
368
+                    // upgrading them. Other apps my need to rely on modifying
369
+                    // user and/or filesystem aspects.
370
+                    \OC_App::loadApp($appId);
371
+                }
372
+            }
373
+        }
374
+    }
375
+
376
+    /**
377
+     * check if the current enabled apps are compatible with the current
378
+     * ownCloud version. disable them if not.
379
+     * This is important if you upgrade ownCloud and have non ported 3rd
380
+     * party apps installed.
381
+     *
382
+     * @return array
383
+     * @throws \Exception
384
+     */
385
+    private function checkAppsRequirements() {
386
+        $isCoreUpgrade = $this->isCodeUpgrade();
387
+        $apps = OC_App::getEnabledApps();
388
+        $version = implode('.', Util::getVersion());
389
+        $disabledApps = [];
390
+        $appManager = \OC::$server->getAppManager();
391
+        foreach ($apps as $app) {
392
+            // check if the app is compatible with this version of ownCloud
393
+            $info = OC_App::getAppInfo($app);
394
+            if(!OC_App::isAppCompatible($version, $info)) {
395
+                if ($appManager->isShipped($app)) {
396
+                    throw new \UnexpectedValueException('The files of the app "' . $app . '" were not correctly replaced before running the update');
397
+                }
398
+                \OC::$server->getAppManager()->disableApp($app);
399
+                $this->emit('\OC\Updater', 'incompatibleAppDisabled', array($app));
400
+            }
401
+            // no need to disable any app in case this is a non-core upgrade
402
+            if (!$isCoreUpgrade) {
403
+                continue;
404
+            }
405
+            // shipped apps will remain enabled
406
+            if ($appManager->isShipped($app)) {
407
+                continue;
408
+            }
409
+            // authentication and session apps will remain enabled as well
410
+            if (OC_App::isType($app, ['session', 'authentication'])) {
411
+                continue;
412
+            }
413
+        }
414
+        return $disabledApps;
415
+    }
416
+
417
+    /**
418
+     * @return bool
419
+     */
420
+    private function isCodeUpgrade() {
421
+        $installedVersion = $this->config->getSystemValue('version', '0.0.0');
422
+        $currentVersion = implode('.', Util::getVersion());
423
+        if (version_compare($currentVersion, $installedVersion, '>')) {
424
+            return true;
425
+        }
426
+        return false;
427
+    }
428
+
429
+    /**
430
+     * @param array $disabledApps
431
+     * @throws \Exception
432
+     */
433
+    private function upgradeAppStoreApps(array $disabledApps) {
434
+        foreach($disabledApps as $app) {
435
+            try {
436
+                $this->emit('\OC\Updater', 'checkAppStoreAppBefore', [$app]);
437
+                if ($this->installer->isUpdateAvailable($app)) {
438
+                    $this->emit('\OC\Updater', 'upgradeAppStoreApp', [$app]);
439
+                    $this->installer->updateAppstoreApp($app);
440
+                }
441
+                $this->emit('\OC\Updater', 'checkAppStoreApp', [$app]);
442
+            } catch (\Exception $ex) {
443
+                $this->log->logException($ex, ['app' => 'core']);
444
+            }
445
+        }
446
+    }
447
+
448
+    /**
449
+     * Forward messages emitted by the repair routine
450
+     */
451
+    private function emitRepairEvents() {
452
+        $dispatcher = \OC::$server->getEventDispatcher();
453
+        $dispatcher->addListener('\OC\Repair::warning', function ($event) {
454
+            if ($event instanceof GenericEvent) {
455
+                $this->emit('\OC\Updater', 'repairWarning', $event->getArguments());
456
+            }
457
+        });
458
+        $dispatcher->addListener('\OC\Repair::error', function ($event) {
459
+            if ($event instanceof GenericEvent) {
460
+                $this->emit('\OC\Updater', 'repairError', $event->getArguments());
461
+            }
462
+        });
463
+        $dispatcher->addListener('\OC\Repair::info', function ($event) {
464
+            if ($event instanceof GenericEvent) {
465
+                $this->emit('\OC\Updater', 'repairInfo', $event->getArguments());
466
+            }
467
+        });
468
+        $dispatcher->addListener('\OC\Repair::step', function ($event) {
469
+            if ($event instanceof GenericEvent) {
470
+                $this->emit('\OC\Updater', 'repairStep', $event->getArguments());
471
+            }
472
+        });
473
+    }
474
+
475
+    private function logAllEvents() {
476
+        $log = $this->log;
477
+
478
+        $dispatcher = \OC::$server->getEventDispatcher();
479
+        $dispatcher->addListener('\OC\DB\Migrator::executeSql', function($event) use ($log) {
480
+            if (!$event instanceof GenericEvent) {
481
+                return;
482
+            }
483
+            $log->info('\OC\DB\Migrator::executeSql: ' . $event->getSubject() . ' (' . $event->getArgument(0) . ' of ' . $event->getArgument(1) . ')', ['app' => 'updater']);
484
+        });
485
+        $dispatcher->addListener('\OC\DB\Migrator::checkTable', function($event) use ($log) {
486
+            if (!$event instanceof GenericEvent) {
487
+                return;
488
+            }
489
+            $log->info('\OC\DB\Migrator::checkTable: ' . $event->getSubject() . ' (' . $event->getArgument(0) . ' of ' . $event->getArgument(1) . ')', ['app' => 'updater']);
490
+        });
491
+
492
+        $repairListener = function($event) use ($log) {
493
+            if (!$event instanceof GenericEvent) {
494
+                return;
495
+            }
496
+            switch ($event->getSubject()) {
497
+                case '\OC\Repair::startProgress':
498
+                    $log->info('\OC\Repair::startProgress: Starting ... ' . $event->getArgument(1) .  ' (' . $event->getArgument(0) . ')', ['app' => 'updater']);
499
+                    break;
500
+                case '\OC\Repair::advance':
501
+                    $desc = $event->getArgument(1);
502
+                    if (empty($desc)) {
503
+                        $desc = '';
504
+                    }
505
+                    $log->info('\OC\Repair::advance: ' . $desc . ' (' . $event->getArgument(0) . ')', ['app' => 'updater']);
506
+
507
+                    break;
508
+                case '\OC\Repair::finishProgress':
509
+                    $log->info('\OC\Repair::finishProgress', ['app' => 'updater']);
510
+                    break;
511
+                case '\OC\Repair::step':
512
+                    $log->info('\OC\Repair::step: Repair step: ' . $event->getArgument(0), ['app' => 'updater']);
513
+                    break;
514
+                case '\OC\Repair::info':
515
+                    $log->info('\OC\Repair::info: Repair info: ' . $event->getArgument(0), ['app' => 'updater']);
516
+                    break;
517
+                case '\OC\Repair::warning':
518
+                    $log->warning('\OC\Repair::warning: Repair warning: ' . $event->getArgument(0), ['app' => 'updater']);
519
+                    break;
520
+                case '\OC\Repair::error':
521
+                    $log->error('\OC\Repair::error: Repair error: ' . $event->getArgument(0), ['app' => 'updater']);
522
+                    break;
523
+            }
524
+        };
525
+
526
+        $dispatcher->addListener('\OC\Repair::startProgress', $repairListener);
527
+        $dispatcher->addListener('\OC\Repair::advance', $repairListener);
528
+        $dispatcher->addListener('\OC\Repair::finishProgress', $repairListener);
529
+        $dispatcher->addListener('\OC\Repair::step', $repairListener);
530
+        $dispatcher->addListener('\OC\Repair::info', $repairListener);
531
+        $dispatcher->addListener('\OC\Repair::warning', $repairListener);
532
+        $dispatcher->addListener('\OC\Repair::error', $repairListener);
533
+
534
+
535
+        $this->listen('\OC\Updater', 'maintenanceEnabled', function () use($log) {
536
+            $log->info('\OC\Updater::maintenanceEnabled: Turned on maintenance mode', ['app' => 'updater']);
537
+        });
538
+        $this->listen('\OC\Updater', 'maintenanceDisabled', function () use($log) {
539
+            $log->info('\OC\Updater::maintenanceDisabled: Turned off maintenance mode', ['app' => 'updater']);
540
+        });
541
+        $this->listen('\OC\Updater', 'maintenanceActive', function () use($log) {
542
+            $log->info('\OC\Updater::maintenanceActive: Maintenance mode is kept active', ['app' => 'updater']);
543
+        });
544
+        $this->listen('\OC\Updater', 'updateEnd', function ($success) use($log) {
545
+            if ($success) {
546
+                $log->info('\OC\Updater::updateEnd: Update successful', ['app' => 'updater']);
547
+            } else {
548
+                $log->error('\OC\Updater::updateEnd: Update failed', ['app' => 'updater']);
549
+            }
550
+        });
551
+        $this->listen('\OC\Updater', 'dbUpgradeBefore', function () use($log) {
552
+            $log->info('\OC\Updater::dbUpgradeBefore: Updating database schema', ['app' => 'updater']);
553
+        });
554
+        $this->listen('\OC\Updater', 'dbUpgrade', function () use($log) {
555
+            $log->info('\OC\Updater::dbUpgrade: Updated database', ['app' => 'updater']);
556
+        });
557
+        $this->listen('\OC\Updater', 'dbSimulateUpgradeBefore', function () use($log) {
558
+            $log->info('\OC\Updater::dbSimulateUpgradeBefore: Checking whether the database schema can be updated (this can take a long time depending on the database size)', ['app' => 'updater']);
559
+        });
560
+        $this->listen('\OC\Updater', 'dbSimulateUpgrade', function () use($log) {
561
+            $log->info('\OC\Updater::dbSimulateUpgrade: Checked database schema update', ['app' => 'updater']);
562
+        });
563
+        $this->listen('\OC\Updater', 'incompatibleAppDisabled', function ($app) use($log) {
564
+            $log->info('\OC\Updater::incompatibleAppDisabled: Disabled incompatible app: ' . $app, ['app' => 'updater']);
565
+        });
566
+        $this->listen('\OC\Updater', 'checkAppStoreAppBefore', function ($app) use($log) {
567
+            $log->info('\OC\Updater::checkAppStoreAppBefore: Checking for update of app "' . $app . '" in appstore', ['app' => 'updater']);
568
+        });
569
+        $this->listen('\OC\Updater', 'upgradeAppStoreApp', function ($app) use($log) {
570
+            $log->info('\OC\Updater::upgradeAppStoreApp: Update app "' . $app . '" from appstore', ['app' => 'updater']);
571
+        });
572
+        $this->listen('\OC\Updater', 'checkAppStoreApp', function ($app) use($log) {
573
+            $log->info('\OC\Updater::checkAppStoreApp: Checked for update of app "' . $app . '" in appstore', ['app' => 'updater']);
574
+        });
575
+        $this->listen('\OC\Updater', 'appUpgradeCheckBefore', function () use ($log) {
576
+            $log->info('\OC\Updater::appUpgradeCheckBefore: Checking updates of apps', ['app' => 'updater']);
577
+        });
578
+        $this->listen('\OC\Updater', 'appSimulateUpdate', function ($app) use ($log) {
579
+            $log->info('\OC\Updater::appSimulateUpdate: Checking whether the database schema for <' . $app . '> can be updated (this can take a long time depending on the database size)', ['app' => 'updater']);
580
+        });
581
+        $this->listen('\OC\Updater', 'appUpgradeCheck', function () use ($log) {
582
+            $log->info('\OC\Updater::appUpgradeCheck: Checked database schema update for apps', ['app' => 'updater']);
583
+        });
584
+        $this->listen('\OC\Updater', 'appUpgradeStarted', function ($app) use ($log) {
585
+            $log->info('\OC\Updater::appUpgradeStarted: Updating <' . $app . '> ...', ['app' => 'updater']);
586
+        });
587
+        $this->listen('\OC\Updater', 'appUpgrade', function ($app, $version) use ($log) {
588
+            $log->info('\OC\Updater::appUpgrade: Updated <' . $app . '> to ' . $version, ['app' => 'updater']);
589
+        });
590
+        $this->listen('\OC\Updater', 'failure', function ($message) use($log) {
591
+            $log->error('\OC\Updater::failure: ' . $message, ['app' => 'updater']);
592
+        });
593
+        $this->listen('\OC\Updater', 'setDebugLogLevel', function () use($log) {
594
+            $log->info('\OC\Updater::setDebugLogLevel: Set log level to debug', ['app' => 'updater']);
595
+        });
596
+        $this->listen('\OC\Updater', 'resetLogLevel', function ($logLevel, $logLevelName) use($log) {
597
+            $log->info('\OC\Updater::resetLogLevel: Reset log level to ' . $logLevelName . '(' . $logLevel . ')', ['app' => 'updater']);
598
+        });
599
+        $this->listen('\OC\Updater', 'startCheckCodeIntegrity', function () use($log) {
600
+            $log->info('\OC\Updater::startCheckCodeIntegrity: Starting code integrity check...', ['app' => 'updater']);
601
+        });
602
+        $this->listen('\OC\Updater', 'finishedCheckCodeIntegrity', function () use($log) {
603
+            $log->info('\OC\Updater::finishedCheckCodeIntegrity: Finished code integrity check', ['app' => 'updater']);
604
+        });
605
+
606
+    }
607 607
 
608 608
 }
609 609
 
Please login to merge, or discard this patch.
Spacing   +65 added lines, -65 removed lines patch added patch discarded remove patch
@@ -101,43 +101,43 @@  discard block
 block discarded – undo
101 101
 		$this->logAllEvents();
102 102
 
103 103
 		$logLevel = $this->config->getSystemValue('loglevel', Util::WARN);
104
-		$this->emit('\OC\Updater', 'setDebugLogLevel', [ $logLevel, $this->logLevelNames[$logLevel] ]);
104
+		$this->emit('\OC\Updater', 'setDebugLogLevel', [$logLevel, $this->logLevelNames[$logLevel]]);
105 105
 		$this->config->setSystemValue('loglevel', Util::DEBUG);
106 106
 
107 107
 		$wasMaintenanceModeEnabled = $this->config->getSystemValue('maintenance', false);
108 108
 
109
-		if(!$wasMaintenanceModeEnabled) {
109
+		if (!$wasMaintenanceModeEnabled) {
110 110
 			$this->config->setSystemValue('maintenance', true);
111 111
 			$this->emit('\OC\Updater', 'maintenanceEnabled');
112 112
 		}
113 113
 
114 114
 		$installedVersion = $this->config->getSystemValue('version', '0.0.0');
115 115
 		$currentVersion = implode('.', \OCP\Util::getVersion());
116
-		$this->log->debug('starting upgrade from ' . $installedVersion . ' to ' . $currentVersion, array('app' => 'core'));
116
+		$this->log->debug('starting upgrade from '.$installedVersion.' to '.$currentVersion, array('app' => 'core'));
117 117
 
118 118
 		$success = true;
119 119
 		try {
120 120
 			$this->doUpgrade($currentVersion, $installedVersion);
121 121
 		} catch (HintException $exception) {
122 122
 			$this->log->logException($exception, ['app' => 'core']);
123
-			$this->emit('\OC\Updater', 'failure', array($exception->getMessage() . ': ' .$exception->getHint()));
123
+			$this->emit('\OC\Updater', 'failure', array($exception->getMessage().': '.$exception->getHint()));
124 124
 			$success = false;
125 125
 		} catch (\Exception $exception) {
126 126
 			$this->log->logException($exception, ['app' => 'core']);
127
-			$this->emit('\OC\Updater', 'failure', array(get_class($exception) . ': ' .$exception->getMessage()));
127
+			$this->emit('\OC\Updater', 'failure', array(get_class($exception).': '.$exception->getMessage()));
128 128
 			$success = false;
129 129
 		}
130 130
 
131 131
 		$this->emit('\OC\Updater', 'updateEnd', array($success));
132 132
 
133
-		if(!$wasMaintenanceModeEnabled && $success) {
133
+		if (!$wasMaintenanceModeEnabled && $success) {
134 134
 			$this->config->setSystemValue('maintenance', false);
135 135
 			$this->emit('\OC\Updater', 'maintenanceDisabled');
136 136
 		} else {
137 137
 			$this->emit('\OC\Updater', 'maintenanceActive');
138 138
 		}
139 139
 
140
-		$this->emit('\OC\Updater', 'resetLogLevel', [ $logLevel, $this->logLevelNames[$logLevel] ]);
140
+		$this->emit('\OC\Updater', 'resetLogLevel', [$logLevel, $this->logLevelNames[$logLevel]]);
141 141
 		$this->config->setSystemValue('loglevel', $logLevel);
142 142
 		$this->config->setSystemValue('installed', true);
143 143
 
@@ -151,7 +151,7 @@  discard block
 block discarded – undo
151 151
 	 */
152 152
 	private function getAllowedPreviousVersions() {
153 153
 		// this should really be a JSON file
154
-		require \OC::$SERVERROOT . '/version.php';
154
+		require \OC::$SERVERROOT.'/version.php';
155 155
 		/** @var array $OC_VersionCanBeUpgradedFrom */
156 156
 		return $OC_VersionCanBeUpgradedFrom;
157 157
 	}
@@ -163,7 +163,7 @@  discard block
 block discarded – undo
163 163
 	 */
164 164
 	private function getVendor() {
165 165
 		// this should really be a JSON file
166
-		require \OC::$SERVERROOT . '/version.php';
166
+		require \OC::$SERVERROOT.'/version.php';
167 167
 		/** @var string $vendor */
168 168
 		return (string) $vendor;
169 169
 	}
@@ -177,7 +177,7 @@  discard block
 block discarded – undo
177 177
 	 */
178 178
 	public function isUpgradePossible($oldVersion, $newVersion, array $allowedPreviousVersions) {
179 179
 		$version = explode('.', $oldVersion);
180
-		$majorMinor = $version[0] . '.' . $version[1];
180
+		$majorMinor = $version[0].'.'.$version[1];
181 181
 
182 182
 		$currentVendor = $this->config->getAppValue('core', 'vendor', '');
183 183
 
@@ -224,7 +224,7 @@  discard block
 block discarded – undo
224 224
 		// create empty file in data dir, so we can later find
225 225
 		// out that this is indeed an ownCloud data directory
226 226
 		// (in case it didn't exist before)
227
-		file_put_contents($this->config->getSystemValue('datadirectory', \OC::$SERVERROOT . '/data') . '/.ocdata', '');
227
+		file_put_contents($this->config->getSystemValue('datadirectory', \OC::$SERVERROOT.'/data').'/.ocdata', '');
228 228
 
229 229
 		// pre-upgrade repairs
230 230
 		$repair = new Repair(Repair::getBeforeUpgradeRepairSteps(), \OC::$server->getEventDispatcher());
@@ -255,7 +255,7 @@  discard block
 block discarded – undo
255 255
 		foreach ($errors as $appId => $exception) {
256 256
 			/** @var \Exception $exception */
257 257
 			$this->log->logException($exception, ['app' => $appId]);
258
-			$this->emit('\OC\Updater', 'failure', [$appId . ': ' . $exception->getMessage()]);
258
+			$this->emit('\OC\Updater', 'failure', [$appId.': '.$exception->getMessage()]);
259 259
 		}
260 260
 
261 261
 		// post-upgrade repairs
@@ -266,7 +266,7 @@  discard block
 block discarded – undo
266 266
 		$this->config->setAppValue('core', 'lastupdatedat', 0);
267 267
 
268 268
 		// Check for code integrity if not disabled
269
-		if(\OC::$server->getIntegrityCodeChecker()->isCodeCheckEnforced()) {
269
+		if (\OC::$server->getIntegrityCodeChecker()->isCodeCheckEnforced()) {
270 270
 			$this->emit('\OC\Updater', 'startCheckCodeIntegrity');
271 271
 			$this->checker->runInstanceVerification();
272 272
 			$this->emit('\OC\Updater', 'finishedCheckCodeIntegrity');
@@ -307,12 +307,12 @@  discard block
 block discarded – undo
307 307
 				 * @link https://github.com/owncloud/core/issues/10980
308 308
 				 * @see \OC_App::updateApp
309 309
 				 */
310
-				if (file_exists(\OC_App::getAppPath($appId) . '/appinfo/preupdate.php')) {
310
+				if (file_exists(\OC_App::getAppPath($appId).'/appinfo/preupdate.php')) {
311 311
 					$this->includePreUpdate($appId);
312 312
 				}
313
-				if (file_exists(\OC_App::getAppPath($appId) . '/appinfo/database.xml')) {
313
+				if (file_exists(\OC_App::getAppPath($appId).'/appinfo/database.xml')) {
314 314
 					$this->emit('\OC\Updater', 'appSimulateUpdate', array($appId));
315
-					\OC_DB::simulateUpdateDbFromStructure(\OC_App::getAppPath($appId) . '/appinfo/database.xml');
315
+					\OC_DB::simulateUpdateDbFromStructure(\OC_App::getAppPath($appId).'/appinfo/database.xml');
316 316
 				}
317 317
 			}
318 318
 		}
@@ -325,7 +325,7 @@  discard block
 block discarded – undo
325 325
 	 * @param string $appId
326 326
 	 */
327 327
 	private function includePreUpdate($appId) {
328
-		include \OC_App::getAppPath($appId) . '/appinfo/preupdate.php';
328
+		include \OC_App::getAppPath($appId).'/appinfo/preupdate.php';
329 329
 	}
330 330
 
331 331
 	/**
@@ -343,7 +343,7 @@  discard block
 block discarded – undo
343 343
 		foreach ($apps as $appId) {
344 344
 			$priorityType = false;
345 345
 			foreach ($priorityTypes as $type) {
346
-				if(!isset($stacks[$type])) {
346
+				if (!isset($stacks[$type])) {
347 347
 					$stacks[$type] = array();
348 348
 				}
349 349
 				if (\OC_App::isType($appId, [$type])) {
@@ -363,7 +363,7 @@  discard block
 block discarded – undo
363 363
 					\OC_App::updateApp($appId);
364 364
 					$this->emit('\OC\Updater', 'appUpgrade', [$appId, \OC_App::getAppVersion($appId)]);
365 365
 				}
366
-				if($type !== $pseudoOtherType) {
366
+				if ($type !== $pseudoOtherType) {
367 367
 					// load authentication, filesystem and logging apps after
368 368
 					// upgrading them. Other apps my need to rely on modifying
369 369
 					// user and/or filesystem aspects.
@@ -391,9 +391,9 @@  discard block
 block discarded – undo
391 391
 		foreach ($apps as $app) {
392 392
 			// check if the app is compatible with this version of ownCloud
393 393
 			$info = OC_App::getAppInfo($app);
394
-			if(!OC_App::isAppCompatible($version, $info)) {
394
+			if (!OC_App::isAppCompatible($version, $info)) {
395 395
 				if ($appManager->isShipped($app)) {
396
-					throw new \UnexpectedValueException('The files of the app "' . $app . '" were not correctly replaced before running the update');
396
+					throw new \UnexpectedValueException('The files of the app "'.$app.'" were not correctly replaced before running the update');
397 397
 				}
398 398
 				\OC::$server->getAppManager()->disableApp($app);
399 399
 				$this->emit('\OC\Updater', 'incompatibleAppDisabled', array($app));
@@ -431,7 +431,7 @@  discard block
 block discarded – undo
431 431
 	 * @throws \Exception
432 432
 	 */
433 433
 	private function upgradeAppStoreApps(array $disabledApps) {
434
-		foreach($disabledApps as $app) {
434
+		foreach ($disabledApps as $app) {
435 435
 			try {
436 436
 				$this->emit('\OC\Updater', 'checkAppStoreAppBefore', [$app]);
437 437
 				if ($this->installer->isUpdateAvailable($app)) {
@@ -450,22 +450,22 @@  discard block
 block discarded – undo
450 450
 	 */
451 451
 	private function emitRepairEvents() {
452 452
 		$dispatcher = \OC::$server->getEventDispatcher();
453
-		$dispatcher->addListener('\OC\Repair::warning', function ($event) {
453
+		$dispatcher->addListener('\OC\Repair::warning', function($event) {
454 454
 			if ($event instanceof GenericEvent) {
455 455
 				$this->emit('\OC\Updater', 'repairWarning', $event->getArguments());
456 456
 			}
457 457
 		});
458
-		$dispatcher->addListener('\OC\Repair::error', function ($event) {
458
+		$dispatcher->addListener('\OC\Repair::error', function($event) {
459 459
 			if ($event instanceof GenericEvent) {
460 460
 				$this->emit('\OC\Updater', 'repairError', $event->getArguments());
461 461
 			}
462 462
 		});
463
-		$dispatcher->addListener('\OC\Repair::info', function ($event) {
463
+		$dispatcher->addListener('\OC\Repair::info', function($event) {
464 464
 			if ($event instanceof GenericEvent) {
465 465
 				$this->emit('\OC\Updater', 'repairInfo', $event->getArguments());
466 466
 			}
467 467
 		});
468
-		$dispatcher->addListener('\OC\Repair::step', function ($event) {
468
+		$dispatcher->addListener('\OC\Repair::step', function($event) {
469 469
 			if ($event instanceof GenericEvent) {
470 470
 				$this->emit('\OC\Updater', 'repairStep', $event->getArguments());
471 471
 			}
@@ -480,13 +480,13 @@  discard block
 block discarded – undo
480 480
 			if (!$event instanceof GenericEvent) {
481 481
 				return;
482 482
 			}
483
-			$log->info('\OC\DB\Migrator::executeSql: ' . $event->getSubject() . ' (' . $event->getArgument(0) . ' of ' . $event->getArgument(1) . ')', ['app' => 'updater']);
483
+			$log->info('\OC\DB\Migrator::executeSql: '.$event->getSubject().' ('.$event->getArgument(0).' of '.$event->getArgument(1).')', ['app' => 'updater']);
484 484
 		});
485 485
 		$dispatcher->addListener('\OC\DB\Migrator::checkTable', function($event) use ($log) {
486 486
 			if (!$event instanceof GenericEvent) {
487 487
 				return;
488 488
 			}
489
-			$log->info('\OC\DB\Migrator::checkTable: ' . $event->getSubject() . ' (' . $event->getArgument(0) . ' of ' . $event->getArgument(1) . ')', ['app' => 'updater']);
489
+			$log->info('\OC\DB\Migrator::checkTable: '.$event->getSubject().' ('.$event->getArgument(0).' of '.$event->getArgument(1).')', ['app' => 'updater']);
490 490
 		});
491 491
 
492 492
 		$repairListener = function($event) use ($log) {
@@ -495,30 +495,30 @@  discard block
 block discarded – undo
495 495
 			}
496 496
 			switch ($event->getSubject()) {
497 497
 				case '\OC\Repair::startProgress':
498
-					$log->info('\OC\Repair::startProgress: Starting ... ' . $event->getArgument(1) .  ' (' . $event->getArgument(0) . ')', ['app' => 'updater']);
498
+					$log->info('\OC\Repair::startProgress: Starting ... '.$event->getArgument(1).' ('.$event->getArgument(0).')', ['app' => 'updater']);
499 499
 					break;
500 500
 				case '\OC\Repair::advance':
501 501
 					$desc = $event->getArgument(1);
502 502
 					if (empty($desc)) {
503 503
 						$desc = '';
504 504
 					}
505
-					$log->info('\OC\Repair::advance: ' . $desc . ' (' . $event->getArgument(0) . ')', ['app' => 'updater']);
505
+					$log->info('\OC\Repair::advance: '.$desc.' ('.$event->getArgument(0).')', ['app' => 'updater']);
506 506
 
507 507
 					break;
508 508
 				case '\OC\Repair::finishProgress':
509 509
 					$log->info('\OC\Repair::finishProgress', ['app' => 'updater']);
510 510
 					break;
511 511
 				case '\OC\Repair::step':
512
-					$log->info('\OC\Repair::step: Repair step: ' . $event->getArgument(0), ['app' => 'updater']);
512
+					$log->info('\OC\Repair::step: Repair step: '.$event->getArgument(0), ['app' => 'updater']);
513 513
 					break;
514 514
 				case '\OC\Repair::info':
515
-					$log->info('\OC\Repair::info: Repair info: ' . $event->getArgument(0), ['app' => 'updater']);
515
+					$log->info('\OC\Repair::info: Repair info: '.$event->getArgument(0), ['app' => 'updater']);
516 516
 					break;
517 517
 				case '\OC\Repair::warning':
518
-					$log->warning('\OC\Repair::warning: Repair warning: ' . $event->getArgument(0), ['app' => 'updater']);
518
+					$log->warning('\OC\Repair::warning: Repair warning: '.$event->getArgument(0), ['app' => 'updater']);
519 519
 					break;
520 520
 				case '\OC\Repair::error':
521
-					$log->error('\OC\Repair::error: Repair error: ' . $event->getArgument(0), ['app' => 'updater']);
521
+					$log->error('\OC\Repair::error: Repair error: '.$event->getArgument(0), ['app' => 'updater']);
522 522
 					break;
523 523
 			}
524 524
 		};
@@ -532,74 +532,74 @@  discard block
 block discarded – undo
532 532
 		$dispatcher->addListener('\OC\Repair::error', $repairListener);
533 533
 
534 534
 
535
-		$this->listen('\OC\Updater', 'maintenanceEnabled', function () use($log) {
535
+		$this->listen('\OC\Updater', 'maintenanceEnabled', function() use($log) {
536 536
 			$log->info('\OC\Updater::maintenanceEnabled: Turned on maintenance mode', ['app' => 'updater']);
537 537
 		});
538
-		$this->listen('\OC\Updater', 'maintenanceDisabled', function () use($log) {
538
+		$this->listen('\OC\Updater', 'maintenanceDisabled', function() use($log) {
539 539
 			$log->info('\OC\Updater::maintenanceDisabled: Turned off maintenance mode', ['app' => 'updater']);
540 540
 		});
541
-		$this->listen('\OC\Updater', 'maintenanceActive', function () use($log) {
541
+		$this->listen('\OC\Updater', 'maintenanceActive', function() use($log) {
542 542
 			$log->info('\OC\Updater::maintenanceActive: Maintenance mode is kept active', ['app' => 'updater']);
543 543
 		});
544
-		$this->listen('\OC\Updater', 'updateEnd', function ($success) use($log) {
544
+		$this->listen('\OC\Updater', 'updateEnd', function($success) use($log) {
545 545
 			if ($success) {
546 546
 				$log->info('\OC\Updater::updateEnd: Update successful', ['app' => 'updater']);
547 547
 			} else {
548 548
 				$log->error('\OC\Updater::updateEnd: Update failed', ['app' => 'updater']);
549 549
 			}
550 550
 		});
551
-		$this->listen('\OC\Updater', 'dbUpgradeBefore', function () use($log) {
551
+		$this->listen('\OC\Updater', 'dbUpgradeBefore', function() use($log) {
552 552
 			$log->info('\OC\Updater::dbUpgradeBefore: Updating database schema', ['app' => 'updater']);
553 553
 		});
554
-		$this->listen('\OC\Updater', 'dbUpgrade', function () use($log) {
554
+		$this->listen('\OC\Updater', 'dbUpgrade', function() use($log) {
555 555
 			$log->info('\OC\Updater::dbUpgrade: Updated database', ['app' => 'updater']);
556 556
 		});
557
-		$this->listen('\OC\Updater', 'dbSimulateUpgradeBefore', function () use($log) {
557
+		$this->listen('\OC\Updater', 'dbSimulateUpgradeBefore', function() use($log) {
558 558
 			$log->info('\OC\Updater::dbSimulateUpgradeBefore: Checking whether the database schema can be updated (this can take a long time depending on the database size)', ['app' => 'updater']);
559 559
 		});
560
-		$this->listen('\OC\Updater', 'dbSimulateUpgrade', function () use($log) {
560
+		$this->listen('\OC\Updater', 'dbSimulateUpgrade', function() use($log) {
561 561
 			$log->info('\OC\Updater::dbSimulateUpgrade: Checked database schema update', ['app' => 'updater']);
562 562
 		});
563
-		$this->listen('\OC\Updater', 'incompatibleAppDisabled', function ($app) use($log) {
564
-			$log->info('\OC\Updater::incompatibleAppDisabled: Disabled incompatible app: ' . $app, ['app' => 'updater']);
563
+		$this->listen('\OC\Updater', 'incompatibleAppDisabled', function($app) use($log) {
564
+			$log->info('\OC\Updater::incompatibleAppDisabled: Disabled incompatible app: '.$app, ['app' => 'updater']);
565 565
 		});
566
-		$this->listen('\OC\Updater', 'checkAppStoreAppBefore', function ($app) use($log) {
567
-			$log->info('\OC\Updater::checkAppStoreAppBefore: Checking for update of app "' . $app . '" in appstore', ['app' => 'updater']);
566
+		$this->listen('\OC\Updater', 'checkAppStoreAppBefore', function($app) use($log) {
567
+			$log->info('\OC\Updater::checkAppStoreAppBefore: Checking for update of app "'.$app.'" in appstore', ['app' => 'updater']);
568 568
 		});
569
-		$this->listen('\OC\Updater', 'upgradeAppStoreApp', function ($app) use($log) {
570
-			$log->info('\OC\Updater::upgradeAppStoreApp: Update app "' . $app . '" from appstore', ['app' => 'updater']);
569
+		$this->listen('\OC\Updater', 'upgradeAppStoreApp', function($app) use($log) {
570
+			$log->info('\OC\Updater::upgradeAppStoreApp: Update app "'.$app.'" from appstore', ['app' => 'updater']);
571 571
 		});
572
-		$this->listen('\OC\Updater', 'checkAppStoreApp', function ($app) use($log) {
573
-			$log->info('\OC\Updater::checkAppStoreApp: Checked for update of app "' . $app . '" in appstore', ['app' => 'updater']);
572
+		$this->listen('\OC\Updater', 'checkAppStoreApp', function($app) use($log) {
573
+			$log->info('\OC\Updater::checkAppStoreApp: Checked for update of app "'.$app.'" in appstore', ['app' => 'updater']);
574 574
 		});
575
-		$this->listen('\OC\Updater', 'appUpgradeCheckBefore', function () use ($log) {
575
+		$this->listen('\OC\Updater', 'appUpgradeCheckBefore', function() use ($log) {
576 576
 			$log->info('\OC\Updater::appUpgradeCheckBefore: Checking updates of apps', ['app' => 'updater']);
577 577
 		});
578
-		$this->listen('\OC\Updater', 'appSimulateUpdate', function ($app) use ($log) {
579
-			$log->info('\OC\Updater::appSimulateUpdate: Checking whether the database schema for <' . $app . '> can be updated (this can take a long time depending on the database size)', ['app' => 'updater']);
578
+		$this->listen('\OC\Updater', 'appSimulateUpdate', function($app) use ($log) {
579
+			$log->info('\OC\Updater::appSimulateUpdate: Checking whether the database schema for <'.$app.'> can be updated (this can take a long time depending on the database size)', ['app' => 'updater']);
580 580
 		});
581
-		$this->listen('\OC\Updater', 'appUpgradeCheck', function () use ($log) {
581
+		$this->listen('\OC\Updater', 'appUpgradeCheck', function() use ($log) {
582 582
 			$log->info('\OC\Updater::appUpgradeCheck: Checked database schema update for apps', ['app' => 'updater']);
583 583
 		});
584
-		$this->listen('\OC\Updater', 'appUpgradeStarted', function ($app) use ($log) {
585
-			$log->info('\OC\Updater::appUpgradeStarted: Updating <' . $app . '> ...', ['app' => 'updater']);
584
+		$this->listen('\OC\Updater', 'appUpgradeStarted', function($app) use ($log) {
585
+			$log->info('\OC\Updater::appUpgradeStarted: Updating <'.$app.'> ...', ['app' => 'updater']);
586 586
 		});
587
-		$this->listen('\OC\Updater', 'appUpgrade', function ($app, $version) use ($log) {
588
-			$log->info('\OC\Updater::appUpgrade: Updated <' . $app . '> to ' . $version, ['app' => 'updater']);
587
+		$this->listen('\OC\Updater', 'appUpgrade', function($app, $version) use ($log) {
588
+			$log->info('\OC\Updater::appUpgrade: Updated <'.$app.'> to '.$version, ['app' => 'updater']);
589 589
 		});
590
-		$this->listen('\OC\Updater', 'failure', function ($message) use($log) {
591
-			$log->error('\OC\Updater::failure: ' . $message, ['app' => 'updater']);
590
+		$this->listen('\OC\Updater', 'failure', function($message) use($log) {
591
+			$log->error('\OC\Updater::failure: '.$message, ['app' => 'updater']);
592 592
 		});
593
-		$this->listen('\OC\Updater', 'setDebugLogLevel', function () use($log) {
593
+		$this->listen('\OC\Updater', 'setDebugLogLevel', function() use($log) {
594 594
 			$log->info('\OC\Updater::setDebugLogLevel: Set log level to debug', ['app' => 'updater']);
595 595
 		});
596
-		$this->listen('\OC\Updater', 'resetLogLevel', function ($logLevel, $logLevelName) use($log) {
597
-			$log->info('\OC\Updater::resetLogLevel: Reset log level to ' . $logLevelName . '(' . $logLevel . ')', ['app' => 'updater']);
596
+		$this->listen('\OC\Updater', 'resetLogLevel', function($logLevel, $logLevelName) use($log) {
597
+			$log->info('\OC\Updater::resetLogLevel: Reset log level to '.$logLevelName.'('.$logLevel.')', ['app' => 'updater']);
598 598
 		});
599
-		$this->listen('\OC\Updater', 'startCheckCodeIntegrity', function () use($log) {
599
+		$this->listen('\OC\Updater', 'startCheckCodeIntegrity', function() use($log) {
600 600
 			$log->info('\OC\Updater::startCheckCodeIntegrity: Starting code integrity check...', ['app' => 'updater']);
601 601
 		});
602
-		$this->listen('\OC\Updater', 'finishedCheckCodeIntegrity', function () use($log) {
602
+		$this->listen('\OC\Updater', 'finishedCheckCodeIntegrity', function() use($log) {
603 603
 			$log->info('\OC\Updater::finishedCheckCodeIntegrity: Finished code integrity check', ['app' => 'updater']);
604 604
 		});
605 605
 
Please login to merge, or discard this patch.
lib/private/App/AppManager.php 1 patch
Indentation   +409 added lines, -409 removed lines patch added patch discarded remove patch
@@ -44,413 +44,413 @@
 block discarded – undo
44 44
 
45 45
 class AppManager implements IAppManager {
46 46
 
47
-	/**
48
-	 * Apps with these types can not be enabled for certain groups only
49
-	 * @var string[]
50
-	 */
51
-	protected $protectedAppTypes = [
52
-		'filesystem',
53
-		'prelogin',
54
-		'authentication',
55
-		'logging',
56
-		'prevent_group_restriction',
57
-	];
58
-
59
-	/** @var IUserSession */
60
-	private $userSession;
61
-
62
-	/** @var AppConfig */
63
-	private $appConfig;
64
-
65
-	/** @var IGroupManager */
66
-	private $groupManager;
67
-
68
-	/** @var ICacheFactory */
69
-	private $memCacheFactory;
70
-
71
-	/** @var EventDispatcherInterface */
72
-	private $dispatcher;
73
-
74
-	/** @var string[] $appId => $enabled */
75
-	private $installedAppsCache;
76
-
77
-	/** @var string[] */
78
-	private $shippedApps;
79
-
80
-	/** @var string[] */
81
-	private $alwaysEnabled;
82
-
83
-	/** @var array */
84
-	private $appInfos = [];
85
-
86
-	/** @var array */
87
-	private $appVersions = [];
88
-
89
-	/**
90
-	 * @param IUserSession $userSession
91
-	 * @param AppConfig $appConfig
92
-	 * @param IGroupManager $groupManager
93
-	 * @param ICacheFactory $memCacheFactory
94
-	 * @param EventDispatcherInterface $dispatcher
95
-	 */
96
-	public function __construct(IUserSession $userSession,
97
-								AppConfig $appConfig,
98
-								IGroupManager $groupManager,
99
-								ICacheFactory $memCacheFactory,
100
-								EventDispatcherInterface $dispatcher) {
101
-		$this->userSession = $userSession;
102
-		$this->appConfig = $appConfig;
103
-		$this->groupManager = $groupManager;
104
-		$this->memCacheFactory = $memCacheFactory;
105
-		$this->dispatcher = $dispatcher;
106
-	}
107
-
108
-	/**
109
-	 * @return string[] $appId => $enabled
110
-	 */
111
-	private function getInstalledAppsValues() {
112
-		if (!$this->installedAppsCache) {
113
-			$values = $this->appConfig->getValues(false, 'enabled');
114
-
115
-			$alwaysEnabledApps = $this->getAlwaysEnabledApps();
116
-			foreach($alwaysEnabledApps as $appId) {
117
-				$values[$appId] = 'yes';
118
-			}
119
-
120
-			$this->installedAppsCache = array_filter($values, function ($value) {
121
-				return $value !== 'no';
122
-			});
123
-			ksort($this->installedAppsCache);
124
-		}
125
-		return $this->installedAppsCache;
126
-	}
127
-
128
-	/**
129
-	 * List all installed apps
130
-	 *
131
-	 * @return string[]
132
-	 */
133
-	public function getInstalledApps() {
134
-		return array_keys($this->getInstalledAppsValues());
135
-	}
136
-
137
-	/**
138
-	 * List all apps enabled for a user
139
-	 *
140
-	 * @param \OCP\IUser $user
141
-	 * @return string[]
142
-	 */
143
-	public function getEnabledAppsForUser(IUser $user) {
144
-		$apps = $this->getInstalledAppsValues();
145
-		$appsForUser = array_filter($apps, function ($enabled) use ($user) {
146
-			return $this->checkAppForUser($enabled, $user);
147
-		});
148
-		return array_keys($appsForUser);
149
-	}
150
-
151
-	/**
152
-	 * Check if an app is enabled for user
153
-	 *
154
-	 * @param string $appId
155
-	 * @param \OCP\IUser $user (optional) if not defined, the currently logged in user will be used
156
-	 * @return bool
157
-	 */
158
-	public function isEnabledForUser($appId, $user = null) {
159
-		if ($this->isAlwaysEnabled($appId)) {
160
-			return true;
161
-		}
162
-		if ($user === null) {
163
-			$user = $this->userSession->getUser();
164
-		}
165
-		$installedApps = $this->getInstalledAppsValues();
166
-		if (isset($installedApps[$appId])) {
167
-			return $this->checkAppForUser($installedApps[$appId], $user);
168
-		} else {
169
-			return false;
170
-		}
171
-	}
172
-
173
-	/**
174
-	 * @param string $enabled
175
-	 * @param IUser $user
176
-	 * @return bool
177
-	 */
178
-	private function checkAppForUser($enabled, $user) {
179
-		if ($enabled === 'yes') {
180
-			return true;
181
-		} elseif ($user === null) {
182
-			return false;
183
-		} else {
184
-			if(empty($enabled)){
185
-				return false;
186
-			}
187
-
188
-			$groupIds = json_decode($enabled);
189
-
190
-			if (!is_array($groupIds)) {
191
-				$jsonError = json_last_error();
192
-				\OC::$server->getLogger()->warning('AppManger::checkAppForUser - can\'t decode group IDs: ' . print_r($enabled, true) . ' - json error code: ' . $jsonError, ['app' => 'lib']);
193
-				return false;
194
-			}
195
-
196
-			$userGroups = $this->groupManager->getUserGroupIds($user);
197
-			foreach ($userGroups as $groupId) {
198
-				if (in_array($groupId, $groupIds, true)) {
199
-					return true;
200
-				}
201
-			}
202
-			return false;
203
-		}
204
-	}
205
-
206
-	/**
207
-	 * Check if an app is installed in the instance
208
-	 *
209
-	 * @param string $appId
210
-	 * @return bool
211
-	 */
212
-	public function isInstalled($appId) {
213
-		$installedApps = $this->getInstalledAppsValues();
214
-		return isset($installedApps[$appId]);
215
-	}
216
-
217
-	/**
218
-	 * Enable an app for every user
219
-	 *
220
-	 * @param string $appId
221
-	 * @throws AppPathNotFoundException
222
-	 */
223
-	public function enableApp($appId) {
224
-		// Check if app exists
225
-		$this->getAppPath($appId);
226
-
227
-		$this->installedAppsCache[$appId] = 'yes';
228
-		$this->appConfig->setValue($appId, 'enabled', 'yes');
229
-		$this->dispatcher->dispatch(ManagerEvent::EVENT_APP_ENABLE, new ManagerEvent(
230
-			ManagerEvent::EVENT_APP_ENABLE, $appId
231
-		));
232
-		$this->clearAppsCache();
233
-	}
234
-
235
-	/**
236
-	 * Whether a list of types contains a protected app type
237
-	 *
238
-	 * @param string[] $types
239
-	 * @return bool
240
-	 */
241
-	public function hasProtectedAppType($types) {
242
-		if (empty($types)) {
243
-			return false;
244
-		}
245
-
246
-		$protectedTypes = array_intersect($this->protectedAppTypes, $types);
247
-		return !empty($protectedTypes);
248
-	}
249
-
250
-	/**
251
-	 * Enable an app only for specific groups
252
-	 *
253
-	 * @param string $appId
254
-	 * @param \OCP\IGroup[] $groups
255
-	 * @throws \Exception if app can't be enabled for groups
256
-	 */
257
-	public function enableAppForGroups($appId, $groups) {
258
-		$info = $this->getAppInfo($appId);
259
-		if (!empty($info['types'])) {
260
-			$protectedTypes = array_intersect($this->protectedAppTypes, $info['types']);
261
-			if (!empty($protectedTypes)) {
262
-				throw new \Exception("$appId can't be enabled for groups.");
263
-			}
264
-		}
265
-
266
-		$groupIds = array_map(function ($group) {
267
-			/** @var \OCP\IGroup $group */
268
-			return $group->getGID();
269
-		}, $groups);
270
-		$this->installedAppsCache[$appId] = json_encode($groupIds);
271
-		$this->appConfig->setValue($appId, 'enabled', json_encode($groupIds));
272
-		$this->dispatcher->dispatch(ManagerEvent::EVENT_APP_ENABLE_FOR_GROUPS, new ManagerEvent(
273
-			ManagerEvent::EVENT_APP_ENABLE_FOR_GROUPS, $appId, $groups
274
-		));
275
-		$this->clearAppsCache();
276
-	}
277
-
278
-	/**
279
-	 * Disable an app for every user
280
-	 *
281
-	 * @param string $appId
282
-	 * @throws \Exception if app can't be disabled
283
-	 */
284
-	public function disableApp($appId) {
285
-		if ($this->isAlwaysEnabled($appId)) {
286
-			throw new \Exception("$appId can't be disabled.");
287
-		}
288
-		unset($this->installedAppsCache[$appId]);
289
-		$this->appConfig->setValue($appId, 'enabled', 'no');
290
-
291
-		// run uninstall steps
292
-		$appData = $this->getAppInfo($appId);
293
-		if (!is_null($appData)) {
294
-			\OC_App::executeRepairSteps($appId, $appData['repair-steps']['uninstall']);
295
-		}
296
-
297
-		$this->dispatcher->dispatch(ManagerEvent::EVENT_APP_DISABLE, new ManagerEvent(
298
-			ManagerEvent::EVENT_APP_DISABLE, $appId
299
-		));
300
-		$this->clearAppsCache();
301
-	}
302
-
303
-	/**
304
-	 * Get the directory for the given app.
305
-	 *
306
-	 * @param string $appId
307
-	 * @return string
308
-	 * @throws AppPathNotFoundException if app folder can't be found
309
-	 */
310
-	public function getAppPath($appId) {
311
-		$appPath = \OC_App::getAppPath($appId);
312
-		if($appPath === false) {
313
-			throw new AppPathNotFoundException('Could not find path for ' . $appId);
314
-		}
315
-		return $appPath;
316
-	}
317
-
318
-	/**
319
-	 * Clear the cached list of apps when enabling/disabling an app
320
-	 */
321
-	public function clearAppsCache() {
322
-		$settingsMemCache = $this->memCacheFactory->createDistributed('settings');
323
-		$settingsMemCache->clear('listApps');
324
-	}
325
-
326
-	/**
327
-	 * Returns a list of apps that need upgrade
328
-	 *
329
-	 * @param string $version Nextcloud version as array of version components
330
-	 * @return array list of app info from apps that need an upgrade
331
-	 *
332
-	 * @internal
333
-	 */
334
-	public function getAppsNeedingUpgrade($version) {
335
-		$appsToUpgrade = [];
336
-		$apps = $this->getInstalledApps();
337
-		foreach ($apps as $appId) {
338
-			$appInfo = $this->getAppInfo($appId);
339
-			$appDbVersion = $this->appConfig->getValue($appId, 'installed_version');
340
-			if ($appDbVersion
341
-				&& isset($appInfo['version'])
342
-				&& version_compare($appInfo['version'], $appDbVersion, '>')
343
-				&& \OC_App::isAppCompatible($version, $appInfo)
344
-			) {
345
-				$appsToUpgrade[] = $appInfo;
346
-			}
347
-		}
348
-
349
-		return $appsToUpgrade;
350
-	}
351
-
352
-	/**
353
-	 * Returns the app information from "appinfo/info.xml".
354
-	 *
355
-	 * @param string $appId app id
356
-	 *
357
-	 * @param bool $path
358
-	 * @param null $lang
359
-	 * @return array app info
360
-	 */
361
-	public function getAppInfo(string $appId, bool $path = false, $lang = null) {
362
-		if ($path) {
363
-			$file = $appId;
364
-		} else {
365
-			if ($lang === null && isset($this->appInfos[$appId])) {
366
-				return $this->appInfos[$appId];
367
-			}
368
-			try {
369
-				$appPath = $this->getAppPath($appId);
370
-			} catch (AppPathNotFoundException $e) {
371
-				return null;
372
-			}
373
-			$file = $appPath . '/appinfo/info.xml';
374
-		}
375
-
376
-		$parser = new InfoParser($this->memCacheFactory->createLocal('core.appinfo'));
377
-		$data = $parser->parse($file);
378
-
379
-		if (is_array($data)) {
380
-			$data = \OC_App::parseAppInfo($data, $lang);
381
-		}
382
-
383
-		if ($lang === null) {
384
-			$this->appInfos[$appId] = $data;
385
-		}
386
-
387
-		return $data;
388
-	}
389
-
390
-	public function getAppVersion(string $appId, bool $useCache = true): string {
391
-		if(!$useCache || !isset($this->appVersions[$appId])) {
392
-			$appInfo = \OC::$server->getAppManager()->getAppInfo($appId);
393
-			$this->appVersions[$appId] = ($appInfo !== null && isset($appInfo['version'])) ? $appInfo['version'] : '0';
394
-		}
395
-		return $this->appVersions[$appId];
396
-	}
397
-
398
-	/**
399
-	 * Returns a list of apps incompatible with the given version
400
-	 *
401
-	 * @param string $version Nextcloud version as array of version components
402
-	 *
403
-	 * @return array list of app info from incompatible apps
404
-	 *
405
-	 * @internal
406
-	 */
407
-	public function getIncompatibleApps(string $version): array {
408
-		$apps = $this->getInstalledApps();
409
-		$incompatibleApps = array();
410
-		foreach ($apps as $appId) {
411
-			$info = $this->getAppInfo($appId);
412
-			if (!\OC_App::isAppCompatible($version, $info)) {
413
-				$incompatibleApps[] = $info;
414
-			}
415
-		}
416
-		return $incompatibleApps;
417
-	}
418
-
419
-	/**
420
-	 * @inheritdoc
421
-	 * In case you change this method, also change \OC\App\CodeChecker\InfoChecker::isShipped()
422
-	 */
423
-	public function isShipped($appId) {
424
-		$this->loadShippedJson();
425
-		return in_array($appId, $this->shippedApps, true);
426
-	}
427
-
428
-	private function isAlwaysEnabled($appId) {
429
-		$alwaysEnabled = $this->getAlwaysEnabledApps();
430
-		return in_array($appId, $alwaysEnabled, true);
431
-	}
432
-
433
-	/**
434
-	 * In case you change this method, also change \OC\App\CodeChecker\InfoChecker::loadShippedJson()
435
-	 * @throws \Exception
436
-	 */
437
-	private function loadShippedJson() {
438
-		if ($this->shippedApps === null) {
439
-			$shippedJson = \OC::$SERVERROOT . '/core/shipped.json';
440
-			if (!file_exists($shippedJson)) {
441
-				throw new \Exception("File not found: $shippedJson");
442
-			}
443
-			$content = json_decode(file_get_contents($shippedJson), true);
444
-			$this->shippedApps = $content['shippedApps'];
445
-			$this->alwaysEnabled = $content['alwaysEnabled'];
446
-		}
447
-	}
448
-
449
-	/**
450
-	 * @inheritdoc
451
-	 */
452
-	public function getAlwaysEnabledApps() {
453
-		$this->loadShippedJson();
454
-		return $this->alwaysEnabled;
455
-	}
47
+    /**
48
+     * Apps with these types can not be enabled for certain groups only
49
+     * @var string[]
50
+     */
51
+    protected $protectedAppTypes = [
52
+        'filesystem',
53
+        'prelogin',
54
+        'authentication',
55
+        'logging',
56
+        'prevent_group_restriction',
57
+    ];
58
+
59
+    /** @var IUserSession */
60
+    private $userSession;
61
+
62
+    /** @var AppConfig */
63
+    private $appConfig;
64
+
65
+    /** @var IGroupManager */
66
+    private $groupManager;
67
+
68
+    /** @var ICacheFactory */
69
+    private $memCacheFactory;
70
+
71
+    /** @var EventDispatcherInterface */
72
+    private $dispatcher;
73
+
74
+    /** @var string[] $appId => $enabled */
75
+    private $installedAppsCache;
76
+
77
+    /** @var string[] */
78
+    private $shippedApps;
79
+
80
+    /** @var string[] */
81
+    private $alwaysEnabled;
82
+
83
+    /** @var array */
84
+    private $appInfos = [];
85
+
86
+    /** @var array */
87
+    private $appVersions = [];
88
+
89
+    /**
90
+     * @param IUserSession $userSession
91
+     * @param AppConfig $appConfig
92
+     * @param IGroupManager $groupManager
93
+     * @param ICacheFactory $memCacheFactory
94
+     * @param EventDispatcherInterface $dispatcher
95
+     */
96
+    public function __construct(IUserSession $userSession,
97
+                                AppConfig $appConfig,
98
+                                IGroupManager $groupManager,
99
+                                ICacheFactory $memCacheFactory,
100
+                                EventDispatcherInterface $dispatcher) {
101
+        $this->userSession = $userSession;
102
+        $this->appConfig = $appConfig;
103
+        $this->groupManager = $groupManager;
104
+        $this->memCacheFactory = $memCacheFactory;
105
+        $this->dispatcher = $dispatcher;
106
+    }
107
+
108
+    /**
109
+     * @return string[] $appId => $enabled
110
+     */
111
+    private function getInstalledAppsValues() {
112
+        if (!$this->installedAppsCache) {
113
+            $values = $this->appConfig->getValues(false, 'enabled');
114
+
115
+            $alwaysEnabledApps = $this->getAlwaysEnabledApps();
116
+            foreach($alwaysEnabledApps as $appId) {
117
+                $values[$appId] = 'yes';
118
+            }
119
+
120
+            $this->installedAppsCache = array_filter($values, function ($value) {
121
+                return $value !== 'no';
122
+            });
123
+            ksort($this->installedAppsCache);
124
+        }
125
+        return $this->installedAppsCache;
126
+    }
127
+
128
+    /**
129
+     * List all installed apps
130
+     *
131
+     * @return string[]
132
+     */
133
+    public function getInstalledApps() {
134
+        return array_keys($this->getInstalledAppsValues());
135
+    }
136
+
137
+    /**
138
+     * List all apps enabled for a user
139
+     *
140
+     * @param \OCP\IUser $user
141
+     * @return string[]
142
+     */
143
+    public function getEnabledAppsForUser(IUser $user) {
144
+        $apps = $this->getInstalledAppsValues();
145
+        $appsForUser = array_filter($apps, function ($enabled) use ($user) {
146
+            return $this->checkAppForUser($enabled, $user);
147
+        });
148
+        return array_keys($appsForUser);
149
+    }
150
+
151
+    /**
152
+     * Check if an app is enabled for user
153
+     *
154
+     * @param string $appId
155
+     * @param \OCP\IUser $user (optional) if not defined, the currently logged in user will be used
156
+     * @return bool
157
+     */
158
+    public function isEnabledForUser($appId, $user = null) {
159
+        if ($this->isAlwaysEnabled($appId)) {
160
+            return true;
161
+        }
162
+        if ($user === null) {
163
+            $user = $this->userSession->getUser();
164
+        }
165
+        $installedApps = $this->getInstalledAppsValues();
166
+        if (isset($installedApps[$appId])) {
167
+            return $this->checkAppForUser($installedApps[$appId], $user);
168
+        } else {
169
+            return false;
170
+        }
171
+    }
172
+
173
+    /**
174
+     * @param string $enabled
175
+     * @param IUser $user
176
+     * @return bool
177
+     */
178
+    private function checkAppForUser($enabled, $user) {
179
+        if ($enabled === 'yes') {
180
+            return true;
181
+        } elseif ($user === null) {
182
+            return false;
183
+        } else {
184
+            if(empty($enabled)){
185
+                return false;
186
+            }
187
+
188
+            $groupIds = json_decode($enabled);
189
+
190
+            if (!is_array($groupIds)) {
191
+                $jsonError = json_last_error();
192
+                \OC::$server->getLogger()->warning('AppManger::checkAppForUser - can\'t decode group IDs: ' . print_r($enabled, true) . ' - json error code: ' . $jsonError, ['app' => 'lib']);
193
+                return false;
194
+            }
195
+
196
+            $userGroups = $this->groupManager->getUserGroupIds($user);
197
+            foreach ($userGroups as $groupId) {
198
+                if (in_array($groupId, $groupIds, true)) {
199
+                    return true;
200
+                }
201
+            }
202
+            return false;
203
+        }
204
+    }
205
+
206
+    /**
207
+     * Check if an app is installed in the instance
208
+     *
209
+     * @param string $appId
210
+     * @return bool
211
+     */
212
+    public function isInstalled($appId) {
213
+        $installedApps = $this->getInstalledAppsValues();
214
+        return isset($installedApps[$appId]);
215
+    }
216
+
217
+    /**
218
+     * Enable an app for every user
219
+     *
220
+     * @param string $appId
221
+     * @throws AppPathNotFoundException
222
+     */
223
+    public function enableApp($appId) {
224
+        // Check if app exists
225
+        $this->getAppPath($appId);
226
+
227
+        $this->installedAppsCache[$appId] = 'yes';
228
+        $this->appConfig->setValue($appId, 'enabled', 'yes');
229
+        $this->dispatcher->dispatch(ManagerEvent::EVENT_APP_ENABLE, new ManagerEvent(
230
+            ManagerEvent::EVENT_APP_ENABLE, $appId
231
+        ));
232
+        $this->clearAppsCache();
233
+    }
234
+
235
+    /**
236
+     * Whether a list of types contains a protected app type
237
+     *
238
+     * @param string[] $types
239
+     * @return bool
240
+     */
241
+    public function hasProtectedAppType($types) {
242
+        if (empty($types)) {
243
+            return false;
244
+        }
245
+
246
+        $protectedTypes = array_intersect($this->protectedAppTypes, $types);
247
+        return !empty($protectedTypes);
248
+    }
249
+
250
+    /**
251
+     * Enable an app only for specific groups
252
+     *
253
+     * @param string $appId
254
+     * @param \OCP\IGroup[] $groups
255
+     * @throws \Exception if app can't be enabled for groups
256
+     */
257
+    public function enableAppForGroups($appId, $groups) {
258
+        $info = $this->getAppInfo($appId);
259
+        if (!empty($info['types'])) {
260
+            $protectedTypes = array_intersect($this->protectedAppTypes, $info['types']);
261
+            if (!empty($protectedTypes)) {
262
+                throw new \Exception("$appId can't be enabled for groups.");
263
+            }
264
+        }
265
+
266
+        $groupIds = array_map(function ($group) {
267
+            /** @var \OCP\IGroup $group */
268
+            return $group->getGID();
269
+        }, $groups);
270
+        $this->installedAppsCache[$appId] = json_encode($groupIds);
271
+        $this->appConfig->setValue($appId, 'enabled', json_encode($groupIds));
272
+        $this->dispatcher->dispatch(ManagerEvent::EVENT_APP_ENABLE_FOR_GROUPS, new ManagerEvent(
273
+            ManagerEvent::EVENT_APP_ENABLE_FOR_GROUPS, $appId, $groups
274
+        ));
275
+        $this->clearAppsCache();
276
+    }
277
+
278
+    /**
279
+     * Disable an app for every user
280
+     *
281
+     * @param string $appId
282
+     * @throws \Exception if app can't be disabled
283
+     */
284
+    public function disableApp($appId) {
285
+        if ($this->isAlwaysEnabled($appId)) {
286
+            throw new \Exception("$appId can't be disabled.");
287
+        }
288
+        unset($this->installedAppsCache[$appId]);
289
+        $this->appConfig->setValue($appId, 'enabled', 'no');
290
+
291
+        // run uninstall steps
292
+        $appData = $this->getAppInfo($appId);
293
+        if (!is_null($appData)) {
294
+            \OC_App::executeRepairSteps($appId, $appData['repair-steps']['uninstall']);
295
+        }
296
+
297
+        $this->dispatcher->dispatch(ManagerEvent::EVENT_APP_DISABLE, new ManagerEvent(
298
+            ManagerEvent::EVENT_APP_DISABLE, $appId
299
+        ));
300
+        $this->clearAppsCache();
301
+    }
302
+
303
+    /**
304
+     * Get the directory for the given app.
305
+     *
306
+     * @param string $appId
307
+     * @return string
308
+     * @throws AppPathNotFoundException if app folder can't be found
309
+     */
310
+    public function getAppPath($appId) {
311
+        $appPath = \OC_App::getAppPath($appId);
312
+        if($appPath === false) {
313
+            throw new AppPathNotFoundException('Could not find path for ' . $appId);
314
+        }
315
+        return $appPath;
316
+    }
317
+
318
+    /**
319
+     * Clear the cached list of apps when enabling/disabling an app
320
+     */
321
+    public function clearAppsCache() {
322
+        $settingsMemCache = $this->memCacheFactory->createDistributed('settings');
323
+        $settingsMemCache->clear('listApps');
324
+    }
325
+
326
+    /**
327
+     * Returns a list of apps that need upgrade
328
+     *
329
+     * @param string $version Nextcloud version as array of version components
330
+     * @return array list of app info from apps that need an upgrade
331
+     *
332
+     * @internal
333
+     */
334
+    public function getAppsNeedingUpgrade($version) {
335
+        $appsToUpgrade = [];
336
+        $apps = $this->getInstalledApps();
337
+        foreach ($apps as $appId) {
338
+            $appInfo = $this->getAppInfo($appId);
339
+            $appDbVersion = $this->appConfig->getValue($appId, 'installed_version');
340
+            if ($appDbVersion
341
+                && isset($appInfo['version'])
342
+                && version_compare($appInfo['version'], $appDbVersion, '>')
343
+                && \OC_App::isAppCompatible($version, $appInfo)
344
+            ) {
345
+                $appsToUpgrade[] = $appInfo;
346
+            }
347
+        }
348
+
349
+        return $appsToUpgrade;
350
+    }
351
+
352
+    /**
353
+     * Returns the app information from "appinfo/info.xml".
354
+     *
355
+     * @param string $appId app id
356
+     *
357
+     * @param bool $path
358
+     * @param null $lang
359
+     * @return array app info
360
+     */
361
+    public function getAppInfo(string $appId, bool $path = false, $lang = null) {
362
+        if ($path) {
363
+            $file = $appId;
364
+        } else {
365
+            if ($lang === null && isset($this->appInfos[$appId])) {
366
+                return $this->appInfos[$appId];
367
+            }
368
+            try {
369
+                $appPath = $this->getAppPath($appId);
370
+            } catch (AppPathNotFoundException $e) {
371
+                return null;
372
+            }
373
+            $file = $appPath . '/appinfo/info.xml';
374
+        }
375
+
376
+        $parser = new InfoParser($this->memCacheFactory->createLocal('core.appinfo'));
377
+        $data = $parser->parse($file);
378
+
379
+        if (is_array($data)) {
380
+            $data = \OC_App::parseAppInfo($data, $lang);
381
+        }
382
+
383
+        if ($lang === null) {
384
+            $this->appInfos[$appId] = $data;
385
+        }
386
+
387
+        return $data;
388
+    }
389
+
390
+    public function getAppVersion(string $appId, bool $useCache = true): string {
391
+        if(!$useCache || !isset($this->appVersions[$appId])) {
392
+            $appInfo = \OC::$server->getAppManager()->getAppInfo($appId);
393
+            $this->appVersions[$appId] = ($appInfo !== null && isset($appInfo['version'])) ? $appInfo['version'] : '0';
394
+        }
395
+        return $this->appVersions[$appId];
396
+    }
397
+
398
+    /**
399
+     * Returns a list of apps incompatible with the given version
400
+     *
401
+     * @param string $version Nextcloud version as array of version components
402
+     *
403
+     * @return array list of app info from incompatible apps
404
+     *
405
+     * @internal
406
+     */
407
+    public function getIncompatibleApps(string $version): array {
408
+        $apps = $this->getInstalledApps();
409
+        $incompatibleApps = array();
410
+        foreach ($apps as $appId) {
411
+            $info = $this->getAppInfo($appId);
412
+            if (!\OC_App::isAppCompatible($version, $info)) {
413
+                $incompatibleApps[] = $info;
414
+            }
415
+        }
416
+        return $incompatibleApps;
417
+    }
418
+
419
+    /**
420
+     * @inheritdoc
421
+     * In case you change this method, also change \OC\App\CodeChecker\InfoChecker::isShipped()
422
+     */
423
+    public function isShipped($appId) {
424
+        $this->loadShippedJson();
425
+        return in_array($appId, $this->shippedApps, true);
426
+    }
427
+
428
+    private function isAlwaysEnabled($appId) {
429
+        $alwaysEnabled = $this->getAlwaysEnabledApps();
430
+        return in_array($appId, $alwaysEnabled, true);
431
+    }
432
+
433
+    /**
434
+     * In case you change this method, also change \OC\App\CodeChecker\InfoChecker::loadShippedJson()
435
+     * @throws \Exception
436
+     */
437
+    private function loadShippedJson() {
438
+        if ($this->shippedApps === null) {
439
+            $shippedJson = \OC::$SERVERROOT . '/core/shipped.json';
440
+            if (!file_exists($shippedJson)) {
441
+                throw new \Exception("File not found: $shippedJson");
442
+            }
443
+            $content = json_decode(file_get_contents($shippedJson), true);
444
+            $this->shippedApps = $content['shippedApps'];
445
+            $this->alwaysEnabled = $content['alwaysEnabled'];
446
+        }
447
+    }
448
+
449
+    /**
450
+     * @inheritdoc
451
+     */
452
+    public function getAlwaysEnabledApps() {
453
+        $this->loadShippedJson();
454
+        return $this->alwaysEnabled;
455
+    }
456 456
 }
Please login to merge, or discard this patch.