Completed
Push — master ( b0c18b...05b249 )
by Roeland
21:12
created

OC_Util   F

Complexity

Total Complexity 225

Size/Duplication

Total Lines 1469
Duplicated Lines 4.9 %

Coupling/Cohesion

Components 2
Dependencies 46

Importance

Changes 0
Metric Value
dl 72
loc 1469
rs 0.8
c 0
b 0
f 0
wmc 225
lcom 2
cbo 46

54 Methods

Rating   Name   Duplication   Size   Complexity  
A normalizeUnicode() 0 13 4
A getHumanVersion() 0 8 3
B isValidFileName() 0 21 6
B needUpgrade() 0 39 9
A getAppManager() 0 3 1
A initLocalStorageRootFS() 0 10 2
B initObjectStoreRootFS() 8 26 6
B initObjectStoreMultibucketRootFS() 8 33 7
F setupFS() 0 135 23
A isPublicLinkPasswordRequired() 0 4 1
A isSharingDisabledForUser() 13 21 5
A isDefaultExpireDateEnforced() 0 10 2
A getUserQuota() 0 11 3
B copySkeleton() 0 40 9
B copyr() 0 30 6
A tearDownFS() 0 6 1
A getVersion() 0 4 1
A getVersionString() 0 4 1
A getEditionString() 0 3 1
A getChannel() 0 4 1
A getBuild() 0 4 1
A loadVersion() 0 19 2
A generatePath() 0 11 3
A addScript() 0 9 3
A addVendorScript() 0 4 1
A addTranslations() 0 11 3
A addStyle() 0 4 1
A addVendorStyle() 0 4 1
B addExternalResource() 16 20 7
A addHeader() 0 13 2
F checkServer() 0 249 44
A checkDatabaseVersion() 0 26 5
A checkDataDirectoryPermissions() 0 22 4
A checkDataDirectoryValidity() 13 18 3
A checkLoggedIn() 14 18 3
A checkAdminUser() 0 7 2
A checkSubAdminUser() 0 14 3
B getDefaultPageUrl() 0 32 8
A redirectToDefaultPage() 0 5 1
A getInstanceId() 0 9 2
A sanitizeHTML() 0 11 2
A encodePath() 0 5 1
A createHtaccessTestFile() 0 27 4
A isHtaccessWorking() 0 31 5
A isSetLocaleWorking() 0 7 2
A isAnnotationsWorking() 0 6 2
A fileInfoLoaded() 0 3 1
A obEnd() 0 5 2
A runningOnMac() 0 3 1
A runningOnHhvm() 0 3 1
A getTheme() 0 11 3
A deleteFromOpcodeCache() 0 14 4
A clearOpcodeCache() 0 14 4
A isIe() 0 7 2

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like OC_Util often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use OC_Util, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * @copyright Copyright (c) 2016, ownCloud, Inc.
4
 *
5
 * @author Adam Williamson <[email protected]>
6
 * @author Andreas Fischer <[email protected]>
7
 * @author Arthur Schiwon <[email protected]>
8
 * @author Bart Visscher <[email protected]>
9
 * @author Bernhard Posselt <[email protected]>
10
 * @author Birk Borkason <[email protected]>
11
 * @author Björn Schießle <[email protected]>
12
 * @author Brice Maron <[email protected]>
13
 * @author Christoph Wurst <[email protected]>
14
 * @author Christopher Schäpers <[email protected]>
15
 * @author Clark Tomlinson <[email protected]>
16
 * @author cmeh <[email protected]>
17
 * @author Felix Epp <[email protected]>
18
 * @author Florin Peter <[email protected]>
19
 * @author Frank Karlitschek <[email protected]>
20
 * @author Georg Ehrke <[email protected]>
21
 * @author helix84 <[email protected]>
22
 * @author Ilja Neumann <[email protected]>
23
 * @author Individual IT Services <[email protected]>
24
 * @author Jakob Sack <[email protected]>
25
 * @author Joas Schilling <[email protected]>
26
 * @author Jörn Friedrich Dreyer <[email protected]>
27
 * @author Kawohl <[email protected]>
28
 * @author Lukas Reschke <[email protected]>
29
 * @author Markus Goetz <[email protected]>
30
 * @author Martin Mattel <[email protected]>
31
 * @author Marvin Thomas Rabe <[email protected]>
32
 * @author Michael Gapczynski <[email protected]>
33
 * @author Morris Jobke <[email protected]>
34
 * @author rakekniven <[email protected]>
35
 * @author Robin Appelman <[email protected]>
36
 * @author Robin McCorkell <[email protected]>
37
 * @author Roeland Jago Douma <[email protected]>
38
 * @author Sebastian Wessalowski <[email protected]>
39
 * @author Stefan Rado <[email protected]>
40
 * @author Stefan Weil <[email protected]>
41
 * @author Thomas Müller <[email protected]>
42
 * @author Thomas Tanghus <[email protected]>
43
 * @author Victor Dubiniuk <[email protected]>
44
 * @author Vincent Petry <[email protected]>
45
 * @author Volkan Gezer <[email protected]>
46
 *
47
 * @license AGPL-3.0
48
 *
49
 * This code is free software: you can redistribute it and/or modify
50
 * it under the terms of the GNU Affero General Public License, version 3,
51
 * as published by the Free Software Foundation.
52
 *
53
 * This program is distributed in the hope that it will be useful,
54
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
55
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
56
 * GNU Affero General Public License for more details.
57
 *
58
 * You should have received a copy of the GNU Affero General Public License, version 3,
59
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
60
 *
61
 */
