Completed
Push — master ( 10351c...a1c969 )
by Joas
15:17
created

CheckSetupController::isRandomnessSecure()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
nc 2
nop 0
dl 0
loc 8
rs 10
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 OC\MemoryInfo;
45
use OCP\AppFramework\Controller;
46
use OCP\AppFramework\Http\DataDisplayResponse;
47
use OCP\AppFramework\Http\DataResponse;
48
use OCP\AppFramework\Http\RedirectResponse;
49
use OCP\Http\Client\IClientService;
50
use OCP\IConfig;
51
use OCP\IDateTimeFormatter;
52
use OCP\IDBConnection;
53
use OCP\IL10N;
54
use OCP\ILogger;
55
use OCP\IRequest;
56
use OCP\IURLGenerator;
57
use OCP\Lock\ILockingProvider;
58
use OCP\Security\ISecureRandom;
59
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
60
use Symfony\Component\EventDispatcher\GenericEvent;
61
62
/**
63
 * @package OC\Settings\Controller
64
 */
65
class CheckSetupController extends Controller {
66
	/** @var IConfig */
67
	private $config;
68
	/** @var IClientService */
69
	private $clientService;
70
	/** @var \OC_Util */
71
	private $util;
72
	/** @var IURLGenerator */
73
	private $urlGenerator;
74
	/** @var IL10N */
75
	private $l10n;
76
	/** @var Checker */
77
	private $checker;
78
	/** @var ILogger */
79
	private $logger;
80
	/** @var EventDispatcherInterface */
81
	private $dispatcher;
82
	/** @var IDBConnection|Connection */
83
	private $db;
84
	/** @var ILockingProvider */
85
	private $lockingProvider;
86
	/** @var IDateTimeFormatter */
87
	private $dateTimeFormatter;
88
	/** @var MemoryInfo */
89
	private $memoryInfo;
90
	/** @var ISecureRandom */
91
	private $secureRandom;
92
93
	public function __construct($AppName,
94
								IRequest $request,
95
								IConfig $config,
96
								IClientService $clientService,
97
								IURLGenerator $urlGenerator,
98
								\OC_Util $util,
99
								IL10N $l10n,
100
								Checker $checker,
101
								ILogger $logger,
102
								EventDispatcherInterface $dispatcher,
103
								IDBConnection $db,
104
								ILockingProvider $lockingProvider,
105
								IDateTimeFormatter $dateTimeFormatter,
106
								MemoryInfo $memoryInfo,
107
								ISecureRandom $secureRandom) {
108
		parent::__construct($AppName, $request);
109
		$this->config = $config;
110
		$this->clientService = $clientService;
111
		$this->util = $util;
112
		$this->urlGenerator = $urlGenerator;
113
		$this->l10n = $l10n;
114
		$this->checker = $checker;
115
		$this->logger = $logger;
116
		$this->dispatcher = $dispatcher;
117
		$this->db = $db;
118
		$this->lockingProvider = $lockingProvider;
119
		$this->dateTimeFormatter = $dateTimeFormatter;
120
		$this->memoryInfo = $memoryInfo;
121
		$this->secureRandom = $secureRandom;
122
	}
123
124
	/**
125
	 * Checks if the server can connect to the internet using HTTPS and HTTP
126
	 * @return bool
127
	 */
128
	private function isInternetConnectionWorking() {
129
		if ($this->config->getSystemValue('has_internet_connection', true) === false) {
130
			return false;
131
		}
132
133
		$siteArray = ['www.nextcloud.com',
134
						'www.startpage.com',
135
						'www.eff.org',
136
						'www.edri.org',
137
			];
138
139
		foreach($siteArray as $site) {
140
			if ($this->isSiteReachable($site)) {
141
				return true;
142
			}
143
		}
144
		return false;
145
	}
146
147
	/**
148
	* Checks if the Nextcloud server can connect to a specific URL using both HTTPS and HTTP
149
	* @return bool
150
	*/
151
	private function isSiteReachable($sitename) {
152
		$httpSiteName = 'http://' . $sitename . '/';
153
		$httpsSiteName = 'https://' . $sitename . '/';
154
155
		try {
156
			$client = $this->clientService->newClient();
157
			$client->get($httpSiteName);
158
			$client->get($httpsSiteName);
159
		} catch (\Exception $e) {
160
			$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...
161
			return false;
162
		}
163
		return true;
164
	}
165
166
	/**
167
	 * Checks whether a local memcache is installed or not
168
	 * @return bool
169
	 */
170
	private function isMemcacheConfigured() {
171
		return $this->config->getSystemValue('memcache.local', null) !== null;
172
	}
173
174
	/**
175
	 * Whether PHP can generate "secure" pseudorandom integers
176
	 *
177
	 * @return bool
178
	 */
179
	private function isRandomnessSecure() {
180
		try {
181
			$this->secureRandom->generate(1);
182
		} catch (\Exception $ex) {
183
			return false;
184
		}
185
		return true;
186
	}
187
188
	/**
189
	 * Public for the sake of unit-testing
190
	 *
191
	 * @return array
192
	 */
193
	protected function getCurlVersion() {
194
		return curl_version();
195
	}
196
197
	/**
198
	 * Check if the used  SSL lib is outdated. Older OpenSSL and NSS versions do
199
	 * have multiple bugs which likely lead to problems in combination with
200
	 * functionality required by ownCloud such as SNI.
201
	 *
202
	 * @link https://github.com/owncloud/core/issues/17446#issuecomment-122877546
203
	 * @link https://bugzilla.redhat.com/show_bug.cgi?id=1241172
204
	 * @return string
205
	 */
206
	private function isUsedTlsLibOutdated() {
207
		// Don't run check when:
208
		// 1. Server has `has_internet_connection` set to false
209
		// 2. AppStore AND S2S is disabled
210
		if(!$this->config->getSystemValue('has_internet_connection', true)) {
211
			return '';
212
		}
213
		if(!$this->config->getSystemValue('appstoreenabled', true)
214
			&& $this->config->getAppValue('files_sharing', 'outgoing_server2server_share_enabled', 'yes') === 'no'
215
			&& $this->config->getAppValue('files_sharing', 'incoming_server2server_share_enabled', 'yes') === 'no') {
216
			return '';
217
		}
218
219
		$versionString = $this->getCurlVersion();
220
		if(isset($versionString['ssl_version'])) {
221
			$versionString = $versionString['ssl_version'];
222
		} else {
223
			return '';
224
		}
225
226
		$features = (string)$this->l10n->t('installing and updating apps via the app store or Federated Cloud Sharing');
227
		if(!$this->config->getSystemValue('appstoreenabled', true)) {
228
			$features = (string)$this->l10n->t('Federated Cloud Sharing');
229
		}
230
231
		// Check if at least OpenSSL after 1.01d or 1.0.2b
232
		if(strpos($versionString, 'OpenSSL/') === 0) {
233
			$majorVersion = substr($versionString, 8, 5);
234
			$patchRelease = substr($versionString, 13, 6);
235
236
			if(($majorVersion === '1.0.1' && ord($patchRelease) < ord('d')) ||
237
				($majorVersion === '1.0.2' && ord($patchRelease) < ord('b'))) {
238
				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]);
239
			}
240
		}
