Passed
Push — master ( 5e4469...fe5813 )
by Morris
12:17
created

CheckSetupController::getCurlVersion()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 2
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 Doctrine\DBAL\Types\Type;
38
use GuzzleHttp\Exception\ClientException;
39
use OC;
40
use OC\AppFramework\Http;
41
use OC\DB\Connection;
42
use OC\DB\MissingIndexInformation;
43
use OC\DB\SchemaWrapper;
44
use OC\IntegrityCheck\Checker;
45
use OC\Lock\NoopLockingProvider;
46
use OC\MemoryInfo;
47
use OCP\AppFramework\Controller;
48
use OCP\AppFramework\Http\DataDisplayResponse;
49
use OCP\AppFramework\Http\DataResponse;
50
use OCP\AppFramework\Http\RedirectResponse;
51
use OCP\Http\Client\IClientService;
52
use OCP\IConfig;
53
use OCP\IDateTimeFormatter;
54
use OCP\IDBConnection;
55
use OCP\IL10N;
56
use OCP\ILogger;
57
use OCP\IRequest;
58
use OCP\IURLGenerator;
59
use OCP\Lock\ILockingProvider;
60
use OCP\Security\ISecureRandom;
61
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
62
use Symfony\Component\EventDispatcher\GenericEvent;
63
64
/**
65
 * @package OC\Settings\Controller
66
 */
