Completed
Push — master ( 08f41d...a423c8 )
by
unknown
30:41 queued 15:07
created

OC_Util::getInstanceId()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
nc 2
nop 0
dl 0
loc 9
rs 9.9666
c 0
b 0
f 0
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\IConfig;
62
use OCP\IGroupManager;
63
use OCP\IUser;
64
65
class OC_Util {
66
	public static $scripts = [];
67
	public static $styles = [];
68
	public static $headers = [];
69
	private static $rootMounted = false;
70
	private static $fsSetup = false;
71
	private static $version;
72
	const EDITION_COMMUNITY = 'Community';
73
	const EDITION_ENTERPRISE = 'Enterprise';
74
75
	protected static function getAppManager() {
76
		return \OC::$server->getAppManager();
77
	}
78
79
	private static function initLocalStorageRootFS() {
80
		// mount local file backend as root
81
		$configDataDirectory = \OC::$server->getSystemConfig()->getValue("datadirectory", OC::$SERVERROOT . "/data");
82
		//first set up the local "root" storage
83
		\OC\Files\Filesystem::initMountManager();
84
		if (!self::$rootMounted) {
85
			\OC\Files\Filesystem::mount('\OC\Files\Storage\Local', ['datadir' => $configDataDirectory], '/');
86
			self::$rootMounted = true;
87
		}
88
	}
89
90
	/**
91
	 * mounting an object storage as the root fs will in essence remove the
92
	 * necessity of a data folder being present.
93
	 * TODO make home storage aware of this and use the object storage instead of local disk access
94
	 *
95
	 * @param array $config containing 'class' and optional 'arguments'
96
	 */
97
	private static function initObjectStoreRootFS($config) {
98
		// check misconfiguration
99
		if (empty($config['class'])) {
100
			\OCP\Util::writeLog('files', 'No class given for objectstore', \OCP\Util::ERROR);
101
		}
102
		if (!isset($config['arguments'])) {
103
			$config['arguments'] = [];
104
		}
105
106
		// instantiate object store implementation
107
		$name = $config['class'];
108
		if (\strpos($name, 'OCA\\') === 0 && \substr_count($name, '\\') >= 2) {
109
			$segments = \explode('\\', $name);
110
			OC_App::loadApp(\strtolower($segments[1]));
111
		}
112
		$config['arguments']['objectstore'] = new $config['class']($config['arguments']);
113
		// mount with plain / root object store implementation
114
		$config['class'] = '\OC\Files\ObjectStore\ObjectStoreStorage';
115
116
		// mount object storage as root
117
		\OC\Files\Filesystem::initMountManager();
118
		if (!self::$rootMounted) {
119
			\OC\Files\Filesystem::mount($config['class'], $config['arguments'], '/');
120
			self::$rootMounted = true;
121
		}
122
	}
123
124
	/**
125
	 * Can be set up
126
	 *
127
	 * @param string $user
128
	 * @return boolean
129
	 * @description configure the initial filesystem based on the configuration
130
	 */
131
	public static function setupFS($user = '') {
132
		//setting up the filesystem twice can only lead to trouble
133
		if (self::$fsSetup) {
134
			return false;
135
		}
136
137
		\OC::$server->getEventLogger()->start('setup_fs', 'Setup filesystem');
138
139
		// If we are not forced to load a specific user we load the one that is logged in
140
		if ($user === null) {
141
			$user = '';
142
		} elseif ($user == "" && OC_User::isLoggedIn()) {
143
			$user = OC_User::getUser();
144
		}
145
146
		// load all filesystem apps before, so no setup-hook gets lost
147
		OC_App::loadApps(['filesystem']);
148
149
		// the filesystem will finish when $user is not empty,
150
		// mark fs setup here to avoid doing the setup from loading
151
		// OC_Filesystem
152
		if ($user != '') {
153
			self::$fsSetup = true;
154
		}
155
156
		\OC\Files\Filesystem::initMountManager();
157
158
		\OC\Files\Filesystem::logWarningWhenAddingStorageWrapper(false);
159
		\OC\Files\Filesystem::addStorageWrapper('mount_options', function ($mountPoint, \OCP\Files\Storage $storage, \OCP\Files\Mount\IMountPoint $mount) {
160
			if ($storage->instanceOfStorage('\OC\Files\Storage\Common')) {
161
				/** @var \OC\Files\Storage\Common $storage */
162
				$storage->setMountOptions($mount->getOptions());
163
			}
164
			return $storage;
165
		});
166
167
		\OC\Files\Filesystem::addStorageWrapper('enable_sharing', function ($mountPoint, \OCP\Files\Storage $storage, \OCP\Files\Mount\IMountPoint $mount) {
168 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...
169
				return new \OC\Files\Storage\Wrapper\PermissionsMask([
170
					'storage' => $storage,
171
					'mask' => \OCP\Constants::PERMISSION_ALL - \OCP\Constants::PERMISSION_SHARE
172
				]);
173
			}
174
			return $storage;
175
		});
176
177
		// install storage availability wrapper, before most other wrappers
178
		\OC\Files\Filesystem::addStorageWrapper('oc_availability', function ($mountPoint, $storage) {
179
			if (!$storage->instanceOfStorage('\OCA\Files_Sharing\SharedStorage') && !$storage->isLocal()) {
180
				return new \OC\Files\Storage\Wrapper\Availability(['storage' => $storage]);
181
			}
182
			return $storage;
183
		});
184
185
		// install storage checksum wrapper
186
		\OC\Files\Filesystem::addStorageWrapper('oc_checksum', function ($mountPoint, \OCP\Files\Storage\IStorage $storage) {
187
			if (!$storage->instanceOfStorage('\OCA\Files_Sharing\SharedStorage')) {
188
				return new \OC\Files\Storage\Wrapper\Checksum(['storage' => $storage]);
189
			}
190
191
			return $storage;
192
		}, 1);
193
194
		\OC\Files\Filesystem::addStorageWrapper('oc_encoding', function ($mountPoint, \OCP\Files\Storage $storage, \OCP\Files\Mount\IMountPoint $mount) {
195
			if ($mount->getOption('encoding_compatibility', false) && !$storage->instanceOfStorage('\OCA\Files_Sharing\SharedStorage') && !$storage->isLocal()) {
196
				return new \OC\Files\Storage\Wrapper\Encoding(['storage' => $storage]);
197
			}
198
			return $storage;
199
		});
200
201
		\OC\Files\Filesystem::addStorageWrapper('oc_quota', function ($mountPoint, $storage) {
202
			// set up quota for home storages, even for other users
203
			// which can happen when using sharing
204
205
			/**
206
			 * @var \OC\Files\Storage\Storage $storage
207
			 */
208
			if ($storage->instanceOfStorage('\OC\Files\Storage\Home')
209
				|| $storage->instanceOfStorage('\OC\Files\ObjectStore\HomeObjectStoreStorage')
210
			) {
211
				/** @var \OC\Files\Storage\Home $storage */
212
				if (\is_object($storage->getUser())) {
213
					$user = $storage->getUser()->getUID();
214
					$quota = OC_Util::getUserQuota($user);
215
					if ($quota !== \OCP\Files\FileInfo::SPACE_UNLIMITED) {
216
						return new \OC\Files\Storage\Wrapper\Quota(['storage' => $storage, 'quota' => $quota, 'root' => 'files']);
217
					}
218
				}
219
			}
220
221
			return $storage;
222
		});
223
224
		OC_Hook::emit('OC_Filesystem', 'preSetup', ['user' => $user]);
225
		\OC\Files\Filesystem::logWarningWhenAddingStorageWrapper(true);
226
227
		// Make users storage readonly if he is a guest or in a read_only group
228
229
		$isGuest = \OC::$server->getConfig()->getUserValue(
230
			$user,
0 ignored issues
show
Bug introduced by
It seems like $user defined by \OC_User::getUser() on line 143 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...
231
			'owncloud',
232
			'isGuest',
233
			false
234
		);
235
236
		if (!$isGuest) {
237
			$readOnlyGroups = \json_decode(\OC::$server->getConfig()->getAppValue(
238
				'core',
239
				'read_only_groups',
240
				'[]'
241
			), true);
242
243
			if (!\is_array($readOnlyGroups)) {
244
				$readOnlyGroups = [];
245
			}
246
247
			$userGroups = \array_keys(
248
				\OC::$server->getGroupManager()->getUserIdGroups($user)
0 ignored issues
show
Bug introduced by
It seems like $user defined by \OC_User::getUser() on line 143 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...
249
			);
250
251
			$readOnlyGroupMemberships = \array_intersect(
252
				$readOnlyGroups,
253
				$userGroups
254
			);
255
		}
256
257
		if ($isGuest === '1' || !empty($readOnlyGroupMemberships)) {
258
			\OC\Files\Filesystem::addStorageWrapper(
259
				'oc_readonly',
260
				function ($mountPoint, $storage) use ($user) {
261
					if ($mountPoint === '/' || $mountPoint === "/$user/") {
262
						return new \OC\Files\Storage\Wrapper\ReadOnlyJail(
263
							[
264
								'storage' => $storage,
265
								'mask' => \OCP\Constants::PERMISSION_READ,
266
								'path' => 'files'
267
							]
268
						);
269
					}
270
271
					return $storage;
272
				}
273
			);
274
		}
275
276
		//check if we are using an object storage
277
		$objectStore = \OC::$server->getSystemConfig()->getValue('objectstore', null);
278
		if (isset($objectStore)) {
279
			self::initObjectStoreRootFS($objectStore);
280
		} else {
281
			self::initLocalStorageRootFS();
282
		}
283
284
		if ($user != '' && !OCP\User::userExists($user)) {
0 ignored issues
show
Bug introduced by
It seems like $user defined by \OC_User::getUser() on line 143 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...
285
			\OC::$server->getEventLogger()->end('setup_fs');
286
			return false;
287
		}
288
289
		//if we aren't logged in, there is no use to set up the filesystem
290
		if ($user != "") {
291
			$userDir = '/' . $user . '/files';
292
293
			//jail the user into his "home" directory
294
			\OC\Files\Filesystem::init($user, $userDir);
295
296
			OC_Hook::emit('OC_Filesystem', 'setup', ['user' => $user, 'user_dir' => $userDir]);
297
		}
298
		\OC::$server->getEventLogger()->end('setup_fs');
299
		return true;
300
	}
