Completed
Pull Request — master (#28440)
by Thomas
13:25
created

OC_Util::getDefaultPageUrl()   D

Complexity

Conditions 10
Paths 38

Size

Total Lines 41
Code Lines 28

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 10
eloc 28
nc 38
nop 0
dl 0
loc 41
rs 4.8196
c 0
b 0
f 0

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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