67
class CheckSetupController extends Controller {
68
	/** @var IConfig */
69
	private $config;
70
	/** @var IClientService */
71
	private $clientService;
72
	/** @var \OC_Util */
73
	private $util;
74
	/** @var IURLGenerator */
75
	private $urlGenerator;
76
	/** @var IL10N */
77
	private $l10n;
78
	/** @var Checker */
79
	private $checker;
80
	/** @var ILogger */
81
	private $logger;
82
	/** @var EventDispatcherInterface */
83
	private $dispatcher;
84
	/** @var IDBConnection|Connection */
85
	private $db;
86
	/** @var ILockingProvider */
87
	private $lockingProvider;
88
	/** @var IDateTimeFormatter */
89
	private $dateTimeFormatter;
90
	/** @var MemoryInfo */
91
	private $memoryInfo;
92
	/** @var ISecureRandom */
93
	private $secureRandom;
94
95
	public function __construct($AppName,
96
								IRequest $request,
97
								IConfig $config,
98
								IClientService $clientService,
99
								IURLGenerator $urlGenerator,
100
								\OC_Util $util,
101
								IL10N $l10n,
102
								Checker $checker,
103
								ILogger $logger,
104
								EventDispatcherInterface $dispatcher,
105
								IDBConnection $db,
106
								ILockingProvider $lockingProvider,
107
								IDateTimeFormatter $dateTimeFormatter,
108
								MemoryInfo $memoryInfo,
109
								ISecureRandom $secureRandom) {
110
		parent::__construct($AppName, $request);
111
		$this->config = $config;
112
		$this->clientService = $clientService;
113
		$this->util = $util;
114
		$this->urlGenerator = $urlGenerator;
115
		$this->l10n = $l10n;
116
		$this->checker = $checker;
117
		$this->logger = $logger;
118
		$this->dispatcher = $dispatcher;
119
		$this->db = $db;
120
		$this->lockingProvider = $lockingProvider;
121
		$this->dateTimeFormatter = $dateTimeFormatter;
122
		$this->memoryInfo = $memoryInfo;
123
		$this->secureRandom = $secureRandom;
124
	}
125
126
	/**
127
	 * Checks if the server can connect to the internet using HTTPS and HTTP
128
	 * @return bool
129
	 */
130
	private function isInternetConnectionWorking() {
131
		if ($this->config->getSystemValue('has_internet_connection', true) === false) {
132
			return false;
133
		}
134
135
		$siteArray = $this->config->getSystemValue('connectivity_check_domains', [
136
			'www.nextcloud.com', 'www.startpage.com', 'www.eff.org', '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']);
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 $this->l10n->t('cURL is using an outdated %1$s version (%2$s). Please update your operating system or features such as %3$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) {
251
				if($e->getResponse()->getStatusCode() === 400) {
252
					return $this->l10n->t('cURL is using an outdated %1$s version (%2$s). Please update your operating system or features such as %3$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.1.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->getHeader('REMOTE_ADDR');
291
292
		if (\is_array($trustedProxies) && \in_array($remoteAddress, $trustedProxies)) {
293
			return $remoteAddress !== $this->request->getRemoteAddress();
294
		}
295
		// either not enabled or working correctly
296
		return true;
297
	}
298
299
	/**
300
	 * Checks if the correct memcache module for PHP is installed. Only
301
	 * fails if memcached is configured and the working module is not installed.
302
	 *
303
	 * @return bool
304
	 */
305
	private function isCorrectMemcachedPHPModuleInstalled() {
306
		if ($this->config->getSystemValue('memcache.distributed', null) !== '\OC\Memcache\Memcached') {
307
			return true;
308
		}
309
310
		// there are two different memcached modules for PHP
311
		// we only support memcached and not memcache
312
		// https://code.google.com/p/memcached/wiki/PHPClientComparison
313
		return !(!extension_loaded('memcached') && extension_loaded('memcache'));
314
	}
315
316
	/**
317
	 * Checks if set_time_limit is not disabled.
318
	 *
319
	 * @return bool
320
	 */
321
	private function isSettimelimitAvailable() {
322
		if (function_exists('set_time_limit')
323
			&& strpos(@ini_get('disable_functions'), 'set_time_limit') === false) {
324
			return true;
325
		}
326
327
		return false;
328
	}
329
330
	/**
331
	 * @return RedirectResponse
332
	 */
333
	public function rescanFailedIntegrityCheck() {
334
		$this->checker->runInstanceVerification();
335
		return new RedirectResponse(
336
			$this->urlGenerator->linkToRoute('settings.AdminSettings.index')
337
		);
338
	}
339
340
	/**
341
	 * @NoCSRFRequired
342
	 * @return DataResponse
343
	 */
344
	public function getFailedIntegrityCheckFiles() {
345
		if(!$this->checker->isCodeCheckEnforced()) {
346
			return new DataDisplayResponse('Integrity checker has been disabled. Integrity cannot be verified.');
0 ignored issues
show
Bug Best Practice introduced by
The expression return new OCP\AppFramew...y cannot be verified.') returns the type OCP\AppFramework\Http\DataDisplayResponse which is incompatible with the documented return type OCP\AppFramework\Http\DataResponse.
Loading history...
347
		}
348
349
		$completeResults = $this->checker->getResults();
350
351
		if(!empty($completeResults)) {
352
			$formattedTextResponse = 'Technical information
353
=====================
354
The following list covers which files have failed the integrity check. Please read
355
the previous linked documentation to learn more about the errors and how to fix
356
them.
357
358
Results
359
=======
360
';
361
			foreach($completeResults as $context => $contextResult) {
362
				$formattedTextResponse .= "- $context\n";
363
364
				foreach($contextResult as $category => $result) {
365
					$formattedTextResponse .= "\t- $category\n";
366
					if($category !== 'EXCEPTION') {
367
						foreach ($result as $key => $results) {
368
							$formattedTextResponse .= "\t\t- $key\n";
369
						}
370
					} else {
371
						foreach ($result as $key => $results) {
372
							$formattedTextResponse .= "\t\t- $results\n";
373
						}
374
					}
375
376
				}
377
			}
378
379
			$formattedTextResponse .= '
380
Raw output
381
==========
382
';
383
			$formattedTextResponse .= print_r($completeResults, true);
384
		} else {
385
			$formattedTextResponse = 'No errors have been found.';
386
		}
387
388
389
		$response = new DataDisplayResponse(
390
			$formattedTextResponse,
391
			Http::STATUS_OK,
392
			[
393
				'Content-Type' => 'text/plain',
394
			]
395
		);
396
397
		return $response;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $response returns the type OCP\AppFramework\Http\DataDisplayResponse which is incompatible with the documented return type OCP\AppFramework\Http\DataResponse.
Loading history...
398
	}
399
400
	/**
401
	 * Checks whether a PHP opcache is properly set up
402
	 * @return bool
403
	 */
404
	protected function isOpcacheProperlySetup() {
405
		$iniWrapper = new IniGetWrapper();
406
407
		if(!$iniWrapper->getBool('opcache.enable')) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $iniWrapper->getBool('opcache.enable') of type boolean|null is loosely compared to false; this is ambiguous if the boolean can be false. You might want to explicitly use !== null instead.

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

$a = canBeFalseAndNull();

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

// Better use one of the explicit versions:
if ($a !== null) { }
if ($a !== false) { }
if ($a !== null && $a !== false) { }
Loading history...
408
			return false;
409
		}
410
411
		if(!$iniWrapper->getBool('opcache.save_comments')) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $iniWrapper->getBool('opcache.save_comments') of type boolean|null is loosely compared to false; this is ambiguous if the boolean can be false. You might want to explicitly use !== null instead.

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

$a = canBeFalseAndNull();

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

// Better use one of the explicit versions:
if ($a !== null) { }
if ($a !== false) { }
if ($a !== null && $a !== false) { }
Loading history...
412
			return false;
413
		}
414
415
		if(!$iniWrapper->getBool('opcache.enable_cli')) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $iniWrapper->getBool('opcache.enable_cli') of type boolean|null is loosely compared to false; this is ambiguous if the boolean can be false. You might want to explicitly use !== null instead.

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

$a = canBeFalseAndNull();

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

// Better use one of the explicit versions:
if ($a !== null) { }
if ($a !== false) { }
if ($a !== null && $a !== false) { }
Loading history...
416
			return false;
417
		}
418
419
		if($iniWrapper->getNumeric('opcache.max_accelerated_files') < 10000) {
420
			return false;
421
		}
422
423
		if($iniWrapper->getNumeric('opcache.memory_consumption') < 128) {
424
			return false;
425
		}
426
427
		if($iniWrapper->getNumeric('opcache.interned_strings_buffer') < 8) {
428
			return false;
429
		}
430
431
		return true;
432
	}
433
434
	/**
435
	 * Check if the required FreeType functions are present
436
	 * @return bool
437
	 */
438
	protected function hasFreeTypeSupport() {
439
		return function_exists('imagettfbbox') && function_exists('imagettftext');
440
	}
441
442
	protected function hasMissingIndexes(): array {
443
		$indexInfo = new MissingIndexInformation();
444
		// Dispatch event so apps can also hint for pending index updates if needed
445
		$event = new GenericEvent($indexInfo);
446
		$this->dispatcher->dispatch(IDBConnection::CHECK_MISSING_INDEXES_EVENT, $event);
447
448
		return $indexInfo->getListOfMissingIndexes();
449
	}
450
451
	/**
452
	 * warn if outdated version of a memcache module is used
453
	 */
454
	protected function getOutdatedCaches(): array {
455
		$caches = [
456
			'apcu'	=> ['name' => 'APCu', 'version' => '4.0.6'],
457
			'redis'	=> ['name' => 'Redis', 'version' => '2.2.5'],
458
		];
459
		$outdatedCaches = [];
460
		foreach ($caches as $php_module => $data) {
461
			$isOutdated = extension_loaded($php_module) && version_compare(phpversion($php_module), $data['version'], '<');
462
			if ($isOutdated) {
463
				$outdatedCaches[] = $data;
464
			}
465
		}
466
467
		return $outdatedCaches;
468
	}
469
470
	protected function isSqliteUsed() {
471
		return strpos($this->config->getSystemValue('dbtype'), 'sqlite') !== false;
472
	}
473
474
	protected function isReadOnlyConfig(): bool {
475
		return \OC_Helper::isReadOnlyConfigEnabled();
476
	}
477
478
	protected function hasValidTransactionIsolationLevel(): bool {
479
		try {
480
			if ($this->db->getDatabasePlatform() instanceof SqlitePlatform) {
481
				return true;
482
			}
483
484
			return $this->db->getTransactionIsolation() === Connection::TRANSACTION_READ_COMMITTED;
0 ignored issues
show
Bug introduced by
The method getTransactionIsolation() does not exist on OCP\IDBConnection. Since it exists in all sub-types, consider adding an abstract or default implementation to OCP\IDBConnection. ( Ignorable by Annotation )

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

484
			return $this->db->/** @scrutinizer ignore-call */ getTransactionIsolation() === Connection::TRANSACTION_READ_COMMITTED;
Loading history...
485
		} catch (DBALException $e) {
486
			// ignore
487
		}
488
489
		return true;
490
	}
491
492
	protected function hasFileinfoInstalled(): bool {
493
		return \OC_Util::fileInfoLoaded();
494
	}
495
496
	protected function hasWorkingFileLocking(): bool {
497
		return !($this->lockingProvider instanceof NoopLockingProvider);
498
	}
499
500
	protected function getSuggestedOverwriteCliURL(): string {
501
		$suggestedOverwriteCliUrl = '';
502
		if ($this->config->getSystemValue('overwrite.cli.url', '') === '') {
503
			$suggestedOverwriteCliUrl = $this->request->getServerProtocol() . '://' . $this->request->getInsecureServerHost() . \OC::$WEBROOT;
504
			if (!$this->config->getSystemValue('config_is_read_only', false)) {
505
				// Set the overwrite URL when it was not set yet.
506
				$this->config->setSystemValue('overwrite.cli.url', $suggestedOverwriteCliUrl);
507
				$suggestedOverwriteCliUrl = '';
508
			}
509
		}
510
		return $suggestedOverwriteCliUrl;
511
	}
512
513
	protected function getLastCronInfo(): array {
514
		$lastCronRun = $this->config->getAppValue('core', 'lastcron', 0);
515
		return [
516
			'diffInSeconds' => time() - $lastCronRun,
517
			'relativeTime' => $this->dateTimeFormatter->formatTimeSpan($lastCronRun),
0 ignored issues
show
Bug introduced by
$lastCronRun of type string is incompatible with the type DateTime|integer expected by parameter $timestamp of OCP\IDateTimeFormatter::formatTimeSpan(). ( Ignorable by Annotation )

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

517
			'relativeTime' => $this->dateTimeFormatter->formatTimeSpan(/** @scrutinizer ignore-type */ $lastCronRun),
Loading history...
518
			'backgroundJobsUrl' => $this->urlGenerator->linkToRoute('settings.AdminSettings.index', ['section' => 'server']) . '#backgroundjobs',
519
		];
520
	}
521
522
	protected function getCronErrors() {
523
		$errors = json_decode($this->config->getAppValue('core', 'cronErrors', ''), true);
524
525
		if (is_array($errors)) {
526
			return $errors;
527
		}
528
529
		return [];
530
	}
531
532
	protected function isPHPMailerUsed(): bool {
533
		return $this->config->getSystemValue('mail_smtpmode', 'smtp') === 'php';
534
	}
535
536
	protected function hasOpcacheLoaded(): bool {
537
		return function_exists('opcache_get_status');
538
	}
539
540
	/**
541
	 * Iterates through the configured app roots and
542
	 * tests if the subdirectories are owned by the same user than the current user.
543
	 *
544
	 * @return array
545
	 */
546
	protected function getAppDirsWithDifferentOwner(): array {
547
		$currentUser = posix_getuid();
548
		$appDirsWithDifferentOwner = [[]];
549
550
		foreach (OC::$APPSROOTS as $appRoot) {
551
			if ($appRoot['writable'] === true) {
552
				$appDirsWithDifferentOwner[] = $this->getAppDirsWithDifferentOwnerForAppRoot($currentUser, $appRoot);
553
			}
554
		}
555
556
		$appDirsWithDifferentOwner = array_merge(...$appDirsWithDifferentOwner);
557
		sort($appDirsWithDifferentOwner);
558
559
		return $appDirsWithDifferentOwner;
560
	}
561
562
	/**
563
	 * Tests if the directories for one apps directory are writable by the current user.
564
	 *
565
	 * @param int $currentUser The current user
566
	 * @param array $appRoot The app root config
567
	 * @return string[] The none writable directory paths inside the app root
568
	 */
569
	private function getAppDirsWithDifferentOwnerForAppRoot(int $currentUser, array $appRoot): array {
570
		$appDirsWithDifferentOwner = [];
571
		$appsPath = $appRoot['path'];
572
		$appsDir = new DirectoryIterator($appRoot['path']);
573
574
		foreach ($appsDir as $fileInfo) {
575
			if ($fileInfo->isDir() && !$fileInfo->isDot()) {
576
				$absAppPath = $appsPath . DIRECTORY_SEPARATOR . $fileInfo->getFilename();
577
				$appDirUser = fileowner($absAppPath);
578
				if ($appDirUser !== $currentUser) {
579
					$appDirsWithDifferentOwner[] = $absAppPath;
580
				}
581
			}
582
		}
583
584
		return $appDirsWithDifferentOwner;
585
	}
586
587
	/**
588
	 * Checks for potential PHP modules that would improve the instance
589
	 *
590
	 * @return string[] A list of PHP modules that is recommended
591
	 */
592
	protected function hasRecommendedPHPModules(): array {
593
		$recommendedPHPModules = [];
594
595
		if (!function_exists('grapheme_strlen')) {
596
			$recommendedPHPModules[] = 'intl';
597
		}
598
599
		if ($this->config->getAppValue('theming', 'enabled', 'no') === 'yes') {
600
			if (!extension_loaded('imagick')) {
601
				$recommendedPHPModules[] = 'imagick';
602
			}
603
		}
604
605
		return $recommendedPHPModules;
606
	}
607
608
	protected function hasBigIntConversionPendingColumns(): array {
609
		// copy of ConvertFilecacheBigInt::getColumnsByTable()
610
		$tables = [
611
			'activity' => ['activity_id', 'object_id'],
612
			'activity_mq' => ['mail_id'],
613
			'filecache' => ['fileid', 'storage', 'parent', 'mimetype', 'mimepart', 'mtime', 'storage_mtime'],
614
			'mimetypes' => ['id'],
615
			'storages' => ['numeric_id'],
616
		];
617
618
		$schema = new SchemaWrapper($this->db);
619
		$isSqlite = $this->db->getDatabasePlatform() instanceof SqlitePlatform;
620
		$pendingColumns = [];
621
622
		foreach ($tables as $tableName => $columns) {
623
			if (!$schema->hasTable($tableName)) {
624
				continue;
625
			}
626
627
			$table = $schema->getTable($tableName);
628
			foreach ($columns as $columnName) {
629
				$column = $table->getColumn($columnName);
630
				$isAutoIncrement = $column->getAutoincrement();
631
				$isAutoIncrementOnSqlite = $isSqlite && $isAutoIncrement;
632
				if ($column->getType()->getName() !== Type::BIGINT && !$isAutoIncrementOnSqlite) {
633
					$pendingColumns[] = $tableName . '.' . $columnName;
634
				}
635
			}
636
		}
637
638
		return $pendingColumns;
639
	}
640
641
	/**
642
	 * @return DataResponse
643
	 */
644
	public function check() {
645
		return new DataResponse(
646
			[
647
				'isGetenvServerWorking' => !empty(getenv('PATH')),
648
				'isReadOnlyConfig' => $this->isReadOnlyConfig(),
649
				'hasValidTransactionIsolationLevel' => $this->hasValidTransactionIsolationLevel(),
650
				'outdatedCaches' => $this->getOutdatedCaches(),
651
				'hasFileinfoInstalled' => $this->hasFileinfoInstalled(),
652
				'hasWorkingFileLocking' => $this->hasWorkingFileLocking(),
653
				'suggestedOverwriteCliURL' => $this->getSuggestedOverwriteCliURL(),
654
				'cronInfo' => $this->getLastCronInfo(),
655
				'cronErrors' => $this->getCronErrors(),
656
				'serverHasInternetConnection' => $this->isInternetConnectionWorking(),
657
				'isMemcacheConfigured' => $this->isMemcacheConfigured(),
658
				'memcacheDocs' => $this->urlGenerator->linkToDocs('admin-performance'),
659
				'isRandomnessSecure' => $this->isRandomnessSecure(),
660
				'securityDocs' => $this->urlGenerator->linkToDocs('admin-security'),
661
				'isUsedTlsLibOutdated' => $this->isUsedTlsLibOutdated(),
662
				'phpSupported' => $this->isPhpSupported(),
663
				'forwardedForHeadersWorking' => $this->forwardedForHeadersWorking(),
664
				'reverseProxyDocs' => $this->urlGenerator->linkToDocs('admin-reverse-proxy'),
665
				'isCorrectMemcachedPHPModuleInstalled' => $this->isCorrectMemcachedPHPModuleInstalled(),
666
				'hasPassedCodeIntegrityCheck' => $this->checker->hasPassedCheck(),
667
				'codeIntegrityCheckerDocumentation' => $this->urlGenerator->linkToDocs('admin-code-integrity'),
668
				'isOpcacheProperlySetup' => $this->isOpcacheProperlySetup(),
669
				'hasOpcacheLoaded' => $this->hasOpcacheLoaded(),
670
				'phpOpcacheDocumentation' => $this->urlGenerator->linkToDocs('admin-php-opcache'),
671
				'isSettimelimitAvailable' => $this->isSettimelimitAvailable(),
672
				'hasFreeTypeSupport' => $this->hasFreeTypeSupport(),
673
				'missingIndexes' => $this->hasMissingIndexes(),
674
				'isSqliteUsed' => $this->isSqliteUsed(),
675
				'databaseConversionDocumentation' => $this->urlGenerator->linkToDocs('admin-db-conversion'),
676
				'isPHPMailerUsed' => $this->isPHPMailerUsed(),
677
				'mailSettingsDocumentation' => $this->urlGenerator->getAbsoluteURL('index.php/settings/admin'),
678
				'isMemoryLimitSufficient' => $this->memoryInfo->isMemoryLimitSufficient(),
679
				'appDirsWithDifferentOwner' => $this->getAppDirsWithDifferentOwner(),
680
				'recommendedPHPModules' => $this->hasRecommendedPHPModules(),
681
				'pendingBigIntConversionColumns' => $this->hasBigIntConversionPendingColumns(),
682
			]
683
		);
684
	}
685
}
686