Completed
Push — master ( f13c2b...55d0f3 )
by Morris
27:13 queued 01:06
created

CheckSetupController::hasMissingIndexes()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 5
nc 1
nop 0
dl 0
loc 8
rs 9.4285
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 GuzzleHttp\Exception\ClientException;
35
use OC\AppFramework\Http;
36
use OC\DB\MissingIndexInformation;
37
use OC\IntegrityCheck\Checker;
38
use OCP\AppFramework\Controller;
39
use OCP\AppFramework\Http\DataDisplayResponse;
40
use OCP\AppFramework\Http\DataResponse;
41
use OCP\AppFramework\Http\RedirectResponse;
42
use OCP\Http\Client\IClientService;
43
use OCP\IConfig;
44
use OCP\IDBConnection;
45
use OCP\IL10N;
46
use OCP\ILogger;
47
use OCP\IRequest;
48
use OCP\IURLGenerator;
49
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
50
use Symfony\Component\EventDispatcher\GenericEvent;
51
52
/**
53
 * @package OC\Settings\Controller
54
 */
55
class CheckSetupController extends Controller {
56
	/** @var IConfig */
57
	private $config;
58
	/** @var IClientService */
59
	private $clientService;
60
	/** @var \OC_Util */
61
	private $util;
62
	/** @var IURLGenerator */
63
	private $urlGenerator;
64
	/** @var IL10N */
65
	private $l10n;
66
	/** @var Checker */
67
	private $checker;
68
	/** @var ILogger */
69
	private $logger;
70
	/** @var EventDispatcherInterface */
71
	private $dispatcher;
72
73 View Code Duplication
	public function __construct($AppName,
74
								IRequest $request,
75
								IConfig $config,
76
								IClientService $clientService,
77
								IURLGenerator $urlGenerator,
78
								\OC_Util $util,
79
								IL10N $l10n,
80
								Checker $checker,
81
								ILogger $logger,
82
								EventDispatcherInterface $dispatcher) {
83
		parent::__construct($AppName, $request);
84
		$this->config = $config;
85
		$this->clientService = $clientService;
86
		$this->util = $util;
87
		$this->urlGenerator = $urlGenerator;
88
		$this->l10n = $l10n;
89
		$this->checker = $checker;
90
		$this->logger = $logger;
91
		$this->dispatcher = $dispatcher;
92
	}
93
94
	/**
95
	 * Checks if the server can connect to the internet using HTTPS and HTTP
96
	 * @return bool
97
	 */
98
	private function isInternetConnectionWorking() {
99
		if ($this->config->getSystemValue('has_internet_connection', true) === false) {
100
			return false;
101
		}
102
103
		$siteArray = ['www.nextcloud.com',
104
						'www.startpage.com',
105
						'www.eff.org',
106
						'www.edri.org',
107
			];
108
109
		foreach($siteArray as $site) {
110
			if ($this->isSiteReachable($site)) {
111
				return true;
112
			}
113
		}
114
		return false;
115
	}
116
117
	/**
118
	* Checks if the Nextcloud server can connect to a specific URL using both HTTPS and HTTP
119
	* @return bool
120
	*/
121
	private function isSiteReachable($sitename) {
122
		$httpSiteName = 'http://' . $sitename . '/';
123
		$httpsSiteName = 'https://' . $sitename . '/';
124
125
		try {
126
			$client = $this->clientService->newClient();
127
			$client->get($httpSiteName);
128
			$client->get($httpsSiteName);
129
		} catch (\Exception $e) {
130
			$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...
131
			return false;
132
		}
133
		return true;
134
	}
135
136
	/**
137
	 * Checks whether a local memcache is installed or not
138
	 * @return bool
139
	 */
140
	private function isMemcacheConfigured() {
141
		return $this->config->getSystemValue('memcache.local', null) !== null;
142
	}
143
144
	/**
145
	 * Whether /dev/urandom is available to the PHP controller
146
	 *
147
	 * @return bool
148
	 */
149
	private function isUrandomAvailable() {
150
		if(@file_exists('/dev/urandom')) {
151
			$file = fopen('/dev/urandom', 'rb');
152
			if($file) {
153
				fclose($file);
154
				return true;
155
			}
156
		}
157
158
		return false;
159
	}
160
161
	/**
162
	 * Public for the sake of unit-testing
163
	 *
164
	 * @return array
165
	 */
166
	protected function getCurlVersion() {
167
		return curl_version();
168
	}
169
170
	/**
171
	 * Check if the used  SSL lib is outdated. Older OpenSSL and NSS versions do
172
	 * have multiple bugs which likely lead to problems in combination with
173
	 * functionality required by ownCloud such as SNI.
174
	 *
175
	 * @link https://github.com/owncloud/core/issues/17446#issuecomment-122877546
176
	 * @link https://bugzilla.redhat.com/show_bug.cgi?id=1241172
177
	 * @return string
178
	 */
179
	private function isUsedTlsLibOutdated() {
180
		// Don't run check when:
181
		// 1. Server has `has_internet_connection` set to false
182
		// 2. AppStore AND S2S is disabled
183
		if(!$this->config->getSystemValue('has_internet_connection', true)) {
184
			return '';
185
		}
186
		if(!$this->config->getSystemValue('appstoreenabled', true)
187
			&& $this->config->getAppValue('files_sharing', 'outgoing_server2server_share_enabled', 'yes') === 'no'
188
			&& $this->config->getAppValue('files_sharing', 'incoming_server2server_share_enabled', 'yes') === 'no') {
189
			return '';
190
		}
191
192
		$versionString = $this->getCurlVersion();
193
		if(isset($versionString['ssl_version'])) {
194
			$versionString = $versionString['ssl_version'];
195
		} else {
196
			return '';
197
		}
198
199
		$features = (string)$this->l10n->t('installing and updating apps via the app store or Federated Cloud Sharing');
200
		if(!$this->config->getSystemValue('appstoreenabled', true)) {
201
			$features = (string)$this->l10n->t('Federated Cloud Sharing');
202
		}
203
204
		// Check if at least OpenSSL after 1.01d or 1.0.2b
205
		if(strpos($versionString, 'OpenSSL/') === 0) {
206
			$majorVersion = substr($versionString, 8, 5);
207
			$patchRelease = substr($versionString, 13, 6);
208
209
			if(($majorVersion === '1.0.1' && ord($patchRelease) < ord('d')) ||
210
				($majorVersion === '1.0.2' && ord($patchRelease) < ord('b'))) {
211
				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]);
212
			}
213
		}