241
242
		// Check if NSS and perform heuristic check
243
		if(strpos($versionString, 'NSS/') === 0) {
244
			try {
245
				$firstClient = $this->clientService->newClient();
246
				$firstClient->get('https://nextcloud.com/');
247
248
				$secondClient = $this->clientService->newClient();
249
				$secondClient->get('https://nextcloud.com/');
250
			} 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...
251
				if($e->getResponse()->getStatusCode() === 400) {
252
					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]);
253
				}
254
			}
255
		}
256
257
		return '';
258
	}
259
260
	/**
261
	 * Whether the version is outdated
262
	 *
263
	 * @return bool
264
	 */
265
	protected function isPhpOutdated() {
266
		if (version_compare(PHP_VERSION, '7.0.0', '<')) {
267
			return true;
268
		}
269
270
		return false;
271
	}
272
273
	/**
274
	 * Whether the php version is still supported (at time of release)
275
	 * according to: https://secure.php.net/supported-versions.php
276
	 *
277
	 * @return array
278
	 */
279
	private function isPhpSupported() {
280
		return ['eol' => $this->isPhpOutdated(), 'version' => PHP_VERSION];
281
	}
282
283
	/**
284
	 * Check if the reverse proxy configuration is working as expected
285
	 *
286
	 * @return bool
287
	 */
288
	private function forwardedForHeadersWorking() {
289
		$trustedProxies = $this->config->getSystemValue('trusted_proxies', []);
290
		$remoteAddress = $this->request->getRemoteAddress();
291
292
		if (is_array($trustedProxies) && in_array($remoteAddress, $trustedProxies)) {
293
			return false;
294
		}
295
296
		// either not enabled or working correctly
297
		return true;
298
	}