62
63
use OCP\IConfig;
64
use OCP\IGroupManager;
65
use OCP\ILogger;
66
use OCP\IUser;
67
use OC\AppFramework\Http\Request;
68
69
class OC_Util {
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
70
	public static $scripts = array();
71
	public static $styles = array();
72
	public static $headers = array();
73
	private static $rootMounted = false;
74
	private static $fsSetup = false;
75
76
	/** @var array Local cache of version.php */
77
	private static $versionCache = null;
78
79
	protected static function getAppManager() {
80
		return \OC::$server->getAppManager();
81
	}
82
83
	private static function initLocalStorageRootFS() {
84
		// mount local file backend as root
85
		$configDataDirectory = \OC::$server->getSystemConfig()->getValue("datadirectory", OC::$SERVERROOT . "/data");
86
		//first set up the local "root" storage
87
		\OC\Files\Filesystem::initMountManager();
88
		if (!self::$rootMounted) {
89
			\OC\Files\Filesystem::mount('\OC\Files\Storage\Local', array('datadir' => $configDataDirectory), '/');
90
			self::$rootMounted = true;
91
		}
92
	}
93
94
	/**
95
	 * mounting an object storage as the root fs will in essence remove the
96
	 * necessity of a data folder being present.
97
	 * TODO make home storage aware of this and use the object storage instead of local disk access
98
	 *
99
	 * @param array $config containing 'class' and optional 'arguments'
100
	 * @suppress PhanDeprecatedFunction
101
	 */
102
	private static function initObjectStoreRootFS($config) {
103
		// check misconfiguration
104
		if (empty($config['class'])) {
105
			\OCP\Util::writeLog('files', 'No class given for objectstore', ILogger::ERROR);
106
		}
107
		if (!isset($config['arguments'])) {
108
			$config['arguments'] = array();
109
		}
110
111
		// instantiate object store implementation
112
		$name = $config['class'];
113 View Code Duplication
		if (strpos($name, 'OCA\\') === 0 && substr_count($name, '\\') >= 2) {
114
			$segments = explode('\\', $name);
115
			OC_App::loadApp(strtolower($segments[1]));
116
		}
117
		$config['arguments']['objectstore'] = new $config['class']($config['arguments']);
118
		// mount with plain / root object store implementation
119
		$config['class'] = '\OC\Files\ObjectStore\ObjectStoreStorage';
120
121
		// mount object storage as root
122
		\OC\Files\Filesystem::initMountManager();
123 View Code Duplication
		if (!self::$rootMounted) {
124
			\OC\Files\Filesystem::mount($config['class'], $config['arguments'], '/');
125
			self::$rootMounted = true;
126
		}
127
	}
128
129
	/**
130
	 * mounting an object storage as the root fs will in essence remove the
131
	 * necessity of a data folder being present.
132
	 *
133
	 * @param array $config containing 'class' and optional 'arguments'
134
	 * @suppress PhanDeprecatedFunction
135
	 */
136
	private static function initObjectStoreMultibucketRootFS($config) {
137
		// check misconfiguration
138
		if (empty($config['class'])) {
139
			\OCP\Util::writeLog('files', 'No class given for objectstore', ILogger::ERROR);
140
		}
141
		if (!isset($config['arguments'])) {
142
			$config['arguments'] = array();
143
		}
144
145
		// instantiate object store implementation
146
		$name = $config['class'];
147 View Code Duplication
		if (strpos($name, 'OCA\\') === 0 && substr_count($name, '\\') >= 2) {
148
			$segments = explode('\\', $name);
149
			OC_App::loadApp(strtolower($segments[1]));
150
		}
151
152
		if (!isset($config['arguments']['bucket'])) {
153
			$config['arguments']['bucket'] = '';
154
		}
155
		// put the root FS always in first bucket for multibucket configuration
156
		$config['arguments']['bucket'] .= '0';
157
158
		$config['arguments']['objectstore'] = new $config['class']($config['arguments']);
159
		// mount with plain / root object store implementation
160
		$config['class'] = '\OC\Files\ObjectStore\ObjectStoreStorage';
161
162
		// mount object storage as root
163
		\OC\Files\Filesystem::initMountManager();
164 View Code Duplication
		if (!self::$rootMounted) {
165
			\OC\Files\Filesystem::mount($config['class'], $config['arguments'], '/');
166
			self::$rootMounted = true;
167
		}
168
	}
169
170
	/**
171
	 * Can be set up
172
	 *
173
	 * @param string $user
174
	 * @return boolean
175
	 * @description configure the initial filesystem based on the configuration
176
	 * @suppress PhanDeprecatedFunction
177
	 * @suppress PhanAccessMethodInternal
178
	 */
179
	public static function setupFS($user = '') {
180
		//setting up the filesystem twice can only lead to trouble
181
		if (self::$fsSetup) {
182
			return false;
183
		}
184
185
		\OC::$server->getEventLogger()->start('setup_fs', 'Setup filesystem');
186
187
		// If we are not forced to load a specific user we load the one that is logged in
188
		if ($user === null) {
189
			$user = '';
190
		} else if ($user == "" && \OC::$server->getUserSession()->isLoggedIn()) {
191
			$user = OC_User::getUser();
192
		}
193
194
		// load all filesystem apps before, so no setup-hook gets lost
195
		OC_App::loadApps(array('filesystem'));
196
197
		// the filesystem will finish when $user is not empty,
198
		// mark fs setup here to avoid doing the setup from loading
199
		// OC_Filesystem
200
		if ($user != '') {
201
			self::$fsSetup = true;
202
		}
203
204
		\OC\Files\Filesystem::initMountManager();
205
206
		\OC\Files\Filesystem::logWarningWhenAddingStorageWrapper(false);
207
		\OC\Files\Filesystem::addStorageWrapper('mount_options', function ($mountPoint, \OCP\Files\Storage $storage, \OCP\Files\Mount\IMountPoint $mount) {
208
			if ($storage->instanceOfStorage('\OC\Files\Storage\Common')) {
209
				/** @var \OC\Files\Storage\Common $storage */
210
				$storage->setMountOptions($mount->getOptions());
211
			}
212
			return $storage;
213
		});
214
215
		\OC\Files\Filesystem::addStorageWrapper('enable_sharing', function ($mountPoint, \OCP\Files\Storage\IStorage $storage, \OCP\Files\Mount\IMountPoint $mount) {
216
			if (!$mount->getOption('enable_sharing', true)) {
217
				return new \OC\Files\Storage\Wrapper\PermissionsMask([
218
					'storage' => $storage,
219
					'mask' => \OCP\Constants::PERMISSION_ALL - \OCP\Constants::PERMISSION_SHARE
220
				]);
221
			}
222
			return $storage;
223
		});
224
225
		// install storage availability wrapper, before most other wrappers
226
		\OC\Files\Filesystem::addStorageWrapper('oc_availability', function ($mountPoint, \OCP\Files\Storage\IStorage $storage) {
227
			if (!$storage->instanceOfStorage('\OCA\Files_Sharing\SharedStorage') && !$storage->isLocal()) {
228
				return new \OC\Files\Storage\Wrapper\Availability(['storage' => $storage]);
229
			}
230
			return $storage;
231
		});
232
233
		\OC\Files\Filesystem::addStorageWrapper('oc_encoding', function ($mountPoint, \OCP\Files\Storage $storage, \OCP\Files\Mount\IMountPoint $mount) {
234
			if ($mount->getOption('encoding_compatibility', false) && !$storage->instanceOfStorage('\OCA\Files_Sharing\SharedStorage') && !$storage->isLocal()) {
235
				return new \OC\Files\Storage\Wrapper\Encoding(['storage' => $storage]);
236
			}
237
			return $storage;
238
		});
239
240
		\OC\Files\Filesystem::addStorageWrapper('oc_quota', function ($mountPoint, $storage) {
241
			// set up quota for home storages, even for other users
242
			// which can happen when using sharing
243
244
			/**
245
			 * @var \OC\Files\Storage\Storage $storage
246
			 */
247
			if ($storage->instanceOfStorage('\OC\Files\Storage\Home')
248
				|| $storage->instanceOfStorage('\OC\Files\ObjectStore\HomeObjectStoreStorage')
249
			) {
250
				/** @var \OC\Files\Storage\Home $storage */
251
				if (is_object($storage->getUser())) {
252
					$user = $storage->getUser()->getUID();
253
					$quota = OC_Util::getUserQuota($user);
254
					if ($quota !== \OCP\Files\FileInfo::SPACE_UNLIMITED) {
255
						return new \OC\Files\Storage\Wrapper\Quota(array('storage' => $storage, 'quota' => $quota, 'root' => 'files'));
256
					}
257
				}
258
			}
259
260
			return $storage;
261
		});
262
263
		\OC\Files\Filesystem::addStorageWrapper('readonly', function ($mountPoint, \OCP\Files\Storage\IStorage $storage, \OCP\Files\Mount\IMountPoint $mount) {
264
			/*
265
			 * Do not allow any operations that modify the storage
266
			 */
267
			if ($mount->getOption('readonly', false)) {
268
				return new \OC\Files\Storage\Wrapper\PermissionsMask([
269
					'storage' => $storage,
270
					'mask' => \OCP\Constants::PERMISSION_ALL & ~(
271
						\OCP\Constants::PERMISSION_UPDATE |
272
						\OCP\Constants::PERMISSION_CREATE |
273
						\OCP\Constants::PERMISSION_DELETE
274
					),
275
				]);
276
			}
277
			return $storage;
278
		});
279
280
		OC_Hook::emit('OC_Filesystem', 'preSetup', array('user' => $user));
281
		\OC\Files\Filesystem::logWarningWhenAddingStorageWrapper(true);
282
283
		//check if we are using an object storage
284
		$objectStore = \OC::$server->getSystemConfig()->getValue('objectstore', null);
285
		$objectStoreMultibucket = \OC::$server->getSystemConfig()->getValue('objectstore_multibucket', null);
286
287
		// use the same order as in ObjectHomeMountProvider
288
		if (isset($objectStoreMultibucket)) {
289
			self::initObjectStoreMultibucketRootFS($objectStoreMultibucket);
290
		} elseif (isset($objectStore)) {
291
			self::initObjectStoreRootFS($objectStore);
292
		} else {
293
			self::initLocalStorageRootFS();
294
		}
295
296
		if ($user != '' && !\OC::$server->getUserManager()->userExists($user)) {
297
			\OC::$server->getEventLogger()->end('setup_fs');
298
			return false;
299
		}
300
301
		//if we aren't logged in, there is no use to set up the filesystem
302
		if ($user != "") {
303
304
			$userDir = '/' . $user . '/files';
305
306
			//jail the user into his "home" directory
307
			\OC\Files\Filesystem::init($user, $userDir);
308
309
			OC_Hook::emit('OC_Filesystem', 'setup', array('user' => $user, 'user_dir' => $userDir));
310
		}
311
		\OC::$server->getEventLogger()->end('setup_fs');
312
		return true;
313
	}
314
315
	/**
316
	 * check if a password is required for each public link
317
	 *
318
	 * @return boolean
319
	 * @suppress PhanDeprecatedFunction
320
	 */
321
	public static function isPublicLinkPasswordRequired() {
322
		$enforcePassword = \OC::$server->getConfig()->getAppValue('core', 'shareapi_enforce_links_password', 'no');
323
		return $enforcePassword === 'yes';
324
	}
325
326
	/**
327
	 * check if sharing is disabled for the current user
328
	 * @param IConfig $config
329
	 * @param IGroupManager $groupManager
330
	 * @param IUser|null $user
331
	 * @return bool
332
	 */
333
	public static function isSharingDisabledForUser(IConfig $config, IGroupManager $groupManager, $user) {
334
		if ($config->getAppValue('core', 'shareapi_exclude_groups', 'no') === 'yes') {
335
			$groupsList = $config->getAppValue('core', 'shareapi_exclude_groups_list', '');
336
			$excludedGroups = json_decode($groupsList);
337 View Code Duplication
			if (is_null($excludedGroups)) {
338
				$excludedGroups = explode(',', $groupsList);
339
				$newValue = json_encode($excludedGroups);
340
				$config->setAppValue('core', 'shareapi_exclude_groups_list', $newValue);
341
			}
342
			$usersGroups = $groupManager->getUserGroupIds($user);
0 ignored issues
show
Bug introduced by
It seems like $user defined by parameter $user on line 333 can be null; however, OCP\IGroupManager::getUserGroupIds() does not accept null, maybe add an additional type check?

It seems like you allow that null is being passed for a parameter, however the function which is called does not seem to accept null.

We recommend to add an additional type check (or disallow null for the parameter):

function notNullable(stdClass $x) { }

// Unsafe
function withoutCheck(stdClass $x = null) {
    notNullable($x);
}

// Safe - Alternative 1: Adding Additional Type-Check
function withCheck(stdClass $x = null) {
    if ($x instanceof stdClass) {
        notNullable($x);
    }
}

// Safe - Alternative 2: Changing Parameter
function withNonNullableParam(stdClass $x) {
    notNullable($x);
}
Loading history...
343 View Code Duplication
			if (!empty($usersGroups)) {
344
				$remainingGroups = array_diff($usersGroups, $excludedGroups);
345
				// if the user is only in groups which are disabled for sharing then
346
				// sharing is also disabled for the user
347
				if (empty($remainingGroups)) {
348
					return true;
349
				}
350
			}
351
		}
352
		return false;
353
	}
354
355
	/**
356
	 * check if share API enforces a default expire date
357
	 *
358
	 * @return boolean
359
	 * @suppress PhanDeprecatedFunction
360
	 */
361
	public static function isDefaultExpireDateEnforced() {
362
		$isDefaultExpireDateEnabled = \OC::$server->getConfig()->getAppValue('core', 'shareapi_default_expire_date', 'no');
363
		$enforceDefaultExpireDate = false;
364
		if ($isDefaultExpireDateEnabled === 'yes') {
365
			$value = \OC::$server->getConfig()->getAppValue('core', 'shareapi_enforce_expire_date', 'no');
366
			$enforceDefaultExpireDate = $value === 'yes';
367
		}
368
369
		return $enforceDefaultExpireDate;
370
	}
371
372
	/**
373
	 * Get the quota of a user
374
	 *
375
	 * @param string $userId
376
	 * @return float Quota bytes
377
	 */
378
	public static function getUserQuota($userId) {
379
		$user = \OC::$server->getUserManager()->get($userId);
380
		if (is_null($user)) {
381
			return \OCP\Files\FileInfo::SPACE_UNLIMITED;
382
		}
383
		$userQuota = $user->getQuota();
384
		if($userQuota === 'none') {
385
			return \OCP\Files\FileInfo::SPACE_UNLIMITED;
386
		}
387
		return OC_Helper::computerFileSize($userQuota);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The expression \OC_Helper::computerFileSize($userQuota); of type double|false adds false to the return on line 387 which is incompatible with the return type documented by OC_Util::getUserQuota of type double. It seems like you forgot to handle an error condition.
Loading history...
388
	}
389
390
	/**
391
	 * copies the skeleton to the users /files
392
	 *
393
	 * @param String $userId
394
	 * @param \OCP\Files\Folder $userDirectory
395
	 * @throws \RuntimeException
396
	 * @suppress PhanDeprecatedFunction
397
	 */
398
	public static function copySkeleton($userId, \OCP\Files\Folder $userDirectory) {
399
400
		$plainSkeletonDirectory = \OC::$server->getConfig()->getSystemValue('skeletondirectory', \OC::$SERVERROOT . '/core/skeleton');
401
		$userLang = \OC::$server->getL10NFactory()->findLanguage();
402
		$skeletonDirectory = str_replace('{lang}', $userLang, $plainSkeletonDirectory);
403
404
		if (!file_exists($skeletonDirectory)) {
405
			$dialectStart = strpos($userLang, '_');
406
			if ($dialectStart !== false) {
407
				$skeletonDirectory = str_replace('{lang}', substr($userLang, 0, $dialectStart), $plainSkeletonDirectory);
408
			}
409
			if ($dialectStart === false || !file_exists($skeletonDirectory)) {
410
				$skeletonDirectory = str_replace('{lang}', 'default', $plainSkeletonDirectory);
411
			}
412
			if (!file_exists($skeletonDirectory)) {
413
				$skeletonDirectory = '';
414
			}
415
		}
416
417
		$instanceId = \OC::$server->getConfig()->getSystemValue('instanceid', '');
418
419
		if ($instanceId === null) {
420
			throw new \RuntimeException('no instance id!');
421
		}
422
		$appdata = 'appdata_' . $instanceId;
423
		if ($userId === $appdata) {
424
			throw new \RuntimeException('username is reserved name: ' . $appdata);
425
		}
426
427
		if (!empty($skeletonDirectory)) {
428
			\OCP\Util::writeLog(
429
				'files_skeleton',
430
				'copying skeleton for '.$userId.' from '.$skeletonDirectory.' to '.$userDirectory->getFullPath('/'),
431
				ILogger::DEBUG
432
			);
433
			self::copyr($skeletonDirectory, $userDirectory);
434
			// update the file cache
435
			$userDirectory->getStorage()->getScanner()->scan('', \OC\Files\Cache\Scanner::SCAN_RECURSIVE);
436
		}
437
	}
438
439
	/**
440
	 * copies a directory recursively by using streams
441
	 *
442
	 * @param string $source
443
	 * @param \OCP\Files\Folder $target
444
	 * @return void
445
	 */
446
	public static function copyr($source, \OCP\Files\Folder $target) {
447
		$logger = \OC::$server->getLogger();
448
449
		// Verify if folder exists
450
		$dir = opendir($source);
451
		if($dir === false) {
452
			$logger->error(sprintf('Could not opendir "%s"', $source), ['app' => 'core']);
453
			return;
454
		}
455
456
		// Copy the files
457
		while (false !== ($file = readdir($dir))) {
458
			if (!\OC\Files\Filesystem::isIgnoredDir($file)) {
459
				if (is_dir($source . '/' . $file)) {
460
					$child = $target->newFolder($file);
461
					self::copyr($source . '/' . $file, $child);
462
				} else {
463
					$child = $target->newFile($file);
464
					$sourceStream = fopen($source . '/' . $file, 'r');
465
					if($sourceStream === false) {
466
						$logger->error(sprintf('Could not fopen "%s"', $source . '/' . $file), ['app' => 'core']);
467
						closedir($dir);
468
						return;
469
					}
470
					stream_copy_to_stream($sourceStream, $child->fopen('w'));
471
				}
472
			}
473
		}
474
		closedir($dir);
475
	}
476
477
	/**
478
	 * @return void
479
	 * @suppress PhanUndeclaredMethod
480
	 */
481
	public static function tearDownFS() {
482
		\OC\Files\Filesystem::tearDown();
483
		\OC::$server->getRootFolder()->clearCache();
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface OCP\Files\IRootFolder as the method clearCache() does only exist in the following implementations of said interface: OC\Files\Node\Root.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
484
		self::$fsSetup = false;
485
		self::$rootMounted = false;
486
	}
487
488
	/**
489
	 * get the current installed version of ownCloud
490
	 *
491
	 * @return array
492
	 */
493
	public static function getVersion() {
494
		OC_Util::loadVersion();
495
		return self::$versionCache['OC_Version'];
496
	}
497
498
	/**
499
	 * get the current installed version string of ownCloud
500
	 *
501
	 * @return string
502
	 */
503
	public static function getVersionString() {
504
		OC_Util::loadVersion();
505
		return self::$versionCache['OC_VersionString'];
506
	}
507
508
	/**
509
	 * @deprecated the value is of no use anymore
510
	 * @return string
511
	 */
512
	public static function getEditionString() {
513
		return '';
514
	}
515
516
	/**
517
	 * @description get the update channel of the current installed of ownCloud.
518
	 * @return string
519
	 */
520
	public static function getChannel() {
521
		OC_Util::loadVersion();
522
		return \OC::$server->getConfig()->getSystemValue('updater.release.channel', self::$versionCache['OC_Channel']);
523
	}
524
525
	/**
526
	 * @description get the build number of the current installed of ownCloud.
527
	 * @return string
528
	 */
529
	public static function getBuild() {
530
		OC_Util::loadVersion();
531
		return self::$versionCache['OC_Build'];
532
	}
533
534
	/**
535
	 * @description load the version.php into the session as cache
536
	 * @suppress PhanUndeclaredVariable
537
	 */
538
	private static function loadVersion() {
539
		if (self::$versionCache !== null) {
540
			return;
541
		}
542
543
		$timestamp = filemtime(OC::$SERVERROOT . '/version.php');
544
		require OC::$SERVERROOT . '/version.php';
545
		/** @var $timestamp int */
546
		self::$versionCache['OC_Version_Timestamp'] = $timestamp;
547
		/** @var $OC_Version string */
548
		self::$versionCache['OC_Version'] = $OC_Version;
0 ignored issues
show
Bug introduced by
The variable $OC_Version does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
549
		/** @var $OC_VersionString string */
550
		self::$versionCache['OC_VersionString'] = $OC_VersionString;
0 ignored issues
show
Bug introduced by
The variable $OC_VersionString does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
551
		/** @var $OC_Build string */
552
		self::$versionCache['OC_Build'] = $OC_Build;
0 ignored issues
show
Bug introduced by
The variable $OC_Build does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
553
554
		/** @var $OC_Channel string */
555
		self::$versionCache['OC_Channel'] = $OC_Channel;
0 ignored issues
show
Bug introduced by
The variable $OC_Channel does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
556
	}
557
558
	/**
559
	 * generates a path for JS/CSS files. If no application is provided it will create the path for core.
560
	 *
561
	 * @param string $application application to get the files from
562
	 * @param string $directory directory within this application (css, js, vendor, etc)
563
	 * @param string $file the file inside of the above folder
564
	 * @return string the path
565
	 */
566
	private static function generatePath($application, $directory, $file) {
567
		if (is_null($file)) {
568
			$file = $application;
569
			$application = "";
570
		}
571
		if (!empty($application)) {
572
			return "$application/$directory/$file";
573
		} else {
574
			return "$directory/$file";
575
		}
576
	}
577
578
	/**
579
	 * add a javascript file
580
	 *
581
	 * @param string $application application id
582
	 * @param string|null $file filename
583
	 * @param bool $prepend prepend the Script to the beginning of the list
584
	 * @return void
585
	 */
586
	public static function addScript($application, $file = null, $prepend = false) {
587
		$path = OC_Util::generatePath($application, 'js', $file);
588
589
		// core js files need separate handling
590
		if ($application !== 'core' && $file !== null) {
591
			self::addTranslations ( $application );
592
		}
593
		self::addExternalResource($application, $prepend, $path, "script");
594
	}
595
596
	/**
597
	 * add a javascript file from the vendor sub folder
598
	 *
599
	 * @param string $application application id
600
	 * @param string|null $file filename
601
	 * @param bool $prepend prepend the Script to the beginning of the list
602
	 * @return void
603
	 */
604
	public static function addVendorScript($application, $file = null, $prepend = false) {
605
		$path = OC_Util::generatePath($application, 'vendor', $file);
606
		self::addExternalResource($application, $prepend, $path, "script");
607
	}
608
609
	/**
610
	 * add a translation JS file
611
	 *
612
	 * @param string $application application id
613
	 * @param string|null $languageCode language code, defaults to the current language
614
	 * @param bool|null $prepend prepend the Script to the beginning of the list
615
	 */
616
	public static function addTranslations($application, $languageCode = null, $prepend = false) {
617
		if (is_null($languageCode)) {
618
			$languageCode = \OC::$server->getL10NFactory()->findLanguage($application);
619
		}
620
		if (!empty($application)) {
621
			$path = "$application/l10n/$languageCode";
622
		} else {
623
			$path = "l10n/$languageCode";
624
		}
625
		self::addExternalResource($application, $prepend, $path, "script");
0 ignored issues
show
Bug introduced by
It seems like $prepend defined by parameter $prepend on line 616 can also be of type null; however, OC_Util::addExternalResource() does only seem to accept boolean, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
626
	}
627
628
	/**
629
	 * add a css file
630
	 *
631
	 * @param string $application application id
632
	 * @param string|null $file filename
633
	 * @param bool $prepend prepend the Style to the beginning of the list
634
	 * @return void
635
	 */
636
	public static function addStyle($application, $file = null, $prepend = false) {
637
		$path = OC_Util::generatePath($application, 'css', $file);
638
		self::addExternalResource($application, $prepend, $path, "style");
639
	}
640
641
	/**
642
	 * add a css file from the vendor sub folder
643
	 *
644
	 * @param string $application application id
645
	 * @param string|null $file filename
646
	 * @param bool $prepend prepend the Style to the beginning of the list
647
	 * @return void
648
	 */
649
	public static function addVendorStyle($application, $file = null, $prepend = false) {
650
		$path = OC_Util::generatePath($application, 'vendor', $file);
651
		self::addExternalResource($application, $prepend, $path, "style");
652
	}
653
654
	/**
655
	 * add an external resource css/js file
656
	 *
657
	 * @param string $application application id
658
	 * @param bool $prepend prepend the file to the beginning of the list
659
	 * @param string $path
660
	 * @param string $type (script or style)
661
	 * @return void
662
	 */
663
	private static function addExternalResource($application, $prepend, $path, $type = "script") {
0 ignored issues
show
Unused Code introduced by
The parameter $application is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
664
665
		if ($type === "style") {
666 View Code Duplication
			if (!in_array($path, self::$styles)) {
667
				if ($prepend === true) {
668
					array_unshift ( self::$styles, $path );
669
				} else {
670
					self::$styles[] = $path;
671
				}
672
			}
673 View Code Duplication
		} elseif ($type === "script") {
674
			if (!in_array($path, self::$scripts)) {
675
				if ($prepend === true) {
676
					array_unshift ( self::$scripts, $path );
677
				} else {
678
					self::$scripts [] = $path;
679
				}
680
			}
681
		}
682
	}
683
684
	/**
685
	 * Add a custom element to the header
686
	 * If $text is null then the element will be written as empty element.
687
	 * So use "" to get a closing tag.
688
	 * @param string $tag tag name of the element
689
	 * @param array $attributes array of attributes for the element
690
	 * @param string $text the text content for the element
691
	 * @param bool $prepend prepend the header to the beginning of the list
692
	 */
693
	public static function addHeader($tag, $attributes, $text = null, $prepend = false) {
694
		$header = array(
695
			'tag' => $tag,
696
			'attributes' => $attributes,
697
			'text' => $text
698
		);
699
		if ($prepend === true) {
700
			array_unshift (self::$headers, $header);
701
702
		} else {
703
			self::$headers[] = $header;
704
		}
705
	}
706
707
	/**
708
	 * check if the current server configuration is suitable for ownCloud
709
	 *
710
	 * @param \OC\SystemConfig $config
711
	 * @return array arrays with error messages and hints
712
	 */
713
	public static function checkServer(\OC\SystemConfig $config) {
714
		$l = \OC::$server->getL10N('lib');
715
		$errors = array();
716
		$CONFIG_DATADIRECTORY = $config->getValue('datadirectory', OC::$SERVERROOT . '/data');
717
718
		if (!self::needUpgrade($config) && $config->getValue('installed', false)) {
719
			// this check needs to be done every time
720
			$errors = self::checkDataDirectoryValidity($CONFIG_DATADIRECTORY);
721
		}
722
723
		// Assume that if checkServer() succeeded before in this session, then all is fine.
724
		if (\OC::$server->getSession()->exists('checkServer_succeeded') && \OC::$server->getSession()->get('checkServer_succeeded')) {
725
			return $errors;
726
		}
727
728
		$webServerRestart = false;
729
		$setup = new \OC\Setup(
730
			$config,
731
			\OC::$server->getIniWrapper(),
732
			\OC::$server->getL10N('lib'),
733
			\OC::$server->query(\OCP\Defaults::class),
734
			\OC::$server->getLogger(),
735
			\OC::$server->getSecureRandom(),
736
			\OC::$server->query(\OC\Installer::class)
737
		);
738
739
		$urlGenerator = \OC::$server->getURLGenerator();
740
741
		$availableDatabases = $setup->getSupportedDatabases();
742
		if (empty($availableDatabases)) {
743
			$errors[] = array(
744
				'error' => $l->t('No database drivers (sqlite, mysql, or postgresql) installed.'),
745
				'hint' => '' //TODO: sane hint
746
			);
747
			$webServerRestart = true;
748
		}
749
750
		// Check if config folder is writable.
751
		if(!OC_Helper::isReadOnlyConfigEnabled()) {
752
			if (!is_writable(OC::$configDir) or !is_readable(OC::$configDir)) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as or instead of || is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
753
				$errors[] = array(
754
					'error' => $l->t('Cannot write into "config" directory'),
755
					'hint' => $l->t('This can usually be fixed by giving the webserver write access to the config directory. See %s',
756
						[ $urlGenerator->linkToDocs('admin-dir_permissions') ]) . '. '
757
						. $l->t('Or, if you prefer to keep config.php file read only, set the option "config_is_read_only" to true in it. See %s',
758
						[ $urlGenerator->linkToDocs('admin-config') ] )
759
				);
760
			}
761
		}
762
763
		// Check if there is a writable install folder.
764
		if ($config->getValue('appstoreenabled', true)) {
765
			if (OC_App::getInstallPath() === null
766
				|| !is_writable(OC_App::getInstallPath())
767
				|| !is_readable(OC_App::getInstallPath())
768
			) {
769
				$errors[] = array(
770
					'error' => $l->t('Cannot write into "apps" directory'),
771
					'hint' => $l->t('This can usually be fixed by giving the webserver write access to the apps directory'
772
						. ' or disabling the appstore in the config file. See %s',
773
						[$urlGenerator->linkToDocs('admin-dir_permissions')])
774
				);
775
			}
776
		}
777
		// Create root dir.
778
		if ($config->getValue('installed', false)) {
779
			if (!is_dir($CONFIG_DATADIRECTORY)) {
780
				$success = @mkdir($CONFIG_DATADIRECTORY);
781
				if ($success) {
782
					$errors = array_merge($errors, self::checkDataDirectoryPermissions($CONFIG_DATADIRECTORY));
783
				} else {
784
					$errors[] = [
785
						'error' => $l->t('Cannot create "data" directory'),
786
						'hint' => $l->t('This can usually be fixed by giving the webserver write access to the root directory. See %s',
787
							[$urlGenerator->linkToDocs('admin-dir_permissions')])
788
					];
789
				}
790
			} else if (!is_writable($CONFIG_DATADIRECTORY) or !is_readable($CONFIG_DATADIRECTORY)) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as or instead of || is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
791
				//common hint for all file permissions error messages
792
				$permissionsHint = $l->t('Permissions can usually be fixed by giving the webserver write access to the root directory. See %s.',
793
					[$urlGenerator->linkToDocs('admin-dir_permissions')]);
794
				$errors[] = [
795
					'error' => 'Your data directory is not writable',
796
					'hint' => $permissionsHint
797
				];
798
			} else {
799
				$errors = array_merge($errors, self::checkDataDirectoryPermissions($CONFIG_DATADIRECTORY));
800
			}
801
		}
