Completed
Push — master ( 75eb2d...9b96a1 )
by
unknown
28:16 queued 12:57
created

OC_Util::isSharingDisabledForUser()   A

Complexity

Conditions 4
Paths 5

Size

Total Lines 18

Duplication

Lines 5
Ratio 27.78 %

Importance

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

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

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

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

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

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

    return array();
}

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

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

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

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

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

    return array();
}

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

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

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

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

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

    return array();
}

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

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

Loading history...
285
			\OC::$server->getEventLogger()->end('setup_fs');
286
			return false;
287
		}
288
289
		//if we aren't logged in, there is no use to set up the filesystem
290
		if ($user != "") {
291
			$userDir = '/' . $user . '/files';
292
293
			//jail the user into his "home" directory
294
			\OC\Files\Filesystem::init($user, $userDir);
295
296
			OC_Hook::emit('OC_Filesystem', 'setup', ['user' => $user, 'user_dir' => $userDir]);
297
		}
298
		\OC::$server->getEventLogger()->end('setup_fs');
299
		return true;
300
	}
301
302
	/**
303
	 * check if a password is required for each public link.
304
	 * This is deprecated due to not reflecting all the possibilities now. Falling back to
305
	 * enforce password for read-only links. Note that read & write or write-only options won't
306
	 * be considered here
307
	 *
308
	 * @return boolean
309
	 * @deprecated
310
	 */
311
	public static function isPublicLinkPasswordRequired() {
312
		$appConfig = \OC::$server->getAppConfig();
313
		$enforcePassword = $appConfig->getValue('core', 'shareapi_enforce_links_password_read_only', 'no');
314
		return ($enforcePassword === 'yes') ? true : false;
315
	}
316
317
	/**
318
	 * check if share API enforces a default expire date
319
	 *
320
	 * @return boolean
321
	 */
322
	public static function isDefaultExpireDateEnforced() {
323
		$isDefaultExpireDateEnabled = \OCP\Config::getAppValue('core', 'shareapi_default_expire_date', 'no');
324
		$enforceDefaultExpireDate = false;
325
		if ($isDefaultExpireDateEnabled === 'yes') {
326
			$value = \OCP\Config::getAppValue('core', 'shareapi_enforce_expire_date', 'no');
327
			$enforceDefaultExpireDate = ($value === 'yes') ? true : false;
328
		}
329
330
		return $enforceDefaultExpireDate;
331
	}
332
333
	/**
334
	 * Get the quota of a user
335
	 *
336
	 * @param string|IUser $userId
337
	 * @return int Quota bytes
338
	 */
339
	public static function getUserQuota($userId) {
340
		if ($userId instanceof IUser) {
341
			$user = $userId;
342
		} else {
343
			$user = \OC::$server->getUserManager()->get($userId);
344
		}
345
		if ($user === null) {
346
			return \OCP\Files\FileInfo::SPACE_UNLIMITED;
347
		}
348
		$userQuota = $user->getQuota();
349
		if ($userQuota === null || $userQuota === 'default') {
350
			$userQuota = \OC::$server->getConfig()->getAppValue('files', 'default_quota', 'none');
351
		}
352
		if ($userQuota === null || $userQuota === 'none') {
353
			return \OCP\Files\FileInfo::SPACE_UNLIMITED;
354
		}
355
		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 355 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...
356
	}
357
358
	/**
359
	 * copies the skeleton to the users /files
360
	 *
361
	 * @param String $userId
362
	 * @param \OCP\Files\Folder $userDirectory
363
	 * @throws \OC\HintException
364
	 */
365
	public static function copySkeleton($userId, \OCP\Files\Folder $userDirectory) {
366
		$skeletonDirectory = \OCP\Config::getSystemValue('skeletondirectory', \OC::$SERVERROOT . '/core/skeleton');
367
368
		if (!\is_dir($skeletonDirectory)) {
369
			throw new \OC\HintException('The skeleton folder '.$skeletonDirectory.' is not accessible');
370
		}
371
372
		if (!empty($skeletonDirectory)) {
373
			\OCP\Util::writeLog(
374
				'files_skeleton',
375
				'copying skeleton for '.$userId.' from '.$skeletonDirectory.' to '.$userDirectory->getFullPath('/'),
376
				\OCP\Util::DEBUG
377
			);
378
			self::copyr($skeletonDirectory, $userDirectory);
379
			// update the file cache
380
			$userDirectory->getStorage()->getScanner()->scan('', \OC\Files\Cache\Scanner::SCAN_RECURSIVE);
381
		}
382
	}