214
215
		// Check if NSS and perform heuristic check
216
		if(strpos($versionString, 'NSS/') === 0) {
217
			try {
218
				$firstClient = $this->clientService->newClient();
219
				$firstClient->get('https://nextcloud.com/');
220
221
				$secondClient = $this->clientService->newClient();
222
				$secondClient->get('https://nextcloud.com/');
223
			} 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...
224
				if($e->getResponse()->getStatusCode() === 400) {
225
					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]);
226
				}
227
			}
228
		}
229
230
		return '';
231
	}
232
233
	/**
234
	 * Whether the version is outdated
235
	 *
236
	 * @return bool
237
	 */
238
	protected function isPhpOutdated() {
239
		if (version_compare(PHP_VERSION, '7.0.0', '<')) {
240
			return true;
241
		}
242
243
		return false;
244
	}
245
246
	/**
247
	 * Whether the php version is still supported (at time of release)
248
	 * according to: https://secure.php.net/supported-versions.php
249
	 *
250
	 * @return array
251
	 */
252
	private function isPhpSupported() {
253
		return ['eol' => $this->isPhpOutdated(), 'version' => PHP_VERSION];
254
	}
255
256
	/**
257
	 * Check if the reverse proxy configuration is working as expected
258
	 *
259
	 * @return bool
260
	 */
261
	private function forwardedForHeadersWorking() {
262
		$trustedProxies = $this->config->getSystemValue('trusted_proxies', []);
263
		$remoteAddress = $this->request->getRemoteAddress();
264
265
		if (is_array($trustedProxies) && in_array($remoteAddress, $trustedProxies)) {
266
			return false;
267
		}
268
269
		// either not enabled or working correctly
270
		return true;
271
	}
272
273
	/**
274
	 * Checks if the correct memcache module for PHP is installed. Only
275
	 * fails if memcached is configured and the working module is not installed.
276
	 *
277
	 * @return bool
278
	 */
279
	private function isCorrectMemcachedPHPModuleInstalled() {
280
		if ($this->config->getSystemValue('memcache.distributed', null) !== '\OC\Memcache\Memcached') {
281
			return true;
282
		}
283
284
		// there are two different memcached modules for PHP
285
		// we only support memcached and not memcache
286
		// https://code.google.com/p/memcached/wiki/PHPClientComparison
287
		return !(!extension_loaded('memcached') && extension_loaded('memcache'));
288
	}
289
290
	/**
291
	 * Checks if set_time_limit is not disabled.
292
	 *
293
	 * @return bool
294
	 */
295
	private function isSettimelimitAvailable() {
296
		if (function_exists('set_time_limit')
297
			&& strpos(@ini_get('disable_functions'), 'set_time_limit') === false) {
298
			return true;
299
		}
300
301
		return false;
302
	}
303
304
	/**
305
	 * @return RedirectResponse
306
	 */
307
	public function rescanFailedIntegrityCheck() {
308
		$this->checker->runInstanceVerification();
309
		return new RedirectResponse(
310
			$this->urlGenerator->linkToRoute('settings.AdminSettings.index')
311
		);
312
	}
313
314
	/**
315
	 * @NoCSRFRequired
316
	 * @return DataResponse
317
	 */