802
803
		if (!OC_Util::isSetLocaleWorking()) {
804
			$errors[] = array(
805
				'error' => $l->t('Setting locale to %s failed',
806
					array('en_US.UTF-8/fr_FR.UTF-8/es_ES.UTF-8/de_DE.UTF-8/ru_RU.UTF-8/'
807
						. 'pt_BR.UTF-8/it_IT.UTF-8/ja_JP.UTF-8/zh_CN.UTF-8')),
808
				'hint' => $l->t('Please install one of these locales on your system and restart your webserver.')
809
			);
810
		}
811
812
		// Contains the dependencies that should be checked against
813
		// classes = class_exists
814
		// functions = function_exists
815
		// defined = defined
816
		// ini = ini_get
817
		// If the dependency is not found the missing module name is shown to the EndUser
818
		// When adding new checks always verify that they pass on Travis as well
819
		// for ini settings, see https://github.com/owncloud/administration/blob/master/travis-ci/custom.ini
820
		$dependencies = array(
821
			'classes' => array(
822
				'ZipArchive' => 'zip',
823
				'DOMDocument' => 'dom',
824
				'XMLWriter' => 'XMLWriter',
825
				'XMLReader' => 'XMLReader',
826
			),
827
			'functions' => [
828
				'xml_parser_create' => 'libxml',
829
				'mb_strcut' => 'mb multibyte',
830
				'ctype_digit' => 'ctype',
831
				'json_encode' => 'JSON',
832
				'gd_info' => 'GD',
833
				'gzencode' => 'zlib',
834
				'iconv' => 'iconv',
835
				'simplexml_load_string' => 'SimpleXML',
836
				'hash' => 'HASH Message Digest Framework',
837
				'curl_init' => 'cURL',
838
				'openssl_verify' => 'OpenSSL',
839
			],
840
			'defined' => array(
841
				'PDO::ATTR_DRIVER_NAME' => 'PDO'
842
			),
843
			'ini' => [
844
				'default_charset' => 'UTF-8',
845
			],
846
		);