383
384
	/**
385
	 * copies a directory recursively by using streams
386
	 *
387
	 * @param string $source
388
	 * @param \OCP\Files\Folder $target
389
	 * @return void
390
	 * @throws NoReadAccessException
391
	 */
392
	public static function copyr($source, \OCP\Files\Folder $target) {
393
		$dir = @\opendir($source);
394
		if ($dir === false) {
395
			throw new NoReadAccessException('No read permission for folder ' . $source);
396
		}
397
		while (($file = \readdir($dir)) !== false) {
398
			if (!\OC\Files\Filesystem::isIgnoredDir($file)) {
399
				if (\is_dir($source . '/' . $file)) {
400
					$child = $target->newFolder($file);
401
					self::copyr($source . '/' . $file, $child);
402
				} else {
403
					$sourceFileHandle = @\fopen($source . '/' . $file, 'r');
404
					if ($sourceFileHandle === false) {
405
						throw new NoReadAccessException('No read permission for file ' . $file);
406
					}
407
					$child = $target->newFile($file);
408
					$targetFileHandle = $child->fopen('w');
409
					\stream_copy_to_stream($sourceFileHandle, $targetFileHandle);
410
					\fclose($targetFileHandle);
411
					\fclose($sourceFileHandle);
412
413
					// update cache sizes
414
					$cache = $target->getStorage()->getCache();
415
					if ($cache instanceof \OC\Files\Cache\Cache) {
416
						$cache->correctFolderSize($child->getInternalPath());
417
					}
418
				}
419
			}
420
		}
421
		\closedir($dir);
422
	}
423
424
	/**
425
	 * @return void
426
	 */
427
	public static function tearDownFS() {
428
		\OC\Files\Filesystem::tearDown();
429
		self::$fsSetup = false;
430
		self::$rootMounted = false;
431
	}
432
433
	/**
434
	 * get the current installed version of ownCloud
435
	 *
436
	 * @return array
437
	 */
438
	public static function getVersion() {
439
		OC_Util::loadVersion();
440
		return self::$version['OC_Version'];
441
	}
442
443
	/**
444
	 * get the current installed version string of ownCloud
445
	 *
446
	 * @return string
447
	 */
448
	public static function getVersionString() {
449
		OC_Util::loadVersion();
450
		return self::$version['OC_VersionString'];
451
	}
452
453
	/**
454
	 * @description get the current installed edition of ownCloud.
455
	 * There is the community edition that returns "Community" and
456
	 * the enterprise edition that returns "Enterprise".
457
	 * @return string
458
	 */
459
	public static function getEditionString() {
460
		if (OC_App::isEnabled('enterprise_key')) {
461
			return OC_Util::EDITION_ENTERPRISE;
462
		} else {
463
			return OC_Util::EDITION_COMMUNITY;
464
		}
465
	}
466
467
	/**
468
	 * @description get the update channel of the current installed of ownCloud.
469
	 * @return string
470
	 */
471
	public static function getChannel() {
472
		OC_Util::loadVersion();
473
		return self::$version['OC_Channel'];
474
	}
475
476
	/**
477
	 * @description get the build number of the current installed of ownCloud.
478
	 * @return string
479
	 */
480
	public static function getBuild() {
481
		OC_Util::loadVersion();
482
		return self::$version['OC_Build'];
483
	}
484
485
	/**
486
	 * @description load the version.php into the session as cache
487
	 */
488
	private static function loadVersion() {
489
		require __DIR__ . '/../../../version.php';
490
		/** @var $OC_Version string */
491
		/** @var $OC_VersionString string */
492
		/** @var $OC_Build string */
493
		/** @var $OC_Channel string */
494
		self::$version = [
495
			'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...
496
			'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...
497
			'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...
498
			'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...
499
		];
500
501
		// Allow overriding update channel
502
503
		if (\OC::$server->getSystemConfig()->getValue('installed', false)) {
504
			$channel = \OC::$server->getConfig()->getAppValue('core', 'OC_Channel', $OC_Channel);
505
			self::$version['OC_Channel'] = $channel;
506
		}
507
	}
