OC_Util   F
last analyzed

Complexity

Total Complexity 218

Size/Duplication

Total Lines 1396
Duplicated Lines 4.3 %

Coupling/Cohesion

Components 5
Dependencies 55

Importance

Changes 0
Metric Value
dl 60
loc 1396
rs 0.8
c 0
b 0
f 0
wmc 218
lcom 5
cbo 55

53 Methods

Rating   Name   Duplication   Size   Complexity  
A getAppManager() 0 3 1
A initLocalStorageRootFS() 0 10 2
B initObjectStoreRootFS() 0 26 6
F setupFS() 6 170 28
A isPublicLinkPasswordRequired() 0 5 2
A isDefaultExpireDateEnforced() 0 10 3
B getUserQuota() 0 18 7
A copySkeleton() 0 18 3
B copyr() 0 31 7
A tearDownFS() 0 5 1
A getVersion() 0 4 1
A getVersionString() 0 4 1
A getEditionString() 0 7 2
A getChannel() 0 4 1
A getBuild() 0 4 1
A loadVersion() 0 20 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 19 7
A addHeader() 7 7 1
A formatDate() 0 12 4
F checkServer() 0 223 40
A checkDatabaseVersion() 0 26 5
A checkDataDirectoryPermissions() 0 19 3
A checkDataDirectoryValidity() 13 18 3
A checkLoggedIn() 0 26 4
A checkAdminUser() 0 7 2
A rememberLoginAllowed() 11 11 4
A checkSubAdminUser() 5 15 4
B getDefaultPageUrl() 0 31 7
A redirectToDefaultPage() 0 5 1
A getInstanceId() 0 9 2
A sanitizeHTML() 0 11 2
A encodePath() 0 5 1
A createHtaccessTestFile() 0 29 5
A isSetLocaleWorking() 0 7 2
A isAnnotationsWorking() 0 6 2
A fileInfoLoaded() 0 3 1
A obEnd() 0 5 2
A runningOn() 0 9 3
A runningOnHhvm() 0 3 1
A getTheme() 0 5 1
A deleteFromOpcodeCache() 0 14 4
A clearOpcodeCache() 0 14 4
A normalizeUnicode() 0 13 4
A basename() 0 5 1
A getHumanVersion() 0 8 3
B isValidFileName() 0 21 6
B needUpgrade() 0 39 9

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
 * @author Adam Williamson <[email protected]>
4
 * @author Andreas Fischer <[email protected]>
5
 * @author Arthur Schiwon <[email protected]>
6
 * @author Bart Visscher <[email protected]>
7
 * @author Bernhard Posselt <[email protected]>
8
 * @author Birk Borkason <[email protected]>
9
 * @author Björn Schießle <[email protected]>
10
 * @author Brice Maron <[email protected]>
11
 * @author Christopher Schäpers <[email protected]>
12
 * @author Christoph Wurst <[email protected]>
13
 * @author Clark Tomlinson <[email protected]>
14
 * @author cmeh <[email protected]>
15
 * @author Florin Peter <[email protected]>
16
 * @author Frank Karlitschek <[email protected]>
17
 * @author Georg Ehrke <[email protected]>
18
 * @author helix84 <[email protected]>
19
 * @author Individual IT Services <[email protected]>
20
 * @author Jakob Sack <[email protected]>
21
 * @author Joas Schilling <[email protected]>
22
 * @author Jörn Friedrich Dreyer <[email protected]>
23
 * @author Kawohl <[email protected]>
24
 * @author Lukas Reschke <[email protected]>
25
 * @author Markus Goetz <[email protected]>
26
 * @author Martin Mattel <[email protected]>
27
 * @author Marvin Thomas Rabe <[email protected]>
28
 * @author Michael Gapczynski <[email protected]>
29
 * @author Morris Jobke <[email protected]>
30
 * @author Philipp Schaffrath <[email protected]>
31
 * @author Robin Appelman <[email protected]>
32
 * @author Robin McCorkell <[email protected]>
33
 * @author Roeland Jago Douma <[email protected]>
34
 * @author Stefan Rado <[email protected]>
35
 * @author Stefan Weil <[email protected]>
36
 * @author Thomas Müller <[email protected]>
37
 * @author Thomas Tanghus <[email protected]>
38
 * @author Victor Dubiniuk <[email protected]>
39
 * @author Vincent Petry <[email protected]>
40
 * @author Volkan Gezer <[email protected]>
41
 *
42
 * @copyright Copyright (c) 2018, ownCloud GmbH
43
 * @license AGPL-3.0
44
 *
45
 * This code is free software: you can redistribute it and/or modify
46
 * it under the terms of the GNU Affero General Public License, version 3,
47
 * as published by the Free Software Foundation.
48
 *
49
 * This program is distributed in the hope that it will be useful,
50
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
51
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
52
 * GNU Affero General Public License for more details.
53
 *
54
 * You should have received a copy of the GNU Affero General Public License, version 3,
55
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
56
 *
57
 */
