Passed
Push — master ( 9af8c0...c15172 )
by Roeland
15:34 queued 11s
created

Installer::isUpdateAvailable()   C

Complexity

Conditions 12
Paths 36

Size

Total Lines 39
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 12
eloc 23
nc 36
nop 2
dl 0
loc 39
rs 6.9666
c 0
b 0
f 0

How to fix   Complexity   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
/**
3
 * @copyright Copyright (c) 2016, ownCloud, Inc.
4
 * @copyright Copyright (c) 2016, Lukas Reschke <[email protected]>
5
 *
6
 * @author Arthur Schiwon <[email protected]>
7
 * @author Brice Maron <[email protected]>
8
 * @author Christoph Wurst <[email protected]>
9
 * @author Daniel Kesselberg <[email protected]>
10
 * @author Frank Karlitschek <[email protected]>
11
 * @author Georg Ehrke <[email protected]>
12
 * @author Joas Schilling <[email protected]>
13
 * @author John Molakvoæ (skjnldsv) <[email protected]>
14
 * @author Julius Härtl <[email protected]>
15
 * @author Kamil Domanski <[email protected]>
16
 * @author Lukas Reschke <[email protected]>
17
 * @author Markus Staab <[email protected]>
18
 * @author Morris Jobke <[email protected]>
19
 * @author Robin Appelman <[email protected]>
20
 * @author Roeland Jago Douma <[email protected]>
21
 * @author root "root@oc.(none)"
22
 * @author Thomas Müller <[email protected]>
23
 * @author Thomas Tanghus <[email protected]>
24
 *
25
 * @license AGPL-3.0
26
 *
27
 * This code is free software: you can redistribute it and/or modify
28
 * it under the terms of the GNU Affero General Public License, version 3,
29
 * as published by the Free Software Foundation.
30
 *
31
 * This program is distributed in the hope that it will be useful,
32
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
33
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
34
 * GNU Affero General Public License for more details.
35
 *
36
 * You should have received a copy of the GNU Affero General Public License, version 3,
37
 * along with this program. If not, see <http://www.gnu.org/licenses/>
38
 *
39
 */
40
41
namespace OC;
42
43
use Doctrine\DBAL\Exception\TableExistsException;
44
use OC\App\AppStore\Bundles\Bundle;
45
use OC\App\AppStore\Fetcher\AppFetcher;
46
use OC\AppFramework\Bootstrap\Coordinator;
47
use OC\Archive\TAR;
48
use OC\DB\Connection;
49
use OC\DB\MigrationService;
50
use OC_App;
51
use OC_Helper;
52
use OCP\Http\Client\IClientService;
53
use OCP\IConfig;
54
use OCP\ILogger;
55
use OCP\ITempManager;
56
use phpseclib\File\X509;
57
58
/**
59
 * This class provides the functionality needed to install, update and remove apps
60
 */