301
302
	/**
303
	 * check if a password is required for each public link.
304
	 * This is deprecated due to not reflecting all the possibilities now. Falling back to
305
	 * enforce password for read-only links. Note that read & write or write-only options won't
306
	 * be considered here
307
	 *
308
	 * @return boolean
309
	 * @deprecated
310
	 */
311
	public static function isPublicLinkPasswordRequired() {
312
		$appConfig = \OC::$server->getAppConfig();
313
		$enforcePassword = $appConfig->getValue('core', 'shareapi_enforce_links_password_read_only', 'no');
314
		return ($enforcePassword === 'yes') ? true : false;
315
	}
316
317
	/**
318
	 * check if sharing is disabled for the current user
319
	 * @param IConfig $config
320
	 * @param IGroupManager $groupManager
321
	 * @param IUser|null $user
322
	 * @return bool
323
	 */
324
	public static function isSharingDisabledForUser(IConfig $config, IGroupManager $groupManager, $user) {
325
		if ($config->getAppValue('core', 'shareapi_exclude_groups', 'no') === 'yes') {
326
			$groupsList = $config->getAppValue('core', 'shareapi_exclude_groups_list', '');
327
			$excludedGroups = \json_decode($groupsList);
328 View Code Duplication
			if ($excludedGroups === 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...
329
				$excludedGroups = \explode(',', $groupsList);
330
				$newValue = \json_encode($excludedGroups);
331
				$config->setAppValue('core', 'shareapi_exclude_groups_list', $newValue);
332
			}
333
			$usersGroups = $groupManager->getUserGroupIds($user);
0 ignored issues
show
Bug introduced by
It seems like $user defined by parameter $user on line 324 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...
334
			$matchingGroups = \array_intersect($usersGroups, $excludedGroups);
335
			if (!empty($matchingGroups)) {
336
				// If the user is a member of any of the excluded groups they cannot use sharing
337
				return true;
338
			}
339
		}
340
		return false;
341
	}
342
343
	/**
344
	 * check if share API enforces a default expire date
345
	 *
346
	 * @return boolean
347
	 */
348
	public static function isDefaultExpireDateEnforced() {
349
		$isDefaultExpireDateEnabled = \OCP\Config::getAppValue('core', 'shareapi_default_expire_date', 'no');
350
		$enforceDefaultExpireDate = false;
351
		if ($isDefaultExpireDateEnabled === 'yes') {
352
			$value = \OCP\Config::getAppValue('core', 'shareapi_enforce_expire_date', 'no');
353
			$enforceDefaultExpireDate = ($value === 'yes') ? true : false;
354
		}
355
356
		return $enforceDefaultExpireDate;
357
	}
358
359
	/**
360
	 * Get the quota of a user
361
	 *
362
	 * @param string|IUser $userId
363
	 * @return int Quota bytes
364
	 */
365
	public static function getUserQuota($userId) {
366
		if ($userId instanceof IUser) {
367
			$user = $userId;
368
		} else {
369
			$user = \OC::$server->getUserManager()->get($userId);
370
		}
371
		if ($user === null) {
372
			return \OCP\Files\FileInfo::SPACE_UNLIMITED;
373
		}
374
		$userQuota = $user->getQuota();
375
		if ($userQuota === null || $userQuota === 'default') {
376
			$userQuota = \OC::$server->getConfig()->getAppValue('files', 'default_quota', 'none');
377
		}
378
		if ($userQuota === null || $userQuota === 'none') {
379
			return \OCP\Files\FileInfo::SPACE_UNLIMITED;
380
		}
381
		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 381 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...
382
	}
383
384
	/**
385
	 * copies the skeleton to the users /files
386
	 *
387
	 * @param String $userId
388
	 * @param \OCP\Files\Folder $userDirectory
389
	 * @throws \OC\HintException
390
	 */