847
		$missingDependencies = array();
848
		$invalidIniSettings = [];
849
		$moduleHint = $l->t('Please ask your server administrator to install the module.');
850
851
		/**
852
		 * FIXME: The dependency check does not work properly on HHVM on the moment
853
		 *        and prevents installation. Once HHVM is more compatible with our
854
		 *        approach to check for these values we should re-enable those
855
		 *        checks.
856
		 */
857
		$iniWrapper = \OC::$server->getIniWrapper();
858
		if (!self::runningOnHhvm()) {
859
			foreach ($dependencies['classes'] as $class => $module) {
860
				if (!class_exists($class)) {
861
					$missingDependencies[] = $module;
862
				}
863
			}
864
			foreach ($dependencies['functions'] as $function => $module) {
865
				if (!function_exists($function)) {
866
					$missingDependencies[] = $module;
867
				}
868
			}
869
			foreach ($dependencies['defined'] as $defined => $module) {
870
				if (!defined($defined)) {
871
					$missingDependencies[] = $module;
872
				}
873
			}
874
			foreach ($dependencies['ini'] as $setting => $expected) {
875
				if (is_bool($expected)) {
876
					if ($iniWrapper->getBool($setting) !== $expected) {
877
						$invalidIniSettings[] = [$setting, $expected];
878
					}
879
				}
880
				if (is_int($expected)) {
881
					if ($iniWrapper->getNumeric($setting) !== $expected) {
882
						$invalidIniSettings[] = [$setting, $expected];
883
					}
884
				}
885
				if (is_string($expected)) {
886
					if (strtolower($iniWrapper->getString($setting)) !== strtolower($expected)) {
887
						$invalidIniSettings[] = [$setting, $expected];
888
					}
889
				}
890
			}
891
		}