299
300
	/**
301
	 * Checks if the correct memcache module for PHP is installed. Only
302
	 * fails if memcached is configured and the working module is not installed.
303
	 *
304
	 * @return bool
305
	 */
306
	private function isCorrectMemcachedPHPModuleInstalled() {
307
		if ($this->config->getSystemValue('memcache.distributed', null) !== '\OC\Memcache\Memcached') {
308
			return true;
309
		}
310
311
		// there are two different memcached modules for PHP
312
		// we only support memcached and not memcache
313
		// https://code.google.com/p/memcached/wiki/PHPClientComparison
314
		return !(!extension_loaded('memcached') && extension_loaded('memcache'));
315
	}
316
317
	/**
318
	 * Checks if set_time_limit is not disabled.
319
	 *
320
	 * @return bool
321
	 */
322
	private function isSettimelimitAvailable() {
323
		if (function_exists('set_time_limit')
324
			&& strpos(@ini_get('disable_functions'), 'set_time_limit') === false) {
325
			return true;
326
		}
327
328
		return false;
329
	}
330
331
	/**
332
	 * @return RedirectResponse
333
	 */
334
	public function rescanFailedIntegrityCheck() {
335
		$this->checker->runInstanceVerification();
336
		return new RedirectResponse(
337
			$this->urlGenerator->linkToRoute('settings.AdminSettings.index')
338
		);
339
	}
340
341
	/**
342
	 * @NoCSRFRequired
343
	 * @return DataResponse
344
	 */
345
	public function getFailedIntegrityCheckFiles() {
346
		if(!$this->checker->isCodeCheckEnforced()) {
347
			return new DataDisplayResponse('Integrity checker has been disabled. Integrity cannot be verified.');
348
		}
349
350
		$completeResults = $this->checker->getResults();
351
352
		if(!empty($completeResults)) {
353
			$formattedTextResponse = 'Technical information
354
=====================
355
The following list covers which files have failed the integrity check. Please read
356
the previous linked documentation to learn more about the errors and how to fix
357
them.
358
359
Results
360
=======
361
';
362
			foreach($completeResults as $context => $contextResult) {
363
				$formattedTextResponse .= "- $context\n";
364
365
				foreach($contextResult as $category => $result) {
366
					$formattedTextResponse .= "\t- $category\n";
367
					if($category !== 'EXCEPTION') {
368
						foreach ($result as $key => $results) {
369
							$formattedTextResponse .= "\t\t- $key\n";
370
						}
371
					} else {
372
						foreach ($result as $key => $results) {
373
							$formattedTextResponse .= "\t\t- $results\n";
374
						}
375
					}
376
377
				}
378
			}
379
380
			$formattedTextResponse .= '
381
Raw output
382
==========
383
';
384
			$formattedTextResponse .= print_r($completeResults, true);
385
		} else {
386
			$formattedTextResponse = 'No errors have been found.';
387
		}
388
389
390
		$response = new DataDisplayResponse(
391
			$formattedTextResponse,
392
			Http::STATUS_OK,
393
			[
394
				'Content-Type' => 'text/plain',
395
			]
396
		);
397
398
		return $response;
399
	}
400
401
	/**
402
	 * Checks whether a PHP opcache is properly set up
403
	 * @return bool
404
	 */
405
	protected function isOpcacheProperlySetup() {
406
		$iniWrapper = new IniGetWrapper();
407
408
		if(!$iniWrapper->getBool('opcache.enable')) {
409
			return false;
410
		}
411
412
		if(!$iniWrapper->getBool('opcache.save_comments')) {
413
			return false;
414
		}
415
416
		if(!$iniWrapper->getBool('opcache.enable_cli')) {
417
			return false;
418
		}
419
420
		if($iniWrapper->getNumeric('opcache.max_accelerated_files') < 10000) {
421
			return false;
422
		}
423
424
		if($iniWrapper->getNumeric('opcache.memory_consumption') < 128) {
425
			return false;
426
		}
427
428
		if($iniWrapper->getNumeric('opcache.interned_strings_buffer') < 8) {
429
			return false;
430
		}
431
432
		return true;
433
	}
434
435
	/**
436
	 * Check if the required FreeType functions are present
437
	 * @return bool
438
	 */
439
	protected function hasFreeTypeSupport() {
440
		return function_exists('imagettfbbox') && function_exists('imagettftext');
441
	}
