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