58
59
use OCP\Authentication\Exceptions\AccountCheckException;
60
use OCP\Files\NoReadAccessException;
61
use OCP\Files\Storage\IStorage;
62
use OCP\IUser;
63
64
class OC_Util {
65
	public static $scripts = [];
66
	public static $styles = [];
67
	public static $headers = [];
68
	private static $rootMounted = false;
69
	private static $fsSetup = false;
70
	private static $version;
71
	const EDITION_COMMUNITY = 'Community';
72
	const EDITION_ENTERPRISE = 'Enterprise';
73
74
	protected static function getAppManager() {
75
		return \OC::$server->getAppManager();
76
	}
77
78
	private static function initLocalStorageRootFS() {
79
		// mount local file backend as root
80
		$configDataDirectory = \OC::$server->getSystemConfig()->getValue("datadirectory", OC::$SERVERROOT . "/data");
81
		//first set up the local "root" storage
82
		\OC\Files\Filesystem::initMountManager();
83
		if (!self::$rootMounted) {
84
			\OC\Files\Filesystem::mount('\OC\Files\Storage\Local', ['datadir' => $configDataDirectory], '/');
85
			self::$rootMounted = true;
86
		}
87
	}
88
89
	/**
90
	 * mounting an object storage as the root fs will in essence remove the
91
	 * necessity of a data folder being present.
92
	 * TODO make home storage aware of this and use the object storage instead of local disk access
93
	 *
94
	 * @param array $config containing 'class' and optional 'arguments'
95
	 */
96
	private static function initObjectStoreRootFS($config) {
97
		// check misconfiguration
98
		if (empty($config['class'])) {
99
			\OCP\Util::writeLog('files', 'No class given for objectstore', \OCP\Util::ERROR);
100
		}
101
		if (!isset($config['arguments'])) {
102
			$config['arguments'] = [];
103
		}
104
105
		// instantiate object store implementation
106
		$name = $config['class'];
107
		if (\strpos($name, 'OCA\\') === 0 && \substr_count($name, '\\') >= 2) {
108
			$segments = \explode('\\', $name);
109
			OC_App::loadApp(\strtolower($segments[1]));
110
		}
111
		$config['arguments']['objectstore'] = new $config['class']($config['arguments']);
112
		// mount with plain / root object store implementation
113
		$config['class'] = '\OC\Files\ObjectStore\ObjectStoreStorage';
114
115
		// mount object storage as root
116
		\OC\Files\Filesystem::initMountManager();
117
		if (!self::$rootMounted) {
118
			\OC\Files\Filesystem::mount($config['class'], $config['arguments'], '/');
119
			self::$rootMounted = true;
120
		}
121
	}
122
123
	/**
124
	 * Can be set up
125
	 *
126
	 * @param string $user
127
	 * @return boolean
128
	 * @description configure the initial filesystem based on the configuration
129
	 */
130
	public static function setupFS($user = '') {
131
		//setting up the filesystem twice can only lead to trouble
132
		if (self::$fsSetup) {
133
			return false;
134
		}
135
136
		\OC::$server->getEventLogger()->start('setup_fs', 'Setup filesystem');
137
138
		// If we are not forced to load a specific user we load the one that is logged in
139
		if ($user === null) {
140
			$user = '';
141
		} elseif ($user == "" && OC_User::isLoggedIn()) {
142
			$user = OC_User::getUser();
143
		}
144
145
		// load all filesystem apps before, so no setup-hook gets lost
146
		OC_App::loadApps(['filesystem']);
147
148
		// the filesystem will finish when $user is not empty,
149
		// mark fs setup here to avoid doing the setup from loading
150
		// OC_Filesystem
151
		if ($user != '') {
152
			self::$fsSetup = true;
153
		}
154
155
		\OC\Files\Filesystem::initMountManager();
156
157
		\OC\Files\Filesystem::logWarningWhenAddingStorageWrapper(false);
158
		\OC\Files\Filesystem::addStorageWrapper('mount_options', function ($mountPoint, IStorage $storage, \OCP\Files\Mount\IMountPoint $mount) {
159
			if ($storage->instanceOfStorage('\OC\Files\Storage\Common')) {
160
				/** @var \OC\Files\Storage\Common $storage */
161
				$storage->setMountOptions($mount->getOptions());
162
			}
163
			return $storage;
164
		});
165
166
		\OC\Files\Filesystem::addStorageWrapper('enable_sharing', function ($mountPoint, IStorage $storage, \OCP\Files\Mount\IMountPoint $mount) {
167 View Code Duplication
			if (!$mount->getOption('enable_sharing', true)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
168
				return new \OC\Files\Storage\Wrapper\PermissionsMask([
169
					'storage' => $storage,
170
					'mask' => \OCP\Constants::PERMISSION_ALL - \OCP\Constants::PERMISSION_SHARE
171
				]);
172
			}
173
			return $storage;
174
		});
175
176
		// install storage availability wrapper, before most other wrappers
177
		\OC\Files\Filesystem::addStorageWrapper('oc_availability', function ($mountPoint, $storage) {
178
			if (!$storage->instanceOfStorage('\OCA\Files_Sharing\SharedStorage') && !$storage->isLocal()) {
179
				return new \OC\Files\Storage\Wrapper\Availability(['storage' => $storage]);
180
			}
181
			return $storage;
182
		});
183
184
		// install storage checksum wrapper
185
		\OC\Files\Filesystem::addStorageWrapper('oc_checksum', function ($mountPoint, IStorage $storage) {
186
			if (!$storage->instanceOfStorage('\OCA\Files_Sharing\SharedStorage')) {
187
				return new \OC\Files\Storage\Wrapper\Checksum(['storage' => $storage]);
188
			}
189
190
			return $storage;
191
		}, 1);
192
193
		\OC\Files\Filesystem::addStorageWrapper('oc_encoding', function ($mountPoint, IStorage $storage, \OCP\Files\Mount\IMountPoint $mount) {
194
			if ($mount->getOption('encoding_compatibility', false) && !$storage->instanceOfStorage('\OCA\Files_Sharing\SharedStorage') && !$storage->isLocal()) {
195
				return new \OC\Files\Storage\Wrapper\Encoding(['storage' => $storage]);
196
			}
197
			return $storage;
198
		});
199
200
		\OC\Files\Filesystem::addStorageWrapper('oc_quota', function ($mountPoint, $storage) {
201
			// set up quota for home storages, even for other users
202
			// which can happen when using sharing
203
204
			/**
205
			 * @var \OC\Files\Storage\Storage $storage
206
			 */
207
			if ($storage->instanceOfStorage('\OC\Files\Storage\Home')
208
				|| $storage->instanceOfStorage('\OC\Files\ObjectStore\HomeObjectStoreStorage')
209
			) {
210
				/** @var \OC\Files\Storage\Home $storage */
211
				if (\is_object($storage->getUser())) {
212
					$user = $storage->getUser()->getUID();
213
					$quota = OC_Util::getUserQuota($user);
214
					if ($quota !== \OCP\Files\FileInfo::SPACE_UNLIMITED) {
215
						return new \OC\Files\Storage\Wrapper\Quota(['storage' => $storage, 'quota' => $quota, 'root' => 'files']);
216
					}
217
				}
218
			}
219
220
			return $storage;
221
		});
222
223
		OC_Hook::emit('OC_Filesystem', 'preSetup', ['user' => $user]);
224
		\OC\Files\Filesystem::logWarningWhenAddingStorageWrapper(true);
225
226
		// Make users storage readonly if he is a guest or in a read_only group
227
228
		$isGuest = \OC::$server->getConfig()->getUserValue(
229
			$user,
0 ignored issues
show
Bug introduced by
It seems like $user defined by \OC_User::getUser() on line 142 can also be of type boolean; however, OCP\IConfig::getUserValue() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
230
			'owncloud',
231
			'isGuest',
232
			false
233
		);
234
235
		if (!$isGuest) {
236
			$readOnlyGroups = \json_decode(\OC::$server->getConfig()->getAppValue(
237
				'core',
238
				'read_only_groups',
239
				'[]'
240
			), true);
241
242
			if (!\is_array($readOnlyGroups)) {
243
				$readOnlyGroups = [];
244
			}
245
246
			$userGroups = \array_keys(
247
				\OC::$server->getGroupManager()->getUserIdGroups($user)
0 ignored issues
show
Bug introduced by
It seems like $user defined by \OC_User::getUser() on line 142 can also be of type boolean; however, OC\Group\Manager::getUserIdGroups() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
248
			);
249
250
			$readOnlyGroupMemberships = \array_intersect(
251
				$readOnlyGroups,
252
				$userGroups
253
			);
254
		}
255
256
		if ($isGuest === '1' || !empty($readOnlyGroupMemberships)) {
257
			\OC\Files\Filesystem::addStorageWrapper(
258
				'oc_readonly',
259
				function ($mountPoint, $storage) use ($user) {
260
					if ($mountPoint === '/' || $mountPoint === "/$user/") {
261
						return new \OC\Files\Storage\Wrapper\ReadOnlyJail(
262
							[
263
								'storage' => $storage,
264
								'mask' => \OCP\Constants::PERMISSION_READ,
265
								'path' => 'files'
266
							]
267
						);
268
					}
269
270
					return $storage;
271
				}
272
			);
273
		}
274
275
		//check if we are using an object storage
276
		$objectStore = \OC::$server->getSystemConfig()->getValue('objectstore', null);
277
		if (isset($objectStore)) {
278
			self::initObjectStoreRootFS($objectStore);
279
		} else {
280
			self::initLocalStorageRootFS();
281
		}
282
283
		if ($user != '' && !OCP\User::userExists($user)) {
0 ignored issues
show
Bug introduced by
It seems like $user defined by \OC_User::getUser() on line 142 can also be of type boolean; however, OCP\User::userExists() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
284
			\OC::$server->getEventLogger()->end('setup_fs');
285
			return false;
286
		}
287
288
		//if we aren't logged in, there is no use to set up the filesystem
289
		if ($user != "") {
290
			$userDir = '/' . $user . '/files';
291
292
			//jail the user into his "home" directory
293
			\OC\Files\Filesystem::init($user, $userDir);
294
295
			OC_Hook::emit('OC_Filesystem', 'setup', ['user' => $user, 'user_dir' => $userDir]);
296
		}
297
		\OC::$server->getEventLogger()->end('setup_fs');
298
		return true;
299
	}
300
301
	/**
302
	 * check if a password is required for each public link.
303
	 * This is deprecated due to not reflecting all the possibilities now. Falling back to
304
	 * enforce password for read-only links. Note that read & write or write-only options won't
305
	 * be considered here
306
	 *
307
	 * @return boolean
308
	 * @deprecated
309
	 */
310
	public static function isPublicLinkPasswordRequired() {
311
		$appConfig = \OC::$server->getAppConfig();
312
		$enforcePassword = $appConfig->getValue('core', 'shareapi_enforce_links_password_read_only', 'no');
313
		return ($enforcePassword === 'yes') ? true : false;
314
	}
315
316
	/**
317
	 * check if share API enforces a default expire date
318
	 *
319
	 * @return boolean
320
	 */
321
	public static function isDefaultExpireDateEnforced() {
322
		$isDefaultExpireDateEnabled = \OC::$server->getConfig()->getAppValue('core', 'shareapi_default_expire_date', 'no');
323
		$enforceDefaultExpireDate = false;
324
		if ($isDefaultExpireDateEnabled === 'yes') {
325
			$value = \OC::$server->getConfig()->getAppValue('core', 'shareapi_enforce_expire_date', 'no');
326
			$enforceDefaultExpireDate = ($value === 'yes') ? true : false;
327
		}
328
329
		return $enforceDefaultExpireDate;
330
	}
331
332
	/**
333
	 * Get the quota of a user
334
	 *
335
	 * @param string|IUser $userId
336
	 * @return int Quota bytes
337
	 */
338
	public static function getUserQuota($userId) {
339
		if ($userId instanceof IUser) {
340
			$user = $userId;
341
		} else {
342
			$user = \OC::$server->getUserManager()->get($userId);
343
		}
344
		if ($user === null) {
345
			return \OCP\Files\FileInfo::SPACE_UNLIMITED;
346
		}
347
		$userQuota = $user->getQuota();
348
		if ($userQuota === null || $userQuota === 'default') {
349
			$userQuota = \OC::$server->getConfig()->getAppValue('files', 'default_quota', 'none');
350
		}
351
		if ($userQuota === null || $userQuota === 'none') {
352
			return \OCP\Files\FileInfo::SPACE_UNLIMITED;
353
		}
354
		return OC_Helper::computerFileSize($userQuota);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The expression \OC_Helper::computerFileSize($userQuota); of type false|double adds false to the return on line 354 which is incompatible with the return type documented by OC_Util::getUserQuota of type integer. It seems like you forgot to handle an error condition.
Loading history...
355
	}
356
357
	/**
358
	 * copies the skeleton to the users /files
359
	 *
360
	 * @param String $userId
361
	 * @param \OCP\Files\Folder $userDirectory
362
	 * @throws \OC\HintException
363
	 */
364
	public static function copySkeleton($userId, \OCP\Files\Folder $userDirectory) {
365
		$skeletonDirectory = \OC::$server->getConfig()->getSystemValue('skeletondirectory', \OC::$SERVERROOT . '/core/skeleton');
366
367
		if (!\is_dir($skeletonDirectory)) {
368
			throw new \OC\HintException('The skeleton folder '.$skeletonDirectory.' is not accessible');
369
		}
370
371
		if (!empty($skeletonDirectory)) {
372
			\OCP\Util::writeLog(
373
				'files_skeleton',
374
				'copying skeleton for '.$userId.' from '.$skeletonDirectory.' to '.$userDirectory->getFullPath('/'),
375
				\OCP\Util::DEBUG
376
			);
377
			self::copyr($skeletonDirectory, $userDirectory);
378
			// update the file cache
379
			$userDirectory->getStorage()->getScanner()->scan('', \OC\Files\Cache\Scanner::SCAN_RECURSIVE);
380
		}
381
	}
382
383
	/**
384
	 * copies a directory recursively by using streams
385
	 *
386
	 * @param string $source
387
	 * @param \OCP\Files\Folder $target
388
	 * @return void
389
	 * @throws NoReadAccessException
390
	 */
391
	public static function copyr($source, \OCP\Files\Folder $target) {
392
		$dir = @\opendir($source);
393
		if ($dir === false) {
394
			throw new NoReadAccessException('No read permission for folder ' . $source);
395
		}
396
		while (($file = \readdir($dir)) !== false) {
397
			if (!\OC\Files\Filesystem::isIgnoredDir($file)) {
398
				if (\is_dir($source . '/' . $file)) {
399
					$child = $target->newFolder($file);
400
					self::copyr($source . '/' . $file, $child);
401
				} else {
402
					$sourceFileHandle = @\fopen($source . '/' . $file, 'r');
403
					if ($sourceFileHandle === false) {
404
						throw new NoReadAccessException('No read permission for file ' . $file);
405
					}
406
					$child = $target->newFile($file);
407
					$targetFileHandle = $child->fopen('w');
408
					\stream_copy_to_stream($sourceFileHandle, $targetFileHandle);
409
					\fclose($targetFileHandle);
410
					\fclose($sourceFileHandle);
411
412
					// update cache sizes
413
					$cache = $target->getStorage()->getCache();
414
					if ($cache instanceof \OC\Files\Cache\Cache) {
415
						$cache->correctFolderSize($child->getInternalPath());
416
					}
417
				}
418
			}
419
		}
420
		\closedir($dir);
421
	}
422
423
	/**
424
	 * @return void
425
	 */
426
	public static function tearDownFS() {
427
		\OC\Files\Filesystem::tearDown();
428
		self::$fsSetup = false;
429
		self::$rootMounted = false;
430
	}
431
432
	/**
433
	 * get the current installed version of ownCloud
434
	 *
435
	 * @return array
436
	 */
437
	public static function getVersion() {
438
		OC_Util::loadVersion();
439
		return self::$version['OC_Version'];
440
	}
441
442
	/**
443
	 * get the current installed version string of ownCloud
444
	 *
445
	 * @return string
446
	 */
447
	public static function getVersionString() {
448
		OC_Util::loadVersion();
449
		return self::$version['OC_VersionString'];
450
	}
451
452
	/**
453
	 * @description get the current installed edition of ownCloud.
454
	 * There is the community edition that returns "Community" and
455
	 * the enterprise edition that returns "Enterprise".
456
	 * @return string
457
	 */
458
	public static function getEditionString() {
459
		if (OC_App::isEnabled('enterprise_key')) {
460
			return OC_Util::EDITION_ENTERPRISE;
461
		} else {
462
			return OC_Util::EDITION_COMMUNITY;
463
		}
464
	}
465
466
	/**
467
	 * @description get the update channel of the current installed of ownCloud.
468
	 * @return string
469
	 */
470
	public static function getChannel() {
471
		OC_Util::loadVersion();
472
		return self::$version['OC_Channel'];
473
	}
474
475
	/**
476
	 * @description get the build number of the current installed of ownCloud.
477
	 * @return string
478
	 */
479
	public static function getBuild() {
480
		OC_Util::loadVersion();
481
		return self::$version['OC_Build'];
482
	}
483
484
	/**
485
	 * @description load the version.php into the session as cache
486
	 */
487
	private static function loadVersion() {
488
		require __DIR__ . '/../../../version.php';
489
		/** @var $OC_Version string */
490
		/** @var $OC_VersionString string */
491
		/** @var $OC_Build string */
492
		/** @var $OC_Channel string */
493
		self::$version = [
494
			'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...
495
			'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...
496
			'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...
497
			'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...
498
		];
499
500
		// Allow overriding update channel
501
502
		if (\OC::$server->getSystemConfig()->getValue('installed', false)) {
503
			$channel = \OC::$server->getConfig()->getAppValue('core', 'OC_Channel', $OC_Channel);
504
			self::$version['OC_Channel'] = $channel;
505
		}
506
	}
507
508
	/**
509
	 * generates a path for JS/CSS files. If no application is provided it will create the path for core.
510
	 *
511
	 * @param string $application application to get the files from
512
	 * @param string $directory directory within this application (css, js, vendor, etc)
513
	 * @param string $file the file inside of the above folder
514
	 * @return string the path
515
	 */
516
	private static function generatePath($application, $directory, $file) {
517
		if ($file === null) {
518
			$file = $application;
519
			$application = "";
520
		}
521
		if (!empty($application)) {
522
			return "$application/$directory/$file";
523
		} else {
524
			return "$directory/$file";
525
		}
526
	}
527
528
	/**
529
	 * add a javascript file
530
	 *
531
	 * @param string $application application id
532
	 * @param string|null $file filename
533
	 * @param bool $prepend prepend the Script to the beginning of the list
534
	 * @return void
535
	 */
536
	public static function addScript($application, $file = null, $prepend = false) {
537
		$path = OC_Util::generatePath($application, 'js', $file);
538
539
		// core js files need separate handling
540
		if ($application !== 'core' && $file !== null) {
541
			self::addTranslations($application);
542
		}
543
		self::addExternalResource($application, $prepend, $path, "script");
544
	}
545
546
	/**
547
	 * add a javascript file from the vendor sub folder
548
	 *
549
	 * @param string $application application id
550
	 * @param string|null $file filename
551
	 * @param bool $prepend prepend the Script to the beginning of the list
552
	 * @return void
553
	 */
554
	public static function addVendorScript($application, $file = null, $prepend = false) {
555
		$path = OC_Util::generatePath($application, 'vendor', $file);
556
		self::addExternalResource($application, $prepend, $path, "script");
557
	}
558
559
	/**
560
	 * add a translation JS file
561
	 *
562
	 * @param string $application application id
563
	 * @param string $languageCode language code, defaults to the current language
564
	 * @param bool $prepend prepend the Script to the beginning of the list
565
	 */
566
	public static function addTranslations($application, $languageCode = null, $prepend = false) {
567
		if ($languageCode === null) {
568
			$languageCode = \OC::$server->getL10NFactory()->findLanguage($application);
569
		}
570
		if (!empty($application)) {
571
			$path = "$application/l10n/$languageCode";
572
		} else {
573
			$path = "l10n/$languageCode";
574
		}
575
		self::addExternalResource($application, $prepend, $path, "script");
576
	}
577
578
	/**
579
	 * add a css file
580
	 *
581
	 * @param string $application application id
582
	 * @param string|null $file filename
583
	 * @param bool $prepend prepend the Style to the beginning of the list
584
	 * @return void
585
	 */
586
	public static function addStyle($application, $file = null, $prepend = false) {
587
		$path = OC_Util::generatePath($application, 'css', $file);
588
		self::addExternalResource($application, $prepend, $path, "style");
589
	}
590
591
	/**
592
	 * add a css file from the vendor sub folder
593
	 *
594
	 * @param string $application application id
595
	 * @param string|null $file filename
596
	 * @param bool $prepend prepend the Style to the beginning of the list
597
	 * @return void
598
	 */
599
	public static function addVendorStyle($application, $file = null, $prepend = false) {
600
		$path = OC_Util::generatePath($application, 'vendor', $file);
601
		self::addExternalResource($application, $prepend, $path, "style");
602
	}
603
604
	/**
605
	 * add an external resource css/js file
606
	 *
607
	 * @param string $application application id
608
	 * @param bool $prepend prepend the file to the beginning of the list
609
	 * @param string $path
610
	 * @param string $type (script or style)
611
	 * @return void
612
	 */
613
	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...
614
		if ($type === "style") {
615 View Code Duplication
			if (!\in_array($path, self::$styles)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
616
				if ($prepend === true) {
617
					\array_unshift(self::$styles, $path);
618
				} else {
619
					self::$styles[] = $path;
620
				}
621
			}
622 View Code Duplication
		} elseif ($type === "script") {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
623
			if (!\in_array($path, self::$scripts)) {
624
				if ($prepend === true) {
625
					\array_unshift(self::$scripts, $path);
626
				} else {
627
					self::$scripts [] = $path;
628
				}
629
			}
630
		}
631
	}
