Passed
Push — master ( d4d642...38efad )
by Morris
19:47 queued 08:31
created

CheckSetupController::isOpcacheProperlySetup()   A

Complexity

Conditions 6
Paths 6

Size

Total Lines 24
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 12
nc 6
nop 0
dl 0
loc 24
rs 9.2222
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 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
								IL10N $l10n,
99
								Checker $checker,
100
								ILogger $logger,
101
								EventDispatcherInterface $dispatcher,
102
								IDBConnection $db,
103
								ILockingProvider $lockingProvider,
104
								IDateTimeFormatter $dateTimeFormatter,
105
								MemoryInfo $memoryInfo,
106
								ISecureRandom $secureRandom) {
107
		parent::__construct($AppName, $request);
108
		$this->config = $config;
109
		$this->clientService = $clientService;
110
		$this->urlGenerator = $urlGenerator;
111
		$this->l10n = $l10n;
112
		$this->checker = $checker;
113
		$this->logger = $logger;
114
		$this->dispatcher = $dispatcher;
115
		$this->db = $db;
116
		$this->lockingProvider = $lockingProvider;
117
		$this->dateTimeFormatter = $dateTimeFormatter;
118
		$this->memoryInfo = $memoryInfo;
119
		$this->secureRandom = $secureRandom;
120
	}
121
122
	/**
123
	 * Checks if the server can connect to the internet using HTTPS and HTTP
124
	 * @return bool
125
	 */
126
	private function isInternetConnectionWorking() {
127
		if ($this->config->getSystemValue('has_internet_connection', true) === false) {
128
			return false;
129
		}
130
131
		$siteArray = $this->config->getSystemValue('connectivity_check_domains', [
132
			'www.nextcloud.com', 'www.startpage.com', 'www.eff.org', 'www.edri.org'
133
		]);
134
135
		foreach($siteArray as $site) {
136
			if ($this->isSiteReachable($site)) {
137
				return true;
138
			}
139
		}
140
		return false;
141
	}
142
143
	/**
144
	* Checks if the Nextcloud server can connect to a specific URL using both HTTPS and HTTP
145
	* @return bool
146
	*/
147
	private function isSiteReachable($sitename) {
148
		$httpSiteName = 'http://' . $sitename . '/';
149
		$httpsSiteName = 'https://' . $sitename . '/';
150
151
		try {
152
			$client = $this->clientService->newClient();
153
			$client->get($httpSiteName);
154
			$client->get($httpsSiteName);
155
		} catch (\Exception $e) {
156
			$this->logger->logException($e, ['app' => 'internet_connection_check']);
157
			return false;
158
		}
159
		return true;
160
	}
161
162
	/**
163
	 * Checks whether a local memcache is installed or not
164
	 * @return bool
165
	 */
166
	private function isMemcacheConfigured() {
167
		return $this->config->getSystemValue('memcache.local', null) !== null;
168
	}
169
170
	/**
171
	 * Whether PHP can generate "secure" pseudorandom integers
172
	 *
173
	 * @return bool
174
	 */
175
	private function isRandomnessSecure() {
176
		try {
177
			$this->secureRandom->generate(1);
178
		} catch (\Exception $ex) {
179
			return false;
180
		}
181
		return true;
182
	}
183
184
	/**
185
	 * Public for the sake of unit-testing
186
	 *
187
	 * @return array
188
	 */
189
	protected function getCurlVersion() {
190
		return curl_version();
191
	}
192
193
	/**
194
	 * Check if the used  SSL lib is outdated. Older OpenSSL and NSS versions do
195
	 * have multiple bugs which likely lead to problems in combination with
196
	 * functionality required by ownCloud such as SNI.
197
	 *
198
	 * @link https://github.com/owncloud/core/issues/17446#issuecomment-122877546
199
	 * @link https://bugzilla.redhat.com/show_bug.cgi?id=1241172
200
	 * @return string
201
	 */