391
	public static function copySkeleton($userId, \OCP\Files\Folder $userDirectory) {
392
		$skeletonDirectory = \OCP\Config::getSystemValue('skeletondirectory', \OC::$SERVERROOT . '/core/skeleton');
393
394
		if (!\is_dir($skeletonDirectory)) {
395
			throw new \OC\HintException('The skeleton folder '.$skeletonDirectory.' is not accessible');
396
		}
397
398
		if (!empty($skeletonDirectory)) {
399
			\OCP\Util::writeLog(
400
				'files_skeleton',
401
				'copying skeleton for '.$userId.' from '.$skeletonDirectory.' to '.$userDirectory->getFullPath('/'),
402
				\OCP\Util::DEBUG
403
			);
404
			self::copyr($skeletonDirectory, $userDirectory);
405
			// update the file cache
406
			$userDirectory->getStorage()->getScanner()->scan('', \OC\Files\Cache\Scanner::SCAN_RECURSIVE);
407
		}
408
	}
409
410
	/**
411
	 * copies a directory recursively by using streams
412
	 *
413
	 * @param string $source
414
	 * @param \OCP\Files\Folder $target
415
	 * @return void
416
	 * @throws NoReadAccessException
417
	 */
418
	public static function copyr($source, \OCP\Files\Folder $target) {
419
		$dir = @\opendir($source);
420
		if ($dir === false) {
421
			throw new NoReadAccessException('No read permission for folder ' . $source);
422
		}
423
		while (($file = \readdir($dir)) !== false) {
424
			if (!\OC\Files\Filesystem::isIgnoredDir($file)) {
425
				if (\is_dir($source . '/' . $file)) {
426
					$child = $target->newFolder($file);
427
					self::copyr($source . '/' . $file, $child);
428
				} else {
429
					$sourceFileHandle = @\fopen($source . '/' . $file, 'r');
430
					if ($sourceFileHandle === false) {
431
						throw new NoReadAccessException('No read permission for file ' . $file);
432
					}
433
					$child = $target->newFile($file);
434
					$targetFileHandle = $child->fopen('w');
435
					\stream_copy_to_stream($sourceFileHandle, $targetFileHandle);
436
					\fclose($targetFileHandle);
437
					\fclose($sourceFileHandle);
438
439
					// update cache sizes
440
					$cache = $target->getStorage()->getCache();
441
					if ($cache instanceof \OC\Files\Cache\Cache) {
442
						$cache->correctFolderSize($child->getInternalPath());
443
					}
444
				}
445
			}
446
		}
447
		\closedir($dir);
448
	}
449
450
	/**
451
	 * @return void
452
	 */
453
	public static function tearDownFS() {
454
		\OC\Files\Filesystem::tearDown();
455
		self::$fsSetup = false;
456
		self::$rootMounted = false;
457
	}
458
459
	/**
460
	 * get the current installed version of ownCloud
461
	 *
462
	 * @return array
463
	 */
464
	public static function getVersion() {
465
		OC_Util::loadVersion();
466
		return self::$version['OC_Version'];
467
	}
468
469
	/**
470
	 * get the current installed version string of ownCloud
471
	 *
472
	 * @return string
473
	 */
474
	public static function getVersionString() {
475
		OC_Util::loadVersion();
476
		return self::$version['OC_VersionString'];
477
	}
478
479
	/**
480
	 * @description get the current installed edition of ownCloud.
481
	 * There is the community edition that returns "Community" and
482
	 * the enterprise edition that returns "Enterprise".
483
	 * @return string
484
	 */
485
	public static function getEditionString() {
486
		if (OC_App::isEnabled('enterprise_key')) {
487
			return OC_Util::EDITION_ENTERPRISE;
488
		} else {
489
			return OC_Util::EDITION_COMMUNITY;
490
		}
491
	}
492
493
	/**
494
	 * @description get the update channel of the current installed of ownCloud.
495
	 * @return string
496
	 */
497
	public static function getChannel() {
498
		OC_Util::loadVersion();
499
		return self::$version['OC_Channel'];
500
	}
501
502
	/**
503
	 * @description get the build number of the current installed of ownCloud.
504
	 * @return string
505
	 */
506
	public static function getBuild() {
507
		OC_Util::loadVersion();
508
		return self::$version['OC_Build'];
509
	}
510
511
	/**
512
	 * @description load the version.php into the session as cache
513
	 */
514
	private static function loadVersion() {
515
		require __DIR__ . '/../../../version.php';
516
		/** @var $OC_Version string */
517
		/** @var $OC_VersionString string */
518
		/** @var $OC_Build string */
519
		/** @var $OC_Channel string */
520
		self::$version = [
521
			'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...
522
			'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...
523
			'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...
524
			'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...
525
		];
526
527
		// Allow overriding update channel
528
529
		if (\OC::$server->getSystemConfig()->getValue('installed', false)) {
530
			$channel = \OC::$server->getConfig()->getAppValue('core', 'OC_Channel', $OC_Channel);
531
			self::$version['OC_Channel'] = $channel;
532
		}
533
	}
534
535
	/**
536
	 * generates a path for JS/CSS files. If no application is provided it will create the path for core.
537
	 *
538
	 * @param string $application application to get the files from
539
	 * @param string $directory directory within this application (css, js, vendor, etc)
540
	 * @param string $file the file inside of the above folder
541
	 * @return string the path
542
	 */
543
	private static function generatePath($application, $directory, $file) {
544
		if ($file === null) {
545
			$file = $application;
546
			$application = "";
547
		}
548
		if (!empty($application)) {
549
			return "$application/$directory/$file";
550
		} else {
551
			return "$directory/$file";
552
		}
553
	}
554
555
	/**
556
	 * add a javascript file
557
	 *
558
	 * @param string $application application id
559
	 * @param string|null $file filename
560
	 * @param bool $prepend prepend the Script to the beginning of the list
561
	 * @return void
562
	 */
563
	public static function addScript($application, $file = null, $prepend = false) {
564
		$path = OC_Util::generatePath($application, 'js', $file);
565
566
		// core js files need separate handling
567
		if ($application !== 'core' && $file !== null) {
568
			self::addTranslations($application);
569
		}
570
		self::addExternalResource($application, $prepend, $path, "script");
571
	}
572
573
	/**
574
	 * add a javascript file from the vendor sub folder
575
	 *
576
	 * @param string $application application id
577
	 * @param string|null $file filename
578
	 * @param bool $prepend prepend the Script to the beginning of the list
579
	 * @return void
580
	 */
581
	public static function addVendorScript($application, $file = null, $prepend = false) {
582
		$path = OC_Util::generatePath($application, 'vendor', $file);
583
		self::addExternalResource($application, $prepend, $path, "script");
584
	}
585
586
	/**
587
	 * add a translation JS file
588
	 *
589
	 * @param string $application application id
590
	 * @param string $languageCode language code, defaults to the current language
591
	 * @param bool $prepend prepend the Script to the beginning of the list
592
	 */
593
	public static function addTranslations($application, $languageCode = null, $prepend = false) {
594
		if ($languageCode === null) {
595
			$languageCode = \OC::$server->getL10NFactory()->findLanguage($application);
596
		}
597
		if (!empty($application)) {
598
			$path = "$application/l10n/$languageCode";
599
		} else {
600
			$path = "l10n/$languageCode";
601
		}
602
		self::addExternalResource($application, $prepend, $path, "script");
603
	}