632
633
	/**
634
	 * Add a custom element to the header
635
	 * If $text is null then the element will be written as empty element.
636
	 * So use "" to get a closing tag.
637
	 * @param string $tag tag name of the element
638
	 * @param array $attributes array of attributes for the element
639
	 * @param string $text the text content for the element
640
	 */
641 View Code Duplication
	public static function addHeader($tag, $attributes, $text=null) {
642
		self::$headers[] = [
643
			'tag' => $tag,
644
			'attributes' => $attributes,
645
			'text' => $text
646
		];
647
	}
648
649
	/**
650
	 * formats a timestamp in the "right" way
651
	 *
652
	 * @param int $timestamp
653
	 * @param bool $dateOnly option to omit time from the result
654
	 * @param DateTimeZone|string $timeZone where the given timestamp shall be converted to
655
	 * @return string timestamp
656
	 *
657
	 * @deprecated Use \OC::$server->query('DateTimeFormatter') instead
658
	 */
659
	public static function formatDate($timestamp, $dateOnly = false, $timeZone = null) {
660
		if ($timeZone !== null && !$timeZone instanceof \DateTimeZone) {
661
			$timeZone = new \DateTimeZone($timeZone);
662
		}
663
664
		/** @var \OC\DateTimeFormatter $formatter */
665
		$formatter = \OC::$server->query('DateTimeFormatter');
666
		if ($dateOnly) {
667
			return $formatter->formatDate($timestamp, 'long', $timeZone);
668
		}
669
		return $formatter->formatDateTime($timestamp, 'long', 'long', $timeZone);
670
	}
