Completed
Push — master ( 080572...37869d )
by Morris
150:57 queued 132:00
created

getAppDirsWithDifferentOwner()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
nc 3
nop 0
dl 0
loc 16
rs 9.7333
c 0
b 0
f 0
1
<?php
2
/**
3
 * @copyright Copyright (c) 2016, ownCloud, Inc.
4
 *
5
 * @author Arthur Schiwon <[email protected]>
6
 * @author Bjoern Schiessle <[email protected]>
7
 * @author Derek <[email protected]>
8
 * @author Joas Schilling <[email protected]>
9
 * @author Ko- <[email protected]>
10
 * @author Lukas Reschke <[email protected]>
11
 * @author Morris Jobke <[email protected]>
12
 * @author Robin McCorkell <[email protected]>
13
 * @author Roeland Jago Douma <[email protected]>
14
 *
15
 * @license AGPL-3.0
16
 *
17
 * This code is free software: you can redistribute it and/or modify
18
 * it under the terms of the GNU Affero General Public License, version 3,
19
 * as published by the Free Software Foundation.
20
 *
21
 * This program is distributed in the hope that it will be useful,
22
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24
 * GNU Affero General Public License for more details.
25
 *
26
 * You should have received a copy of the GNU Affero General Public License, version 3,
27
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
28
 *
29
 */
30
31
namespace OC\Settings\Controller;
32
33
use bantu\IniGetWrapper\IniGetWrapper;
34
use DirectoryIterator;
35
use Doctrine\DBAL\DBALException;
36
use Doctrine\DBAL\Platforms\SqlitePlatform;
37
use GuzzleHttp\Exception\ClientException;
38
use OC;
39
use OC\AppFramework\Http;
40
use OC\DB\Connection;
41
use OC\DB\MissingIndexInformation;
42
use OC\IntegrityCheck\Checker;
43
use OC\Lock\NoopLockingProvider;
44
use OCP\AppFramework\Controller;
45
use OCP\AppFramework\Http\DataDisplayResponse;
46
use OCP\AppFramework\Http\DataResponse;
47
use OCP\AppFramework\Http\RedirectResponse;
48
use OCP\Http\Client\IClientService;
49
use OCP\IConfig;
50
use OCP\IDateTimeFormatter;
51
use OCP\IDBConnection;
52
use OCP\IL10N;
53
use OCP\ILogger;
54
use OCP\IRequest;
55
use OCP\IURLGenerator;
56
use OCP\Lock\ILockingProvider;
57
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
58
use Symfony\Component\EventDispatcher\GenericEvent;
59
60
/**
61
 * @package OC\Settings\Controller
62
 */