892
893
		foreach($missingDependencies as $missingDependency) {
894
			$errors[] = array(
895
				'error' => $l->t('PHP module %s not installed.', array($missingDependency)),
896
				'hint' => $moduleHint
897
			);
898
			$webServerRestart = true;
899
		}
900
		foreach($invalidIniSettings as $setting) {
901
			if(is_bool($setting[1])) {
902
				$setting[1] = $setting[1] ? 'on' : 'off';
903
			}
904
			$errors[] = [
905
				'error' => $l->t('PHP setting "%s" is not set to "%s".', [$setting[0], var_export($setting[1], true)]),
906
				'hint' =>  $l->t('Adjusting this setting in php.ini will make Nextcloud run again')
907
			];
908
			$webServerRestart = true;
909
		}
910
911
		/**
912
		 * The mbstring.func_overload check can only be performed if the mbstring
913
		 * module is installed as it will return null if the checking setting is
914
		 * not available and thus a check on the boolean value fails.
915
		 *
916
		 * TODO: Should probably be implemented in the above generic dependency
917
		 *       check somehow in the long-term.
918
		 */
919
		if($iniWrapper->getBool('mbstring.func_overload') !== null &&
920
			$iniWrapper->getBool('mbstring.func_overload') === true) {
921
			$errors[] = array(
922
				'error' => $l->t('mbstring.func_overload is set to "%s" instead of the expected value "0"', [$iniWrapper->getString('mbstring.func_overload')]),
923
				'hint' => $l->t('To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini')
924
			);
925
		}