61
class Installer {
62
	/** @var AppFetcher */
63
	private $appFetcher;
64
	/** @var IClientService */
65
	private $clientService;
66
	/** @var ITempManager */
67
	private $tempManager;
68
	/** @var ILogger */
69
	private $logger;
70
	/** @var IConfig */
71
	private $config;
72
	/** @var array - for caching the result of app fetcher */
73
	private $apps = null;
74
	/** @var bool|null - for caching the result of the ready status */
75
	private $isInstanceReadyForUpdates = null;
76
	/** @var bool */
77
	private $isCLI;
78
79
	/**
80
	 * @param AppFetcher $appFetcher
81
	 * @param IClientService $clientService
82
	 * @param ITempManager $tempManager
83
	 * @param ILogger $logger
84
	 * @param IConfig $config
85
	 */
86
	public function __construct(
87
		AppFetcher $appFetcher,
88
		IClientService $clientService,
89
		ITempManager $tempManager,
90
		ILogger $logger,
91
		IConfig $config,
92
		bool $isCLI
93
	) {
94
		$this->appFetcher = $appFetcher;
95
		$this->clientService = $clientService;
96
		$this->tempManager = $tempManager;
97
		$this->logger = $logger;
98
		$this->config = $config;
99
		$this->isCLI = $isCLI;
100
	}
101
102
	/**
103
	 * Installs an app that is located in one of the app folders already
104
	 *
105
	 * @param string $appId App to install
106
	 * @param bool $forceEnable
107
	 * @throws \Exception
108
	 * @return string app ID
109
	 */
110
	public function installApp(string $appId, bool $forceEnable = false): string {
111
		$app = \OC_App::findAppInDirectories($appId);
112
		if ($app === false) {
113
			throw new \Exception('App not found in any app directory');
114
		}
115
116
		$basedir = $app['path'].'/'.$appId;
117
118
		if (is_file($basedir . '/appinfo/database.xml')) {
119
			throw new \Exception('The appinfo/database.xml file is not longer supported. Used in ' . $appId);
120
		}
121
122
		$info = OC_App::getAppInfo($basedir.'/appinfo/info.xml', true);
123
124
		$l = \OC::$server->getL10N('core');
125
126
		if (!is_array($info)) {
127
			throw new \Exception(
128
				$l->t('App "%s" cannot be installed because appinfo file cannot be read.',
129
					[$appId]
130
				)
131
			);
132
		}
133
134
		$ignoreMaxApps = $this->config->getSystemValue('app_install_overwrite', []);
135
		$ignoreMax = $forceEnable || in_array($appId, $ignoreMaxApps, true);
136
137
		$version = implode('.', \OCP\Util::getVersion());
138
		if (!\OC_App::isAppCompatible($version, $info, $ignoreMax)) {
139
			throw new \Exception(
140
				// TODO $l
141
				$l->t('App "%s" cannot be installed because it is not compatible with this version of the server.',
142
					[$info['name']]
143
				)
144
			);
145
		}
146
147
		// check for required dependencies
148
		\OC_App::checkAppDependencies($this->config, $l, $info, $ignoreMax);
149
		/** @var Coordinator $coordinator */
150
		$coordinator = \OC::$server->get(Coordinator::class);
151
		$coordinator->runLazyRegistration($appId);
152
		\OC_App::registerAutoloading($appId, $basedir);
153
154
		$previousVersion = $this->config->getAppValue($info['id'], 'installed_version', false);
0 ignored issues
show
Bug introduced by
false of type false is incompatible with the type string expected by parameter $default of OCP\IConfig::getAppValue(). ( Ignorable by Annotation )

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

154
		$previousVersion = $this->config->getAppValue($info['id'], 'installed_version', /** @scrutinizer ignore-type */ false);
Loading history...
155
		if ($previousVersion) {
156
			OC_App::executeRepairSteps($appId, $info['repair-steps']['pre-migration']);
157
		}
158
159
		//install the database
160
		$ms = new MigrationService($info['id'], \OC::$server->get(Connection::class));
161
		$ms->migrate('latest', true);
162
163
		if ($previousVersion) {
164
			OC_App::executeRepairSteps($appId, $info['repair-steps']['post-migration']);
165
		}
166
167
		\OC_App::setupBackgroundJobs($info['background-jobs']);
168
169
		//run appinfo/install.php
170
		self::includeAppScript($basedir . '/appinfo/install.php');
171
172
		$appData = OC_App::getAppInfo($appId);
173
		OC_App::executeRepairSteps($appId, $appData['repair-steps']['install']);
174
175
		//set the installed version
176
		\OC::$server->getConfig()->setAppValue($info['id'], 'installed_version', OC_App::getAppVersion($info['id'], false));
177
		\OC::$server->getConfig()->setAppValue($info['id'], 'enabled', 'no');
178
179
		//set remote/public handlers
180
		foreach ($info['remote'] as $name => $path) {
181
			\OC::$server->getConfig()->setAppValue('core', 'remote_'.$name, $info['id'].'/'.$path);
182
		}
183
		foreach ($info['public'] as $name => $path) {
184
			\OC::$server->getConfig()->setAppValue('core', 'public_'.$name, $info['id'].'/'.$path);
185
		}
186
187
		OC_App::setAppTypes($info['id']);
188
189
		return $info['id'];
190
	}
191
192
	/**
193
	 * Updates the specified app from the appstore
194
	 *
195
	 * @param string $appId
196
	 * @param bool [$allowUnstable] Allow unstable releases
0 ignored issues
show
Documentation Bug introduced by
The doc comment [$allowUnstable] at position 0 could not be parsed: Unknown type name '[' at position 0 in [$allowUnstable].
Loading history...
197
	 * @return bool
198
	 */
199
	public function updateAppstoreApp($appId, $allowUnstable = false) {
200
		if ($this->isUpdateAvailable($appId, $allowUnstable)) {
201
			try {
202
				$this->downloadApp($appId, $allowUnstable);
203
			} catch (\Exception $e) {
204
				$this->logger->logException($e, [
205
					'level' => ILogger::ERROR,
0 ignored issues
show
Deprecated Code introduced by
The constant OCP\ILogger::ERROR has been deprecated: 20.0.0 ( Ignorable by Annotation )

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

205
					'level' => /** @scrutinizer ignore-deprecated */ ILogger::ERROR,

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

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

Loading history...
206
					'app' => 'core',
207
				]);
208
				return false;
209
			}
210
			return OC_App::updateApp($appId);
211
		}
212
213
		return false;
214
	}
215
216
	/**
217
	 * Split the certificate file in individual certs
218
	 *
219
	 * @param string $cert
220
	 * @return string[]
221
	 */
222
	private function splitCerts(string $cert): array {
223
		preg_match_all('([\-]{3,}[\S\ ]+?[\-]{3,}[\S\s]+?[\-]{3,}[\S\ ]+?[\-]{3,})', $cert, $matches);
224
225
		return $matches[0];
226
	}
227
228
	/**
229
	 * Downloads an app and puts it into the app directory
230
	 *
231
	 * @param string $appId
232
	 * @param bool [$allowUnstable]
0 ignored issues
show
Documentation Bug introduced by
The doc comment [$allowUnstable] at position 0 could not be parsed: Unknown type name '[' at position 0 in [$allowUnstable].
Loading history...
233
	 *
234
	 * @throws \Exception If the installation was not successful
235
	 */
236
	public function downloadApp($appId, $allowUnstable = false) {
237
		$appId = strtolower($appId);
238
239
		$apps = $this->appFetcher->get($allowUnstable);
240
		foreach ($apps as $app) {
241
			if ($app['id'] === $appId) {
242
				// Load the certificate
243
				$certificate = new X509();
244
				$rootCrt = file_get_contents(__DIR__ . '/../../resources/codesigning/root.crt');
245
				$rootCrts = $this->splitCerts($rootCrt);
246
				foreach ($rootCrts as $rootCrt) {
247
					$certificate->loadCA($rootCrt);
248
				}
249
				$loadedCertificate = $certificate->loadX509($app['certificate']);
250
251
				// Verify if the certificate has been revoked
252
				$crl = new X509();
253
				foreach ($rootCrts as $rootCrt) {
254
					$crl->loadCA($rootCrt);
255
				}
256
				$crl->loadCRL(file_get_contents(__DIR__ . '/../../resources/codesigning/root.crl'));
257
				if ($crl->validateSignature() !== true) {
258
					throw new \Exception('Could not validate CRL signature');
259
				}
260
				$csn = $loadedCertificate['tbsCertificate']['serialNumber']->toString();
261
				$revoked = $crl->getRevoked($csn);
262
				if ($revoked !== false) {
263
					throw new \Exception(
264
						sprintf(
265
							'Certificate "%s" has been revoked',
266
							$csn
267
						)
268
					);
269
				}
270
271
				// Verify if the certificate has been issued by the Nextcloud Code Authority CA
272
				if ($certificate->validateSignature() !== true) {
273
					throw new \Exception(
274
						sprintf(
275
							'App with id %s has a certificate not issued by a trusted Code Signing Authority',
276
							$appId
277
						)
278
					);
279
				}
280
281
				// Verify if the certificate is issued for the requested app id
282
				$certInfo = openssl_x509_parse($app['certificate']);
283
				if (!isset($certInfo['subject']['CN'])) {
284
					throw new \Exception(
285
						sprintf(
286
							'App with id %s has a cert with no CN',
287
							$appId
288
						)
289
					);
290
				}
291
				if ($certInfo['subject']['CN'] !== $appId) {
292
					throw new \Exception(
293
						sprintf(
294
							'App with id %s has a cert issued to %s',
295
							$appId,
296
							$certInfo['subject']['CN']
297
						)
298
					);
299
				}
300
301
				// Download the release
302
				$tempFile = $this->tempManager->getTemporaryFile('.tar.gz');
303
				$timeout = $this->isCLI ? 0 : 120;
304
				$client = $this->clientService->newClient();
305
				$client->get($app['releases'][0]['download'], ['sink' => $tempFile, 'timeout' => $timeout]);
306
307
				// Check if the signature actually matches the downloaded content
308
				$certificate = openssl_get_publickey($app['certificate']);
309
				$verified = (bool)openssl_verify(file_get_contents($tempFile), base64_decode($app['releases'][0]['signature']), $certificate, OPENSSL_ALGO_SHA512);
310
				openssl_free_key($certificate);
311
312
				if ($verified === true) {
313
					// Seems to match, let's proceed
314
					$extractDir = $this->tempManager->getTemporaryFolder();
315
					$archive = new TAR($tempFile);
316
317
					if ($archive) {
318
						if (!$archive->extract($extractDir)) {
319
							$errorMessage = 'Could not extract app ' . $appId;
320
321
							$archiveError = $archive->getError();
322
							if ($archiveError instanceof \PEAR_Error) {
323
								$errorMessage .= ': ' . $archiveError->getMessage();
324
							}
325
326
							throw new \Exception($errorMessage);
327
						}
328
						$allFiles = scandir($extractDir);
329
						$folders = array_diff($allFiles, ['.', '..']);
330
						$folders = array_values($folders);
331
332
						if (count($folders) > 1) {
333
							throw new \Exception(
334
								sprintf(
335
									'Extracted app %s has more than 1 folder',
336
									$appId
337
								)
338
							);
339
						}
340
341
						// Check if appinfo/info.xml has the same app ID as well
342
						$loadEntities = libxml_disable_entity_loader(false);
343
						$xml = simplexml_load_file($extractDir . '/' . $folders[0] . '/appinfo/info.xml');
344
						libxml_disable_entity_loader($loadEntities);
345
						if ((string)$xml->id !== $appId) {
346
							throw new \Exception(
347
								sprintf(
348
									'App for id %s has a wrong app ID in info.xml: %s',
349
									$appId,
350
									(string)$xml->id
351
								)
352
							);
353
						}
354
355
						// Check if the version is lower than before
356
						$currentVersion = OC_App::getAppVersion($appId);
357
						$newVersion = (string)$xml->version;
358
						if (version_compare($currentVersion, $newVersion) === 1) {
359
							throw new \Exception(
360
								sprintf(
361
									'App for id %s has version %s and tried to update to lower version %s',
362
									$appId,
363
									$currentVersion,
364
									$newVersion
365
								)
366
							);
367
						}
368
369
						$baseDir = OC_App::getInstallPath() . '/' . $appId;
0 ignored issues
show
Bug introduced by
Are you sure OC_App::getInstallPath() of type false|string can be used in concatenation? ( Ignorable by Annotation )

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

369
						$baseDir = /** @scrutinizer ignore-type */ OC_App::getInstallPath() . '/' . $appId;
Loading history...
370
						// Remove old app with the ID if existent
371
						OC_Helper::rmdirr($baseDir);
372
						// Move to app folder
373
						if (@mkdir($baseDir)) {
374
							$extractDir .= '/' . $folders[0];
375
							OC_Helper::copyr($extractDir, $baseDir);
376
						}
377
						OC_Helper::copyr($extractDir, $baseDir);
378
						OC_Helper::rmdirr($extractDir);
379
						return;
380
					} else {
381
						throw new \Exception(
382
							sprintf(
383
								'Could not extract app with ID %s to %s',
384
								$appId,
385
								$extractDir
386
							)
387
						);
388
					}
389
				} else {
390
					// Signature does not match
391
					throw new \Exception(
392
						sprintf(
393
							'App with id %s has invalid signature',
394
							$appId
395
						)
396
					);
397
				}
398
			}
399
		}
400
401
		throw new \Exception(
402
			sprintf(
403
				'Could not download app %s',
404
				$appId
405
			)
406
		);
407
	}
408
409
	/**
410
	 * Check if an update for the app is available
411
	 *
412
	 * @param string $appId
413
	 * @param bool $allowUnstable
414
	 * @return string|false false or the version number of the update
415
	 */
416
	public function isUpdateAvailable($appId, $allowUnstable = false) {
417
		if ($this->isInstanceReadyForUpdates === null) {
418
			$installPath = OC_App::getInstallPath();
419
			if ($installPath === false || $installPath === null) {
420
				$this->isInstanceReadyForUpdates = false;
421
			} else {
422
				$this->isInstanceReadyForUpdates = true;
423
			}
424
		}
425
426
		if ($this->isInstanceReadyForUpdates === false) {
427
			return false;
428
		}
429
430
		if ($this->isInstalledFromGit($appId) === true) {
431
			return false;
432
		}
433
434
		if ($this->apps === null) {
435
			$this->apps = $this->appFetcher->get($allowUnstable);
436
		}
437
438
		foreach ($this->apps as $app) {
439
			if ($app['id'] === $appId) {
440
				$currentVersion = OC_App::getAppVersion($appId);
441
442
				if (!isset($app['releases'][0]['version'])) {
443
					return false;
444
				}
445
				$newestVersion = $app['releases'][0]['version'];
446
				if ($currentVersion !== '0' && version_compare($newestVersion, $currentVersion, '>')) {
447
					return $newestVersion;
448
				} else {
449
					return false;
450
				}
451
			}
452
		}
453
454
		return false;
455
	}
456
457
	/**
458
	 * Check if app has been installed from git
459
	 * @param string $name name of the application to remove
460
	 * @return boolean
461
	 *
462
	 * The function will check if the path contains a .git folder
463
	 */
464
	private function isInstalledFromGit($appId) {
465
		$app = \OC_App::findAppInDirectories($appId);
466
		if ($app === false) {
467
			return false;
468
		}
469
		$basedir = $app['path'].'/'.$appId;
470
		return file_exists($basedir.'/.git/');
471
	}
472
473
	/**
474
	 * Check if app is already downloaded
475
	 * @param string $name name of the application to remove
476
	 * @return boolean
477
	 *
478
	 * The function will check if the app is already downloaded in the apps repository
479
	 */
480
	public function isDownloaded($name) {
481
		foreach (\OC::$APPSROOTS as $dir) {
482
			$dirToTest = $dir['path'];
483
			$dirToTest .= '/';
484
			$dirToTest .= $name;
485
			$dirToTest .= '/';
486
487
			if (is_dir($dirToTest)) {
488
				return true;
489
			}
490
		}
491
492
		return false;
493
	}
494
495
	/**
496
	 * Removes an app
497
	 * @param string $appId ID of the application to remove
498
	 * @return boolean
499
	 *
500
	 *
501
	 * This function works as follows
502
	 *   -# call uninstall repair steps
503
	 *   -# removing the files
504
	 *
505
	 * The function will not delete preferences, tables and the configuration,
506
	 * this has to be done by the function oc_app_uninstall().
507
	 */
508
	public function removeApp($appId) {
509
		if ($this->isDownloaded($appId)) {
510
			if (\OC::$server->getAppManager()->isShipped($appId)) {
511
				return false;
512
			}
513
			$appDir = OC_App::getInstallPath() . '/' . $appId;
0 ignored issues
show
Bug introduced by
Are you sure OC_App::getInstallPath() of type false|string can be used in concatenation? ( Ignorable by Annotation )

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

513
			$appDir = /** @scrutinizer ignore-type */ OC_App::getInstallPath() . '/' . $appId;
Loading history...
514
			OC_Helper::rmdirr($appDir);
515
			return true;
516
		} else {
517
			\OCP\Util::writeLog('core', 'can\'t remove app '.$appId.'. It is not installed.', ILogger::ERROR);
0 ignored issues
show
Deprecated Code introduced by
The constant OCP\ILogger::ERROR has been deprecated: 20.0.0 ( Ignorable by Annotation )

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

517
			\OCP\Util::writeLog('core', 'can\'t remove app '.$appId.'. It is not installed.', /** @scrutinizer ignore-deprecated */ ILogger::ERROR);

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

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

Loading history...
518
519
			return false;
520
		}
521
	}
522
523
	/**
524
	 * Installs the app within the bundle and marks the bundle as installed
525
	 *
526
	 * @param Bundle $bundle
527
	 * @throws \Exception If app could not get installed
528
	 */
529
	public function installAppBundle(Bundle $bundle) {
530
		$appIds = $bundle->getAppIdentifiers();
531
		foreach ($appIds as $appId) {
532
			if (!$this->isDownloaded($appId)) {
533
				$this->downloadApp($appId);
534
			}
535
			$this->installApp($appId);
536
			$app = new OC_App();
537
			$app->enable($appId);
538
		}
539
		$bundles = json_decode($this->config->getAppValue('core', 'installed.bundles', json_encode([])), true);
540
		$bundles[] = $bundle->getIdentifier();
541
		$this->config->setAppValue('core', 'installed.bundles', json_encode($bundles));
542
	}
543
544
	/**
545
	 * Installs shipped apps
546
	 *
547
	 * This function installs all apps found in the 'apps' directory that should be enabled by default;
548
	 * @param bool $softErrors When updating we ignore errors and simply log them, better to have a
549
	 *                         working ownCloud at the end instead of an aborted update.
550
	 * @return array Array of error messages (appid => Exception)
551
	 */
552
	public static function installShippedApps($softErrors = false) {
553
		$appManager = \OC::$server->getAppManager();
554
		$config = \OC::$server->getConfig();
555
		$errors = [];
556
		foreach (\OC::$APPSROOTS as $app_dir) {
557
			if ($dir = opendir($app_dir['path'])) {
558
				while (false !== ($filename = readdir($dir))) {
559
					if ($filename[0] !== '.' and is_dir($app_dir['path']."/$filename")) {
560
						if (file_exists($app_dir['path']."/$filename/appinfo/info.xml")) {
561
							if ($config->getAppValue($filename, "installed_version", null) === null) {
562
								$info = OC_App::getAppInfo($filename);
563
								$enabled = isset($info['default_enable']);
564
								if (($enabled || in_array($filename, $appManager->getAlwaysEnabledApps()))
565
									  && $config->getAppValue($filename, 'enabled') !== 'no') {
566
									if ($softErrors) {
567
										try {
568
											Installer::installShippedApp($filename);
569
										} catch (HintException $e) {
570
											if ($e->getPrevious() instanceof TableExistsException) {
571
												$errors[$filename] = $e;
572
												continue;
573
											}
574
											throw $e;
575
										}
576
									} else {
577
										Installer::installShippedApp($filename);
578
									}
579
									$config->setAppValue($filename, 'enabled', 'yes');
580
								}
581
							}
582
						}
583
					}
584
				}
585
				closedir($dir);
586
			}
587
		}
588
589
		return $errors;
590
	}
591
592
	/**
593
	 * install an app already placed in the app folder
594
	 * @param string $app id of the app to install
595
	 * @return integer
596
	 */
597
	public static function installShippedApp($app) {
598
		//install the database
599
		$appPath = OC_App::getAppPath($app);
600
		\OC_App::registerAutoloading($app, $appPath);
601
602
		$ms = new MigrationService($app, \OC::$server->get(Connection::class));
603
		$ms->migrate('latest', true);
604
605
		//run appinfo/install.php
606
		self::includeAppScript("$appPath/appinfo/install.php");
607
608
		$info = OC_App::getAppInfo($app);
609
		if (is_null($info)) {
610
			return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type integer.
Loading history...
611
		}
612
		\OC_App::setupBackgroundJobs($info['background-jobs']);
613
614
		OC_App::executeRepairSteps($app, $info['repair-steps']['install']);
615
616
		$config = \OC::$server->getConfig();
617
618
		$config->setAppValue($app, 'installed_version', OC_App::getAppVersion($app));
619
		if (array_key_exists('ocsid', $info)) {
620
			$config->setAppValue($app, 'ocsid', $info['ocsid']);
621
		}
622
623
		//set remote/public handlers
624
		foreach ($info['remote'] as $name => $path) {
625
			$config->setAppValue('core', 'remote_'.$name, $app.'/'.$path);
626
		}
627
		foreach ($info['public'] as $name => $path) {
628
			$config->setAppValue('core', 'public_'.$name, $app.'/'.$path);
629
		}
630
631
		OC_App::setAppTypes($info['id']);
632
633
		return $info['id'];
634
	}
635
636
	/**
637
	 * @param string $script
638
	 */
639
	private static function includeAppScript($script) {
640
		if (file_exists($script)) {
641
			include $script;
642
		}
643
	}
644
}
645