Completed
Pull Request — master (#31816)
by Phil
15:27
created

OC_Util::createHtaccessTestFile()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 29
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

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