926
927
		if(function_exists('xml_parser_create') &&
928
			LIBXML_LOADED_VERSION < 20700 ) {
929
			$version = LIBXML_LOADED_VERSION;
930
			$major = floor($version/10000);
931
			$version -= ($major * 10000);
932
			$minor = floor($version/100);
933
			$version -= ($minor * 100);
934
			$patch = $version;
935
			$errors[] = array(
936
				'error' => $l->t('libxml2 2.7.0 is at least required. Currently %s is installed.', [$major . '.' . $minor . '.' . $patch]),
937
				'hint' => $l->t('To fix this issue update your libxml2 version and restart your web server.')
938
			);
939
		}
940
941
		if (!self::isAnnotationsWorking()) {
942
			$errors[] = array(
943
				'error' => $l->t('PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible.'),
944
				'hint' => $l->t('This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator.')
945
			);
946
		}
947
948
		if (!\OC::$CLI && $webServerRestart) {
949
			$errors[] = array(
950
				'error' => $l->t('PHP modules have been installed, but they are still listed as missing?'),
951
				'hint' => $l->t('Please ask your server administrator to restart the web server.')
952
			);
953
		}
954
955
		$errors = array_merge($errors, self::checkDatabaseVersion());
956
957
		// Cache the result of this function
958
		\OC::$server->getSession()->set('checkServer_succeeded', count($errors) == 0);
959
960
		return $errors;
961
	}
962
963
	/**
964
	 * Check the database version
965
	 *
966
	 * @return array errors array
967
	 */
968
	public static function checkDatabaseVersion() {
969
		$l = \OC::$server->getL10N('lib');
970
		$errors = array();
971
		$dbType = \OC::$server->getSystemConfig()->getValue('dbtype', 'sqlite');
972
		if ($dbType === 'pgsql') {
973
			// check PostgreSQL version
974
			try {
975
				$result = \OC_DB::executeAudited('SHOW SERVER_VERSION');
976
				$data = $result->fetchRow();
977
				if (isset($data['server_version'])) {
978
					$version = $data['server_version'];
979
					if (version_compare($version, '9.0.0', '<')) {
980
						$errors[] = array(
981
							'error' => $l->t('PostgreSQL >= 9 required'),
982
							'hint' => $l->t('Please upgrade your database version')
983
						);
984
					}
985
				}
986
			} catch (\Doctrine\DBAL\DBALException $e) {
0 ignored issues
show
Bug introduced by
The class Doctrine\DBAL\DBALException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
987
				$logger = \OC::$server->getLogger();
988
				$logger->warning('Error occurred while checking PostgreSQL version, assuming >= 9');
989
				$logger->logException($e);
990
			}
991
		}
992
		return $errors;
993
	}
994
995
	/**
996
	 * Check for correct file permissions of data directory
997
	 *
998
	 * @param string $dataDirectory
999
	 * @return array arrays with error messages and hints
1000
	 */
1001
	public static function checkDataDirectoryPermissions($dataDirectory) {
1002
		if(\OC::$server->getConfig()->getSystemValue('check_data_directory_permissions', true) === false) {
1003
			return  [];
1004
		}
1005
		$l = \OC::$server->getL10N('lib');
1006
		$errors = [];
1007
		$permissionsModHint = $l->t('Please change the permissions to 0770 so that the directory'
1008
			. ' cannot be listed by other users.');
1009
		$perms = substr(decoct(@fileperms($dataDirectory)), -3);
1010
		if (substr($perms, -1) !== '0') {
1011
			chmod($dataDirectory, 0770);
1012
			clearstatcache();
1013
			$perms = substr(decoct(@fileperms($dataDirectory)), -3);
1014
			if ($perms[2] !== '0') {
1015
				$errors[] = [
1016
					'error' => $l->t('Your data directory is readable by other users'),
1017
					'hint' => $permissionsModHint
1018
				];
1019
			}
1020
		}
1021
		return $errors;
1022
	}
1023
1024
	/**
1025
	 * Check that the data directory exists and is valid by
1026
	 * checking the existence of the ".ocdata" file.
1027
	 *
1028
	 * @param string $dataDirectory data directory path
1029
	 * @return array errors found
1030
	 */
1031
	public static function checkDataDirectoryValidity($dataDirectory) {
1032
		$l = \OC::$server->getL10N('lib');
1033
		$errors = [];
1034 View Code Duplication
		if ($dataDirectory[0] !== '/') {
1035
			$errors[] = [
1036
				'error' => $l->t('Your data directory must be an absolute path'),
1037
				'hint' => $l->t('Check the value of "datadirectory" in your configuration')
1038
			];
1039
		}
1040 View Code Duplication
		if (!file_exists($dataDirectory . '/.ocdata')) {
1041
			$errors[] = [
1042
				'error' => $l->t('Your data directory is invalid'),
1043
				'hint' => $l->t('Ensure there is a file called ".ocdata"' .
1044
					' in the root of the data directory.')
1045
			];
1046
		}
1047
		return $errors;
1048
	}
1049
1050
	/**
1051
	 * Check if the user is logged in, redirects to home if not. With
1052
	 * redirect URL parameter to the request URI.
1053
	 *
1054
	 * @return void
1055
	 */
1056
	public static function checkLoggedIn() {
1057
		// Check if we are a user
1058 View Code Duplication
		if (!\OC::$server->getUserSession()->isLoggedIn()) {
1059
			header('Location: ' . \OC::$server->getURLGenerator()->linkToRoute(
1060
						'core.login.showLoginForm',
1061
						[
1062
							'redirect_url' => \OC::$server->getRequest()->getRequestUri(),
1063
						]
1064
					)
1065
			);
1066
			exit();
0 ignored issues
show
Coding Style Compatibility introduced by
The method checkLoggedIn() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
1067
		}
1068
		// Redirect to 2FA challenge selection if 2FA challenge was not solved yet
1069 View Code Duplication
		if (\OC::$server->getTwoFactorAuthManager()->needsSecondFactor(\OC::$server->getUserSession()->getUser())) {
1070
			header('Location: ' . \OC::$server->getURLGenerator()->linkToRoute('core.TwoFactorChallenge.selectChallenge'));
1071
			exit();
0 ignored issues
show
Coding Style Compatibility introduced by
The method checkLoggedIn() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
1072
		}
1073
	}
1074
1075
	/**
1076
	 * Check if the user is a admin, redirects to home if not
1077
	 *
1078
	 * @return void
1079
	 */
1080
	public static function checkAdminUser() {
1081
		OC_Util::checkLoggedIn();
1082
		if (!OC_User::isAdminUser(OC_User::getUser())) {
1083
			header('Location: ' . \OCP\Util::linkToAbsolute('', 'index.php'));
1084
			exit();
0 ignored issues
show
Coding Style Compatibility introduced by
The method checkAdminUser() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
1085
		}
1086
	}
1087
1088
	/**
1089
	 * Check if the user is a subadmin, redirects to home if not
1090
	 *
1091
	 * @return null|boolean $groups where the current user is subadmin
1092
	 */
1093
	public static function checkSubAdminUser() {
1094
		OC_Util::checkLoggedIn();
1095
		$userObject = \OC::$server->getUserSession()->getUser();
1096
		$isSubAdmin = false;
1097
		if($userObject !== null) {
1098
			$isSubAdmin = \OC::$server->getGroupManager()->getSubAdmin()->isSubAdmin($userObject);
1099
		}
1100
1101
		if (!$isSubAdmin) {
1102
			header('Location: ' . \OCP\Util::linkToAbsolute('', 'index.php'));
1103
			exit();
0 ignored issues
show
Coding Style Compatibility introduced by
The method checkSubAdminUser() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
1104
		}
1105
		return true;
1106
	}