604
605
	/**
606
	 * add a css file
607
	 *
608
	 * @param string $application application id
609
	 * @param string|null $file filename
610
	 * @param bool $prepend prepend the Style to the beginning of the list
611
	 * @return void
612
	 */
613
	public static function addStyle($application, $file = null, $prepend = false) {
614
		$path = OC_Util::generatePath($application, 'css', $file);
615
		self::addExternalResource($application, $prepend, $path, "style");
616
	}
617
618
	/**
619
	 * add a css file from the vendor sub folder
620
	 *
621
	 * @param string $application application id
622
	 * @param string|null $file filename
623
	 * @param bool $prepend prepend the Style to the beginning of the list
624
	 * @return void
625
	 */
626
	public static function addVendorStyle($application, $file = null, $prepend = false) {
627
		$path = OC_Util::generatePath($application, 'vendor', $file);
628
		self::addExternalResource($application, $prepend, $path, "style");
629
	}
630
631
	/**
632
	 * add an external resource css/js file
633
	 *
634
	 * @param string $application application id
635
	 * @param bool $prepend prepend the file to the beginning of the list
636
	 * @param string $path
637
	 * @param string $type (script or style)
638
	 * @return void
639
	 */
640
	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...
641
		if ($type === "style") {
642 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...
643
				if ($prepend === true) {
644
					\array_unshift(self::$styles, $path);
645
				} else {
646
					self::$styles[] = $path;
647
				}
648
			}
649 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...
650
			if (!\in_array($path, self::$scripts)) {
651
				if ($prepend === true) {
652
					\array_unshift(self::$scripts, $path);
653
				} else {
654
					self::$scripts [] = $path;
655
				}
656
			}
657
		}
658
	}
659
660
	/**
661
	 * Add a custom element to the header
662
	 * If $text is null then the element will be written as empty element.
663
	 * So use "" to get a closing tag.
664
	 * @param string $tag tag name of the element
665
	 * @param array $attributes array of attributes for the element
666
	 * @param string $text the text content for the element
667
	 */
668 View Code Duplication
	public static function addHeader($tag, $attributes, $text=null) {
669
		self::$headers[] = [
670
			'tag' => $tag,
671
			'attributes' => $attributes,
672
			'text' => $text
673
		];
674
	}
675
676
	/**
677
	 * formats a timestamp in the "right" way
678
	 *
679
	 * @param int $timestamp
680
	 * @param bool $dateOnly option to omit time from the result
681
	 * @param DateTimeZone|string $timeZone where the given timestamp shall be converted to
682
	 * @return string timestamp
683
	 *
684
	 * @deprecated Use \OC::$server->query('DateTimeFormatter') instead
685
	 */
686
	public static function formatDate($timestamp, $dateOnly = false, $timeZone = null) {
687
		if ($timeZone !== null && !$timeZone instanceof \DateTimeZone) {
688
			$timeZone = new \DateTimeZone($timeZone);
689
		}
690
691
		/** @var \OC\DateTimeFormatter $formatter */
692
		$formatter = \OC::$server->query('DateTimeFormatter');
693
		if ($dateOnly) {
694
			return $formatter->formatDate($timestamp, 'long', $timeZone);
695
		}
696
		return $formatter->formatDateTime($timestamp, 'long', 'long', $timeZone);
697
	}
698
699
	/**
700
	 * check if the current server configuration is suitable for ownCloud
701
	 *
702
	 * @param \OCP\IConfig $config
703
	 * @return array arrays with error messages and hints
704
	 */