508
509
	/**
510
	 * generates a path for JS/CSS files. If no application is provided it will create the path for core.
511
	 *
512
	 * @param string $application application to get the files from
513
	 * @param string $directory directory within this application (css, js, vendor, etc)
514
	 * @param string $file the file inside of the above folder
515
	 * @return string the path
516
	 */
517
	private static function generatePath($application, $directory, $file) {
518
		if ($file === null) {
519
			$file = $application;
520
			$application = "";
521
		}
522
		if (!empty($application)) {
523
			return "$application/$directory/$file";
524
		} else {
525
			return "$directory/$file";
526
		}
527
	}
528
529
	/**
530
	 * add a javascript file
531
	 *
532
	 * @param string $application application id
533
	 * @param string|null $file filename
534
	 * @param bool $prepend prepend the Script to the beginning of the list
535
	 * @return void
536
	 */
537
	public static function addScript($application, $file = null, $prepend = false) {
538
		$path = OC_Util::generatePath($application, 'js', $file);
539
540
		// core js files need separate handling
541
		if ($application !== 'core' && $file !== null) {
542
			self::addTranslations($application);
543
		}
544
		self::addExternalResource($application, $prepend, $path, "script");
545
	}
546
547
	/**
548
	 * add a javascript file from the vendor sub folder
549
	 *
550
	 * @param string $application application id
551
	 * @param string|null $file filename
552
	 * @param bool $prepend prepend the Script to the beginning of the list
553
	 * @return void
554
	 */
555
	public static function addVendorScript($application, $file = null, $prepend = false) {
556
		$path = OC_Util::generatePath($application, 'vendor', $file);
557
		self::addExternalResource($application, $prepend, $path, "script");
558
	}
559
560
	/**
561
	 * add a translation JS file
562
	 *
563
	 * @param string $application application id
564
	 * @param string $languageCode language code, defaults to the current language
565
	 * @param bool $prepend prepend the Script to the beginning of the list
566
	 */
567
	public static function addTranslations($application, $languageCode = null, $prepend = false) {
568
		if ($languageCode === null) {
569
			$languageCode = \OC::$server->getL10NFactory()->findLanguage($application);
570
		}
571
		if (!empty($application)) {
572
			$path = "$application/l10n/$languageCode";
573
		} else {
574
			$path = "l10n/$languageCode";
575
		}
576
		self::addExternalResource($application, $prepend, $path, "script");
577
	}
578
579
	/**
580
	 * add a css file
581
	 *
582
	 * @param string $application application id
583
	 * @param string|null $file filename
584
	 * @param bool $prepend prepend the Style to the beginning of the list
585
	 * @return void
586
	 */
587
	public static function addStyle($application, $file = null, $prepend = false) {
588
		$path = OC_Util::generatePath($application, 'css', $file);
589
		self::addExternalResource($application, $prepend, $path, "style");
590
	}
591
592
	/**
593
	 * add a css file from the vendor sub folder
594
	 *
595
	 * @param string $application application id
596
	 * @param string|null $file filename
597
	 * @param bool $prepend prepend the Style to the beginning of the list
598
	 * @return void
599
	 */
600
	public static function addVendorStyle($application, $file = null, $prepend = false) {
601
		$path = OC_Util::generatePath($application, 'vendor', $file);
602
		self::addExternalResource($application, $prepend, $path, "style");
603
	}
604
605
	/**
606
	 * add an external resource css/js file
607
	 *
608
	 * @param string $application application id
609
	 * @param bool $prepend prepend the file to the beginning of the list
610
	 * @param string $path
611
	 * @param string $type (script or style)
612
	 * @return void
613
	 */
614
	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...
615
		if ($type === "style") {
616 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...
617
				if ($prepend === true) {
618
					\array_unshift(self::$styles, $path);
619
				} else {
620
					self::$styles[] = $path;
621
				}
622
			}
