Completed
Pull Request — master (#31285)
by Thomas
60:03 queued 48:12
created

OC_Util::isDefaultExpireDateEnforced()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 10
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

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