705
	public static function checkServer(\OCP\IConfig $config) {
706
		$l = \OC::$server->getL10N('lib');
707
		$errors = [];
708
		$CONFIG_DATADIRECTORY = $config->getSystemValue('datadirectory', OC::$SERVERROOT . '/data');
709
710
		if (!self::needUpgrade($config) && $config->getSystemValue('installed', false)) {
711
			// this check needs to be done every time
712
			$errors = self::checkDataDirectoryValidity($CONFIG_DATADIRECTORY);
713
		}
714
715
		// Assume that if checkServer() succeeded before in this session, then all is fine.
716
		if (\OC::$server->getSession()->exists('checkServer_succeeded') && \OC::$server->getSession()->get('checkServer_succeeded')) {
717
			return $errors;
718
		}
719
720
		$webServerRestart = false;
721
		$setup = new \OC\Setup($config, \OC::$server->getIniWrapper(), \OC::$server->getL10N('lib'),
722
			new \OC_Defaults(), \OC::$server->getLogger(), \OC::$server->getSecureRandom());
723
724
		$urlGenerator = \OC::$server->getURLGenerator();
725
726
		$availableDatabases = $setup->getSupportedDatabases();
727
		if (empty($availableDatabases)) {
728
			$errors[] = [
729
				'error' => $l->t('No database drivers (sqlite, mysql, or postgresql) installed.'),
730
				'hint' => '' //TODO: sane hint
731
			];
732
			$webServerRestart = true;
733
		}
734
735
		// Check if config folder is writable.
736
		if (!\OC::$server->getConfig()->isSystemConfigReadOnly()) {
737
			if (!\is_writable(OC::$configDir) or !\is_readable(OC::$configDir)) {
738
				$errors[] = [
739
					'error' => $l->t('Cannot write into "config" directory'),
740
					'hint' => $l->t('This can usually be fixed by '
741
						. '%sgiving the webserver write access to the config directory%s.',
742
						['<a href="' . $urlGenerator->linkToDocs('admin-dir_permissions') . '" target="_blank" rel="noreferrer">', '</a>'])
743
				];
744
			}
745
		}
746
747
		// Create root dir.
748
		if ($config->getSystemValue('installed', false)) {
749
			if (!\is_dir($CONFIG_DATADIRECTORY)) {
750
				$success = @\mkdir($CONFIG_DATADIRECTORY);
751
				if ($success) {
752
					$errors = \array_merge($errors, self::checkDataDirectoryPermissions($CONFIG_DATADIRECTORY));
753
				} else {
754
					$errors[] = [
755
						'error' => $l->t('Cannot create "data" directory'),
756
						'hint' => $l->t('This can usually be fixed by '
757
							. '<a href="%s" target="_blank" rel="noreferrer">giving the webserver write access to the root directory</a>.',
758
							[$urlGenerator->linkToDocs('admin-dir_permissions')])
759
					];
760
				}
761
			} elseif (!\is_writable($CONFIG_DATADIRECTORY) or !\is_readable($CONFIG_DATADIRECTORY)) {
762
				//common hint for all file permissions error messages
763
				$permissionsHint = $l->t('Permissions can usually be fixed by '
764
					. '%sgiving the webserver write access to the root directory%s.',
765
					['<a href="' . $urlGenerator->linkToDocs('admin-dir_permissions') . '" target="_blank" rel="noreferrer">', '</a>']);
766
				$errors[] = [
767
					'error' => 'Your Data directory is not writable by ownCloud',
768
					'hint' => $permissionsHint
769
				];
770
			} else {
771
				$errors = \array_merge($errors, self::checkDataDirectoryPermissions($CONFIG_DATADIRECTORY));
772
			}
773
		}
774
775
		if (!OC_Util::isSetLocaleWorking()) {
776
			$errors[] = [
777
				'error' => $l->t('Setting locale to %s failed',
778
					['en_US.UTF-8/fr_FR.UTF-8/es_ES.UTF-8/de_DE.UTF-8/ru_RU.UTF-8/'
779
						. 'pt_BR.UTF-8/it_IT.UTF-8/ja_JP.UTF-8/zh_CN.UTF-8']),
780
				'hint' => $l->t('Please install one of these locales on your system and restart your webserver.')
781
			];
782
		}
783
784
		// Contains the dependencies that should be checked against
785
		// classes = class_exists
786
		// functions = function_exists
787
		// defined = defined
788
		// ini = ini_get
789
		// If the dependency is not found the missing module name is shown to the EndUser
790
		// When adding new checks always verify that they pass on Travis as well
791
		// for ini settings, see https://github.com/owncloud/administration/blob/master/travis-ci/custom.ini
792
		$dependencies = [
793
			'classes' => [
794
				'ZipArchive' => 'zip',
795
				'DOMDocument' => 'dom',
796
				'XMLWriter' => 'XMLWriter',
797
				'XMLReader' => 'XMLReader',
798
				'Collator' => 'intl',
799
			],
800
			'functions' => [
801
				'xml_parser_create' => 'libxml',
802
				'mb_strcut' => 'mb multibyte',
803
				'ctype_digit' => 'ctype',
804
				'json_encode' => 'JSON',
805
				'gd_info' => 'GD',
806
				'gzencode' => 'zlib',
807
				'iconv' => 'iconv',
808
				'simplexml_load_string' => 'SimpleXML',
809
				'hash' => 'HASH Message Digest Framework',
810
				'curl_init' => 'cURL',
811
			],
812
			'defined' => [
813
				'PDO::ATTR_DRIVER_NAME' => 'PDO'
814
			],
815
			'ini' => [
816
				'default_charset' => 'UTF-8',
817
			],
818
		];
819
		$missingDependencies = [];
820
		$invalidIniSettings = [];
821
		$moduleHint = $l->t('Please ask your server administrator to install the module.');
822
823
		/**
824
		 * FIXME: The dependency check does not work properly on HHVM on the moment
825
		 *        and prevents installation. Once HHVM is more compatible with our
826
		 *        approach to check for these values we should re-enable those
827
		 *        checks.
828
		 */
829
		$iniWrapper = \OC::$server->getIniWrapper();
830
		if (!self::runningOnHhvm()) {
831
			foreach ($dependencies['classes'] as $class => $module) {
832
				if (!\class_exists($class)) {
833
					$missingDependencies[] = $module;
834
				}
835
			}
836
			foreach ($dependencies['functions'] as $function => $module) {
837
				if (!\function_exists($function)) {
838
					$missingDependencies[] = $module;
839
				}
840
			}
841
			foreach ($dependencies['defined'] as $defined => $module) {
842
				if (!\defined($defined)) {
843
					$missingDependencies[] = $module;
844
				}
845
			}
846
			foreach ($dependencies['ini'] as $setting => $expected) {
847
				if (\is_bool($expected)) {
848
					if ($iniWrapper->getBool($setting) !== $expected) {
849
						$invalidIniSettings[] = [$setting, $expected];
850
					}
851
				}
852
				if (\is_int($expected)) {
853
					if ($iniWrapper->getNumeric($setting) !== $expected) {
854
						$invalidIniSettings[] = [$setting, $expected];
855
					}
856
				}
857
				if (\is_string($expected)) {
858
					if (\strtolower($iniWrapper->getString($setting)) !== \strtolower($expected)) {
859
						$invalidIniSettings[] = [$setting, $expected];
860
					}
861
				}
862
			}
863
		}
864
865
		foreach ($missingDependencies as $missingDependency) {
866
			$errors[] = [
867
				'error' => $l->t('PHP module %s not installed.', [$missingDependency]),
868
				'hint' => $moduleHint
869
			];
870
			$webServerRestart = true;
871
		}
872
		foreach ($invalidIniSettings as $setting) {
873
			if (\is_bool($setting[1])) {
874
				$setting[1] = ($setting[1]) ? 'on' : 'off';
875
			}
876
			$errors[] = [
877
				'error' => $l->t('PHP setting "%s" is not set to "%s".', [$setting[0], \var_export($setting[1], true)]),
878
				'hint' =>  $l->t('Adjusting this setting in php.ini will make ownCloud run again')
879
			];
880
			$webServerRestart = true;
881
		}
882
883
		/**
884
		 * The mbstring.func_overload check can only be performed if the mbstring
885
		 * module is installed as it will return null if the checking setting is
886
		 * not available and thus a check on the boolean value fails.
887
		 *
888
		 * TODO: Should probably be implemented in the above generic dependency
889
		 *       check somehow in the long-term.
890
		 */
891
		if ($iniWrapper->getBool('mbstring.func_overload') !== null &&
892
			$iniWrapper->getBool('mbstring.func_overload') === true) {
893
			$errors[] = [
894
				'error' => $l->t('mbstring.func_overload is set to "%s" instead of the expected value "0"', [$iniWrapper->getString('mbstring.func_overload')]),
895
				'hint' => $l->t('To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini')
896
			];
897
		}
898
899
		if (\function_exists('xml_parser_create') &&
900
			\version_compare('2.7.0', LIBXML_DOTTED_VERSION) === 1) {
901
			$errors[] = [
902
				'error' => $l->t('libxml2 2.7.0 is at least required. Currently %s is installed.', [LIBXML_DOTTED_VERSION]),
903
				'hint' => $l->t('To fix this issue update your libxml2 version and restart your web server.')
904
			];
905
		}
906
907
		if (!self::isAnnotationsWorking()) {
908
			$errors[] = [
909
				'error' => $l->t('PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible.'),
910
				'hint' => $l->t('This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator.')
911
			];
912
		}
913
914
		if (!\OC::$CLI && $webServerRestart) {
915
			$errors[] = [
916
				'error' => $l->t('PHP modules have been installed, but they are still listed as missing?'),
917
				'hint' => $l->t('Please ask your server administrator to restart the web server.')
918
			];
919
		}
920
921
		$errors = \array_merge($errors, self::checkDatabaseVersion());
922
923
		// Cache the result of this function
924
		\OC::$server->getSession()->set('checkServer_succeeded', \count($errors) == 0);
925
926
		return $errors;
927
	}
928
929
	/**
930
	 * Check the database version
931
	 *
932
	 * @return array errors array
933
	 */