623 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...
624
			if (!\in_array($path, self::$scripts)) {
625
				if ($prepend === true) {
626
					\array_unshift(self::$scripts, $path);
627
				} else {
628
					self::$scripts [] = $path;
629
				}
630
			}
631
		}
632
	}
633
634
	/**
635
	 * Add a custom element to the header
636
	 * If $text is null then the element will be written as empty element.
637
	 * So use "" to get a closing tag.
638
	 * @param string $tag tag name of the element
639
	 * @param array $attributes array of attributes for the element
640
	 * @param string $text the text content for the element
641
	 */
642 View Code Duplication
	public static function addHeader($tag, $attributes, $text=null) {
643
		self::$headers[] = [
644
			'tag' => $tag,
645
			'attributes' => $attributes,
646
			'text' => $text
647
		];
648
	}
649
650
	/**
651
	 * formats a timestamp in the "right" way
652
	 *
653
	 * @param int $timestamp
654
	 * @param bool $dateOnly option to omit time from the result
655
	 * @param DateTimeZone|string $timeZone where the given timestamp shall be converted to
656
	 * @return string timestamp
657
	 *
658
	 * @deprecated Use \OC::$server->query('DateTimeFormatter') instead
659
	 */
660
	public static function formatDate($timestamp, $dateOnly = false, $timeZone = null) {
661
		if ($timeZone !== null && !$timeZone instanceof \DateTimeZone) {
662
			$timeZone = new \DateTimeZone($timeZone);
663
		}
664
665
		/** @var \OC\DateTimeFormatter $formatter */
666
		$formatter = \OC::$server->query('DateTimeFormatter');
667
		if ($dateOnly) {
668
			return $formatter->formatDate($timestamp, 'long', $timeZone);
669
		}
670
		return $formatter->formatDateTime($timestamp, 'long', 'long', $timeZone);
671
	}
672
673
	/**
674
	 * check if the current server configuration is suitable for ownCloud
675
	 *
676
	 * @param \OCP\IConfig $config
677
	 * @return array arrays with error messages and hints
678
	 */