671
672
	/**
673
	 * check if the current server configuration is suitable for ownCloud
674
	 *
675
	 * @param \OCP\IConfig $config
676
	 * @return array arrays with error messages and hints
677
	 */
678
	public static function checkServer(\OCP\IConfig $config) {
679
		$l = \OC::$server->getL10N('lib');
680
		$errors = [];
681
		$CONFIG_DATADIRECTORY = $config->getSystemValue('datadirectory', OC::$SERVERROOT . '/data');
682
683
		if (!self::needUpgrade($config) && $config->getSystemValue('installed', false)) {
684
			// this check needs to be done every time
685
			$errors = self::checkDataDirectoryValidity($CONFIG_DATADIRECTORY);
686
		}
687
688
		// Assume that if checkServer() succeeded before in this session, then all is fine.
689
		if (\OC::$server->getSession()->exists('checkServer_succeeded') && \OC::$server->getSession()->get('checkServer_succeeded')) {
690
			return $errors;
691
		}
692
693
		$webServerRestart = false;
694
		$setup = new \OC\Setup($config, \OC::$server->getIniWrapper(), \OC::$server->getL10N('lib'),
695
			new \OC_Defaults(), \OC::$server->getLogger(), \OC::$server->getSecureRandom());
696
697
		$urlGenerator = \OC::$server->getURLGenerator();
698
699
		$availableDatabases = $setup->getSupportedDatabases();
700
		if (empty($availableDatabases)) {
701
			$errors[] = [
702
				'error' => $l->t('No database drivers (sqlite, mysql, or postgresql) installed.'),
703
				'hint' => '' //TODO: sane hint
704
			];
705
			$webServerRestart = true;
706
		}
707
708
		// Check if config folder is writable.
709
		if (!\OC::$server->getConfig()->isSystemConfigReadOnly()) {
710
			if (!\is_writable(OC::$configDir) or !\is_readable(OC::$configDir)) {
711
				$errors[] = [
712
					'error' => $l->t('Cannot write into "config" directory'),
713
					'hint' => $l->t('This can usually be fixed by '
714
						. '%sgiving the webserver write access to the config directory%s.',
715
						['<a href="' . $urlGenerator->linkToDocs('admin-dir_permissions') . '" target="_blank" rel="noreferrer">', '</a>'])
716
				];
717
			}
718
		}
719
720
		// Create root dir.
721
		if ($config->getSystemValue('installed', false)) {
722
			if (!\is_dir($CONFIG_DATADIRECTORY)) {
723
				$success = @\mkdir($CONFIG_DATADIRECTORY);
724
				if ($success) {
725
					$errors = \array_merge($errors, self::checkDataDirectoryPermissions($CONFIG_DATADIRECTORY));
726
				} else {
727
					$errors[] = [
728
						'error' => $l->t('Cannot create "data" directory'),
729
						'hint' => $l->t('This can usually be fixed by '
730
							. '<a href="%s" target="_blank" rel="noreferrer">giving the webserver write access to the root directory</a>.',
731
							[$urlGenerator->linkToDocs('admin-dir_permissions')])
732
					];
733
				}
734
			} elseif (!\is_writable($CONFIG_DATADIRECTORY) or !\is_readable($CONFIG_DATADIRECTORY)) {
735
				//common hint for all file permissions error messages
736
				$permissionsHint = $l->t('Permissions can usually be fixed by '
737
					. '%sgiving the webserver write access to the root directory%s.',
738
					['<a href="' . $urlGenerator->linkToDocs('admin-dir_permissions') . '" target="_blank" rel="noreferrer">', '</a>']);
739
				$errors[] = [
740
					'error' => 'Your Data directory is not writable by ownCloud',
741
					'hint' => $permissionsHint
742
				];
743
			} else {
744
				$errors = \array_merge($errors, self::checkDataDirectoryPermissions($CONFIG_DATADIRECTORY));
745
			}
746
		}
747
748
		if (!OC_Util::isSetLocaleWorking()) {
749
			$errors[] = [
750
				'error' => $l->t('Setting locale to %s failed',
751
					['en_US.UTF-8/fr_FR.UTF-8/es_ES.UTF-8/de_DE.UTF-8/ru_RU.UTF-8/'
752
						. 'pt_BR.UTF-8/it_IT.UTF-8/ja_JP.UTF-8/zh_CN.UTF-8']),
753
				'hint' => $l->t('Please install one of these locales on your system and restart your webserver.')
754
			];
755
		}
756
757
		// Contains the dependencies that should be checked against
758
		// classes = class_exists
759
		// functions = function_exists
760
		// defined = defined
761
		// ini = ini_get
762
		// If the dependency is not found the missing module name is shown to the EndUser
763
		// When adding new checks always verify that they pass on Travis as well
764
		// for ini settings, see https://github.com/owncloud/administration/blob/master/travis-ci/custom.ini
765
		$dependencies = [
766
			'classes' => [
767
				'ZipArchive' => 'zip',
768
				'DOMDocument' => 'dom',
769
				'XMLWriter' => 'XMLWriter',
770
				'XMLReader' => 'XMLReader',
771
				'Collator' => 'intl',
772
			],
773
			'functions' => [
774
				'xml_parser_create' => 'libxml',
775
				'mb_strcut' => 'mb multibyte',
776
				'ctype_digit' => 'ctype',
777
				'json_encode' => 'JSON',
778
				'gd_info' => 'GD',
779
				'gzencode' => 'zlib',
780
				'iconv' => 'iconv',
781
				'simplexml_load_string' => 'SimpleXML',
782
				'hash' => 'HASH Message Digest Framework',
783
				'curl_init' => 'cURL',
784
			],
785
			'defined' => [
786
				'PDO::ATTR_DRIVER_NAME' => 'PDO'
787
			],
788
			'ini' => [
789
				'default_charset' => 'UTF-8',
790
			],
791
		];
792
		$missingDependencies = [];
793
		$invalidIniSettings = [];
794
		$moduleHint = $l->t('Please ask your server administrator to install the module.');
795
796
		/**
797
		 * FIXME: The dependency check does not work properly on HHVM on the moment
798
		 *        and prevents installation. Once HHVM is more compatible with our
799
		 *        approach to check for these values we should re-enable those
800
		 *        checks.
801
		 */
802
		$iniWrapper = \OC::$server->getIniWrapper();
803
		if (!self::runningOnHhvm()) {
804
			foreach ($dependencies['classes'] as $class => $module) {
805
				if (!\class_exists($class)) {
806
					$missingDependencies[] = $module;
807
				}
808
			}
809
			foreach ($dependencies['functions'] as $function => $module) {
810
				if (!\function_exists($function)) {
811
					$missingDependencies[] = $module;
812
				}
813
			}
814
			foreach ($dependencies['defined'] as $defined => $module) {
815
				if (!\defined($defined)) {
816
					$missingDependencies[] = $module;
817
				}
818
			}
819
			foreach ($dependencies['ini'] as $setting => $expected) {
820
				if (\is_bool($expected)) {
821
					if ($iniWrapper->getBool($setting) !== $expected) {
822
						$invalidIniSettings[] = [$setting, $expected];
823
					}
824
				}
825
				if (\is_int($expected)) {
826
					if ($iniWrapper->getNumeric($setting) !== $expected) {
827
						$invalidIniSettings[] = [$setting, $expected];
828
					}
829
				}
830
				if (\is_string($expected)) {
831
					if (\strtolower($iniWrapper->getString($setting)) !== \strtolower($expected)) {
832
						$invalidIniSettings[] = [$setting, $expected];
833
					}
834
				}
835
			}
836
		}
837
838
		foreach ($missingDependencies as $missingDependency) {
839
			$errors[] = [
840
				'error' => $l->t('PHP module %s not installed.', [$missingDependency]),
841
				'hint' => $moduleHint
842
			];
843
			$webServerRestart = true;
844
		}
845
		foreach ($invalidIniSettings as $setting) {
846
			if (\is_bool($setting[1])) {
847
				$setting[1] = ($setting[1]) ? 'on' : 'off';
848
			}
849
			$errors[] = [
850
				'error' => $l->t('PHP setting "%s" is not set to "%s".', [$setting[0], \var_export($setting[1], true)]),
851
				'hint' =>  $l->t('Adjusting this setting in php.ini will make ownCloud run again')
852
			];
853
			$webServerRestart = true;
854
		}
855
856
		/**
857
		 * The mbstring.func_overload check can only be performed if the mbstring
858
		 * module is installed as it will return null if the checking setting is
859
		 * not available and thus a check on the boolean value fails.
860
		 *
861
		 * TODO: Should probably be implemented in the above generic dependency
862
		 *       check somehow in the long-term.
863
		 */
864
		if ($iniWrapper->getBool('mbstring.func_overload') !== null &&
865
			$iniWrapper->getBool('mbstring.func_overload') === true) {
866
			$errors[] = [
867
				'error' => $l->t('mbstring.func_overload is set to "%s" instead of the expected value "0"', [$iniWrapper->getString('mbstring.func_overload')]),
868
				'hint' => $l->t('To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini')
869
			];
870
		}
871
872
		if (\function_exists('xml_parser_create') &&
873
			\version_compare('2.7.0', LIBXML_DOTTED_VERSION) === 1) {
874
			$errors[] = [
875
				'error' => $l->t('libxml2 2.7.0 is at least required. Currently %s is installed.', [LIBXML_DOTTED_VERSION]),
876
				'hint' => $l->t('To fix this issue update your libxml2 version and restart your web server.')
877
			];
878
		}
879
880
		if (!self::isAnnotationsWorking()) {
881
			$errors[] = [
882
				'error' => $l->t('PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible.'),
883
				'hint' => $l->t('This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator.')
884
			];
885
		}
886
887
		if (!\OC::$CLI && $webServerRestart) {
888
			$errors[] = [
889
				'error' => $l->t('PHP modules have been installed, but they are still listed as missing?'),
890
				'hint' => $l->t('Please ask your server administrator to restart the web server.')
891
			];
892
		}
893
894
		$errors = \array_merge($errors, self::checkDatabaseVersion());
895
896
		// Cache the result of this function
897
		\OC::$server->getSession()->set('checkServer_succeeded', \count($errors) == 0);
898
899
		return $errors;
900
	}