934
	public static function checkDatabaseVersion() {
935
		$l = \OC::$server->getL10N('lib');
936
		$errors = [];
937
		$dbType = \OC::$server->getSystemConfig()->getValue('dbtype', 'sqlite');
938
		if ($dbType === 'pgsql') {
939
			// check PostgreSQL version
940
			try {
941
				$result = \OC_DB::executeAudited('SHOW SERVER_VERSION');
942
				$data = $result->fetchRow();
943
				if (isset($data['server_version'])) {
944
					$version = $data['server_version'];
945
					if (\version_compare($version, '9.0.0', '<')) {
946
						$errors[] = [
947
							'error' => $l->t('PostgreSQL >= 9 required'),
948
							'hint' => $l->t('Please upgrade your database version')
949
						];
950
					}
951
				}
952
			} catch (\Doctrine\DBAL\DBALException $e) {
953
				$logger = \OC::$server->getLogger();
954
				$logger->warning('Error occurred while checking PostgreSQL version, assuming >= 9');
955
				$logger->logException($e);
956
			}
957
		}
958
		return $errors;
959
	}
960
961
	/**
962
	 * Check for correct file permissions of data directory
963
	 *
964
	 * @param string $dataDirectory
965
	 * @return array arrays with error messages and hints
966
	 */
967
	public static function checkDataDirectoryPermissions($dataDirectory) {
968
		$l = \OC::$server->getL10N('lib');
969
		$errors = [];
970
		$permissionsModHint = $l->t('Please change the permissions to 0770 so that the directory'
971
			. ' cannot be listed by other users.');
972
		$perms = \substr(\decoct(@\fileperms($dataDirectory)), -3);
973
		if (\substr($perms, -1) != '0') {
974
			\chmod($dataDirectory, 0770);
975
			\clearstatcache();
976
			$perms = \substr(\decoct(@\fileperms($dataDirectory)), -3);
977
			if (\substr($perms, 2, 1) != '0') {
978
				$errors[] = [
979
					'error' => $l->t('Your Data directory is readable by other users'),
980
					'hint' => $permissionsModHint
981
				];
982
			}
983
		}
984
		return $errors;
985
	}
986
987
	/**
988
	 * Check that the data directory exists and is valid by
989
	 * checking the existence of the ".ocdata" file.
990
	 *
991
	 * @param string $dataDirectory data directory path
992
	 * @return array errors found
993
	 */
994
	public static function checkDataDirectoryValidity($dataDirectory) {
995
		$l = \OC::$server->getL10N('lib');
996
		$errors = [];
997 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...
998
			$errors[] = [
999
				'error' => $l->t('Your Data directory must be an absolute path'),
1000
				'hint' => $l->t('Check the value of "datadirectory" in your configuration')
1001
			];
1002
		}
1003 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...
1004
			$errors[] = [
1005
				'error' => $l->t('Your Data directory  is invalid'),
1006
				'hint' => $l->t('Please check that the data directory contains a file' .
1007
					' ".ocdata" in its root.')
1008
			];
1009
		}
1010
		return $errors;
1011
	}
1012
1013
	/**
1014
	 * Check if the user is logged in, redirects to home if not. With
1015
	 * redirect URL parameter to the request URI.
1016
	 *
1017
	 * @return void
1018
	 */
1019
	public static function checkLoggedIn() {
1020
		// Check if we are a user
1021
		$userSession = \OC::$server->getUserSession();
1022
		if (!$userSession->isLoggedIn()) {
1023
			\header('Location: ' . \OC::$server->getURLGenerator()->linkToRoute(
1024
						'core.login.showLoginForm',
1025
						[
1026
							'redirect_url' => \urlencode(\OC::$server->getRequest()->getRequestUri()),
1027
						]
1028
					)
1029
			);
1030
			exit();
1031
		}
1032
		// Redirect to index page if 2FA challenge was not solved yet
1033
		if (\OC::$server->getTwoFactorAuthManager()->needsSecondFactor()) {
1034
			\header('Location: ' . \OCP\Util::linkToAbsolute('', 'index.php'));
1035
			exit();
1036
		}
1037
		// Redirect to index page if any IAuthModule check fails
1038
		try {
1039
			\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...
1040
		} catch (AccountCheckException $ex) {
1041
			\header('Location: ' . \OCP\Util::linkToAbsolute('', 'index.php'));
1042
			exit();
1043
		}
1044
	}
1045
1046
	/**
1047
	 * Check if the user is a admin, redirects to home if not
1048
	 *
1049
	 * @return void
1050
	 */
1051
	public static function checkAdminUser() {
1052
		OC_Util::checkLoggedIn();
1053
		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...
1054
			\header('Location: ' . \OCP\Util::linkToAbsolute('', 'index.php'));
1055
			exit();
1056
		}
1057
	}
1058
1059
	/**
1060
	 * Check if it is allowed to remember login.
1061
	 *
1062
	 * @note Every app can set 'rememberlogin' to 'false' to disable the remember login feature
1063
	 *
1064
	 * @return bool
1065
	 */
1066 View Code Duplication
	public static function rememberLoginAllowed() {
1067
		$apps = OC_App::getEnabledApps();
1068
1069
		foreach ($apps as $app) {
1070
			$appInfo = OC_App::getAppInfo($app);
1071
			if (isset($appInfo['rememberlogin']) && $appInfo['rememberlogin'] === 'false') {
1072
				return false;
1073
			}
1074
		}
1075
		return true;
1076
	}
1077
1078
	/**
1079
	 * Check if the user has administration privileges, redirects to home if not
1080
	 *
1081
	 * @return null|boolean $groups where the current user is subadmin
1082
	 */
1083
	public static function checkSubAdminUser() {
1084
		OC_Util::checkLoggedIn();
1085
		$hasUserManagementPrivileges = false;
1086
		$userObject = \OC::$server->getUserSession()->getUser();
1087 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...
1088
			//Admin and SubAdmins are allowed to access user management
1089
			$hasUserManagementPrivileges = \OC::$server->getGroupManager()->isAdmin($userObject->getUID())
1090
				|| \OC::$server->getGroupManager()->getSubAdmin()->isSubAdmin($userObject);
1091
		}
1092
		if (!$hasUserManagementPrivileges) {
1093
			\header('Location: ' . \OCP\Util::linkToAbsolute('', 'index.php'));
1094
			exit();
1095
		}
1096
		return true;
1097
	}
1098
1099
	/**
1100
	 * Returns the URL of the default page
1101
	 * based on the system configuration and
1102
	 * the apps visible for the current user
1103
	 *
1104
	 * @return string URL
1105
	 */
1106
	public static function getDefaultPageUrl() {
1107
		$urlGenerator = \OC::$server->getURLGenerator();
1108
		// Deny the redirect if the URL contains a @
1109
		// This prevents unvalidated redirects like ?redirect_url=:[email protected]
1110
		if (isset($_REQUEST['redirect_url']) && \strpos($_REQUEST['redirect_url'], '@') === false) {
1111
			$location = $urlGenerator->getAbsoluteURL(\urldecode($_REQUEST['redirect_url']));
1112
		} else {
1113
			$defaultPage = \OC::$server->getAppConfig()->getValue('core', 'defaultpage');
1114
			if ($defaultPage) {
1115
				$location = $urlGenerator->getAbsoluteURL($defaultPage);
1116
			} else {
1117
				$appId = 'files';
1118
				$defaultApps = \explode(',', \OCP\Config::getSystemValue('defaultapp', 'files'));
1119
				// find the first app that is enabled for the current user
1120
				foreach ($defaultApps as $defaultApp) {
1121
					$defaultApp = OC_App::cleanAppId(\strip_tags($defaultApp));
1122
					if (static::getAppManager()->isEnabledForUser($defaultApp)) {
1123
						$appId = $defaultApp;
1124
						break;
1125
					}
1126
				}
1127
1128
				if (\getenv('front_controller_active') === 'true') {
1129
					$location = $urlGenerator->getAbsoluteURL('/apps/' . $appId . '/');
1130
				} else {
1131
					$location = $urlGenerator->getAbsoluteURL('/index.php/apps/' . $appId . '/');
1132
				}
1133
			}
1134
		}
1135
		return $location;
1136
	}
