Completed
Push — master ( 3b689e...b0b1f1 )
by Thomas
09:27
created

OC_Util::generatePath()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 11
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

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