Passed
Push — master ( 17cdcf...aa80aa )
by John
09:42 queued 11s
created

CheckSetupController::forwardedForHeadersWorking()   A

Complexity

Conditions 5
Paths 3

Size

Total Lines 14
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 7
nc 3
nop 0
dl 0
loc 14
rs 9.6111
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 OCA\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
class CheckSetupController extends Controller {
65
	/** @var IConfig */
66
	private $config;
67
	/** @var IClientService */
68
	private $clientService;
69
	/** @var IURLGenerator */
70
	private $urlGenerator;
71
	/** @var IL10N */
72
	private $l10n;
73
	/** @var Checker */
74
	private $checker;
75
	/** @var ILogger */
76
	private $logger;
77
	/** @var EventDispatcherInterface */
78
	private $dispatcher;
79
	/** @var IDBConnection|Connection */
80
	private $db;
81
	/** @var ILockingProvider */
82
	private $lockingProvider;
83
	/** @var IDateTimeFormatter */
84
	private $dateTimeFormatter;
85
	/** @var MemoryInfo */
86
	private $memoryInfo;
87
	/** @var ISecureRandom */
88
	private $secureRandom;
89
90
	public function __construct($AppName,
91
								IRequest $request,
92
								IConfig $config,
93
								IClientService $clientService,
94
								IURLGenerator $urlGenerator,
95
								IL10N $l10n,
96
								Checker $checker,
97
								ILogger $logger,
98
								EventDispatcherInterface $dispatcher,
99
								IDBConnection $db,
100
								ILockingProvider $lockingProvider,
101
								IDateTimeFormatter $dateTimeFormatter,
102
								MemoryInfo $memoryInfo,
103
								ISecureRandom $secureRandom) {
104
		parent::__construct($AppName, $request);
105
		$this->config = $config;
106
		$this->clientService = $clientService;
107
		$this->urlGenerator = $urlGenerator;
108
		$this->l10n = $l10n;
109
		$this->checker = $checker;
110
		$this->logger = $logger;
111
		$this->dispatcher = $dispatcher;
112
		$this->db = $db;
113
		$this->lockingProvider = $lockingProvider;
114
		$this->dateTimeFormatter = $dateTimeFormatter;
115
		$this->memoryInfo = $memoryInfo;
116
		$this->secureRandom = $secureRandom;
117
	}
118
119
	/**
120
	 * Checks if the server can connect to the internet using HTTPS and HTTP
121
	 * @return bool
122
	 */
123
	private function hasInternetConnectivityProblems(): bool {
124
		if ($this->config->getSystemValue('has_internet_connection', true) === false) {
125
			return false;
126
		}
127
128
		$siteArray = $this->config->getSystemValue('connectivity_check_domains', [
129
			'www.nextcloud.com', 'www.startpage.com', 'www.eff.org', 'www.edri.org'
130
		]);
131
132
		foreach($siteArray as $site) {
133
			if ($this->isSiteReachable($site)) {
134
				return false;
135
			}
136
		}
137
		return true;
138
	}
139
140
	/**
141
	* Checks if the Nextcloud server can connect to a specific URL using both HTTPS and HTTP
142
	* @return bool
143
	*/
144
	private function isSiteReachable($sitename) {
145
		$httpSiteName = 'http://' . $sitename . '/';
146
		$httpsSiteName = 'https://' . $sitename . '/';
147
148
		try {
149
			$client = $this->clientService->newClient();
150
			$client->get($httpSiteName);
151
			$client->get($httpsSiteName);
152
		} catch (\Exception $e) {
153
			$this->logger->logException($e, ['app' => 'internet_connection_check']);
154
			return false;
155
		}
156
		return true;
157
	}
158
159
	/**
160
	 * Checks whether a local memcache is installed or not
161
	 * @return bool
162
	 */
163
	private function isMemcacheConfigured() {
164
		return $this->config->getSystemValue('memcache.local', null) !== null;
165
	}
166
167
	/**
168
	 * Whether PHP can generate "secure" pseudorandom integers
169
	 *
170
	 * @return bool
171
	 */
172
	private function isRandomnessSecure() {
173
		try {
174
			$this->secureRandom->generate(1);
175
		} catch (\Exception $ex) {
176
			return false;
177
		}
178
		return true;
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 $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]);
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) {
244
				if($e->getResponse()->getStatusCode() === 400) {
245
					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]);
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.1.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->getHeader('REMOTE_ADDR');
284
285
		if (empty($trustedProxies) && $this->request->getHeader('X-Forwarded-Host') !== '') {
286
			return false;
287
		}
288
289
		if (\is_array($trustedProxies) && \in_array($remoteAddress, $trustedProxies, true)) {
290
			return $remoteAddress !== $this->request->getRemoteAddress();
291
		}
292
293
		// either not enabled or working correctly
294
		return true;
295
	}
296
297
	/**
298
	 * Checks if the correct memcache module for PHP is installed. Only
299
	 * fails if memcached is configured and the working module is not installed.
300
	 *
301
	 * @return bool
302
	 */
303
	private function isCorrectMemcachedPHPModuleInstalled() {
304
		if ($this->config->getSystemValue('memcache.distributed', null) !== '\OC\Memcache\Memcached') {
305
			return true;
306
		}
307
308
		// there are two different memcached modules for PHP
309
		// we only support memcached and not memcache
310
		// https://code.google.com/p/memcached/wiki/PHPClientComparison
311
		return !(!extension_loaded('memcached') && extension_loaded('memcache'));
312
	}
313
314
	/**
315
	 * Checks if set_time_limit is not disabled.
316
	 *
317
	 * @return bool
318
	 */