1137
1138
	/**
1139
	 * Redirect to the user default page
1140
	 *
1141
	 * @return void
1142
	 */
1143
	public static function redirectToDefaultPage() {
1144
		$location = self::getDefaultPageUrl();
1145
		\header('Location: ' . $location);
1146
		exit();
1147
	}
1148
1149
	/**
1150
	 * get an id unique for this instance
1151
	 *
1152
	 * The instance id must begin with a letter. It's typically used as the
1153
	 * session name and appears in a file or directory's 'id' property.
1154
	 *
1155
	 * @return string
1156
	 */
1157
	public static function getInstanceId() {
1158
		$id = \OC::$server->getSystemConfig()->getValue('instanceid', null);
1159
		if ($id === null) {
1160
			// We need to guarantee at least one letter in instanceid so it can be used as the session_name
1161
			$id = 'oc' . \OC::$server->getSecureRandom()->generate(10, \OCP\Security\ISecureRandom::CHAR_LOWER.\OCP\Security\ISecureRandom::CHAR_DIGITS);
1162
			\OC::$server->getSystemConfig()->setValue('instanceid', $id);
1163
		}
1164
		return $id;
1165
	}
1166
1167
	/**
1168
	 * Public function to sanitize HTML
1169
	 *
1170
	 * This function is used to sanitize HTML and should be applied on any
1171
	 * string or array of strings before displaying it on a web page.
1172
	 *
1173
	 * @param string|array $value
1174
	 * @return string|array an array of sanitized strings or a single sanitized string, depends on the input parameter.
1175
	 */
1176
	public static function sanitizeHTML($value) {
1177
		if (\is_array($value)) {
1178
			$value = \array_map(function ($value) {
1179
				return self::sanitizeHTML($value);
1180
			}, $value);
1181
		} else {
1182
			// Specify encoding for PHP<5.4
1183
			$value = \htmlspecialchars((string)$value, ENT_QUOTES, 'UTF-8');
1184
		}
1185
		return $value;
1186
	}
1187
1188
	/**
1189
	 * Public function to encode url parameters
1190
	 *
1191
	 * This function is used to encode path to file before output.
1192
	 * Encoding is done according to RFC 3986 with one exception:
1193
	 * Character '/' is preserved as is.
1194
	 *
1195
	 * @param string $component part of URI to encode
1196
	 * @return string
1197
	 */
1198
	public static function encodePath($component) {
1199
		$encoded = \rawurlencode($component);
1200
		$encoded = \str_replace('%2F', '/', $encoded);
1201
		return $encoded;
1202
	}
1203
1204
	public function createHtaccessTestFile(\OCP\IConfig $config) {
1205
		// php dev server does not support htaccess
1206
		if (\php_sapi_name() === 'cli-server') {
1207
			return false;
1208
		}
1209
		if (\OC::$CLI) {
1210
			return false;
1211
		}
1212
1213
		// testdata
1214
		$fileName = '/htaccesstest.txt';
1215
		// test content containing the string "HTACCESSFAIL"
1216
		$testContent = 'HTACCESSFAIL: This is used for testing whether htaccess is properly enabled to disallow access from the outside. This file can be safely removed.';
1217
1218
		// creating a test file
1219
		$testFile = $config->getSystemValue('datadirectory', OC::$SERVERROOT . '/data') . '/' . $fileName;
1220
1221
		if (\file_exists($testFile)) {// already running this test, possible recursive call
1222
			return false;
1223
		}
1224
1225
		$fp = @\fopen($testFile, 'w');
1226
		if (!$fp) {
1227
			throw new OC\HintException('Can\'t create test file to check for working .htaccess file.',
1228
				'Make sure it is possible for the webserver to write to ' . $testFile);
1229
		}
1230
		\fwrite($fp, $testContent);
1231
		\fclose($fp);
1232
	}
1233
1234
	/**
1235
	 * Check if the setlocal call does not work. This can happen if the right
1236
	 * local packages are not available on the server.
1237
	 *
1238
	 * @return bool
1239
	 */
1240
	public static function isSetLocaleWorking() {
1241
		\Patchwork\Utf8\Bootup::initLocale();
1242
		if (\basename('§') === '') {
1243
			return false;
1244
		}
1245
		return true;
1246
	}
1247
1248
	/**
1249
	 * Check if it's possible to get the inline annotations
1250
	 *
1251
	 * @return bool
1252
	 */
1253
	public static function isAnnotationsWorking() {
1254
		$reflection = new \ReflectionMethod(__METHOD__);
1255
		$docs = $reflection->getDocComment();
1256
1257
		return (\is_string($docs) && \strlen($docs) > 50);
1258
	}
1259
1260
	/**
1261
	 * Check if the PHP module fileinfo is loaded.
1262
	 *
1263
	 * @return bool
1264
	 */
1265
	public static function fileInfoLoaded() {
1266
		return \function_exists('finfo_open');
1267
	}
1268
1269
	/**
1270
	 * clear all levels of output buffering
1271
	 *
1272
	 * @return void
1273
	 */
1274
	public static function obEnd() {
1275
		while (\ob_get_level()) {
1276
			\ob_end_clean();
1277
		}
1278
	}
1279
1280
	/**
1281
	 * Checks whether the server is running on the given OS type
1282
	 *
1283
	 * @param string $osType linux|mac|bsd etc
1284
	 * @return bool true if running on that OS type, false otherwise
1285
	 */
1286
	public static function runningOn($osType) {
1287
		$osType = \strtolower($osType) === 'mac' ? 'darwin' : \strtolower($osType);
1288
1289
		if ($osType === 'bsd') {
1290
			return (\strpos(\strtolower(PHP_OS), $osType) !== false);
1291
		} else {
1292
			return (\strtolower(\substr(PHP_OS, 0, \strlen($osType))) === $osType);
1293
		}
1294
	}
1295
1296
	/**
1297
	 * Checks whether server is running on HHVM
1298
	 *
1299
	 * @return bool True if running on HHVM, false otherwise
1300
	 */
1301
	public static function runningOnHhvm() {
1302
		return \defined('HHVM_VERSION');
1303
	}
1304
1305
	/**
1306
	 * Handles the case that there may not be a theme, then check if a "default"
1307
	 * theme exists and take that one
1308
	 *
1309
	 * @return \OCP\Theme\ITheme the theme
1310
	 */
1311
	public static function getTheme() {
1312
		/** @var \OCP\Theme\IThemeService $themeService */
1313
		$themeService = \OC::$server->query('ThemeService');
1314
		return $themeService->getTheme();
1315
	}