1107
1108
	/**
1109
	 * Returns the URL of the default page
1110
	 * based on the system configuration and
1111
	 * the apps visible for the current user
1112
	 *
1113
	 * @return string URL
1114
	 * @suppress PhanDeprecatedFunction
1115
	 */
1116
	public static function getDefaultPageUrl() {
0 ignored issues
show
Coding Style introduced by
getDefaultPageUrl uses the super-global variable $_REQUEST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
1117
		$urlGenerator = \OC::$server->getURLGenerator();
1118
		// Deny the redirect if the URL contains a @
1119
		// This prevents unvalidated redirects like ?redirect_url=:[email protected]
1120
		if (isset($_REQUEST['redirect_url']) && strpos($_REQUEST['redirect_url'], '@') === false) {
1121
			$location = $urlGenerator->getAbsoluteURL(urldecode($_REQUEST['redirect_url']));
1122
		} else {
1123
			$defaultPage = \OC::$server->getConfig()->getAppValue('core', 'defaultpage');
1124
			if ($defaultPage) {
1125
				$location = $urlGenerator->getAbsoluteURL($defaultPage);
1126
			} else {
1127
				$appId = 'files';
1128
				$config = \OC::$server->getConfig();
1129
				$defaultApps = explode(',', $config->getSystemValue('defaultapp', 'files'));
1130
				// find the first app that is enabled for the current user
1131
				foreach ($defaultApps as $defaultApp) {
1132
					$defaultApp = OC_App::cleanAppId(strip_tags($defaultApp));
1133
					if (static::getAppManager()->isEnabledForUser($defaultApp)) {
1134
						$appId = $defaultApp;
1135
						break;
1136
					}
1137
				}
1138
1139
				if($config->getSystemValue('htaccess.IgnoreFrontController', false) === true || getenv('front_controller_active') === 'true') {
1140
					$location = $urlGenerator->getAbsoluteURL('/apps/' . $appId . '/');
1141
				} else {
1142
					$location = $urlGenerator->getAbsoluteURL('/index.php/apps/' . $appId . '/');
1143
				}
1144
			}
1145
		}
1146
		return $location;
1147
	}
1148
1149
	/**
1150
	 * Redirect to the user default page
1151
	 *
1152
	 * @return void
1153
	 */
1154
	public static function redirectToDefaultPage() {
1155
		$location = self::getDefaultPageUrl();
1156
		header('Location: ' . $location);
1157
		exit();
0 ignored issues
show
Coding Style Compatibility introduced by
The method redirectToDefaultPage() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
1158
	}
1159
1160
	/**
1161
	 * get an id unique for this instance
1162
	 *
1163
	 * @return string
1164
	 */
1165
	public static function getInstanceId() {
1166
		$id = \OC::$server->getSystemConfig()->getValue('instanceid', null);
1167
		if (is_null($id)) {
1168
			// We need to guarantee at least one letter in instanceid so it can be used as the session_name
1169
			$id = 'oc' . \OC::$server->getSecureRandom()->generate(10, \OCP\Security\ISecureRandom::CHAR_LOWER.\OCP\Security\ISecureRandom::CHAR_DIGITS);
1170
			\OC::$server->getSystemConfig()->setValue('instanceid', $id);
1171
		}
1172
		return $id;
1173
	}
1174
1175
	/**
1176
	 * Public function to sanitize HTML
1177
	 *
1178
	 * This function is used to sanitize HTML and should be applied on any
1179
	 * string or array of strings before displaying it on a web page.
1180
	 *
1181
	 * @param string|array $value
1182
	 * @return string|array an array of sanitized strings or a single sanitized string, depends on the input parameter.
1183
	 */
1184
	public static function sanitizeHTML($value) {
1185
		if (is_array($value)) {
1186
			$value = array_map(function($value) {
1187
				return self::sanitizeHTML($value);
1188
			}, $value);
1189
		} else {
1190
			// Specify encoding for PHP<5.4
1191
			$value = htmlspecialchars((string)$value, ENT_QUOTES, 'UTF-8');
1192
		}
1193
		return $value;
1194
	}
1195
1196
	/**
1197
	 * Public function to encode url parameters
1198
	 *
1199
	 * This function is used to encode path to file before output.
1200
	 * Encoding is done according to RFC 3986 with one exception:
1201
	 * Character '/' is preserved as is.
1202
	 *
1203
	 * @param string $component part of URI to encode
1204
	 * @return string
1205
	 */
1206
	public static function encodePath($component) {
1207
		$encoded = rawurlencode($component);
1208
		$encoded = str_replace('%2F', '/', $encoded);
1209
		return $encoded;
1210
	}
1211
1212
1213
	public function createHtaccessTestFile(\OCP\IConfig $config) {
1214
		// php dev server does not support htaccess
1215
		if (php_sapi_name() === 'cli-server') {
1216
			return false;
1217
		}
1218
1219
		// testdata
1220
		$fileName = '/htaccesstest.txt';
1221
		$testContent = 'This is used for testing whether htaccess is properly enabled to disallow access from the outside. This file can be safely removed.';
1222
1223
		// creating a test file
1224
		$testFile = $config->getSystemValue('datadirectory', OC::$SERVERROOT . '/data') . '/' . $fileName;
1225
1226
		if (file_exists($testFile)) {// already running this test, possible recursive call
1227
			return false;
1228
		}
1229
1230
		$fp = @fopen($testFile, 'w');
1231
		if (!$fp) {
1232
			throw new OC\HintException('Can\'t create test file to check for working .htaccess file.',
1233
				'Make sure it is possible for the webserver to write to ' . $testFile);
1234
		}
1235
		fwrite($fp, $testContent);
1236
		fclose($fp);
1237
1238
		return $testContent;
1239
	}
1240
1241
	/**
1242
	 * Check if the .htaccess file is working
1243
	 * @param \OCP\IConfig $config
1244
	 * @return bool
1245
	 * @throws Exception
1246
	 * @throws \OC\HintException If the test file can't get written.
1247
	 */
1248
	public function isHtaccessWorking(\OCP\IConfig $config) {
1249
1250
		if (\OC::$CLI || !$config->getSystemValue('check_for_working_htaccess', true)) {
1251
			return true;
1252
		}
1253
1254
		$testContent = $this->createHtaccessTestFile($config);
1255
		if ($testContent === false) {
1256
			return false;
1257
		}
1258
1259
		$fileName = '/htaccesstest.txt';
1260
		$testFile = $config->getSystemValue('datadirectory', OC::$SERVERROOT . '/data') . '/' . $fileName;
1261
1262
		// accessing the file via http
1263
		$url = \OC::$server->getURLGenerator()->getAbsoluteURL(OC::$WEBROOT . '/data' . $fileName);
1264
		try {
1265
			$content = \OC::$server->getHTTPClientService()->newClient()->get($url)->getBody();
1266
		} catch (\Exception $e) {
1267
			$content = false;
1268
		}
1269
1270
		// cleanup
1271
		@unlink($testFile);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
1272
1273
		/*
1274
		 * If the content is not equal to test content our .htaccess
1275
		 * is working as required
1276
		 */
1277
		return $content !== $testContent;
1278
	}
1279
1280
	/**
1281
	 * Check if the setlocal call does not work. This can happen if the right
1282
	 * local packages are not available on the server.
1283
	 *
1284
	 * @return bool
1285
	 */
1286
	public static function isSetLocaleWorking() {
1287
		\Patchwork\Utf8\Bootup::initLocale();
1288
		if ('' === basename('§')) {
1289
			return false;
1290
		}
1291
		return true;
1292
	}
1293
1294
	/**
1295
	 * Check if it's possible to get the inline annotations
1296
	 *
1297
	 * @return bool
1298
	 */
1299
	public static function isAnnotationsWorking() {
1300
		$reflection = new \ReflectionMethod(__METHOD__);
1301
		$docs = $reflection->getDocComment();
1302
1303
		return (is_string($docs) && strlen($docs) > 50);
1304
	}
1305
1306
	/**
1307
	 * Check if the PHP module fileinfo is loaded.
1308
	 *
1309
	 * @return bool
1310
	 */
1311
	public static function fileInfoLoaded() {
1312
		return function_exists('finfo_open');
1313
	}