318
	public function getFailedIntegrityCheckFiles() {
319
		if(!$this->checker->isCodeCheckEnforced()) {
320
			return new DataDisplayResponse('Integrity checker has been disabled. Integrity cannot be verified.');
321
		}
322
323
		$completeResults = $this->checker->getResults();
324
325
		if(!empty($completeResults)) {
326
			$formattedTextResponse = 'Technical information
327
=====================
328
The following list covers which files have failed the integrity check. Please read
329
the previous linked documentation to learn more about the errors and how to fix
330
them.
331
332
Results
333
=======
334
';
335
			foreach($completeResults as $context => $contextResult) {
336
				$formattedTextResponse .= "- $context\n";
337
338
				foreach($contextResult as $category => $result) {
339
					$formattedTextResponse .= "\t- $category\n";
340
					if($category !== 'EXCEPTION') {
341
						foreach ($result as $key => $results) {
342
							$formattedTextResponse .= "\t\t- $key\n";
343
						}
344
					} else {
345
						foreach ($result as $key => $results) {
346
							$formattedTextResponse .= "\t\t- $results\n";
347
						}
348
					}
349
350
				}
351
			}
352
353
			$formattedTextResponse .= '
354
Raw output
355
==========
356
';
357
			$formattedTextResponse .= print_r($completeResults, true);
358
		} else {
359
			$formattedTextResponse = 'No errors have been found.';
360
		}
361
362
363
		$response = new DataDisplayResponse(
364
			$formattedTextResponse,
365
			Http::STATUS_OK,
366
			[
367
				'Content-Type' => 'text/plain',
368
			]
369
		);
370
371
		return $response;
372
	}
373
374
	/**
375
	 * Checks whether a PHP opcache is properly set up
376
	 * @return bool
377
	 */
378
	protected function isOpcacheProperlySetup() {
379
		$iniWrapper = new IniGetWrapper();
380
381
		$isOpcacheProperlySetUp = true;
382
383
		if(!$iniWrapper->getBool('opcache.enable')) {
384
			$isOpcacheProperlySetUp = false;
385
		}
386
387
		if(!$iniWrapper->getBool('opcache.save_comments')) {
388
			$isOpcacheProperlySetUp = false;
389
		}
390
391
		if(!$iniWrapper->getBool('opcache.enable_cli')) {
392
			$isOpcacheProperlySetUp = false;
393
		}
394
395
		if($iniWrapper->getNumeric('opcache.max_accelerated_files') < 10000) {
396
			$isOpcacheProperlySetUp = false;
397
		}
398
399
		if($iniWrapper->getNumeric('opcache.memory_consumption') < 128) {
400
			$isOpcacheProperlySetUp = false;
401
		}
402
403
		if($iniWrapper->getNumeric('opcache.interned_strings_buffer') < 8) {
404
			$isOpcacheProperlySetUp = false;
405
		}
406
407
		return $isOpcacheProperlySetUp;
408
	}
409
410
	/**
411
	 * Check if the required FreeType functions are present
412
	 * @return bool
413
	 */
414
	protected function hasFreeTypeSupport() {
415
		return function_exists('imagettfbbox') && function_exists('imagettftext');
416
	}
417
418
	/**
419
	 * Check if the required FreeType functions are present
420
	 * @return bool
421
	 */
422
	protected function hasMissingIndexes() {
423
		$indexInfo = new MissingIndexInformation();
424
		// Dispatch event so apps can also hint for pending index updates if needed
425
		$event = new GenericEvent($indexInfo);
426
		$this->dispatcher->dispatch(IDBConnection::CHECK_MISSING_INDEXES_EVENT, $event);
427
428
		return $indexInfo->getListOfMissingIndexes();
429
	}
430
431
	/**
432
	 * @return DataResponse
433
	 */
434
	public function check() {
435
		return new DataResponse(
436
			[
437
				'serverHasInternetConnection' => $this->isInternetConnectionWorking(),
438
				'isMemcacheConfigured' => $this->isMemcacheConfigured(),
439
				'memcacheDocs' => $this->urlGenerator->linkToDocs('admin-performance'),
440
				'isUrandomAvailable' => $this->isUrandomAvailable(),
441
				'securityDocs' => $this->urlGenerator->linkToDocs('admin-security'),
442
				'isUsedTlsLibOutdated' => $this->isUsedTlsLibOutdated(),
443
				'phpSupported' => $this->isPhpSupported(),
444
				'forwardedForHeadersWorking' => $this->forwardedForHeadersWorking(),
445
				'reverseProxyDocs' => $this->urlGenerator->linkToDocs('admin-reverse-proxy'),
446
				'isCorrectMemcachedPHPModuleInstalled' => $this->isCorrectMemcachedPHPModuleInstalled(),
447
				'hasPassedCodeIntegrityCheck' => $this->checker->hasPassedCheck(),
448
				'codeIntegrityCheckerDocumentation' => $this->urlGenerator->linkToDocs('admin-code-integrity'),
449
				'isOpcacheProperlySetup' => $this->isOpcacheProperlySetup(),
450
				'phpOpcacheDocumentation' => $this->urlGenerator->linkToDocs('admin-php-opcache'),
451
				'isSettimelimitAvailable' => $this->isSettimelimitAvailable(),
452
				'hasFreeTypeSupport' => $this->hasFreeTypeSupport(),
453
				'hasMissingIndexes' => $this->hasMissingIndexes(),
454
			]
455
		);
456
	}
457
}
458