202
	private function isUsedTlsLibOutdated() {
203
		// Don't run check when:
204
		// 1. Server has `has_internet_connection` set to false
205
		// 2. AppStore AND S2S is disabled
206
		if(!$this->config->getSystemValue('has_internet_connection', true)) {
207
			return '';
208
		}
209
		if(!$this->config->getSystemValue('appstoreenabled', true)
210
			&& $this->config->getAppValue('files_sharing', 'outgoing_server2server_share_enabled', 'yes') === 'no'
211
			&& $this->config->getAppValue('files_sharing', 'incoming_server2server_share_enabled', 'yes') === 'no') {
212
			return '';
213
		}
214
215
		$versionString = $this->getCurlVersion();
216
		if(isset($versionString['ssl_version'])) {
217
			$versionString = $versionString['ssl_version'];
218
		} else {
219
			return '';
220
		}
221
222
		$features = (string)$this->l10n->t('installing and updating apps via the app store or Federated Cloud Sharing');
223
		if(!$this->config->getSystemValue('appstoreenabled', true)) {
224
			$features = (string)$this->l10n->t('Federated Cloud Sharing');
225
		}
226
227
		// Check if at least OpenSSL after 1.01d or 1.0.2b
228
		if(strpos($versionString, 'OpenSSL/') === 0) {
229
			$majorVersion = substr($versionString, 8, 5);
230
			$patchRelease = substr($versionString, 13, 6);
231
232
			if(($majorVersion === '1.0.1' && ord($patchRelease) < ord('d')) ||
233
				($majorVersion === '1.0.2' && ord($patchRelease) < ord('b'))) {
234
				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]);
235
			}
236
		}
237
238
		// Check if NSS and perform heuristic check
239
		if(strpos($versionString, 'NSS/') === 0) {
240
			try {
241
				$firstClient = $this->clientService->newClient();
242
				$firstClient->get('https://nextcloud.com/');
243
244
				$secondClient = $this->clientService->newClient();
245
				$secondClient->get('https://nextcloud.com/');
246
			} catch (ClientException $e) {
247
				if($e->getResponse()->getStatusCode() === 400) {
248
					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]);
249
				}
250
			}
251
		}
252
253
		return '';
254
	}
255
256
	/**
257
	 * Whether the version is outdated
258
	 *
259
	 * @return bool
260
	 */
261
	protected function isPhpOutdated() {
262
		if (version_compare(PHP_VERSION, '7.1.0', '<')) {
263
			return true;
264
		}
265
266
		return false;
267
	}
268
269
	/**
270
	 * Whether the php version is still supported (at time of release)
271
	 * according to: https://secure.php.net/supported-versions.php
272
	 *
273
	 * @return array
274
	 */
275
	private function isPhpSupported() {
276
		return ['eol' => $this->isPhpOutdated(), 'version' => PHP_VERSION];
277
	}
278
279
	/**
280
	 * Check if the reverse proxy configuration is working as expected
281
	 *
282
	 * @return bool
283
	 */
284
	private function forwardedForHeadersWorking() {
285
		$trustedProxies = $this->config->getSystemValue('trusted_proxies', []);
286
		$remoteAddress = $this->request->getHeader('REMOTE_ADDR');
287
288
		if (empty($trustedProxies) && $this->request->getHeader('X-Forwarded-Host') !== '') {
289
			return false;
290
		}
291
292
		if (\is_array($trustedProxies) && \in_array($remoteAddress, $trustedProxies, true)) {
293
			return $remoteAddress !== $this->request->getRemoteAddress();
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.');
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...
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;
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...
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')) {
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...
409
			return false;
410
		}
411
412
		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...
413
			return false;
414
		}
415
416
		if($iniWrapper->getNumeric('opcache.max_accelerated_files') < 10000) {
417
			return false;
418
		}
419
420
		if($iniWrapper->getNumeric('opcache.memory_consumption') < 128) {
421
			return false;
422
		}
423
424
		if($iniWrapper->getNumeric('opcache.interned_strings_buffer') < 8) {
425
			return false;
426
		}
427
428
		return true;
429
	}
430
431
	/**
432
	 * Check if the required FreeType functions are present
433
	 * @return bool
434
	 */
435
	protected function hasFreeTypeSupport() {
436
		return function_exists('imagettfbbox') && function_exists('imagettftext');
437
	}
438
439
	protected function hasMissingIndexes(): array {
440
		$indexInfo = new MissingIndexInformation();
441
		// Dispatch event so apps can also hint for pending index updates if needed
442
		$event = new GenericEvent($indexInfo);
443
		$this->dispatcher->dispatch(IDBConnection::CHECK_MISSING_INDEXES_EVENT, $event);
444
445
		return $indexInfo->getListOfMissingIndexes();
446
	}
447
448
	protected function isSqliteUsed() {
449
		return strpos($this->config->getSystemValue('dbtype'), 'sqlite') !== false;
450
	}
451
452
	protected function isReadOnlyConfig(): bool {
453
		return \OC_Helper::isReadOnlyConfigEnabled();
454
	}
455
456
	protected function hasValidTransactionIsolationLevel(): bool {
457
		try {
458
			if ($this->db->getDatabasePlatform() instanceof SqlitePlatform) {
459
				return true;
460
			}
461
462
			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

462
			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

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

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