1314
1315
	/**
1316
	 * clear all levels of output buffering
1317
	 *
1318
	 * @return void
1319
	 */
1320
	public static function obEnd() {
1321
		while (ob_get_level()) {
1322
			ob_end_clean();
1323
		}
1324
	}
1325
1326
	/**
1327
	 * Checks whether the server is running on Mac OS X
1328
	 *
1329
	 * @return bool true if running on Mac OS X, false otherwise
1330
	 */
1331
	public static function runningOnMac() {
1332
		return (strtoupper(substr(PHP_OS, 0, 6)) === 'DARWIN');
1333
	}
1334
1335
	/**
1336
	 * Checks whether server is running on HHVM
1337
	 *
1338
	 * @return bool True if running on HHVM, false otherwise
1339
	 */
1340
	public static function runningOnHhvm() {
1341
		return defined('HHVM_VERSION');
1342
	}
1343
1344
	/**
1345
	 * Handles the case that there may not be a theme, then check if a "default"
1346
	 * theme exists and take that one
1347
	 *
1348
	 * @return string the theme
1349
	 */
1350
	public static function getTheme() {
1351
		$theme = \OC::$server->getSystemConfig()->getValue("theme", '');
1352
1353
		if ($theme === '') {
1354
			if (is_dir(OC::$SERVERROOT . '/themes/default')) {
1355
				$theme = 'default';
1356
			}
1357
		}
1358
1359
		return $theme;
1360
	}
1361
1362
	/**
1363
	 * Clear a single file from the opcode cache
1364
	 * This is useful for writing to the config file
1365
	 * in case the opcode cache does not re-validate files
1366
	 * Returns true if successful, false if unsuccessful:
1367
	 * caller should fall back on clearing the entire cache
1368
	 * with clearOpcodeCache() if unsuccessful
1369
	 *
1370
	 * @param string $path the path of the file to clear from the cache
1371
	 * @return bool true if underlying function returns true, otherwise false
1372
	 */
1373
	public static function deleteFromOpcodeCache($path) {
1374
		$ret = false;
1375
		if ($path) {
1376
			// APC >= 3.1.1
1377
			if (function_exists('apc_delete_file')) {
1378
				$ret = @apc_delete_file($path);
1379
			}
1380
			// Zend OpCache >= 7.0.0, PHP >= 5.5.0
1381
			if (function_exists('opcache_invalidate')) {
1382
				$ret = @opcache_invalidate($path);
1383
			}
1384
		}
1385
		return $ret;
1386
	}
1387
1388
	/**
1389
	 * Clear the opcode cache if one exists
1390
	 * This is necessary for writing to the config file
1391
	 * in case the opcode cache does not re-validate files
1392
	 *
1393
	 * @return void
1394
	 * @suppress PhanDeprecatedFunction
1395
	 * @suppress PhanUndeclaredConstant
1396
	 */
1397
	public static function clearOpcodeCache() {
1398
		// APC
1399
		if (function_exists('apc_clear_cache')) {
1400
			apc_clear_cache();
1401
		}
1402
		// Zend Opcache
1403
		if (function_exists('accelerator_reset')) {
1404
			accelerator_reset();
1405
		}
1406
		// Opcache (PHP >= 5.5)
1407
		if (function_exists('opcache_reset')) {
1408
			@opcache_reset();
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
1409
		}
1410
	}
1411
1412
	/**
1413
	 * Normalize a unicode string
1414
	 *
1415
	 * @param string $value a not normalized string
1416
	 * @return bool|string
1417
	 */
1418
	public static function normalizeUnicode($value) {
1419
		if(Normalizer::isNormalized($value)) {
1420
			return $value;
1421
		}
1422
1423
		$normalizedValue = Normalizer::normalize($value);
1424
		if ($normalizedValue === null || $normalizedValue === false) {
1425
			\OC::$server->getLogger()->warning('normalizing failed for "' . $value . '"', ['app' => 'core']);
1426
			return $value;
1427
		}
1428
1429
		return $normalizedValue;
1430
	}
1431
1432
	/**
1433
	 * A human readable string is generated based on version and build number
1434
	 *
1435
	 * @return string
1436
	 */
1437
	public static function getHumanVersion() {
1438
		$version = OC_Util::getVersionString();
1439
		$build = OC_Util::getBuild();
1440
		if (!empty($build) and OC_Util::getChannel() === 'daily') {
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as and instead of && is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
1441
			$version .= ' Build:' . $build;
1442
		}
1443
		return $version;
1444
	}
1445
1446
	/**
1447
	 * Returns whether the given file name is valid
1448
	 *
1449
	 * @param string $file file name to check
1450
	 * @return bool true if the file name is valid, false otherwise
1451
	 * @deprecated use \OC\Files\View::verifyPath()
1452
	 */
1453
	public static function isValidFileName($file) {
1454
		$trimmed = trim($file);
1455
		if ($trimmed === '') {
1456
			return false;
1457
		}
1458
		if (\OC\Files\Filesystem::isIgnoredDir($trimmed)) {
1459
			return false;
1460
		}
1461
1462
		// detect part files
1463
		if (preg_match('/' . \OCP\Files\FileInfo::BLACKLIST_FILES_REGEX . '/', $trimmed) !== 0) {
1464
			return false;
1465
		}
1466
1467
		foreach (str_split($trimmed) as $char) {
1468
			if (strpos(\OCP\Constants::FILENAME_INVALID_CHARS, $char) !== false) {
1469
				return false;
1470
			}
1471
		}
1472
		return true;
1473
	}
1474
1475
	/**
1476
	 * Check whether the instance needs to perform an upgrade,
1477
	 * either when the core version is higher or any app requires
1478
	 * an upgrade.
1479
	 *
1480
	 * @param \OC\SystemConfig $config
1481
	 * @return bool whether the core or any app needs an upgrade
1482
	 * @throws \OC\HintException When the upgrade from the given version is not allowed
1483
	 */
1484
	public static function needUpgrade(\OC\SystemConfig $config) {
1485
		if ($config->getValue('installed', false)) {
1486
			$installedVersion = $config->getValue('version', '0.0.0');
1487
			$currentVersion = implode('.', \OCP\Util::getVersion());
1488
			$versionDiff = version_compare($currentVersion, $installedVersion);
1489
			if ($versionDiff > 0) {
1490
				return true;
1491
			} else if ($config->getValue('debug', false) && $versionDiff < 0) {
1492
				// downgrade with debug
1493
				$installedMajor = explode('.', $installedVersion);
1494
				$installedMajor = $installedMajor[0] . '.' . $installedMajor[1];
1495
				$currentMajor = explode('.', $currentVersion);
1496
				$currentMajor = $currentMajor[0] . '.' . $currentMajor[1];
1497
				if ($installedMajor === $currentMajor) {
1498
					// Same major, allow downgrade for developers
1499
					return true;
1500
				} else {
1501
					// downgrade attempt, throw exception
1502
					throw new \OC\HintException('Downgrading is not supported and is likely to cause unpredictable issues (from ' . $installedVersion . ' to ' . $currentVersion . ')');
1503
				}
1504
			} else if ($versionDiff < 0) {
1505
				// downgrade attempt, throw exception
1506
				throw new \OC\HintException('Downgrading is not supported and is likely to cause unpredictable issues (from ' . $installedVersion . ' to ' . $currentVersion . ')');
1507
			}
1508
1509
			// also check for upgrades for apps (independently from the user)
1510
			$apps = \OC_App::getEnabledApps(false, true);
1511
			$shouldUpgrade = false;
1512
			foreach ($apps as $app) {
1513
				if (\OC_App::shouldUpgrade($app)) {
1514
					$shouldUpgrade = true;
1515
					break;
1516
				}
1517
			}
1518
			return $shouldUpgrade;
1519
		} else {
1520
			return false;
1521
		}
1522
	}
1523
1524
	/**
1525
	 * is this Internet explorer ?
1526
	 *
1527
	 * @return boolean
1528
	 */
1529
	public static function isIe() {
0 ignored issues
show
Coding Style introduced by
isIe uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
1530
		if (!isset($_SERVER['HTTP_USER_AGENT'])) {
1531
			return false;
1532
		}
1533
1534
		return preg_match(Request::USER_AGENT_IE, $_SERVER['HTTP_USER_AGENT']) === 1;
1535
	}
1536
1537
}
1538