679
	public static function checkServer(\OCP\IConfig $config) {
680
		$l = \OC::$server->getL10N('lib');
681
		$errors = [];
682
		$CONFIG_DATADIRECTORY = $config->getSystemValue('datadirectory', OC::$SERVERROOT . '/data');
683
684
		if (!self::needUpgrade($config) && $config->getSystemValue('installed', false)) {
685
			// this check needs to be done every time
686
			$errors = self::checkDataDirectoryValidity($CONFIG_DATADIRECTORY);
687
		}
688
689
		// Assume that if checkServer() succeeded before in this session, then all is fine.
690
		if (\OC::$server->getSession()->exists('checkServer_succeeded') && \OC::$server->getSession()->get('checkServer_succeeded')) {
691
			return $errors;
692
		}
693
694
		$webServerRestart = false;
695
		$setup = new \OC\Setup($config, \OC::$server->getIniWrapper(), \OC::$server->getL10N('lib'),
696
			new \OC_Defaults(), \OC::$server->getLogger(), \OC::$server->getSecureRandom());
697
698
		$urlGenerator = \OC::$server->getURLGenerator();
699
700
		$availableDatabases = $setup->getSupportedDatabases();
701
		if (empty($availableDatabases)) {
702
			$errors[] = [
703
				'error' => $l->t('No database drivers (sqlite, mysql, or postgresql) installed.'),
704
				'hint' => '' //TODO: sane hint
705
			];
706
			$webServerRestart = true;
707
		}
708
709
		// Check if config folder is writable.
710
		if (!\OC::$server->getConfig()->isSystemConfigReadOnly()) {
711
			if (!\is_writable(OC::$configDir) or !\is_readable(OC::$configDir)) {
712
				$errors[] = [
713
					'error' => $l->t('Cannot write into "config" directory'),
714
					'hint' => $l->t('This can usually be fixed by '
715
						. '%sgiving the webserver write access to the config directory%s.',
716
						['<a href="' . $urlGenerator->linkToDocs('admin-dir_permissions') . '" target="_blank" rel="noreferrer">', '</a>'])
717
				];
718
			}
719
		}
720
721
		// Create root dir.
722
		if ($config->getSystemValue('installed', false)) {
723
			if (!\is_dir($CONFIG_DATADIRECTORY)) {
724
				$success = @\mkdir($CONFIG_DATADIRECTORY);
725
				if ($success) {
726
					$errors = \array_merge($errors, self::checkDataDirectoryPermissions($CONFIG_DATADIRECTORY));
727
				} else {
728
					$errors[] = [
729
						'error' => $l->t('Cannot create "data" directory'),
730
						'hint' => $l->t('This can usually be fixed by '
731
							. '<a href="%s" target="_blank" rel="noreferrer">giving the webserver write access to the root directory</a>.',
732
							[$urlGenerator->linkToDocs('admin-dir_permissions')])
733
					];
734
				}
735
			} elseif (!\is_writable($CONFIG_DATADIRECTORY) or !\is_readable($CONFIG_DATADIRECTORY)) {
736
				//common hint for all file permissions error messages
737
				$permissionsHint = $l->t('Permissions can usually be fixed by '
738
					. '%sgiving the webserver write access to the root directory%s.',
739
					['<a href="' . $urlGenerator->linkToDocs('admin-dir_permissions') . '" target="_blank" rel="noreferrer">', '</a>']);
740
				$errors[] = [
741
					'error' => 'Your Data directory is not writable by ownCloud',
742
					'hint' => $permissionsHint
743
				];
744
			} else {
745
				$errors = \array_merge($errors, self::checkDataDirectoryPermissions($CONFIG_DATADIRECTORY));
746
			}
747
		}
748
749
		if (!OC_Util::isSetLocaleWorking()) {
750
			$errors[] = [
751
				'error' => $l->t('Setting locale to %s failed',
752
					['en_US.UTF-8/fr_FR.UTF-8/es_ES.UTF-8/de_DE.UTF-8/ru_RU.UTF-8/'
753
						. 'pt_BR.UTF-8/it_IT.UTF-8/ja_JP.UTF-8/zh_CN.UTF-8']),
754
				'hint' => $l->t('Please install one of these locales on your system and restart your webserver.')
755
			];
756
		}
757
758
		// Contains the dependencies that should be checked against
759
		// classes = class_exists
760
		// functions = function_exists
761
		// defined = defined
762
		// ini = ini_get
763
		// If the dependency is not found the missing module name is shown to the EndUser
764
		// When adding new checks always verify that they pass on Travis as well
765
		// for ini settings, see https://github.com/owncloud/administration/blob/master/travis-ci/custom.ini
766
		$dependencies = [
767
			'classes' => [
768
				'ZipArchive' => 'zip',
769
				'DOMDocument' => 'dom',
770
				'XMLWriter' => 'XMLWriter',
771
				'XMLReader' => 'XMLReader',
772
				'Collator' => 'intl',
773
			],
774
			'functions' => [
775
				'xml_parser_create' => 'libxml',
776
				'mb_strcut' => 'mb multibyte',
777
				'ctype_digit' => 'ctype',
778
				'json_encode' => 'JSON',
779
				'gd_info' => 'GD',
780
				'gzencode' => 'zlib',
781
				'iconv' => 'iconv',
782
				'simplexml_load_string' => 'SimpleXML',
783
				'hash' => 'HASH Message Digest Framework',
784
				'curl_init' => 'cURL',
785
			],
786
			'defined' => [
787
				'PDO::ATTR_DRIVER_NAME' => 'PDO'
788
			],
789
			'ini' => [
790
				'default_charset' => 'UTF-8',
791
			],
792
		];
793
		$missingDependencies = [];
794
		$invalidIniSettings = [];
795
		$moduleHint = $l->t('Please ask your server administrator to install the module.');
796
797
		/**
798
		 * FIXME: The dependency check does not work properly on HHVM on the moment
799
		 *        and prevents installation. Once HHVM is more compatible with our
800
		 *        approach to check for these values we should re-enable those
801
		 *        checks.
802
		 */
803
		$iniWrapper = \OC::$server->getIniWrapper();
804
		if (!self::runningOnHhvm()) {
805
			foreach ($dependencies['classes'] as $class => $module) {
806
				if (!\class_exists($class)) {
807
					$missingDependencies[] = $module;
808
				}
809
			}
810
			foreach ($dependencies['functions'] as $function => $module) {
811
				if (!\function_exists($function)) {
812
					$missingDependencies[] = $module;
813
				}
814
			}
815
			foreach ($dependencies['defined'] as $defined => $module) {
816
				if (!\defined($defined)) {
817
					$missingDependencies[] = $module;
818
				}
819
			}
820
			foreach ($dependencies['ini'] as $setting => $expected) {
821
				if (\is_bool($expected)) {
822
					if ($iniWrapper->getBool($setting) !== $expected) {
823
						$invalidIniSettings[] = [$setting, $expected];
824
					}
825
				}
826
				if (\is_int($expected)) {
827
					if ($iniWrapper->getNumeric($setting) !== $expected) {
828
						$invalidIniSettings[] = [$setting, $expected];
829
					}
830
				}
831
				if (\is_string($expected)) {
832
					if (\strtolower($iniWrapper->getString($setting)) !== \strtolower($expected)) {
833
						$invalidIniSettings[] = [$setting, $expected];
834
					}
835
				}
836
			}
837
		}
838
839
		foreach ($missingDependencies as $missingDependency) {
840
			$errors[] = [
841
				'error' => $l->t('PHP module %s not installed.', [$missingDependency]),
842
				'hint' => $moduleHint
843
			];
844
			$webServerRestart = true;
845
		}
846
		foreach ($invalidIniSettings as $setting) {
847
			if (\is_bool($setting[1])) {
848
				$setting[1] = ($setting[1]) ? 'on' : 'off';
849
			}
850
			$errors[] = [
851
				'error' => $l->t('PHP setting "%s" is not set to "%s".', [$setting[0], \var_export($setting[1], true)]),
852
				'hint' =>  $l->t('Adjusting this setting in php.ini will make ownCloud run again')
853
			];
854
			$webServerRestart = true;
855
		}
856
857
		/**
858
		 * The mbstring.func_overload check can only be performed if the mbstring
859
		 * module is installed as it will return null if the checking setting is
860
		 * not available and thus a check on the boolean value fails.
861
		 *
862
		 * TODO: Should probably be implemented in the above generic dependency
863
		 *       check somehow in the long-term.
864
		 */
865
		if ($iniWrapper->getBool('mbstring.func_overload') !== null &&
866
			$iniWrapper->getBool('mbstring.func_overload') === true) {
867
			$errors[] = [
868
				'error' => $l->t('mbstring.func_overload is set to "%s" instead of the expected value "0"', [$iniWrapper->getString('mbstring.func_overload')]),
869
				'hint' => $l->t('To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini')
870
			];
871
		}
872
873
		if (\function_exists('xml_parser_create') &&
874
			\version_compare('2.7.0', LIBXML_DOTTED_VERSION) === 1) {
875
			$errors[] = [
876
				'error' => $l->t('libxml2 2.7.0 is at least required. Currently %s is installed.', [LIBXML_DOTTED_VERSION]),
877
				'hint' => $l->t('To fix this issue update your libxml2 version and restart your web server.')
878
			];
879
		}
880
881
		if (!self::isAnnotationsWorking()) {
882
			$errors[] = [
883
				'error' => $l->t('PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible.'),
884
				'hint' => $l->t('This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator.')
885
			];
886
		}
887
888
		if (!\OC::$CLI && $webServerRestart) {
889
			$errors[] = [
890
				'error' => $l->t('PHP modules have been installed, but they are still listed as missing?'),
891
				'hint' => $l->t('Please ask your server administrator to restart the web server.')
892
			];
893
		}
894
895
		$errors = \array_merge($errors, self::checkDatabaseVersion());
896
897
		// Cache the result of this function
898
		\OC::$server->getSession()->set('checkServer_succeeded', \count($errors) == 0);
899
900
		return $errors;
901
	}