319
	private function isSettimelimitAvailable() {
320
		if (function_exists('set_time_limit')
321
			&& strpos(@ini_get('disable_functions'), 'set_time_limit') === false) {
322
			return true;
323
		}
324
325
		return false;
326
	}
327
328
	/**
329
	 * @return RedirectResponse
330
	 */
331
	public function rescanFailedIntegrityCheck() {
332
		$this->checker->runInstanceVerification();
333
		return new RedirectResponse(
334
			$this->urlGenerator->linkToRoute('settings.AdminSettings.index', ['section' => 'overview'])
335
		);
336
	}
337
338
	/**
339
	 * @NoCSRFRequired
340
	 * @return DataResponse
341
	 */
342
	public function getFailedIntegrityCheckFiles() {
343
		if(!$this->checker->isCodeCheckEnforced()) {
344
			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...
345
		}
346
347
		$completeResults = $this->checker->getResults();
348
349
		if(!empty($completeResults)) {
350
			$formattedTextResponse = 'Technical information
351
=====================
352
The following list covers which files have failed the integrity check. Please read
353
the previous linked documentation to learn more about the errors and how to fix
354
them.
355
356
Results
357
=======
358
';
359
			foreach($completeResults as $context => $contextResult) {
360
				$formattedTextResponse .= "- $context\n";
361
362
				foreach($contextResult as $category => $result) {
363
					$formattedTextResponse .= "\t- $category\n";
364
					if($category !== 'EXCEPTION') {
365
						foreach ($result as $key => $results) {
366
							$formattedTextResponse .= "\t\t- $key\n";
367
						}
368
					} else {
369
						foreach ($result as $key => $results) {
370
							$formattedTextResponse .= "\t\t- $results\n";
371
						}
372
					}
373
374
				}
375
			}
376
377
			$formattedTextResponse .= '
378
Raw output
379
==========
380
';
381
			$formattedTextResponse .= print_r($completeResults, true);
382
		} else {
383
			$formattedTextResponse = 'No errors have been found.';
384
		}
385
386
387
		$response = new DataDisplayResponse(
388
			$formattedTextResponse,
389
			Http::STATUS_OK,
390
			[
391
				'Content-Type' => 'text/plain',
392
			]
393
		);
394
395
		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...
396
	}
397
398
	/**
399
	 * Checks whether a PHP opcache is properly set up
400
	 * @return bool
401
	 */
402
	protected function isOpcacheProperlySetup() {
403
		$iniWrapper = new IniGetWrapper();
404
405
		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...
406
			return false;
407
		}
408
409
		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...
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);
0 ignored issues
show
Unused Code introduced by
The call to Symfony\Contracts\EventD...erInterface::dispatch() has too many arguments starting with $event. ( Ignorable by Annotation )

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

440
		$this->dispatcher->/** @scrutinizer ignore-call */ 
441
                     dispatch(IDBConnection::CHECK_MISSING_INDEXES_EVENT, $event);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
441
442
		return $indexInfo->getListOfMissingIndexes();
443
	}
444
445
	protected function isSqliteUsed() {
446
		return strpos($this->config->getSystemValue('dbtype'), 'sqlite') !== false;
447
	}
448
449
	protected function isReadOnlyConfig(): bool {
450
		return \OC_Helper::isReadOnlyConfigEnabled();
451
	}
452
453
	protected function hasValidTransactionIsolationLevel(): bool {
454
		try {
455
			if ($this->db->getDatabasePlatform() instanceof SqlitePlatform) {
456
				return true;
457
			}
458
459
			return $this->db->getTransactionIsolation() === Connection::TRANSACTION_READ_COMMITTED;
0 ignored issues
show
Deprecated Code introduced by
The constant Doctrine\DBAL\Connection...NSACTION_READ_COMMITTED has been deprecated: Use TransactionIsolationLevel::READ_COMMITTED. ( Ignorable by Annotation )

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

459
			return $this->db->getTransactionIsolation() === /** @scrutinizer ignore-deprecated */ Connection::TRANSACTION_READ_COMMITTED;

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...
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

459
			return $this->db->/** @scrutinizer ignore-call */ getTransactionIsolation() === Connection::TRANSACTION_READ_COMMITTED;
Loading history...
460
		} catch (DBALException $e) {
461
			// ignore
462
		}
463
464
		return true;
465
	}
466
467
	protected function hasFileinfoInstalled(): bool {
468
		return \OC_Util::fileInfoLoaded();
469
	}
470
471
	protected function hasWorkingFileLocking(): bool {
472
		return !($this->lockingProvider instanceof NoopLockingProvider);
473
	}
474
475
	protected function getSuggestedOverwriteCliURL(): string {
476
		$suggestedOverwriteCliUrl = '';
477
		if ($this->config->getSystemValue('overwrite.cli.url', '') === '') {
478
			$suggestedOverwriteCliUrl = $this->request->getServerProtocol() . '://' . $this->request->getInsecureServerHost() . \OC::$WEBROOT;
479
			if (!$this->config->getSystemValue('config_is_read_only', false)) {
480
				// Set the overwrite URL when it was not set yet.
481
				$this->config->setSystemValue('overwrite.cli.url', $suggestedOverwriteCliUrl);
482
				$suggestedOverwriteCliUrl = '';
483
			}
484
		}
485
		return $suggestedOverwriteCliUrl;
486
	}
487
488
	protected function getLastCronInfo(): array {
489
		$lastCronRun = $this->config->getAppValue('core', 'lastcron', 0);
490
		return [
491
			'diffInSeconds' => time() - $lastCronRun,
492
			'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

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