442
443
	protected function hasMissingIndexes(): array {
444
		$indexInfo = new MissingIndexInformation();
445
		// Dispatch event so apps can also hint for pending index updates if needed
446
		$event = new GenericEvent($indexInfo);
447
		$this->dispatcher->dispatch(IDBConnection::CHECK_MISSING_INDEXES_EVENT, $event);
448
449
		return $indexInfo->getListOfMissingIndexes();
450
	}
451
452
	/**
453
	 * warn if outdated version of a memcache module is used
454
	 */
455
	protected function getOutdatedCaches(): array {
456
		$caches = [
457
			'apcu'	=> ['name' => 'APCu', 'version' => '4.0.6'],
458
			'redis'	=> ['name' => 'Redis', 'version' => '2.2.5'],
459
		];
460
		$outdatedCaches = [];
461
		foreach ($caches as $php_module => $data) {
462
			$isOutdated = extension_loaded($php_module) && version_compare(phpversion($php_module), $data['version'], '<');
463
			if ($isOutdated) {
464
				$outdatedCaches[] = $data;
465
			}
466
		}
467
468
		return $outdatedCaches;
469
	}
470
471
	protected function isSqliteUsed() {
472
		return strpos($this->config->getSystemValue('dbtype'), 'sqlite') !== false;
473
	}
474
475
	protected function isReadOnlyConfig(): bool {
476
		return \OC_Helper::isReadOnlyConfigEnabled();
477
	}
478
479
	protected function hasValidTransactionIsolationLevel(): bool {
480
		try {
481
			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...
482
				return true;
483
			}
484
485
			return $this->db->getTransactionIsolation() === Connection::TRANSACTION_READ_COMMITTED;
486
		} 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...
487
			// ignore
488
		}
489
490
		return true;
491
	}
492
493
	protected function hasFileinfoInstalled(): bool {
494
		return \OC_Util::fileInfoLoaded();
495
	}
496
497
	protected function hasWorkingFileLocking(): bool {
498
		return !($this->lockingProvider instanceof NoopLockingProvider);
499
	}
500
501
	protected function getSuggestedOverwriteCliURL(): string {
502
		$suggestedOverwriteCliUrl = '';
503
		if ($this->config->getSystemValue('overwrite.cli.url', '') === '') {
504
			$suggestedOverwriteCliUrl = $this->request->getServerProtocol() . '://' . $this->request->getInsecureServerHost() . \OC::$WEBROOT;
505
			if (!$this->config->getSystemValue('config_is_read_only', false)) {
506
				// Set the overwrite URL when it was not set yet.
507
				$this->config->setSystemValue('overwrite.cli.url', $suggestedOverwriteCliUrl);
508
				$suggestedOverwriteCliUrl = '';
509
			}
510
		}
511
		return $suggestedOverwriteCliUrl;
512
	}
513
514
	protected function getLastCronInfo(): array {
515
		$lastCronRun = $this->config->getAppValue('core', 'lastcron', 0);
516
		return [
517
			'diffInSeconds' => time() - $lastCronRun,
518
			'relativeTime' => $this->dateTimeFormatter->formatTimeSpan($lastCronRun),
519
			'backgroundJobsUrl' => $this->urlGenerator->linkToRoute('settings.AdminSettings.index', ['section' => 'server']) . '#backgroundjobs',
520
		];
521
	}
522
523
	protected function getCronErrors() {
524
		$errors = json_decode($this->config->getAppValue('core', 'cronErrors', ''), true);
525
526
		if (is_array($errors)) {
527
			return $errors;
528
		}
529
530
		return [];
531
	}
532
533
	protected function isPhpMailerUsed(): bool {
534
		return $this->config->getSystemValue('mail_smtpmode', 'php') === 'php';
535
	}
536
537
	protected function hasOpcacheLoaded(): bool {
538
		return function_exists('opcache_get_status');
539
	}
540
541
	/**
542
	 * Iterates through the configured app roots and
543
	 * tests if the subdirectories are owned by the same user than the current user.
544
	 *
545
	 * @return array
546
	 */
547
	protected function getAppDirsWithDifferentOwner(): array {
548
		$currentUser = posix_getpwuid(posix_getuid());
549
		$appDirsWithDifferentOwner = [[]];
550
551
		foreach (OC::$APPSROOTS as $appRoot) {
552
			if ($appRoot['writable'] === true) {
553
				$appDirsWithDifferentOwner[] = $this->getAppDirsWithDifferentOwnerForAppRoot($currentUser, $appRoot);
554
			}
555
		}
556
557
		$appDirsWithDifferentOwner = array_merge(...$appDirsWithDifferentOwner);
558
		sort($appDirsWithDifferentOwner);
559
560
		return $appDirsWithDifferentOwner;
561
	}