902
903
	/**
904
	 * Check the database version
905
	 *
906
	 * @return array errors array
907
	 */
908
	public static function checkDatabaseVersion() {
909
		$l = \OC::$server->getL10N('lib');
910
		$errors = [];
911
		$dbType = \OC::$server->getSystemConfig()->getValue('dbtype', 'sqlite');
912
		if ($dbType === 'pgsql') {
913
			// check PostgreSQL version
914
			try {
915
				$result = \OC_DB::executeAudited('SHOW SERVER_VERSION');
916
				$data = $result->fetchRow();
917
				if (isset($data['server_version'])) {
918
					$version = $data['server_version'];
919
					if (\version_compare($version, '9.0.0', '<')) {
920
						$errors[] = [
921
							'error' => $l->t('PostgreSQL >= 9 required'),
922
							'hint' => $l->t('Please upgrade your database version')
923
						];
924
					}
925
				}
926
			} catch (\Doctrine\DBAL\DBALException $e) {
927
				$logger = \OC::$server->getLogger();
928
				$logger->warning('Error occurred while checking PostgreSQL version, assuming >= 9');
929
				$logger->logException($e);
930
			}
931
		}
932
		return $errors;
933
	}
934
935
	/**
936
	 * Check for correct file permissions of data directory
937
	 *
938
	 * @param string $dataDirectory
939
	 * @return array arrays with error messages and hints
940
	 */