901
902
	/**
903
	 * Check the database version
904
	 *
905
	 * @return array errors array
906
	 */
907
	public static function checkDatabaseVersion() {
908
		$l = \OC::$server->getL10N('lib');
909
		$errors = [];
910
		$dbType = \OC::$server->getSystemConfig()->getValue('dbtype', 'sqlite');
911
		if ($dbType === 'pgsql') {
912
			// check PostgreSQL version
913
			try {
914
				$result = \OC_DB::executeAudited('SHOW SERVER_VERSION');
915
				$data = $result->fetchRow();
916
				if (isset($data['server_version'])) {
917
					$version = $data['server_version'];
918
					if (\version_compare($version, '9.0.0', '<')) {
919
						$errors[] = [
920
							'error' => $l->t('PostgreSQL >= 9 required'),
921
							'hint' => $l->t('Please upgrade your database version')
922
						];
923
					}
924
				}
925
			} catch (\Doctrine\DBAL\DBALException $e) {
926
				$logger = \OC::$server->getLogger();
927
				$logger->warning('Error occurred while checking PostgreSQL version, assuming >= 9');
928
				$logger->logException($e);
929
			}
930
		}
931
		return $errors;
932
	}
933
934
	/**
935
	 * Check for correct file permissions of data directory
936
	 *
937
	 * @param string $dataDirectory
938
	 * @return array arrays with error messages and hints
939
	 */