562
563
	/**
564
	 * Tests if the directories for one apps directory are writable by the current user.
565
	 *
566
	 * @param array $currentUser The current user
567
	 * @param array $appRoot The app root config
568
	 * @return string[] The none writable directory paths inside the app root
569
	 */
570
	private function getAppDirsWithDifferentOwnerForAppRoot(array $currentUser, array $appRoot): array {
571
		$appDirsWithDifferentOwner = [];
572
		$appsPath = $appRoot['path'];
573
		$appsDir = new DirectoryIterator($appRoot['path']);
574
575
		foreach ($appsDir as $fileInfo) {
576
			if ($fileInfo->isDir() && !$fileInfo->isDot()) {
577
				$absAppPath = $appsPath . DIRECTORY_SEPARATOR . $fileInfo->getFilename();
578
				$appDirUser = posix_getpwuid(fileowner($absAppPath));
579
				if ($appDirUser !== $currentUser) {
580
					$appDirsWithDifferentOwner[] = $absAppPath;
581
				}
582
			}
583
		}
584
585
		return $appDirsWithDifferentOwner;
586
	}
587
588
	/**
589
	 * @return DataResponse
590
	 */
591
	public function check() {
592
		return new DataResponse(
593
			[
594
				'isGetenvServerWorking' => !empty(getenv('PATH')),
595
				'isReadOnlyConfig' => $this->isReadOnlyConfig(),
596
				'hasValidTransactionIsolationLevel' => $this->hasValidTransactionIsolationLevel(),
597
				'outdatedCaches' => $this->getOutdatedCaches(),
598
				'hasFileinfoInstalled' => $this->hasFileinfoInstalled(),
599
				'hasWorkingFileLocking' => $this->hasWorkingFileLocking(),
600
				'suggestedOverwriteCliURL' => $this->getSuggestedOverwriteCliURL(),
601
				'cronInfo' => $this->getLastCronInfo(),
602
				'cronErrors' => $this->getCronErrors(),
603
				'serverHasInternetConnection' => $this->isInternetConnectionWorking(),
604
				'isMemcacheConfigured' => $this->isMemcacheConfigured(),
605
				'memcacheDocs' => $this->urlGenerator->linkToDocs('admin-performance'),
606
				'isRandomnessSecure' => $this->isRandomnessSecure(),
607
				'securityDocs' => $this->urlGenerator->linkToDocs('admin-security'),
608
				'isUsedTlsLibOutdated' => $this->isUsedTlsLibOutdated(),
609
				'phpSupported' => $this->isPhpSupported(),
610
				'forwardedForHeadersWorking' => $this->forwardedForHeadersWorking(),
611
				'reverseProxyDocs' => $this->urlGenerator->linkToDocs('admin-reverse-proxy'),
612
				'isCorrectMemcachedPHPModuleInstalled' => $this->isCorrectMemcachedPHPModuleInstalled(),
613
				'hasPassedCodeIntegrityCheck' => $this->checker->hasPassedCheck(),
614
				'codeIntegrityCheckerDocumentation' => $this->urlGenerator->linkToDocs('admin-code-integrity'),
615
				'isOpcacheProperlySetup' => $this->isOpcacheProperlySetup(),
616
				'hasOpcacheLoaded' => $this->hasOpcacheLoaded(),
617
				'phpOpcacheDocumentation' => $this->urlGenerator->linkToDocs('admin-php-opcache'),
618
				'isSettimelimitAvailable' => $this->isSettimelimitAvailable(),
619
				'hasFreeTypeSupport' => $this->hasFreeTypeSupport(),
620
				'missingIndexes' => $this->hasMissingIndexes(),
621
				'isSqliteUsed' => $this->isSqliteUsed(),
622
				'databaseConversionDocumentation' => $this->urlGenerator->linkToDocs('admin-db-conversion'),
623
				'isPhpMailerUsed' => $this->isPhpMailerUsed(),
624
				'mailSettingsDocumentation' => $this->urlGenerator->getAbsoluteURL('index.php/settings/admin'),
625
				'isMemoryLimitSufficient' => $this->memoryInfo->isMemoryLimitSufficient(),
626
				'appDirsWithDifferentOwner' => $this->getAppDirsWithDifferentOwner(),
627
			]
628
		);
629
	}
630
}
631