941
	public static function checkDataDirectoryPermissions($dataDirectory) {
942
		$l = \OC::$server->getL10N('lib');
943
		$errors = [];
944
		$permissionsModHint = $l->t('Please change the permissions to 0770 so that the directory'
945
			. ' cannot be listed by other users.');
946
		$perms = \substr(\decoct(@\fileperms($dataDirectory)), -3);
947
		if (\substr($perms, -1) != '0') {
948
			\chmod($dataDirectory, 0770);
949
			\clearstatcache();
950
			$perms = \substr(\decoct(@\fileperms($dataDirectory)), -3);
951
			if (\substr($perms, 2, 1) != '0') {
952
				$errors[] = [
953
					'error' => $l->t('Your Data directory is readable by other users'),
954
					'hint' => $permissionsModHint
955
				];
956
			}
957
		}
958
		return $errors;
959
	}
960
961
	/**
962
	 * Check that the data directory exists and is valid by
963
	 * checking the existence of the ".ocdata" file.
964
	 *
965
	 * @param string $dataDirectory data directory path
966
	 * @return array errors found
967
	 */
968
	public static function checkDataDirectoryValidity($dataDirectory) {
969
		$l = \OC::$server->getL10N('lib');
970
		$errors = [];
971 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...
972
			$errors[] = [
973
				'error' => $l->t('Your Data directory must be an absolute path'),
974
				'hint' => $l->t('Check the value of "datadirectory" in your configuration')
975
			];
976
		}
977 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...
978
			$errors[] = [
979
				'error' => $l->t('Your Data directory  is invalid'),
980
				'hint' => $l->t('Please check that the data directory contains a file' .
981
					' ".ocdata" in its root.')
982
			];
983
		}
984
		return $errors;
985
	}
986
987
	/**
988
	 * Check if the user is logged in, redirects to home if not. With
989
	 * redirect URL parameter to the request URI.
990
	 *
991
	 * @return void
992
	 */
993
	public static function checkLoggedIn() {
994
		// Check if we are a user
995
		$userSession = \OC::$server->getUserSession();
996
		if (!$userSession->isLoggedIn()) {
997
			\header('Location: ' . \OC::$server->getURLGenerator()->linkToRoute(
998
						'core.login.showLoginForm',
999
						[
1000
							'redirect_url' => \urlencode(\OC::$server->getRequest()->getRequestUri()),
1001
						]
1002
					)
1003
			);
1004
			exit();
1005
		}
1006
		// Redirect to index page if 2FA challenge was not solved yet
1007
		if (\OC::$server->getTwoFactorAuthManager()->needsSecondFactor()) {
1008
			\header('Location: ' . \OCP\Util::linkToAbsolute('', 'index.php'));
1009
			exit();
1010
		}
1011
		// Redirect to index page if any IAuthModule check fails
1012
		try {
1013
			\OC::$server->getAccountModuleManager()->check($userSession->getUser());
0 ignored issues
show
Bug introduced by
It seems like $userSession->getUser() can be null; however, check() does not accept null, maybe add an additional type check?

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

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

function doesNotAcceptNull(stdClass $x) { }

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

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

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