940
	public static function checkDataDirectoryPermissions($dataDirectory) {
941
		$l = \OC::$server->getL10N('lib');
942
		$errors = [];
943
		$permissionsModHint = $l->t('Please change the permissions to 0770 so that the directory'
944
			. ' cannot be listed by other users.');
945
		$perms = \substr(\decoct(@\fileperms($dataDirectory)), -3);
946
		if (\substr($perms, -1) != '0') {
947
			\chmod($dataDirectory, 0770);
948
			\clearstatcache();
949
			$perms = \substr(\decoct(@\fileperms($dataDirectory)), -3);
950
			if (\substr($perms, 2, 1) != '0') {
951
				$errors[] = [
952
					'error' => $l->t('Your Data directory is readable by other users'),
953
					'hint' => $permissionsModHint
954
				];
955
			}
956
		}
957
		return $errors;
958
	}
959
960
	/**
961
	 * Check that the data directory exists and is valid by
962
	 * checking the existence of the ".ocdata" file.
963
	 *
964
	 * @param string $dataDirectory data directory path
965
	 * @return array errors found
966
	 */
967
	public static function checkDataDirectoryValidity($dataDirectory) {
968
		$l = \OC::$server->getL10N('lib');
969
		$errors = [];
970 View Code Duplication
		if ($dataDirectory[0] !== '/') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
971
			$errors[] = [
972
				'error' => $l->t('Your Data directory must be an absolute path'),
973
				'hint' => $l->t('Check the value of "datadirectory" in your configuration')
974
			];
975
		}
976 View Code Duplication
		if (!\file_exists($dataDirectory . '/.ocdata')) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
977
			$errors[] = [
978
				'error' => $l->t('Your Data directory  is invalid'),
979
				'hint' => $l->t('Please check that the data directory contains a file' .
980
					' ".ocdata" in its root.')
981
			];
982
		}
983
		return $errors;
984
	}
985
986
	/**
987
	 * Check if the user is logged in, redirects to home if not. With
988
	 * redirect URL parameter to the request URI.
989
	 *
990
	 * @return void
991
	 */
992
	public static function checkLoggedIn() {
993
		// Check if we are a user
994
		$userSession = \OC::$server->getUserSession();
995
		if (!$userSession->isLoggedIn()) {
996
			\header('Location: ' . \OC::$server->getURLGenerator()->linkToRoute(
997
						'core.login.showLoginForm',
998
						[
999
							'redirect_url' => \urlencode(\OC::$server->getRequest()->getRequestUri()),
1000
						]
1001
					)
1002
			);
1003
			exit();
1004
		}
1005
		// Redirect to index page if 2FA challenge was not solved yet
1006
		if (\OC::$server->getTwoFactorAuthManager()->needsSecondFactor()) {
1007
			\header('Location: ' . \OCP\Util::linkToAbsolute('', 'index.php'));
1008
			exit();
1009
		}
1010
		// Redirect to index page if any IAuthModule check fails
1011
		try {
1012
			\OC::$server->getAccountModuleManager()->check($userSession->getUser());
0 ignored issues
show
Bug introduced by
It seems like $userSession->getUser() can be null; however, check() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
1013
		} catch (AccountCheckException $ex) {
1014
			\header('Location: ' . \OCP\Util::linkToAbsolute('', 'index.php'));
1015
			exit();
1016
		}
1017
	}
1018
1019
	/**
1020
	 * Check if the user is a admin, redirects to home if not
1021
	 *
1022
	 * @return void
1023
	 */
1024
	public static function checkAdminUser() {
1025
		OC_Util::checkLoggedIn();
1026
		if (!OC_User::isAdminUser(OC_User::getUser())) {
0 ignored issues
show
Bug introduced by
It seems like \OC_User::getUser() targeting OC_User::getUser() can also be of type boolean; however, OC_User::isAdminUser() does only seem to accept string, maybe add an additional type check?

This check looks at variables that 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...
1027
			\header('Location: ' . \OCP\Util::linkToAbsolute('', 'index.php'));
1028
			exit();
1029
		}
1030
	}
1031
1032
	/**
1033
	 * Check if it is allowed to remember login.
1034
	 *
1035
	 * @note Every app can set 'rememberlogin' to 'false' to disable the remember login feature
1036
	 *
1037
	 * @return bool
1038
	 */
1039 View Code Duplication
	public static function rememberLoginAllowed() {
1040
		$apps = OC_App::getEnabledApps();
1041
1042
		foreach ($apps as $app) {
1043
			$appInfo = OC_App::getAppInfo($app);
1044
			if (isset($appInfo['rememberlogin']) && $appInfo['rememberlogin'] === 'false') {
1045
				return false;
1046
			}
1047
		}
1048
		return true;
1049
	}
1050
1051
	/**
1052
	 * Check if the user has administration privileges, redirects to home if not
1053
	 *
1054
	 * @return null|boolean $groups where the current user is subadmin
1055
	 */
1056
	public static function checkSubAdminUser() {
1057
		OC_Util::checkLoggedIn();
1058
		$hasUserManagementPrivileges = false;
1059
		$userObject = \OC::$server->getUserSession()->getUser();
1060 View Code Duplication
		if ($userObject !== null) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1061
			//Admin and SubAdmins are allowed to access user management
1062
			$hasUserManagementPrivileges = \OC::$server->getGroupManager()->isAdmin($userObject->getUID())
1063
				|| \OC::$server->getGroupManager()->getSubAdmin()->isSubAdmin($userObject);
1064
		}