63
class CheckSetupController extends Controller {
64
	/** @var IConfig */
65
	private $config;
66
	/** @var IClientService */
67
	private $clientService;
68
	/** @var \OC_Util */
69
	private $util;
70
	/** @var IURLGenerator */
71
	private $urlGenerator;
72
	/** @var IL10N */
73
	private $l10n;
74
	/** @var Checker */
75
	private $checker;
76
	/** @var ILogger */
77
	private $logger;
78
	/** @var EventDispatcherInterface */
79
	private $dispatcher;
80
	/** @var IDBConnection|Connection */
81
	private $db;
82
	/** @var ILockingProvider */
83
	private $lockingProvider;
84
	/** @var IDateTimeFormatter */
85
	private $dateTimeFormatter;
86
87 View Code Duplication
	public function __construct($AppName,
88
								IRequest $request,
89
								IConfig $config,
90
								IClientService $clientService,
91
								IURLGenerator $urlGenerator,
92
								\OC_Util $util,
93
								IL10N $l10n,
94
								Checker $checker,
95
								ILogger $logger,
96
								EventDispatcherInterface $dispatcher,
97
								IDBConnection $db,
98
								ILockingProvider $lockingProvider,
99
								IDateTimeFormatter $dateTimeFormatter) {
100
		parent::__construct($AppName, $request);
101
		$this->config = $config;
102
		$this->clientService = $clientService;
103
		$this->util = $util;
104
		$this->urlGenerator = $urlGenerator;
105
		$this->l10n = $l10n;
106
		$this->checker = $checker;
107
		$this->logger = $logger;
108
		$this->dispatcher = $dispatcher;
109
		$this->db = $db;
110
		$this->lockingProvider = $lockingProvider;
111
		$this->dateTimeFormatter = $dateTimeFormatter;
112
	}
113
114
	/**
115
	 * Checks if the server can connect to the internet using HTTPS and HTTP
116
	 * @return bool
117
	 */
118
	private function isInternetConnectionWorking() {
119
		if ($this->config->getSystemValue('has_internet_connection', true) === false) {
120
			return false;
121
		}
122
123
		$siteArray = ['www.nextcloud.com',
124
						'www.startpage.com',
125
						'www.eff.org',
126
						'www.edri.org',
127
			];
128
129
		foreach($siteArray as $site) {
130
			if ($this->isSiteReachable($site)) {
131
				return true;
132
			}
133
		}
134
		return false;
135
	}
136
137
	/**
138
	* Checks if the Nextcloud server can connect to a specific URL using both HTTPS and HTTP
139
	* @return bool
140
	*/
141
	private function isSiteReachable($sitename) {
142
		$httpSiteName = 'http://' . $sitename . '/';
143
		$httpsSiteName = 'https://' . $sitename . '/';
144
145
		try {
146
			$client = $this->clientService->newClient();
147
			$client->get($httpSiteName);
148
			$client->get($httpsSiteName);
149
		} catch (\Exception $e) {
150
			$this->logger->logException($e, ['app' => 'internet_connection_check']);
0 ignored issues
show
Documentation introduced by
$e is of type object<Exception>, but the function expects a object<Throwable>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
151
			return false;
152
		}
153
		return true;
154
	}
155
156
	/**
157
	 * Checks whether a local memcache is installed or not
158
	 * @return bool
159
	 */
160
	private function isMemcacheConfigured() {
161
		return $this->config->getSystemValue('memcache.local', null) !== null;
162
	}
163
164
	/**
165
	 * Whether /dev/urandom is available to the PHP controller
166
	 *
167
	 * @return bool
168
	 */
169
	private function isUrandomAvailable() {
170
		if(@file_exists('/dev/urandom')) {
171
			$file = fopen('/dev/urandom', 'rb');
172
			if($file) {
173
				fclose($file);
174
				return true;
175
			}
176
		}
177
178
		return false;
179
	}
180
181
	/**
182
	 * Public for the sake of unit-testing
183
	 *
184
	 * @return array
185
	 */
186
	protected function getCurlVersion() {
187
		return curl_version();
188
	}
189
190
	/**
191
	 * Check if the used  SSL lib is outdated. Older OpenSSL and NSS versions do
192
	 * have multiple bugs which likely lead to problems in combination with
193
	 * functionality required by ownCloud such as SNI.
194
	 *
195
	 * @link https://github.com/owncloud/core/issues/17446#issuecomment-122877546
196
	 * @link https://bugzilla.redhat.com/show_bug.cgi?id=1241172
197
	 * @return string
198
	 */
199
	private function isUsedTlsLibOutdated() {
200
		// Don't run check when:
201
		// 1. Server has `has_internet_connection` set to false
202
		// 2. AppStore AND S2S is disabled
203
		if(!$this->config->getSystemValue('has_internet_connection', true)) {
204
			return '';
205
		}
206
		if(!$this->config->getSystemValue('appstoreenabled', true)
207
			&& $this->config->getAppValue('files_sharing', 'outgoing_server2server_share_enabled', 'yes') === 'no'
208
			&& $this->config->getAppValue('files_sharing', 'incoming_server2server_share_enabled', 'yes') === 'no') {
209
			return '';
210
		}
211
212
		$versionString = $this->getCurlVersion();
213
		if(isset($versionString['ssl_version'])) {
214
			$versionString = $versionString['ssl_version'];
215
		} else {
216
			return '';
217
		}
218
219
		$features = (string)$this->l10n->t('installing and updating apps via the app store or Federated Cloud Sharing');
220
		if(!$this->config->getSystemValue('appstoreenabled', true)) {
221
			$features = (string)$this->l10n->t('Federated Cloud Sharing');
222
		}
223
224
		// Check if at least OpenSSL after 1.01d or 1.0.2b
225
		if(strpos($versionString, 'OpenSSL/') === 0) {
226
			$majorVersion = substr($versionString, 8, 5);
227
			$patchRelease = substr($versionString, 13, 6);
228
229
			if(($majorVersion === '1.0.1' && ord($patchRelease) < ord('d')) ||
230
				($majorVersion === '1.0.2' && ord($patchRelease) < ord('b'))) {
231
				return (string) $this->l10n->t('cURL is using an outdated %s version (%s). Please update your operating system or features such as %s will not work reliably.', ['OpenSSL', $versionString, $features]);
232
			}
233
		}
234
235
		// Check if NSS and perform heuristic check
236
		if(strpos($versionString, 'NSS/') === 0) {
237
			try {
238
				$firstClient = $this->clientService->newClient();
239
				$firstClient->get('https://nextcloud.com/');
240
241
				$secondClient = $this->clientService->newClient();
242
				$secondClient->get('https://nextcloud.com/');
243
			} catch (ClientException $e) {
0 ignored issues
show
Bug introduced by
The class GuzzleHttp\Exception\ClientException does not exist. Did you forget a USE statement, or did you not list all dependencies?

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

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

Loading history...
244
				if($e->getResponse()->getStatusCode() === 400) {
245
					return (string) $this->l10n->t('cURL is using an outdated %s version (%s). Please update your operating system or features such as %s will not work reliably.', ['NSS', $versionString, $features]);
246
				}
247
			}
248
		}
249
250
		return '';
251
	}
252
253
	/**
254
	 * Whether the version is outdated
255
	 *
256
	 * @return bool
257
	 */
258
	protected function isPhpOutdated() {
259
		if (version_compare(PHP_VERSION, '7.0.0', '<')) {
260
			return true;
261
		}
262
263
		return false;
264
	}
265
266
	/**
267
	 * Whether the php version is still supported (at time of release)
268
	 * according to: https://secure.php.net/supported-versions.php
269
	 *
270
	 * @return array
271
	 */
272
	private function isPhpSupported() {
273
		return ['eol' => $this->isPhpOutdated(), 'version' => PHP_VERSION];
274
	}
275
276
	/**
277
	 * Check if the reverse proxy configuration is working as expected
278
	 *
279
	 * @return bool
280
	 */
281
	private function forwardedForHeadersWorking() {
282
		$trustedProxies = $this->config->getSystemValue('trusted_proxies', []);
283
		$remoteAddress = $this->request->getRemoteAddress();
284
285
		if (is_array($trustedProxies) && in_array($remoteAddress, $trustedProxies)) {
286
			return false;
287
		}
288
289
		// either not enabled or working correctly
290
		return true;
291
	}
292
293
	/**
294
	 * Checks if the correct memcache module for PHP is installed. Only
295
	 * fails if memcached is configured and the working module is not installed.
296
	 *
297
	 * @return bool
298
	 */
299
	private function isCorrectMemcachedPHPModuleInstalled() {
300
		if ($this->config->getSystemValue('memcache.distributed', null) !== '\OC\Memcache\Memcached') {
301
			return true;
302
		}
303
304
		// there are two different memcached modules for PHP
305
		// we only support memcached and not memcache
306
		// https://code.google.com/p/memcached/wiki/PHPClientComparison
307
		return !(!extension_loaded('memcached') && extension_loaded('memcache'));
308
	}
309
310
	/**
311
	 * Checks if set_time_limit is not disabled.
312
	 *
313
	 * @return bool
314
	 */
315
	private function isSettimelimitAvailable() {
316
		if (function_exists('set_time_limit')
317
			&& strpos(@ini_get('disable_functions'), 'set_time_limit') === false) {
318
			return true;
319
		}
320
321
		return false;
322
	}
323
324
	/**
325
	 * @return RedirectResponse
326
	 */
327
	public function rescanFailedIntegrityCheck() {
328
		$this->checker->runInstanceVerification();
329
		return new RedirectResponse(
330
			$this->urlGenerator->linkToRoute('settings.AdminSettings.index')
331
		);
332
	}
333
334
	/**
335
	 * @NoCSRFRequired
336
	 * @return DataResponse
337
	 */
338
	public function getFailedIntegrityCheckFiles() {
339
		if(!$this->checker->isCodeCheckEnforced()) {
340
			return new DataDisplayResponse('Integrity checker has been disabled. Integrity cannot be verified.');
341
		}
342
343
		$completeResults = $this->checker->getResults();
344
345
		if(!empty($completeResults)) {
346
			$formattedTextResponse = 'Technical information
347
=====================
348
The following list covers which files have failed the integrity check. Please read
349
the previous linked documentation to learn more about the errors and how to fix
350
them.
351
352
Results
353
=======
354
';
355
			foreach($completeResults as $context => $contextResult) {
356
				$formattedTextResponse .= "- $context\n";
357
358
				foreach($contextResult as $category => $result) {
359
					$formattedTextResponse .= "\t- $category\n";
360
					if($category !== 'EXCEPTION') {
361
						foreach ($result as $key => $results) {
362
							$formattedTextResponse .= "\t\t- $key\n";
363
						}
364
					} else {
365
						foreach ($result as $key => $results) {
366
							$formattedTextResponse .= "\t\t- $results\n";
367
						}
368
					}
369
370
				}
371
			}
372
373
			$formattedTextResponse .= '
374
Raw output
375
==========
376
';
377
			$formattedTextResponse .= print_r($completeResults, true);
378
		} else {
379
			$formattedTextResponse = 'No errors have been found.';
380
		}
381
382
383
		$response = new DataDisplayResponse(
384
			$formattedTextResponse,
385
			Http::STATUS_OK,
386
			[
387
				'Content-Type' => 'text/plain',
388
			]
389
		);
390
391
		return $response;
392
	}
393
394
	/**
395
	 * Checks whether a PHP opcache is properly set up
396
	 * @return bool
397
	 */
398
	protected function isOpcacheProperlySetup() {
399
		$iniWrapper = new IniGetWrapper();
400
401
		if(!$iniWrapper->getBool('opcache.enable')) {
402
			return false;
403
		}
404
405
		if(!$iniWrapper->getBool('opcache.save_comments')) {
406
			return false;
407
		}
408
409
		if(!$iniWrapper->getBool('opcache.enable_cli')) {
410
			return false;
411
		}
412
413
		if($iniWrapper->getNumeric('opcache.max_accelerated_files') < 10000) {
414
			return false;
415
		}
416
417
		if($iniWrapper->getNumeric('opcache.memory_consumption') < 128) {
418
			return false;
419
		}
420
421
		if($iniWrapper->getNumeric('opcache.interned_strings_buffer') < 8) {
422
			return false;
423
		}
424
425
		return true;
426
	}
427
428
	/**
429
	 * Check if the required FreeType functions are present
430
	 * @return bool
431
	 */
432
	protected function hasFreeTypeSupport() {
433
		return function_exists('imagettfbbox') && function_exists('imagettftext');
434
	}
435
436
	protected function hasMissingIndexes(): array {
437
		$indexInfo = new MissingIndexInformation();
438
		// Dispatch event so apps can also hint for pending index updates if needed
439
		$event = new GenericEvent($indexInfo);
440
		$this->dispatcher->dispatch(IDBConnection::CHECK_MISSING_INDEXES_EVENT, $event);
441
442
		return $indexInfo->getListOfMissingIndexes();
443
	}
444
445
	/**
446
	 * warn if outdated version of a memcache module is used
447
	 */
448
	protected function getOutdatedCaches(): array {
449
		$caches = [
450
			'apcu'	=> ['name' => 'APCu', 'version' => '4.0.6'],
451
			'redis'	=> ['name' => 'Redis', 'version' => '2.2.5'],
452
		];
453
		$outdatedCaches = [];
454
		foreach ($caches as $php_module => $data) {
455
			$isOutdated = extension_loaded($php_module) && version_compare(phpversion($php_module), $data['version'], '<');
456
			if ($isOutdated) {
457
				$outdatedCaches[] = $data;
458
			}
459
		}
460
461
		return $outdatedCaches;
462
	}
463
464
	protected function isSqliteUsed() {
465
		return strpos($this->config->getSystemValue('dbtype'), 'sqlite') !== false;
466
	}
467
468
	protected function isReadOnlyConfig(): bool {
469
		return \OC_Helper::isReadOnlyConfigEnabled();
470
	}
471
472
	protected function hasValidTransactionIsolationLevel(): bool {
473
		try {
474
			if ($this->db->getDatabasePlatform() instanceof SqlitePlatform) {
0 ignored issues
show
Bug introduced by
The class Doctrine\DBAL\Platforms\SqlitePlatform does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
475
				return true;
476
			}
477
478
			return $this->db->getTransactionIsolation() === Connection::TRANSACTION_READ_COMMITTED;
479
		} catch (DBALException $e) {
0 ignored issues
show
Bug introduced by
The class Doctrine\DBAL\DBALException does not exist. Did you forget a USE statement, or did you not list all dependencies?

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

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

Loading history...
480
			// ignore
481
		}
482
483
		return true;
484
	}
485
486
	protected function hasFileinfoInstalled(): bool {
487
		return \OC_Util::fileInfoLoaded();
488
	}
489
490
	protected function hasWorkingFileLocking(): bool {
491
		return !($this->lockingProvider instanceof NoopLockingProvider);
492
	}
493
494
	protected function getSuggestedOverwriteCliURL(): string {
495
		$suggestedOverwriteCliUrl = '';
496
		if ($this->config->getSystemValue('overwrite.cli.url', '') === '') {
497
			$suggestedOverwriteCliUrl = $this->request->getServerProtocol() . '://' . $this->request->getInsecureServerHost() . \OC::$WEBROOT;
498
			if (!$this->config->getSystemValue('config_is_read_only', false)) {
499
				// Set the overwrite URL when it was not set yet.
500
				$this->config->setSystemValue('overwrite.cli.url', $suggestedOverwriteCliUrl);
501
				$suggestedOverwriteCliUrl = '';
502
			}
503
		}
504
		return $suggestedOverwriteCliUrl;
505
	}
506
507
	protected function getLastCronInfo(): array {
508
		$lastCronRun = $this->config->getAppValue('core', 'lastcron', 0);
509
		return [
510
			'diffInSeconds' => time() - $lastCronRun,
511
			'relativeTime' => $this->dateTimeFormatter->formatTimeSpan($lastCronRun),
512
			'backgroundJobsUrl' => $this->urlGenerator->linkToRoute('settings.AdminSettings.index', ['section' => 'server']) . '#backgroundjobs',
513
		];
514
	}
515
516
	protected function getCronErrors() {
517
		$errors = json_decode($this->config->getAppValue('core', 'cronErrors', ''), true);
518
519
		if (is_array($errors)) {
520
			return $errors;
521
		}
522
523
		return [];
524
	}
525
526
	protected function isPhpMailerUsed(): bool {
527
		return $this->config->getSystemValue('mail_smtpmode', 'php') === 'php';
528
	}
529
530
	protected function hasOpcacheLoaded(): bool {
531
		return function_exists('opcache_get_status');
532
	}
533
534
	/**
535
	 * Iterates through the configured app roots and
536
	 * tests if the subdirectories are owned by the same user than the current user.
537
	 *
538
	 * @return array
539
	 */
540
	protected function getAppDirsWithDifferentOwner(): array {
541
		$currentUser = posix_getpwuid(posix_getuid());
542
		$appDirsWithDifferentOwner = [];
543
544
		foreach (OC::$APPSROOTS as $appRoot) {
545
			if ($appRoot['writable'] === true) {
546
				$appDirsWithDifferentOwner = array_merge(
547
					$appDirsWithDifferentOwner,
548
					$this->getAppDirsWithDifferentOwnerForAppRoot($currentUser, $appRoot)
549
				);
550
			}
551
		}
552
553
		sort($appDirsWithDifferentOwner);
554
		return $appDirsWithDifferentOwner;
555
	}
556
557
	/**
558
	 * Tests if the directories for one apps directory are writable by the current user.
559
	 *
560
	 * @param array $currentUser The current user
561
	 * @param array $appRoot The app root config
562
	 * @return string[] The none writable directory paths inside the app root
563
	 */
564
	private function getAppDirsWithDifferentOwnerForAppRoot(array $currentUser, array $appRoot): array {
565
		$appDirsWithDifferentOwner = [];
566
		$appsPath = $appRoot['path'];
567
		$appsDir = new DirectoryIterator($appRoot['path']);
568
569
		foreach ($appsDir as $fileInfo) {
570
			if ($fileInfo->isDir() && !$fileInfo->isDot()) {
571
				$absAppPath = $appsPath . DIRECTORY_SEPARATOR . $fileInfo->getFilename();
572
				$appDirUser = posix_getpwuid(fileowner($absAppPath));
573
				if ($appDirUser !== $currentUser) {
574
					$appDirsWithDifferentOwner[] = $absAppPath . DIRECTORY_SEPARATOR . $fileInfo->getFilename();
575
				}
576
			}
577
		}
578
579
		return $appDirsWithDifferentOwner;
580
	}
581
582
	/**
583
	 * @return DataResponse
584
	 */
585
	public function check() {
586
		return new DataResponse(
587
			[
588
				'isGetenvServerWorking' => !empty(getenv('PATH')),
589
				'isReadOnlyConfig' => $this->isReadOnlyConfig(),
590
				'hasValidTransactionIsolationLevel' => $this->hasValidTransactionIsolationLevel(),
591
				'outdatedCaches' => $this->getOutdatedCaches(),
592
				'hasFileinfoInstalled' => $this->hasFileinfoInstalled(),
593
				'hasWorkingFileLocking' => $this->hasWorkingFileLocking(),
594
				'suggestedOverwriteCliURL' => $this->getSuggestedOverwriteCliURL(),
595
				'cronInfo' => $this->getLastCronInfo(),
596
				'cronErrors' => $this->getCronErrors(),
597
				'serverHasInternetConnection' => $this->isInternetConnectionWorking(),
598
				'isMemcacheConfigured' => $this->isMemcacheConfigured(),
599
				'memcacheDocs' => $this->urlGenerator->linkToDocs('admin-performance'),
600
				'isUrandomAvailable' => $this->isUrandomAvailable(),
601
				'securityDocs' => $this->urlGenerator->linkToDocs('admin-security'),
602
				'isUsedTlsLibOutdated' => $this->isUsedTlsLibOutdated(),
603
				'phpSupported' => $this->isPhpSupported(),
604
				'forwardedForHeadersWorking' => $this->forwardedForHeadersWorking(),
605
				'reverseProxyDocs' => $this->urlGenerator->linkToDocs('admin-reverse-proxy'),
606
				'isCorrectMemcachedPHPModuleInstalled' => $this->isCorrectMemcachedPHPModuleInstalled(),
607
				'hasPassedCodeIntegrityCheck' => $this->checker->hasPassedCheck(),
608
				'codeIntegrityCheckerDocumentation' => $this->urlGenerator->linkToDocs('admin-code-integrity'),
609
				'isOpcacheProperlySetup' => $this->isOpcacheProperlySetup(),
610
				'hasOpcacheLoaded' => $this->hasOpcacheLoaded(),
611
				'phpOpcacheDocumentation' => $this->urlGenerator->linkToDocs('admin-php-opcache'),
612
				'isSettimelimitAvailable' => $this->isSettimelimitAvailable(),
613
				'hasFreeTypeSupport' => $this->hasFreeTypeSupport(),
614
				'missingIndexes' => $this->hasMissingIndexes(),
615
				'isSqliteUsed' => $this->isSqliteUsed(),
616
				'databaseConversionDocumentation' => $this->urlGenerator->linkToDocs('admin-db-conversion'),
617
				'isPhpMailerUsed' => $this->isPhpMailerUsed(),
618
				'mailSettingsDocumentation' => $this->urlGenerator->getAbsoluteURL('index.php/settings/admin'),
619
				'appDirsWithDifferentOwner' => $this->getAppDirsWithDifferentOwner(),
620
			]
621
		);
622
	}
623
}
624