1316
1317
	/**
1318
	 * Clear a single file from the opcode cache
1319
	 * This is useful for writing to the config file
1320
	 * in case the opcode cache does not re-validate files
1321
	 * Returns true if successful, false if unsuccessful:
1322
	 * caller should fall back on clearing the entire cache
1323
	 * with clearOpcodeCache() if unsuccessful
1324
	 *
1325
	 * @param string $path the path of the file to clear from the cache
1326
	 * @return bool true if underlying function returns true, otherwise false
1327
	 */
1328
	public static function deleteFromOpcodeCache($path) {
1329
		$ret = false;
1330
		if ($path) {
1331
			// APC >= 3.1.1
1332
			if (\function_exists('apc_delete_file')) {
1333
				$ret = @apc_delete_file($path);
1334
			}
1335
			// Zend OpCache >= 7.0.0, PHP >= 5.5.0
1336
			if (\function_exists('opcache_invalidate')) {
1337
				$ret = \opcache_invalidate($path);
1338
			}
1339
		}
1340
		return $ret;
1341
	}
1342
1343
	/**
1344
	 * Clear the opcode cache if one exists
1345
	 * This is necessary for writing to the config file
1346
	 * in case the opcode cache does not re-validate files
1347
	 *
1348
	 * @return void
1349
	 */
1350
	public static function clearOpcodeCache() {
1351
		// APC
1352
		if (\function_exists('apc_clear_cache')) {
1353
			\apc_clear_cache();
1354
		}
1355
		// Zend Opcache
1356
		if (\function_exists('accelerator_reset')) {
1357
			\accelerator_reset();
1358
		}
1359
		// XCache
1360
		if (\function_exists('xcache_clear_cache')) {
1361
			if (\OC::$server->getIniWrapper()->getBool('xcache.admin.enable_auth')) {
1362
				\OCP\Util::writeLog('core', 'XCache opcode cache will not be cleared because "xcache.admin.enable_auth" is enabled.', \OCP\Util::WARN);
1363
			} else {
1364
				@\xcache_clear_cache(XC_TYPE_PHP, 0);
1365
			}
1366
		}
1367
		// Opcache (PHP >= 5.5)
1368
		if (\function_exists('opcache_reset')) {
1369
			\opcache_reset();
1370
		}
1371
	}
1372
1373
	/**
1374
	 * Normalize a unicode string
1375
	 *
1376
	 * @param string $value a not normalized string
1377
	 * @return bool|string
1378
	 */
1379
	public static function normalizeUnicode($value) {
1380
		if (Normalizer::isNormalized($value)) {
1381
			return $value;
1382
		}
1383
1384
		$normalizedValue = Normalizer::normalize($value);
1385
		if ($normalizedValue === null || $normalizedValue === false) {
1386
			\OC::$server->getLogger()->warning('normalizing failed for "' . $value . '"', ['app' => 'core']);
1387
			return $value;
1388
		}
1389
1390
		return $normalizedValue;
1391
	}
1392
1393
	/**
1394
	 * @param boolean|string $file
1395
	 * @return string
1396
	 */
1397
	public static function basename($file) {
1398
		$file = \rtrim($file, '/');
1399
		$t = \explode('/', $file);
1400
		return \array_pop($t);
1401
	}
1402
1403
	/**
1404
	 * A human readable string is generated based on version, channel and build number
1405
	 *
1406
	 * @return string
1407
	 */
1408
	public static function getHumanVersion() {
1409
		$version = OC_Util::getVersionString() . ' (' . OC_Util::getChannel() . ')';
1410
		$build = OC_Util::getBuild();
1411
		if (!empty($build) and OC_Util::getChannel() === 'daily') {
1412
			$version .= ' Build:' . $build;
1413
		}
1414
		return $version;
1415
	}
1416
1417
	/**
1418
	 * Returns whether the given file name is valid
1419
	 *
1420
	 * @param string $file file name to check
1421
	 * @return bool true if the file name is valid, false otherwise
1422
	 * @deprecated use \OC\Files\View::verifyPath()
1423
	 */
1424
	public static function isValidFileName($file) {
1425
		$trimmed = \trim($file);
1426
		if ($trimmed === '') {
1427
			return false;
1428
		}
1429
		if (\OC\Files\Filesystem::isIgnoredDir($trimmed)) {
1430
			return false;
1431
		}
1432
1433
		// detect part files
1434
		if (\preg_match('/' . \OCP\Files\FileInfo::BLACKLIST_FILES_REGEX . '/', $trimmed) !== 0) {
1435
			return false;
1436
		}
1437
1438
		foreach (\str_split($trimmed) as $char) {
1439
			if (\strpos(\OCP\Constants::FILENAME_INVALID_CHARS, $char) !== false) {
1440
				return false;
1441
			}
1442
		}
1443
		return true;
1444
	}
1445
1446
	/**
1447
	 * Check whether the instance needs to perform an upgrade,
1448
	 * either when the core version is higher or any app requires
1449
	 * an upgrade.
1450
	 *
1451
	 * @param \OCP\IConfig $config
1452
	 * @return bool whether the core or any app needs an upgrade
1453
	 * @throws \OC\HintException When the upgrade from the given version is not allowed
1454
	 */
1455
	public static function needUpgrade(\OCP\IConfig $config) {
1456
		if ($config->getSystemValue('installed', false)) {
1457
			$installedVersion = $config->getSystemValue('version', '0.0.0');
1458
			$currentVersion = \implode('.', \OCP\Util::getVersion());
1459
			$versionDiff = \version_compare($currentVersion, $installedVersion);
1460
			if ($versionDiff > 0) {
1461
				return true;
1462
			} elseif ($config->getSystemValue('debug', false) && $versionDiff < 0) {
1463
				// downgrade with debug
1464
				$installedMajor = \explode('.', $installedVersion);
1465
				$installedMajor = $installedMajor[0] . '.' . $installedMajor[1];
1466
				$currentMajor = \explode('.', $currentVersion);
1467
				$currentMajor = $currentMajor[0] . '.' . $currentMajor[1];
1468
				if ($installedMajor === $currentMajor) {
1469
					// Same major, allow downgrade for developers
1470
					return true;
1471
				} else {
1472
					// downgrade attempt, throw exception
1473
					throw new \OC\HintException('Downgrading is not supported and is likely to cause unpredictable issues (from ' . $installedVersion . ' to ' . $currentVersion . ')');
1474
				}
1475
			} elseif ($versionDiff < 0) {
1476
				// downgrade attempt, throw exception
1477
				throw new \OC\HintException('Downgrading is not supported and is likely to cause unpredictable issues (from ' . $installedVersion . ' to ' . $currentVersion . ')');
1478
			}
1479
1480
			// also check for upgrades for apps (independently from the user)
1481
			$apps = \OC_App::getEnabledApps(false, true);
1482
			$shouldUpgrade = false;
1483
			foreach ($apps as $app) {
1484
				if (\OC_App::shouldUpgrade($app)) {
1485
					$shouldUpgrade = true;
1486
					break;
1487
				}
1488
			}
1489
			return $shouldUpgrade;
1490
		} else {
1491
			return false;
1492
		}
1493
	}
1494
}
1495