1065
		if (!$hasUserManagementPrivileges) {
1066
			\header('Location: ' . \OCP\Util::linkToAbsolute('', 'index.php'));
1067
			exit();
1068
		}
1069
		return true;
1070
	}
1071
1072
	/**
1073
	 * Returns the URL of the default page
1074
	 * based on the system configuration and
1075
	 * the apps visible for the current user
1076
	 *
1077
	 * @return string URL
1078
	 */
1079
	public static function getDefaultPageUrl() {
1080
		$urlGenerator = \OC::$server->getURLGenerator();
1081
		// Deny the redirect if the URL contains a @
1082
		// This prevents unvalidated redirects like ?redirect_url=:[email protected]
1083
		if (isset($_REQUEST['redirect_url']) && \strpos($_REQUEST['redirect_url'], '@') === false) {
1084
			$location = $urlGenerator->getAbsoluteURL(\urldecode($_REQUEST['redirect_url']));
1085
		} else {
1086
			$defaultPage = \OC::$server->getAppConfig()->getValue('core', 'defaultpage');
1087
			if ($defaultPage) {
1088
				$location = $urlGenerator->getAbsoluteURL($defaultPage);
1089
			} else {
1090
				$appId = 'files';
1091
				$defaultApps = \explode(',', \OC::$server->getConfig()->getSystemValue('defaultapp', 'files'));
1092
				// find the first app that is enabled for the current user
1093
				foreach ($defaultApps as $defaultApp) {
1094
					$defaultApp = OC_App::cleanAppId(\strip_tags($defaultApp));
1095
					if (static::getAppManager()->isEnabledForUser($defaultApp)) {
1096
						$appId = $defaultApp;
1097
						break;
1098
					}
1099
				}
1100
1101
				if (\getenv('front_controller_active') === 'true') {
1102
					$location = $urlGenerator->getAbsoluteURL('/apps/' . $appId . '/');
1103
				} else {
1104
					$location = $urlGenerator->getAbsoluteURL('/index.php/apps/' . $appId . '/');
1105
				}
1106
			}
1107
		}
1108
		return $location;
1109
	}
1110
1111
	/**
1112
	 * Redirect to the user default page
1113
	 *
1114
	 * @return void
1115
	 */
1116
	public static function redirectToDefaultPage() {
1117
		$location = self::getDefaultPageUrl();
1118
		\header('Location: ' . $location);
1119
		exit();
1120
	}
1121
1122
	/**
1123
	 * get an id unique for this instance
1124
	 *
1125
	 * The instance id must begin with a letter. It's typically used as the
1126
	 * session name and appears in a file or directory's 'id' property.
1127
	 *
1128
	 * @return string
1129
	 */
1130
	public static function getInstanceId() {
1131
		$id = \OC::$server->getSystemConfig()->getValue('instanceid', null);
1132
		if ($id === null) {
1133
			// We need to guarantee at least one letter in instanceid so it can be used as the session_name
1134
			$id = 'oc' . \OC::$server->getSecureRandom()->generate(10, \OCP\Security\ISecureRandom::CHAR_LOWER.\OCP\Security\ISecureRandom::CHAR_DIGITS);
1135
			\OC::$server->getSystemConfig()->setValue('instanceid', $id);
1136
		}
1137
		return $id;
1138
	}
1139
1140
	/**
1141
	 * Public function to sanitize HTML
1142
	 *
1143
	 * This function is used to sanitize HTML and should be applied on any
1144
	 * string or array of strings before displaying it on a web page.
1145
	 *
1146
	 * @param string|array $value
1147
	 * @return string|array an array of sanitized strings or a single sanitized string, depends on the input parameter.
1148
	 */
1149
	public static function sanitizeHTML($value) {
1150
		if (\is_array($value)) {
1151
			$value = \array_map(function ($value) {
1152
				return self::sanitizeHTML($value);
1153
			}, $value);
1154
		} else {
1155
			// Specify encoding for PHP<5.4
1156
			$value = \htmlspecialchars((string)$value, ENT_QUOTES, 'UTF-8');
1157
		}
1158
		return $value;
1159
	}
1160
1161
	/**
1162
	 * Public function to encode url parameters
1163
	 *
1164
	 * This function is used to encode path to file before output.
1165
	 * Encoding is done according to RFC 3986 with one exception:
1166
	 * Character '/' is preserved as is.
1167
	 *
1168
	 * @param string $component part of URI to encode
1169
	 * @return string
1170
	 */
1171
	public static function encodePath($component) {
1172
		$encoded = \rawurlencode($component);
1173
		$encoded = \str_replace('%2F', '/', $encoded);
1174
		return $encoded;
1175
	}
1176
1177
	public function createHtaccessTestFile(\OCP\IConfig $config) {
1178
		// php dev server does not support htaccess
1179
		if (\php_sapi_name() === 'cli-server') {
1180
			return false;
1181
		}
1182
		if (\OC::$CLI) {
1183
			return false;
1184
		}
1185
1186
		// testdata
1187
		$fileName = '/htaccesstest.txt';
1188
		// test content containing the string "HTACCESSFAIL"
1189
		$testContent = 'HTACCESSFAIL: This is used for testing whether htaccess is properly enabled to disallow access from the outside. This file can be safely removed.';
1190
1191
		// creating a test file
1192
		$testFile = $config->getSystemValue('datadirectory', OC::$SERVERROOT . '/data') . '/' . $fileName;
1193
1194
		if (\file_exists($testFile)) {// already running this test, possible recursive call
1195
			return false;
1196
		}
1197
1198
		$fp = @\fopen($testFile, 'w');
1199
		if (!$fp) {
1200
			throw new OC\HintException('Can\'t create test file to check for working .htaccess file.',
1201
				'Make sure it is possible for the webserver to write to ' . $testFile);
1202
		}
1203
		\fwrite($fp, $testContent);
1204
		\fclose($fp);
1205
	}
1206
1207
	/**
1208
	 * Check if the setlocal call does not work. This can happen if the right
1209
	 * local packages are not available on the server.
1210
	 *
1211
	 * @return bool
1212
	 */
1213
	public static function isSetLocaleWorking() {
1214
		\Patchwork\Utf8\Bootup::initLocale();
1215
		if (\basename('§') === '') {
1216
			return false;
1217
		}
1218
		return true;
1219
	}
1220
1221
	/**
1222
	 * Check if it's possible to get the inline annotations
1223
	 *
1224
	 * @return bool
1225
	 */
1226
	public static function isAnnotationsWorking() {
1227
		$reflection = new \ReflectionMethod(__METHOD__);
1228
		$docs = $reflection->getDocComment();
1229
1230
		return (\is_string($docs) && \strlen($docs) > 50);
1231
	}
1232
1233
	/**
1234
	 * Check if the PHP module fileinfo is loaded.
1235
	 *
1236
	 * @return bool
1237
	 */
1238
	public static function fileInfoLoaded() {
1239
		return \function_exists('finfo_open');
1240
	}
1241
1242
	/**
1243
	 * clear all levels of output buffering
1244
	 *
1245
	 * @return void
1246
	 */
1247
	public static function obEnd() {
1248
		while (\ob_get_level()) {
1249
			\ob_end_clean();
1250
		}
1251
	}
1252
1253
	/**
1254
	 * Checks whether the server is running on the given OS type
1255
	 *
1256
	 * @param string $osType linux|mac|bsd etc
1257
	 * @return bool true if running on that OS type, false otherwise
1258
	 */
1259
	public static function runningOn($osType) {
1260
		$osType = \strtolower($osType) === 'mac' ? 'darwin' : \strtolower($osType);
1261
1262
		if ($osType === 'bsd') {
1263
			return (\strpos(\strtolower(PHP_OS), $osType) !== false);
1264
		} else {
1265
			return (\strtolower(\substr(PHP_OS, 0, \strlen($osType))) === $osType);
1266
		}
1267
	}
1268
1269
	/**
1270
	 * Checks whether server is running on HHVM
1271
	 *
1272
	 * @return bool True if running on HHVM, false otherwise
1273
	 */
1274
	public static function runningOnHhvm() {
1275
		return \defined('HHVM_VERSION');
1276
	}
1277
1278
	/**
1279
	 * Handles the case that there may not be a theme, then check if a "default"
1280
	 * theme exists and take that one
1281
	 *
1282
	 * @return \OCP\Theme\ITheme the theme
1283
	 */
1284
	public static function getTheme() {
1285
		/** @var \OCP\Theme\IThemeService $themeService */
1286
		$themeService = \OC::$server->query('ThemeService');
1287
		return $themeService->getTheme();
1288
	}
1289
1290
	/**
1291
	 * Clear a single file from the opcode cache
1292
	 * This is useful for writing to the config file
1293
	 * in case the opcode cache does not re-validate files
1294
	 * Returns true if successful, false if unsuccessful:
1295
	 * caller should fall back on clearing the entire cache
1296
	 * with clearOpcodeCache() if unsuccessful
1297
	 *
1298
	 * @param string $path the path of the file to clear from the cache
1299
	 * @return bool true if underlying function returns true, otherwise false
1300
	 */
1301
	public static function deleteFromOpcodeCache($path) {
1302
		$ret = false;
1303
		if ($path) {
1304
			// APC >= 3.1.1
1305
			if (\function_exists('apc_delete_file')) {
1306
				$ret = @apc_delete_file($path);
1307
			}
1308
			// Zend OpCache >= 7.0.0, PHP >= 5.5.0
1309
			if (\function_exists('opcache_invalidate')) {
1310
				$ret = \opcache_invalidate($path);
1311
			}
1312
		}
1313
		return $ret;
1314
	}
1315
1316
	/**
1317
	 * Clear the opcode cache if one exists
1318
	 * This is necessary for writing to the config file
1319
	 * in case the opcode cache does not re-validate files
1320
	 *
1321
	 * @return void
1322
	 */
1323
	public static function clearOpcodeCache() {
1324
		// APC
1325
		if (\function_exists('apc_clear_cache')) {
1326
			\apc_clear_cache();
1327
		}
1328
		// Zend Opcache
1329
		if (\function_exists('accelerator_reset')) {
1330
			\accelerator_reset();
1331
		}
1332
		// Opcache (PHP >= 5.5)
1333
		if (\function_exists('opcache_reset')) {
1334
			\opcache_reset();
1335
		}
1336
	}
1337
1338
	/**
1339
	 * Normalize a unicode string
1340
	 *
1341
	 * @param string $value a not normalized string
1342
	 * @return bool|string
1343
	 */
1344
	public static function normalizeUnicode($value) {
1345
		if (Normalizer::isNormalized($value)) {
1346
			return $value;
1347
		}
1348
1349
		$normalizedValue = Normalizer::normalize($value);
1350
		if ($normalizedValue === null || $normalizedValue === false) {
1351
			\OC::$server->getLogger()->warning('normalizing failed for "' . $value . '"', ['app' => 'core']);
1352
			return $value;
1353
		}
1354
1355
		return $normalizedValue;
1356
	}
1357
1358
	/**
1359
	 * @param boolean|string $file
1360
	 * @return string
1361
	 */
1362
	public static function basename($file) {
1363
		$file = \rtrim($file, '/');
1364
		$t = \explode('/', $file);
1365
		return \array_pop($t);
1366
	}
1367
1368
	/**
1369
	 * A human readable string is generated based on version, channel and build number
1370
	 *
1371
	 * @return string
1372
	 */
1373
	public static function getHumanVersion() {
1374
		$version = OC_Util::getVersionString() . ' (' . OC_Util::getChannel() . ')';
1375
		$build = OC_Util::getBuild();
1376
		if (!empty($build) and OC_Util::getChannel() === 'daily') {
1377
			$version .= ' Build:' . $build;
1378
		}
1379
		return $version;
1380
	}
1381
1382
	/**
1383
	 * Returns whether the given file name is valid
1384
	 *
1385
	 * @param string $file file name to check
1386
	 * @return bool true if the file name is valid, false otherwise
1387
	 * @deprecated use \OC\Files\View::verifyPath()
1388
	 */
1389
	public static function isValidFileName($file) {
1390
		$trimmed = \trim($file);
1391
		if ($trimmed === '') {
1392
			return false;
1393
		}
1394
		if (\OC\Files\Filesystem::isIgnoredDir($trimmed)) {
1395
			return false;
1396
		}
1397
1398
		// detect part files
1399
		if (\preg_match('/' . \OCP\Files\FileInfo::BLACKLIST_FILES_REGEX . '/', $trimmed) !== 0) {
1400
			return false;
1401
		}
1402
1403
		foreach (\str_split($trimmed) as $char) {
1404
			if (\strpos(\OCP\Constants::FILENAME_INVALID_CHARS, $char) !== false) {
1405
				return false;
1406
			}
1407
		}
1408
		return true;
1409
	}
1410
1411
	/**
1412
	 * Check whether the instance needs to perform an upgrade,
1413
	 * either when the core version is higher or any app requires
1414
	 * an upgrade.
1415
	 *
1416
	 * @param \OCP\IConfig $config
1417
	 * @return bool whether the core or any app needs an upgrade
1418
	 * @throws \OC\HintException When the upgrade from the given version is not allowed
1419
	 */
1420
	public static function needUpgrade(\OCP\IConfig $config) {
1421
		if ($config->getSystemValue('installed', false)) {
1422
			$installedVersion = $config->getSystemValue('version', '0.0.0');
1423
			$currentVersion = \implode('.', \OCP\Util::getVersion());
1424
			$versionDiff = \version_compare($currentVersion, $installedVersion);
1425
			if ($versionDiff > 0) {
1426
				return true;
1427
			} elseif ($config->getSystemValue('debug', false) && $versionDiff < 0) {
1428
				// downgrade with debug
1429
				$installedMajor = \explode('.', $installedVersion);
1430
				$installedMajor = $installedMajor[0] . '.' . $installedMajor[1];
1431
				$currentMajor = \explode('.', $currentVersion);
1432
				$currentMajor = $currentMajor[0] . '.' . $currentMajor[1];
1433
				if ($installedMajor === $currentMajor) {
1434
					// Same major, allow downgrade for developers
1435
					return true;
1436
				} else {
1437
					// downgrade attempt, throw exception
1438
					throw new \OC\HintException('Downgrading is not supported and is likely to cause unpredictable issues (from ' . $installedVersion . ' to ' . $currentVersion . ')');
1439
				}
1440
			} elseif ($versionDiff < 0) {
1441
				// downgrade attempt, throw exception
1442
				throw new \OC\HintException('Downgrading is not supported and is likely to cause unpredictable issues (from ' . $installedVersion . ' to ' . $currentVersion . ')');
1443
			}
1444
1445
			// also check for upgrades for apps (independently from the user)
1446
			$apps = \OC_App::getEnabledApps(false, true);
1447
			$shouldUpgrade = false;
1448
			foreach ($apps as $app) {
1449
				if (\OC_App::shouldUpgrade($app)) {
1450
					$shouldUpgrade = true;
1451
					break;
1452
				}
1453
			}
1454
			return $shouldUpgrade;
1455
		} else {
1456
			return false;
1457
		}
1458
	}
1459
}
1460