Completed
Push — master ( af89db...3df2fb )
by Morris
65:06 queued 42:28
created

OC_Util::formatDate()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 12
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 7
nc 4
nop 3
dl 0
loc 12
rs 9.2
c 0
b 0
f 0
1
<?php
2
/**
3
 * @copyright Copyright (c) 2016, ownCloud, Inc.
4
 *
5
 * @author Adam Williamson <[email protected]>
6
 * @author Andreas Fischer <[email protected]>
7
 * @author Arthur Schiwon <[email protected]>
8
 * @author Bart Visscher <[email protected]>
9
 * @author Bernhard Posselt <[email protected]>
10
 * @author Birk Borkason <[email protected]>
11
 * @author Björn Schießle <[email protected]>
12
 * @author Brice Maron <[email protected]>
13
 * @author Christoph Wurst <[email protected]>
14
 * @author Christopher Schäpers <[email protected]>
15
 * @author Clark Tomlinson <[email protected]>
16
 * @author cmeh <[email protected]>
17
 * @author Felix Epp <[email protected]>
18
 * @author Florin Peter <[email protected]>
19
 * @author Frank Karlitschek <[email protected]>
20
 * @author Georg Ehrke <[email protected]>
21
 * @author helix84 <[email protected]>
22
 * @author Ilja Neumann <[email protected]>
23
 * @author Individual IT Services <[email protected]>
24
 * @author Jakob Sack <[email protected]>
25
 * @author Joas Schilling <[email protected]>
26
 * @author Jörn Friedrich Dreyer <[email protected]>
27
 * @author Kawohl <[email protected]>
28
 * @author Lukas Reschke <[email protected]>
29
 * @author Markus Goetz <[email protected]>
30
 * @author Martin Mattel <[email protected]>
31
 * @author Marvin Thomas Rabe <[email protected]>
32
 * @author Michael Gapczynski <[email protected]>
33
 * @author Morris Jobke <[email protected]>
34
 * @author rakekniven <[email protected]>
35
 * @author Robin Appelman <[email protected]>
36
 * @author Robin McCorkell <[email protected]>
37
 * @author Roeland Jago Douma <[email protected]>
38
 * @author Sebastian Wessalowski <[email protected]>
39
 * @author Stefan Rado <[email protected]>
40
 * @author Stefan Weil <[email protected]>
41
 * @author Thomas Müller <[email protected]>
42
 * @author Thomas Tanghus <[email protected]>
43
 * @author Victor Dubiniuk <[email protected]>
44
 * @author Vincent Petry <[email protected]>
45
 * @author Volkan Gezer <[email protected]>
46
 *
47
 * @license AGPL-3.0
48
 *
49
 * This code is free software: you can redistribute it and/or modify
50
 * it under the terms of the GNU Affero General Public License, version 3,
51
 * as published by the Free Software Foundation.
52
 *
53
 * This program is distributed in the hope that it will be useful,
54
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
55
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
56
 * GNU Affero General Public License for more details.
57
 *
58
 * You should have received a copy of the GNU Affero General Public License, version 3,
59
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
60
 *
61
 */
62
63
use OCP\IConfig;
64
use OCP\IGroupManager;
65
use OCP\IUser;
66
67
class OC_Util {
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
68
	public static $scripts = array();
69
	public static $styles = array();
70
	public static $headers = array();
71
	private static $rootMounted = false;
72
	private static $fsSetup = false;
73
74
	/** @var array Local cache of version.php */
75
	private static $versionCache = null;
76
77
	protected static function getAppManager() {
78
		return \OC::$server->getAppManager();
79
	}
80
81
	private static function initLocalStorageRootFS() {
82
		// mount local file backend as root
83
		$configDataDirectory = \OC::$server->getSystemConfig()->getValue("datadirectory", OC::$SERVERROOT . "/data");
84
		//first set up the local "root" storage
85
		\OC\Files\Filesystem::initMountManager();
86
		if (!self::$rootMounted) {
87
			\OC\Files\Filesystem::mount('\OC\Files\Storage\Local', array('datadir' => $configDataDirectory), '/');
88
			self::$rootMounted = true;
89
		}
90
	}
91
92
	/**
93
	 * mounting an object storage as the root fs will in essence remove the
94
	 * necessity of a data folder being present.
95
	 * TODO make home storage aware of this and use the object storage instead of local disk access
96
	 *
97
	 * @param array $config containing 'class' and optional 'arguments'
98
	 * @suppress PhanDeprecatedFunction
99
	 */
100
	private static function initObjectStoreRootFS($config) {
101
		// check misconfiguration
102
		if (empty($config['class'])) {
103
			\OCP\Util::writeLog('files', 'No class given for objectstore', \OCP\Util::ERROR);
104
		}
105
		if (!isset($config['arguments'])) {
106
			$config['arguments'] = array();
107
		}
108
109
		// instantiate object store implementation
110
		$name = $config['class'];
111 View Code Duplication
		if (strpos($name, 'OCA\\') === 0 && substr_count($name, '\\') >= 2) {
112
			$segments = explode('\\', $name);
113
			OC_App::loadApp(strtolower($segments[1]));
114
		}
115
		$config['arguments']['objectstore'] = new $config['class']($config['arguments']);
116
		// mount with plain / root object store implementation
117
		$config['class'] = '\OC\Files\ObjectStore\ObjectStoreStorage';
118
119
		// mount object storage as root
120
		\OC\Files\Filesystem::initMountManager();
121 View Code Duplication
		if (!self::$rootMounted) {
122
			\OC\Files\Filesystem::mount($config['class'], $config['arguments'], '/');
123
			self::$rootMounted = true;
124
		}
125
	}
126
127
	/**
128
	 * mounting an object storage as the root fs will in essence remove the
129
	 * necessity of a data folder being present.
130
	 *
131
	 * @param array $config containing 'class' and optional 'arguments'
132
	 * @suppress PhanDeprecatedFunction
133
	 */
134
	private static function initObjectStoreMultibucketRootFS($config) {
135
		// check misconfiguration
136
		if (empty($config['class'])) {
137
			\OCP\Util::writeLog('files', 'No class given for objectstore', \OCP\Util::ERROR);
138
		}
139
		if (!isset($config['arguments'])) {
140
			$config['arguments'] = array();
141
		}
142
143
		// instantiate object store implementation
144
		$name = $config['class'];
145 View Code Duplication
		if (strpos($name, 'OCA\\') === 0 && substr_count($name, '\\') >= 2) {
146
			$segments = explode('\\', $name);
147
			OC_App::loadApp(strtolower($segments[1]));
148
		}
149
150
		if (!isset($config['arguments']['bucket'])) {
151
			$config['arguments']['bucket'] = '';
152
		}
153
		// put the root FS always in first bucket for multibucket configuration
154
		$config['arguments']['bucket'] .= '0';
155
156
		$config['arguments']['objectstore'] = new $config['class']($config['arguments']);
157
		// mount with plain / root object store implementation
158
		$config['class'] = '\OC\Files\ObjectStore\ObjectStoreStorage';
159
160
		// mount object storage as root
161
		\OC\Files\Filesystem::initMountManager();
162 View Code Duplication
		if (!self::$rootMounted) {
163
			\OC\Files\Filesystem::mount($config['class'], $config['arguments'], '/');
164
			self::$rootMounted = true;
165
		}
166
	}
167
168
	/**
169
	 * Can be set up
170
	 *
171
	 * @param string $user
172
	 * @return boolean
173
	 * @description configure the initial filesystem based on the configuration
174
	 * @suppress PhanDeprecatedFunction
175
	 * @suppress PhanAccessMethodInternal
176
	 */
177
	public static function setupFS($user = '') {
178
		//setting up the filesystem twice can only lead to trouble
179
		if (self::$fsSetup) {
180
			return false;
181
		}
182
183
		\OC::$server->getEventLogger()->start('setup_fs', 'Setup filesystem');
184
185
		// If we are not forced to load a specific user we load the one that is logged in
186
		if ($user === null) {
187
			$user = '';
188
		} else if ($user == "" && \OC::$server->getUserSession()->isLoggedIn()) {
189
			$user = OC_User::getUser();
190
		}
191
192
		// load all filesystem apps before, so no setup-hook gets lost
193
		OC_App::loadApps(array('filesystem'));
194
195
		// the filesystem will finish when $user is not empty,
196
		// mark fs setup here to avoid doing the setup from loading
197
		// OC_Filesystem
198
		if ($user != '') {
199
			self::$fsSetup = true;
200
		}
201
202
		\OC\Files\Filesystem::initMountManager();
203
204
		\OC\Files\Filesystem::logWarningWhenAddingStorageWrapper(false);
205
		\OC\Files\Filesystem::addStorageWrapper('mount_options', function ($mountPoint, \OCP\Files\Storage $storage, \OCP\Files\Mount\IMountPoint $mount) {
206
			if ($storage->instanceOfStorage('\OC\Files\Storage\Common')) {
207
				/** @var \OC\Files\Storage\Common $storage */
208
				$storage->setMountOptions($mount->getOptions());
209
			}
210
			return $storage;
211
		});
212
213
		\OC\Files\Filesystem::addStorageWrapper('enable_sharing', function ($mountPoint, \OCP\Files\Storage\IStorage $storage, \OCP\Files\Mount\IMountPoint $mount) {
214
			if (!$mount->getOption('enable_sharing', true)) {
215
				return new \OC\Files\Storage\Wrapper\PermissionsMask([
216
					'storage' => $storage,
217
					'mask' => \OCP\Constants::PERMISSION_ALL - \OCP\Constants::PERMISSION_SHARE
218
				]);
219
			}
220
			return $storage;
221
		});
222
223
		// install storage availability wrapper, before most other wrappers
224
		\OC\Files\Filesystem::addStorageWrapper('oc_availability', function ($mountPoint, \OCP\Files\Storage\IStorage $storage) {
225
			if (!$storage->instanceOfStorage('\OCA\Files_Sharing\SharedStorage') && !$storage->isLocal()) {
226
				return new \OC\Files\Storage\Wrapper\Availability(['storage' => $storage]);
227
			}
228
			return $storage;
229
		});
230
231
		\OC\Files\Filesystem::addStorageWrapper('oc_encoding', function ($mountPoint, \OCP\Files\Storage $storage, \OCP\Files\Mount\IMountPoint $mount) {
232
			if ($mount->getOption('encoding_compatibility', false) && !$storage->instanceOfStorage('\OCA\Files_Sharing\SharedStorage') && !$storage->isLocal()) {
233
				return new \OC\Files\Storage\Wrapper\Encoding(['storage' => $storage]);
234
			}
235
			return $storage;
236
		});
237
238
		\OC\Files\Filesystem::addStorageWrapper('oc_quota', function ($mountPoint, $storage) {
239
			// set up quota for home storages, even for other users
240
			// which can happen when using sharing
241
242
			/**
243
			 * @var \OC\Files\Storage\Storage $storage
244
			 */
245
			if ($storage->instanceOfStorage('\OC\Files\Storage\Home')
246
				|| $storage->instanceOfStorage('\OC\Files\ObjectStore\HomeObjectStoreStorage')
247
			) {
248
				/** @var \OC\Files\Storage\Home $storage */
249
				if (is_object($storage->getUser())) {
250
					$user = $storage->getUser()->getUID();
251
					$quota = OC_Util::getUserQuota($user);
252
					if ($quota !== \OCP\Files\FileInfo::SPACE_UNLIMITED) {
253
						return new \OC\Files\Storage\Wrapper\Quota(array('storage' => $storage, 'quota' => $quota, 'root' => 'files'));
254
					}
255
				}
256
			}
257
258
			return $storage;
259
		});
260
261
		OC_Hook::emit('OC_Filesystem', 'preSetup', array('user' => $user));
262
		\OC\Files\Filesystem::logWarningWhenAddingStorageWrapper(true);
263
264
		//check if we are using an object storage
265
		$objectStore = \OC::$server->getSystemConfig()->getValue('objectstore', null);
266
		$objectStoreMultibucket = \OC::$server->getSystemConfig()->getValue('objectstore_multibucket', null);
267
268
		// use the same order as in ObjectHomeMountProvider
269
		if (isset($objectStoreMultibucket)) {
270
			self::initObjectStoreMultibucketRootFS($objectStoreMultibucket);
271
		} elseif (isset($objectStore)) {
272
			self::initObjectStoreRootFS($objectStore);
273
		} else {
274
			self::initLocalStorageRootFS();
275
		}
276
277
		if ($user != '' && !\OC::$server->getUserManager()->userExists($user)) {
278
			\OC::$server->getEventLogger()->end('setup_fs');
279
			return false;
280
		}
281
282
		//if we aren't logged in, there is no use to set up the filesystem
283
		if ($user != "") {
284
285
			$userDir = '/' . $user . '/files';
286
287
			//jail the user into his "home" directory
288
			\OC\Files\Filesystem::init($user, $userDir);
289
290
			OC_Hook::emit('OC_Filesystem', 'setup', array('user' => $user, 'user_dir' => $userDir));
291
		}
292
		\OC::$server->getEventLogger()->end('setup_fs');
293
		return true;
294
	}
295
296
	/**
297
	 * check if a password is required for each public link
298
	 *
299
	 * @return boolean
300
	 * @suppress PhanDeprecatedFunction
301
	 */
302
	public static function isPublicLinkPasswordRequired() {
303
		$enforcePassword = \OC::$server->getConfig()->getAppValue('core', 'shareapi_enforce_links_password', 'no');
304
		return $enforcePassword === 'yes';
305
	}
306
307
	/**
308
	 * check if sharing is disabled for the current user
309
	 * @param IConfig $config
310
	 * @param IGroupManager $groupManager
311
	 * @param IUser|null $user
312
	 * @return bool
313
	 */
314
	public static function isSharingDisabledForUser(IConfig $config, IGroupManager $groupManager, $user) {
315
		if ($config->getAppValue('core', 'shareapi_exclude_groups', 'no') === 'yes') {
316
			$groupsList = $config->getAppValue('core', 'shareapi_exclude_groups_list', '');
317
			$excludedGroups = json_decode($groupsList);
318 View Code Duplication
			if (is_null($excludedGroups)) {
319
				$excludedGroups = explode(',', $groupsList);
320
				$newValue = json_encode($excludedGroups);
321
				$config->setAppValue('core', 'shareapi_exclude_groups_list', $newValue);
322
			}
323
			$usersGroups = $groupManager->getUserGroupIds($user);
0 ignored issues
show
Bug introduced by
It seems like $user defined by parameter $user on line 314 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...
324 View Code Duplication
			if (!empty($usersGroups)) {
325
				$remainingGroups = array_diff($usersGroups, $excludedGroups);
326
				// if the user is only in groups which are disabled for sharing then
327
				// sharing is also disabled for the user
328
				if (empty($remainingGroups)) {
329
					return true;
330
				}
331
			}
332
		}
333
		return false;
334
	}
335
336
	/**
337
	 * check if share API enforces a default expire date
338
	 *
339
	 * @return boolean
340
	 * @suppress PhanDeprecatedFunction
341
	 */
342
	public static function isDefaultExpireDateEnforced() {
343
		$isDefaultExpireDateEnabled = \OC::$server->getConfig()->getAppValue('core', 'shareapi_default_expire_date', 'no');
344
		$enforceDefaultExpireDate = false;
345
		if ($isDefaultExpireDateEnabled === 'yes') {
346
			$value = \OC::$server->getConfig()->getAppValue('core', 'shareapi_enforce_expire_date', 'no');
347
			$enforceDefaultExpireDate = $value === 'yes';
348
		}
349
350
		return $enforceDefaultExpireDate;
351
	}
352
353
	/**
354
	 * Get the quota of a user
355
	 *
356
	 * @param string $userId
357
	 * @return float Quota bytes
358
	 */
359
	public static function getUserQuota($userId) {
360
		$user = \OC::$server->getUserManager()->get($userId);
361
		if (is_null($user)) {
362
			return \OCP\Files\FileInfo::SPACE_UNLIMITED;
363
		}
364
		$userQuota = $user->getQuota();
365
		if($userQuota === 'none') {
366
			return \OCP\Files\FileInfo::SPACE_UNLIMITED;
367
		}
368
		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 368 which is incompatible with the return type documented by OC_Util::getUserQuota of type double. It seems like you forgot to handle an error condition.
Loading history...
369
	}
370
371
	/**
372
	 * copies the skeleton to the users /files
373
	 *
374
	 * @param String $userId
375
	 * @param \OCP\Files\Folder $userDirectory
376
	 * @throws \RuntimeException
377
	 * @suppress PhanDeprecatedFunction
378
	 */
379
	public static function copySkeleton($userId, \OCP\Files\Folder $userDirectory) {
380
381
		$plainSkeletonDirectory = \OC::$server->getConfig()->getSystemValue('skeletondirectory', \OC::$SERVERROOT . '/core/skeleton');
382
		$userLang = \OC::$server->getL10NFactory()->findLanguage();
383
		$skeletonDirectory = str_replace('{lang}', $userLang, $plainSkeletonDirectory);
384
385
		if (!file_exists($skeletonDirectory)) {
386
			$dialectStart = strpos($userLang, '_');
387
			if ($dialectStart !== false) {
388
				$skeletonDirectory = str_replace('{lang}', substr($userLang, 0, $dialectStart), $plainSkeletonDirectory);
389
			}
390
			if ($dialectStart === false || !file_exists($skeletonDirectory)) {
391
				$skeletonDirectory = str_replace('{lang}', 'default', $plainSkeletonDirectory);
392
			}
393
			if (!file_exists($skeletonDirectory)) {
394
				$skeletonDirectory = '';
395
			}
396
		}
397
398
		$instanceId = \OC::$server->getConfig()->getSystemValue('instanceid', '');
399
400
		if ($instanceId === null) {
401
			throw new \RuntimeException('no instance id!');
402
		}
403
		$appdata = 'appdata_' . $instanceId;
404
		if ($userId === $appdata) {
405
			throw new \RuntimeException('username is reserved name: ' . $appdata);
406
		}
407
408
		if (!empty($skeletonDirectory)) {
409
			\OCP\Util::writeLog(
410
				'files_skeleton',
411
				'copying skeleton for '.$userId.' from '.$skeletonDirectory.' to '.$userDirectory->getFullPath('/'),
412
				\OCP\Util::DEBUG
413
			);
414
			self::copyr($skeletonDirectory, $userDirectory);
415
			// update the file cache
416
			$userDirectory->getStorage()->getScanner()->scan('', \OC\Files\Cache\Scanner::SCAN_RECURSIVE);
417
		}
418
	}
419
420
	/**
421
	 * copies a directory recursively by using streams
422
	 *
423
	 * @param string $source
424
	 * @param \OCP\Files\Folder $target
425
	 * @return void
426
	 */
427
	public static function copyr($source, \OCP\Files\Folder $target) {
428
		$logger = \OC::$server->getLogger();
429
430
		// Verify if folder exists
431
		$dir = opendir($source);
432
		if($dir === false) {
433
			$logger->error(sprintf('Could not opendir "%s"', $source), ['app' => 'core']);
434
			return;
435
		}
436
437
		// Copy the files
438
		while (false !== ($file = readdir($dir))) {
439
			if (!\OC\Files\Filesystem::isIgnoredDir($file)) {
440
				if (is_dir($source . '/' . $file)) {
441
					$child = $target->newFolder($file);
442
					self::copyr($source . '/' . $file, $child);
443
				} else {
444
					$child = $target->newFile($file);
445
					$sourceStream = fopen($source . '/' . $file, 'r');
446
					if($sourceStream === false) {
447
						$logger->error(sprintf('Could not fopen "%s"', $source . '/' . $file), ['app' => 'core']);
448
						closedir($dir);
449
						return;
450
					}
451
					stream_copy_to_stream($sourceStream, $child->fopen('w'));
452
				}
453
			}
454
		}
455
		closedir($dir);
456
	}
457
458
	/**
459
	 * @return void
460
	 * @suppress PhanUndeclaredMethod
461
	 */
462
	public static function tearDownFS() {
463
		\OC\Files\Filesystem::tearDown();
464
		\OC::$server->getRootFolder()->clearCache();
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface OCP\Files\IRootFolder as the method clearCache() does only exist in the following implementations of said interface: OC\Files\Node\Root.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
465
		self::$fsSetup = false;
466
		self::$rootMounted = false;
467
	}
468
469
	/**
470
	 * get the current installed version of ownCloud
471
	 *
472
	 * @return array
473
	 */
474
	public static function getVersion() {
475
		OC_Util::loadVersion();
476
		return self::$versionCache['OC_Version'];
477
	}
478
479
	/**
480
	 * get the current installed version string of ownCloud
481
	 *
482
	 * @return string
483
	 */
484
	public static function getVersionString() {
485
		OC_Util::loadVersion();
486
		return self::$versionCache['OC_VersionString'];
487
	}
488
489
	/**
490
	 * @deprecated the value is of no use anymore
491
	 * @return string
492
	 */
493
	public static function getEditionString() {
494
		return '';
495
	}
496
497
	/**
498
	 * @description get the update channel of the current installed of ownCloud.
499
	 * @return string
500
	 */
501
	public static function getChannel() {
502
		OC_Util::loadVersion();
503
		return \OC::$server->getConfig()->getSystemValue('updater.release.channel', self::$versionCache['OC_Channel']);
504
	}
505
506
	/**
507
	 * @description get the build number of the current installed of ownCloud.
508
	 * @return string
509
	 */
510
	public static function getBuild() {
511
		OC_Util::loadVersion();
512
		return self::$versionCache['OC_Build'];
513
	}
514
515
	/**
516
	 * @description load the version.php into the session as cache
517
	 * @suppress PhanUndeclaredVariable
518
	 */
519
	private static function loadVersion() {
520
		if (self::$versionCache !== null) {
521
			return;
522
		}
523
524
		$timestamp = filemtime(OC::$SERVERROOT . '/version.php');
525
		require OC::$SERVERROOT . '/version.php';
526
		/** @var $timestamp int */
527
		self::$versionCache['OC_Version_Timestamp'] = $timestamp;
528
		/** @var $OC_Version string */
529
		self::$versionCache['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...
530
		/** @var $OC_VersionString string */
531
		self::$versionCache['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...
532
		/** @var $OC_Build string */
533
		self::$versionCache['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...
534
535
		/** @var $OC_Channel string */
536
		self::$versionCache['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...
537
	}
538
539
	/**
540
	 * generates a path for JS/CSS files. If no application is provided it will create the path for core.
541
	 *
542
	 * @param string $application application to get the files from
543
	 * @param string $directory directory within this application (css, js, vendor, etc)
544
	 * @param string $file the file inside of the above folder
545
	 * @return string the path
546
	 */
547
	private static function generatePath($application, $directory, $file) {
548
		if (is_null($file)) {
549
			$file = $application;
550
			$application = "";
551
		}
552
		if (!empty($application)) {
553
			return "$application/$directory/$file";
554
		} else {
555
			return "$directory/$file";
556
		}
557
	}
558
559
	/**
560
	 * add a javascript file
561
	 *
562
	 * @param string $application application id
563
	 * @param string|null $file filename
564
	 * @param bool $prepend prepend the Script to the beginning of the list
565
	 * @return void
566
	 */
567
	public static function addScript($application, $file = null, $prepend = false) {
568
		$path = OC_Util::generatePath($application, 'js', $file);
569
570
		// core js files need separate handling
571
		if ($application !== 'core' && $file !== null) {
572
			self::addTranslations ( $application );
573
		}
574
		self::addExternalResource($application, $prepend, $path, "script");
575
	}
576
577
	/**
578
	 * add a javascript file from the vendor sub folder
579
	 *
580
	 * @param string $application application id
581
	 * @param string|null $file filename
582
	 * @param bool $prepend prepend the Script to the beginning of the list
583
	 * @return void
584
	 */
585
	public static function addVendorScript($application, $file = null, $prepend = false) {
586
		$path = OC_Util::generatePath($application, 'vendor', $file);
587
		self::addExternalResource($application, $prepend, $path, "script");
588
	}
589
590
	/**
591
	 * add a translation JS file
592
	 *
593
	 * @param string $application application id
594
	 * @param string|null $languageCode language code, defaults to the current language
595
	 * @param bool|null $prepend prepend the Script to the beginning of the list
596
	 */
597
	public static function addTranslations($application, $languageCode = null, $prepend = false) {
598
		if (is_null($languageCode)) {
599
			$languageCode = \OC::$server->getL10NFactory()->findLanguage($application);
600
		}
601
		if (!empty($application)) {
602
			$path = "$application/l10n/$languageCode";
603
		} else {
604
			$path = "l10n/$languageCode";
605
		}
606
		self::addExternalResource($application, $prepend, $path, "script");
0 ignored issues
show
Bug introduced by
It seems like $prepend defined by parameter $prepend on line 597 can also be of type null; however, OC_Util::addExternalResource() does only seem to accept boolean, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
607
	}
608
609
	/**
610
	 * add a css file
611
	 *
612
	 * @param string $application application id
613
	 * @param string|null $file filename
614
	 * @param bool $prepend prepend the Style to the beginning of the list
615
	 * @return void
616
	 */
617
	public static function addStyle($application, $file = null, $prepend = false) {
618
		$path = OC_Util::generatePath($application, 'css', $file);
619
		self::addExternalResource($application, $prepend, $path, "style");
620
	}
621
622
	/**
623
	 * add a css file from the vendor sub folder
624
	 *
625
	 * @param string $application application id
626
	 * @param string|null $file filename
627
	 * @param bool $prepend prepend the Style to the beginning of the list
628
	 * @return void
629
	 */
630
	public static function addVendorStyle($application, $file = null, $prepend = false) {
631
		$path = OC_Util::generatePath($application, 'vendor', $file);
632
		self::addExternalResource($application, $prepend, $path, "style");
633
	}
634
635
	/**
636
	 * add an external resource css/js file
637
	 *
638
	 * @param string $application application id
639
	 * @param bool $prepend prepend the file to the beginning of the list
640
	 * @param string $path
641
	 * @param string $type (script or style)
642
	 * @return void
643
	 */
644
	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...
645
646
		if ($type === "style") {
647 View Code Duplication
			if (!in_array($path, self::$styles)) {
648
				if ($prepend === true) {
649
					array_unshift ( self::$styles, $path );
650
				} else {
651
					self::$styles[] = $path;
652
				}
653
			}
654 View Code Duplication
		} elseif ($type === "script") {
655
			if (!in_array($path, self::$scripts)) {
656
				if ($prepend === true) {
657
					array_unshift ( self::$scripts, $path );
658
				} else {
659
					self::$scripts [] = $path;
660
				}
661
			}
662
		}
663
	}
664
665
	/**
666
	 * Add a custom element to the header
667
	 * If $text is null then the element will be written as empty element.
668
	 * So use "" to get a closing tag.
669
	 * @param string $tag tag name of the element
670
	 * @param array $attributes array of attributes for the element
671
	 * @param string $text the text content for the element
672
	 */
673 View Code Duplication
	public static function addHeader($tag, $attributes, $text=null) {
674
		self::$headers[] = array(
675
			'tag' => $tag,
676
			'attributes' => $attributes,
677
			'text' => $text
678
		);
679
	}
680
681
	/**
682
	 * check if the current server configuration is suitable for ownCloud
683
	 *
684
	 * @param \OC\SystemConfig $config
685
	 * @return array arrays with error messages and hints
686
	 */
687
	public static function checkServer(\OC\SystemConfig $config) {
688
		$l = \OC::$server->getL10N('lib');
689
		$errors = array();
690
		$CONFIG_DATADIRECTORY = $config->getValue('datadirectory', OC::$SERVERROOT . '/data');
691
692
		if (!self::needUpgrade($config) && $config->getValue('installed', false)) {
693
			// this check needs to be done every time
694
			$errors = self::checkDataDirectoryValidity($CONFIG_DATADIRECTORY);
695
		}
696
697
		// Assume that if checkServer() succeeded before in this session, then all is fine.
698
		if (\OC::$server->getSession()->exists('checkServer_succeeded') && \OC::$server->getSession()->get('checkServer_succeeded')) {
699
			return $errors;
700
		}
701
702
		$webServerRestart = false;
703
		$setup = new \OC\Setup(
704
			$config,
705
			\OC::$server->getIniWrapper(),
706
			\OC::$server->getL10N('lib'),
707
			\OC::$server->query(\OCP\Defaults::class),
708
			\OC::$server->getLogger(),
709
			\OC::$server->getSecureRandom(),
710
			\OC::$server->query(\OC\Installer::class)
711
		);
712
713
		$urlGenerator = \OC::$server->getURLGenerator();
714
715
		$availableDatabases = $setup->getSupportedDatabases();
716
		if (empty($availableDatabases)) {
717
			$errors[] = array(
718
				'error' => $l->t('No database drivers (sqlite, mysql, or postgresql) installed.'),
719
				'hint' => '' //TODO: sane hint
720
			);
721
			$webServerRestart = true;
722
		}
723
724
		// Check if config folder is writable.
725
		if(!OC_Helper::isReadOnlyConfigEnabled()) {
726
			if (!is_writable(OC::$configDir) or !is_readable(OC::$configDir)) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as or instead of || is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
727
				$errors[] = array(
728
					'error' => $l->t('Cannot write into "config" directory'),
729
					'hint' => $l->t('This can usually be fixed by giving the webserver write access to the config directory. See %s',
730
						[$urlGenerator->linkToDocs('admin-dir_permissions')])
731
				);
732
			}
733
		}
734
735
		// Check if there is a writable install folder.
736
		if ($config->getValue('appstoreenabled', true)) {
737
			if (OC_App::getInstallPath() === null
738
				|| !is_writable(OC_App::getInstallPath())
739
				|| !is_readable(OC_App::getInstallPath())
740
			) {
741
				$errors[] = array(
742
					'error' => $l->t('Cannot write into "apps" directory'),
743
					'hint' => $l->t('This can usually be fixed by giving the webserver write access to the apps directory'
744
						. ' or disabling the appstore in the config file. See %s',
745
						[$urlGenerator->linkToDocs('admin-dir_permissions')])
746
				);
747
			}
748
		}
749
		// Create root dir.
750
		if ($config->getValue('installed', false)) {
751
			if (!is_dir($CONFIG_DATADIRECTORY)) {
752
				$success = @mkdir($CONFIG_DATADIRECTORY);
753
				if ($success) {
754
					$errors = array_merge($errors, self::checkDataDirectoryPermissions($CONFIG_DATADIRECTORY));
755
				} else {
756
					$errors[] = [
757
						'error' => $l->t('Cannot create "data" directory'),
758
						'hint' => $l->t('This can usually be fixed by giving the webserver write access to the root directory. See %s',
759
							[$urlGenerator->linkToDocs('admin-dir_permissions')])
760
					];
761
				}
762
			} else if (!is_writable($CONFIG_DATADIRECTORY) or !is_readable($CONFIG_DATADIRECTORY)) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as or instead of || is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
763
				//common hint for all file permissions error messages
764
				$permissionsHint = $l->t('Permissions can usually be fixed by giving the webserver write access to the root directory. See %s.',
765
					[$urlGenerator->linkToDocs('admin-dir_permissions')]);
766
				$errors[] = [
767
					'error' => 'Your data directory is not writable',
768
					'hint' => $permissionsHint
769
				];
770
			} else {
771
				$errors = array_merge($errors, self::checkDataDirectoryPermissions($CONFIG_DATADIRECTORY));
772
			}
773
		}
774
775
		if (!OC_Util::isSetLocaleWorking()) {
776
			$errors[] = array(
777
				'error' => $l->t('Setting locale to %s failed',
778
					array('en_US.UTF-8/fr_FR.UTF-8/es_ES.UTF-8/de_DE.UTF-8/ru_RU.UTF-8/'
779
						. 'pt_BR.UTF-8/it_IT.UTF-8/ja_JP.UTF-8/zh_CN.UTF-8')),
780
				'hint' => $l->t('Please install one of these locales on your system and restart your webserver.')
781
			);
782
		}
783
784
		// Contains the dependencies that should be checked against
785
		// classes = class_exists
786
		// functions = function_exists
787
		// defined = defined
788
		// ini = ini_get
789
		// If the dependency is not found the missing module name is shown to the EndUser
790
		// When adding new checks always verify that they pass on Travis as well
791
		// for ini settings, see https://github.com/owncloud/administration/blob/master/travis-ci/custom.ini
792
		$dependencies = array(
793
			'classes' => array(
794
				'ZipArchive' => 'zip',
795
				'DOMDocument' => 'dom',
796
				'XMLWriter' => 'XMLWriter',
797
				'XMLReader' => 'XMLReader',
798
			),
799
			'functions' => [
800
				'xml_parser_create' => 'libxml',
801
				'mb_strcut' => 'mb multibyte',
802
				'ctype_digit' => 'ctype',
803
				'json_encode' => 'JSON',
804
				'gd_info' => 'GD',
805
				'gzencode' => 'zlib',
806
				'iconv' => 'iconv',
807
				'simplexml_load_string' => 'SimpleXML',
808
				'hash' => 'HASH Message Digest Framework',
809
				'curl_init' => 'cURL',
810
				'openssl_verify' => 'OpenSSL',
811
			],
812
			'defined' => array(
813
				'PDO::ATTR_DRIVER_NAME' => 'PDO'
814
			),
815
			'ini' => [
816
				'default_charset' => 'UTF-8',
817
			],
818
		);
819
		$missingDependencies = array();
820
		$invalidIniSettings = [];
821
		$moduleHint = $l->t('Please ask your server administrator to install the module.');
822
823
		/**
824
		 * FIXME: The dependency check does not work properly on HHVM on the moment
825
		 *        and prevents installation. Once HHVM is more compatible with our
826
		 *        approach to check for these values we should re-enable those
827
		 *        checks.
828
		 */
829
		$iniWrapper = \OC::$server->getIniWrapper();
830
		if (!self::runningOnHhvm()) {
831
			foreach ($dependencies['classes'] as $class => $module) {
832
				if (!class_exists($class)) {
833
					$missingDependencies[] = $module;
834
				}
835
			}
836
			foreach ($dependencies['functions'] as $function => $module) {
837
				if (!function_exists($function)) {
838
					$missingDependencies[] = $module;
839
				}
840
			}
841
			foreach ($dependencies['defined'] as $defined => $module) {
842
				if (!defined($defined)) {
843
					$missingDependencies[] = $module;
844
				}
845
			}
846
			foreach ($dependencies['ini'] as $setting => $expected) {
847
				if (is_bool($expected)) {
848
					if ($iniWrapper->getBool($setting) !== $expected) {
849
						$invalidIniSettings[] = [$setting, $expected];
850
					}
851
				}
852
				if (is_int($expected)) {
853
					if ($iniWrapper->getNumeric($setting) !== $expected) {
854
						$invalidIniSettings[] = [$setting, $expected];
855
					}
856
				}
857
				if (is_string($expected)) {
858
					if (strtolower($iniWrapper->getString($setting)) !== strtolower($expected)) {
859
						$invalidIniSettings[] = [$setting, $expected];
860
					}
861
				}
862
			}
863
		}
864
865
		foreach($missingDependencies as $missingDependency) {
866
			$errors[] = array(
867
				'error' => $l->t('PHP module %s not installed.', array($missingDependency)),
868
				'hint' => $moduleHint
869
			);
870
			$webServerRestart = true;
871
		}
872
		foreach($invalidIniSettings as $setting) {
873
			if(is_bool($setting[1])) {
874
				$setting[1] = $setting[1] ? 'on' : 'off';
875
			}
876
			$errors[] = [
877
				'error' => $l->t('PHP setting "%s" is not set to "%s".', [$setting[0], var_export($setting[1], true)]),
878
				'hint' =>  $l->t('Adjusting this setting in php.ini will make Nextcloud run again')
879
			];
880
			$webServerRestart = true;
881
		}
882
883
		/**
884
		 * The mbstring.func_overload check can only be performed if the mbstring
885
		 * module is installed as it will return null if the checking setting is
886
		 * not available and thus a check on the boolean value fails.
887
		 *
888
		 * TODO: Should probably be implemented in the above generic dependency
889
		 *       check somehow in the long-term.
890
		 */
891
		if($iniWrapper->getBool('mbstring.func_overload') !== null &&
892
			$iniWrapper->getBool('mbstring.func_overload') === true) {
893
			$errors[] = array(
894
				'error' => $l->t('mbstring.func_overload is set to "%s" instead of the expected value "0"', [$iniWrapper->getString('mbstring.func_overload')]),
895
				'hint' => $l->t('To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini')
896
			);
897
		}
898
899
		if(function_exists('xml_parser_create') &&
900
			LIBXML_LOADED_VERSION < 20700 ) {
901
			$version = LIBXML_LOADED_VERSION;
902
			$major = floor($version/10000);
903
			$version -= ($major * 10000);
904
			$minor = floor($version/100);
905
			$version -= ($minor * 100);
906
			$patch = $version;
907
			$errors[] = array(
908
				'error' => $l->t('libxml2 2.7.0 is at least required. Currently %s is installed.', [$major . '.' . $minor . '.' . $patch]),
909
				'hint' => $l->t('To fix this issue update your libxml2 version and restart your web server.')
910
			);
911
		}
912
913
		if (!self::isAnnotationsWorking()) {
914
			$errors[] = array(
915
				'error' => $l->t('PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible.'),
916
				'hint' => $l->t('This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator.')
917
			);
918
		}
919
920
		if (!\OC::$CLI && $webServerRestart) {
921
			$errors[] = array(
922
				'error' => $l->t('PHP modules have been installed, but they are still listed as missing?'),
923
				'hint' => $l->t('Please ask your server administrator to restart the web server.')
924
			);
925
		}
926
927
		$errors = array_merge($errors, self::checkDatabaseVersion());
928
929
		// Cache the result of this function
930
		\OC::$server->getSession()->set('checkServer_succeeded', count($errors) == 0);
931
932
		return $errors;
933
	}
934
935
	/**
936
	 * Check the database version
937
	 *
938
	 * @return array errors array
939
	 */
940
	public static function checkDatabaseVersion() {
941
		$l = \OC::$server->getL10N('lib');
942
		$errors = array();
943
		$dbType = \OC::$server->getSystemConfig()->getValue('dbtype', 'sqlite');
944
		if ($dbType === 'pgsql') {
945
			// check PostgreSQL version
946
			try {
947
				$result = \OC_DB::executeAudited('SHOW SERVER_VERSION');
948
				$data = $result->fetchRow();
949
				if (isset($data['server_version'])) {
950
					$version = $data['server_version'];
951
					if (version_compare($version, '9.0.0', '<')) {
952
						$errors[] = array(
953
							'error' => $l->t('PostgreSQL >= 9 required'),
954
							'hint' => $l->t('Please upgrade your database version')
955
						);
956
					}
957
				}
958
			} catch (\Doctrine\DBAL\DBALException $e) {
0 ignored issues
show
Bug introduced by
The class Doctrine\DBAL\DBALException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
959
				$logger = \OC::$server->getLogger();
960
				$logger->warning('Error occurred while checking PostgreSQL version, assuming >= 9');
961
				$logger->logException($e);
962
			}
963
		}
964
		return $errors;
965
	}
966
967
	/**
968
	 * Check for correct file permissions of data directory
969
	 *
970
	 * @param string $dataDirectory
971
	 * @return array arrays with error messages and hints
972
	 */
973
	public static function checkDataDirectoryPermissions($dataDirectory) {
974
		if(\OC::$server->getConfig()->getSystemValue('check_data_directory_permissions', true) === false) {
975
			return  [];
976
		}
977
		$l = \OC::$server->getL10N('lib');
978
		$errors = [];
979
		$permissionsModHint = $l->t('Please change the permissions to 0770 so that the directory'
980
			. ' cannot be listed by other users.');
981
		$perms = substr(decoct(@fileperms($dataDirectory)), -3);
982
		if (substr($perms, -1) !== '0') {
983
			chmod($dataDirectory, 0770);
984
			clearstatcache();
985
			$perms = substr(decoct(@fileperms($dataDirectory)), -3);
986
			if ($perms[2] !== '0') {
987
				$errors[] = [
988
					'error' => $l->t('Your data directory is readable by other users'),
989
					'hint' => $permissionsModHint
990
				];
991
			}
992
		}
993
		return $errors;
994
	}
995
996
	/**
997
	 * Check that the data directory exists and is valid by
998
	 * checking the existence of the ".ocdata" file.
999
	 *
1000
	 * @param string $dataDirectory data directory path
1001
	 * @return array errors found
1002
	 */
1003
	public static function checkDataDirectoryValidity($dataDirectory) {
1004
		$l = \OC::$server->getL10N('lib');
1005
		$errors = [];
1006 View Code Duplication
		if ($dataDirectory[0] !== '/') {
1007
			$errors[] = [
1008
				'error' => $l->t('Your data directory must be an absolute path'),
1009
				'hint' => $l->t('Check the value of "datadirectory" in your configuration')
1010
			];
1011
		}
1012 View Code Duplication
		if (!file_exists($dataDirectory . '/.ocdata')) {
1013
			$errors[] = [
1014
				'error' => $l->t('Your data directory is invalid'),
1015
				'hint' => $l->t('Ensure there is a file called ".ocdata"' .
1016
					' in the root of the data directory.')
1017
			];
1018
		}
1019
		return $errors;
1020
	}
1021
1022
	/**
1023
	 * Check if the user is logged in, redirects to home if not. With
1024
	 * redirect URL parameter to the request URI.
1025
	 *
1026
	 * @return void
1027
	 */
1028
	public static function checkLoggedIn() {
1029
		// Check if we are a user
1030 View Code Duplication
		if (!\OC::$server->getUserSession()->isLoggedIn()) {
1031
			header('Location: ' . \OC::$server->getURLGenerator()->linkToRoute(
1032
						'core.login.showLoginForm',
1033
						[
1034
							'redirect_url' => \OC::$server->getRequest()->getRequestUri(),
1035
						]
1036
					)
1037
			);
1038
			exit();
0 ignored issues
show
Coding Style Compatibility introduced by
The method checkLoggedIn() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
1039
		}
1040
		// Redirect to 2FA challenge selection if 2FA challenge was not solved yet
1041 View Code Duplication
		if (\OC::$server->getTwoFactorAuthManager()->needsSecondFactor(\OC::$server->getUserSession()->getUser())) {
1042
			header('Location: ' . \OC::$server->getURLGenerator()->linkToRoute('core.TwoFactorChallenge.selectChallenge'));
1043
			exit();
0 ignored issues
show
Coding Style Compatibility introduced by
The method checkLoggedIn() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
1044
		}
1045
	}
1046
1047
	/**
1048
	 * Check if the user is a admin, redirects to home if not
1049
	 *
1050
	 * @return void
1051
	 */
1052
	public static function checkAdminUser() {
1053
		OC_Util::checkLoggedIn();
1054
		if (!OC_User::isAdminUser(OC_User::getUser())) {
1055
			header('Location: ' . \OCP\Util::linkToAbsolute('', 'index.php'));
1056
			exit();
0 ignored issues
show
Coding Style Compatibility introduced by
The method checkAdminUser() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
1057
		}
1058
	}
1059
1060
	/**
1061
	 * Check if the user is a subadmin, redirects to home if not
1062
	 *
1063
	 * @return null|boolean $groups where the current user is subadmin
1064
	 */
1065
	public static function checkSubAdminUser() {
1066
		OC_Util::checkLoggedIn();
1067
		$userObject = \OC::$server->getUserSession()->getUser();
1068
		$isSubAdmin = false;
1069
		if($userObject !== null) {
1070
			$isSubAdmin = \OC::$server->getGroupManager()->getSubAdmin()->isSubAdmin($userObject);
1071
		}
1072
1073
		if (!$isSubAdmin) {
1074
			header('Location: ' . \OCP\Util::linkToAbsolute('', 'index.php'));
1075
			exit();
0 ignored issues
show
Coding Style Compatibility introduced by
The method checkSubAdminUser() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
1076
		}
1077
		return true;
1078
	}
1079
1080
	/**
1081
	 * Returns the URL of the default page
1082
	 * based on the system configuration and
1083
	 * the apps visible for the current user
1084
	 *
1085
	 * @return string URL
1086
	 * @suppress PhanDeprecatedFunction
1087
	 */
1088
	public static function getDefaultPageUrl() {
0 ignored issues
show
Coding Style introduced by
getDefaultPageUrl uses the super-global variable $_REQUEST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
1089
		$urlGenerator = \OC::$server->getURLGenerator();
1090
		// Deny the redirect if the URL contains a @
1091
		// This prevents unvalidated redirects like ?redirect_url=:[email protected]
1092
		if (isset($_REQUEST['redirect_url']) && strpos($_REQUEST['redirect_url'], '@') === false) {
1093
			$location = $urlGenerator->getAbsoluteURL(urldecode($_REQUEST['redirect_url']));
1094
		} else {
1095
			$defaultPage = \OC::$server->getConfig()->getAppValue('core', 'defaultpage');
1096
			if ($defaultPage) {
1097
				$location = $urlGenerator->getAbsoluteURL($defaultPage);
1098
			} else {
1099
				$appId = 'files';
1100
				$config = \OC::$server->getConfig();
1101
				$defaultApps = explode(',', $config->getSystemValue('defaultapp', 'files'));
1102
				// find the first app that is enabled for the current user
1103
				foreach ($defaultApps as $defaultApp) {
1104
					$defaultApp = OC_App::cleanAppId(strip_tags($defaultApp));
1105
					if (static::getAppManager()->isEnabledForUser($defaultApp)) {
1106
						$appId = $defaultApp;
1107
						break;
1108
					}
1109
				}
1110
1111
				if($config->getSystemValue('htaccess.IgnoreFrontController', false) === true || getenv('front_controller_active') === 'true') {
1112
					$location = $urlGenerator->getAbsoluteURL('/apps/' . $appId . '/');
1113
				} else {
1114
					$location = $urlGenerator->getAbsoluteURL('/index.php/apps/' . $appId . '/');
1115
				}
1116
			}
1117
		}
1118
		return $location;
1119
	}
1120
1121
	/**
1122
	 * Redirect to the user default page
1123
	 *
1124
	 * @return void
1125
	 */
1126
	public static function redirectToDefaultPage() {
1127
		$location = self::getDefaultPageUrl();
1128
		header('Location: ' . $location);
1129
		exit();
0 ignored issues
show
Coding Style Compatibility introduced by
The method redirectToDefaultPage() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
1130
	}
1131
1132
	/**
1133
	 * get an id unique for this instance
1134
	 *
1135
	 * @return string
1136
	 */
1137
	public static function getInstanceId() {
1138
		$id = \OC::$server->getSystemConfig()->getValue('instanceid', null);
1139
		if (is_null($id)) {
1140
			// We need to guarantee at least one letter in instanceid so it can be used as the session_name
1141
			$id = 'oc' . \OC::$server->getSecureRandom()->generate(10, \OCP\Security\ISecureRandom::CHAR_LOWER.\OCP\Security\ISecureRandom::CHAR_DIGITS);
1142
			\OC::$server->getSystemConfig()->setValue('instanceid', $id);
1143
		}
1144
		return $id;
1145
	}
1146
1147
	/**
1148
	 * Public function to sanitize HTML
1149
	 *
1150
	 * This function is used to sanitize HTML and should be applied on any
1151
	 * string or array of strings before displaying it on a web page.
1152
	 *
1153
	 * @param string|array $value
1154
	 * @return string|array an array of sanitized strings or a single sanitized string, depends on the input parameter.
1155
	 */
1156
	public static function sanitizeHTML($value) {
1157
		if (is_array($value)) {
1158
			$value = array_map(function($value) {
1159
				return self::sanitizeHTML($value);
1160
			}, $value);
1161
		} else {
1162
			// Specify encoding for PHP<5.4
1163
			$value = htmlspecialchars((string)$value, ENT_QUOTES, 'UTF-8');
1164
		}
1165
		return $value;
1166
	}
1167
1168
	/**
1169
	 * Public function to encode url parameters
1170
	 *
1171
	 * This function is used to encode path to file before output.
1172
	 * Encoding is done according to RFC 3986 with one exception:
1173
	 * Character '/' is preserved as is.
1174
	 *
1175
	 * @param string $component part of URI to encode
1176
	 * @return string
1177
	 */
1178
	public static function encodePath($component) {
1179
		$encoded = rawurlencode($component);
1180
		$encoded = str_replace('%2F', '/', $encoded);
1181
		return $encoded;
1182
	}
1183
1184
1185
	public function createHtaccessTestFile(\OCP\IConfig $config) {
1186
		// php dev server does not support htaccess
1187
		if (php_sapi_name() === 'cli-server') {
1188
			return false;
1189
		}
1190
1191
		// testdata
1192
		$fileName = '/htaccesstest.txt';
1193
		$testContent = 'This is used for testing whether htaccess is properly enabled to disallow access from the outside. This file can be safely removed.';
1194
1195
		// creating a test file
1196
		$testFile = $config->getSystemValue('datadirectory', OC::$SERVERROOT . '/data') . '/' . $fileName;
1197
1198
		if (file_exists($testFile)) {// already running this test, possible recursive call
1199
			return false;
1200
		}
1201
1202
		$fp = @fopen($testFile, 'w');
1203
		if (!$fp) {
1204
			throw new OC\HintException('Can\'t create test file to check for working .htaccess file.',
1205
				'Make sure it is possible for the webserver to write to ' . $testFile);
1206
		}
1207
		fwrite($fp, $testContent);
1208
		fclose($fp);
1209
1210
		return $testContent;
1211
	}
1212
1213
	/**
1214
	 * Check if the .htaccess file is working
1215
	 * @param \OCP\IConfig $config
1216
	 * @return bool
1217
	 * @throws Exception
1218
	 * @throws \OC\HintException If the test file can't get written.
1219
	 */
1220
	public function isHtaccessWorking(\OCP\IConfig $config) {
1221
1222
		if (\OC::$CLI || !$config->getSystemValue('check_for_working_htaccess', true)) {
1223
			return true;
1224
		}
1225
1226
		$testContent = $this->createHtaccessTestFile($config);
1227
		if ($testContent === false) {
1228
			return false;
1229
		}
1230
1231
		$fileName = '/htaccesstest.txt';
1232
		$testFile = $config->getSystemValue('datadirectory', OC::$SERVERROOT . '/data') . '/' . $fileName;
1233
1234
		// accessing the file via http
1235
		$url = \OC::$server->getURLGenerator()->getAbsoluteURL(OC::$WEBROOT . '/data' . $fileName);
1236
		try {
1237
			$content = \OC::$server->getHTTPClientService()->newClient()->get($url)->getBody();
1238
		} catch (\Exception $e) {
1239
			$content = false;
1240
		}
1241
1242
		// cleanup
1243
		@unlink($testFile);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
1244
1245
		/*
1246
		 * If the content is not equal to test content our .htaccess
1247
		 * is working as required
1248
		 */
1249
		return $content !== $testContent;
1250
	}
1251
1252
	/**
1253
	 * Check if the setlocal call does not work. This can happen if the right
1254
	 * local packages are not available on the server.
1255
	 *
1256
	 * @return bool
1257
	 */
1258
	public static function isSetLocaleWorking() {
1259
		\Patchwork\Utf8\Bootup::initLocale();
1260
		if ('' === basename('§')) {
1261
			return false;
1262
		}
1263
		return true;
1264
	}
1265
1266
	/**
1267
	 * Check if it's possible to get the inline annotations
1268
	 *
1269
	 * @return bool
1270
	 */
1271
	public static function isAnnotationsWorking() {
1272
		$reflection = new \ReflectionMethod(__METHOD__);
1273
		$docs = $reflection->getDocComment();
1274
1275
		return (is_string($docs) && strlen($docs) > 50);
1276
	}
1277
1278
	/**
1279
	 * Check if the PHP module fileinfo is loaded.
1280
	 *
1281
	 * @return bool
1282
	 */
1283
	public static function fileInfoLoaded() {
1284
		return function_exists('finfo_open');
1285
	}
1286
1287
	/**
1288
	 * clear all levels of output buffering
1289
	 *
1290
	 * @return void
1291
	 */
1292
	public static function obEnd() {
1293
		while (ob_get_level()) {
1294
			ob_end_clean();
1295
		}
1296
	}
1297
1298
	/**
1299
	 * Checks whether the server is running on Mac OS X
1300
	 *
1301
	 * @return bool true if running on Mac OS X, false otherwise
1302
	 */
1303
	public static function runningOnMac() {
1304
		return (strtoupper(substr(PHP_OS, 0, 6)) === 'DARWIN');
1305
	}
1306
1307
	/**
1308
	 * Checks whether server is running on HHVM
1309
	 *
1310
	 * @return bool True if running on HHVM, false otherwise
1311
	 */
1312
	public static function runningOnHhvm() {
1313
		return defined('HHVM_VERSION');
1314
	}
1315
1316
	/**
1317
	 * Handles the case that there may not be a theme, then check if a "default"
1318
	 * theme exists and take that one
1319
	 *
1320
	 * @return string the theme
1321
	 */
1322
	public static function getTheme() {
1323
		$theme = \OC::$server->getSystemConfig()->getValue("theme", '');
1324
1325
		if ($theme === '') {
1326
			if (is_dir(OC::$SERVERROOT . '/themes/default')) {
1327
				$theme = 'default';
1328
			}
1329
		}
1330
1331
		return $theme;
1332
	}
1333
1334
	/**
1335
	 * Clear a single file from the opcode cache
1336
	 * This is useful for writing to the config file
1337
	 * in case the opcode cache does not re-validate files
1338
	 * Returns true if successful, false if unsuccessful:
1339
	 * caller should fall back on clearing the entire cache
1340
	 * with clearOpcodeCache() if unsuccessful
1341
	 *
1342
	 * @param string $path the path of the file to clear from the cache
1343
	 * @return bool true if underlying function returns true, otherwise false
1344
	 */
1345
	public static function deleteFromOpcodeCache($path) {
1346
		$ret = false;
1347
		if ($path) {
1348
			// APC >= 3.1.1
1349
			if (function_exists('apc_delete_file')) {
1350
				$ret = @apc_delete_file($path);
1351
			}
1352
			// Zend OpCache >= 7.0.0, PHP >= 5.5.0
1353
			if (function_exists('opcache_invalidate')) {
1354
				$ret = opcache_invalidate($path);
1355
			}
1356
		}
1357
		return $ret;
1358
	}
1359
1360
	/**
1361
	 * Clear the opcode cache if one exists
1362
	 * This is necessary for writing to the config file
1363
	 * in case the opcode cache does not re-validate files
1364
	 *
1365
	 * @return void
1366
	 * @suppress PhanDeprecatedFunction
1367
	 * @suppress PhanUndeclaredConstant
1368
	 */
1369
	public static function clearOpcodeCache() {
1370
		// APC
1371
		if (function_exists('apc_clear_cache')) {
1372
			apc_clear_cache();
1373
		}
1374
		// Zend Opcache
1375
		if (function_exists('accelerator_reset')) {
1376
			accelerator_reset();
1377
		}
1378
		// XCache
1379
		if (function_exists('xcache_clear_cache')) {
1380
			if (\OC::$server->getIniWrapper()->getBool('xcache.admin.enable_auth')) {
1381
				\OCP\Util::writeLog('core', 'XCache opcode cache will not be cleared because "xcache.admin.enable_auth" is enabled.', \OCP\Util::WARN);
1382
			} else {
1383
				@xcache_clear_cache(XC_TYPE_PHP, 0);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
1384
			}
1385
		}
1386
		// Opcache (PHP >= 5.5)
1387
		if (function_exists('opcache_reset')) {
1388
			opcache_reset();
1389
		}
1390
	}
1391
1392
	/**
1393
	 * Normalize a unicode string
1394
	 *
1395
	 * @param string $value a not normalized string
1396
	 * @return bool|string
1397
	 */
1398
	public static function normalizeUnicode($value) {
1399
		if(Normalizer::isNormalized($value)) {
1400
			return $value;
1401
		}
1402
1403
		$normalizedValue = Normalizer::normalize($value);
1404
		if ($normalizedValue === null || $normalizedValue === false) {
1405
			\OC::$server->getLogger()->warning('normalizing failed for "' . $value . '"', ['app' => 'core']);
1406
			return $value;
1407
		}
1408
1409
		return $normalizedValue;
1410
	}
1411
1412
	/**
1413
	 * A human readable string is generated based on version and build number
1414
	 *
1415
	 * @return string
1416
	 */
1417
	public static function getHumanVersion() {
1418
		$version = OC_Util::getVersionString();
1419
		$build = OC_Util::getBuild();
1420
		if (!empty($build) and OC_Util::getChannel() === 'daily') {
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as and instead of && is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
1421
			$version .= ' Build:' . $build;
1422
		}
1423
		return $version;
1424
	}
1425
1426
	/**
1427
	 * Returns whether the given file name is valid
1428
	 *
1429
	 * @param string $file file name to check
1430
	 * @return bool true if the file name is valid, false otherwise
1431
	 * @deprecated use \OC\Files\View::verifyPath()
1432
	 */
1433
	public static function isValidFileName($file) {
1434
		$trimmed = trim($file);
1435
		if ($trimmed === '') {
1436
			return false;
1437
		}
1438
		if (\OC\Files\Filesystem::isIgnoredDir($trimmed)) {
1439
			return false;
1440
		}
1441
1442
		// detect part files
1443
		if (preg_match('/' . \OCP\Files\FileInfo::BLACKLIST_FILES_REGEX . '/', $trimmed) !== 0) {
1444
			return false;
1445
		}
1446
1447
		foreach (str_split($trimmed) as $char) {
1448
			if (strpos(\OCP\Constants::FILENAME_INVALID_CHARS, $char) !== false) {
1449
				return false;
1450
			}
1451
		}
1452
		return true;
1453
	}
1454
1455
	/**
1456
	 * Check whether the instance needs to perform an upgrade,
1457
	 * either when the core version is higher or any app requires
1458
	 * an upgrade.
1459
	 *
1460
	 * @param \OC\SystemConfig $config
1461
	 * @return bool whether the core or any app needs an upgrade
1462
	 * @throws \OC\HintException When the upgrade from the given version is not allowed
1463
	 */
1464
	public static function needUpgrade(\OC\SystemConfig $config) {
1465
		if ($config->getValue('installed', false)) {
1466
			$installedVersion = $config->getValue('version', '0.0.0');
1467
			$currentVersion = implode('.', \OCP\Util::getVersion());
1468
			$versionDiff = version_compare($currentVersion, $installedVersion);
1469
			if ($versionDiff > 0) {
1470
				return true;
1471
			} else if ($config->getValue('debug', false) && $versionDiff < 0) {
1472
				// downgrade with debug
1473
				$installedMajor = explode('.', $installedVersion);
1474
				$installedMajor = $installedMajor[0] . '.' . $installedMajor[1];
1475
				$currentMajor = explode('.', $currentVersion);
1476
				$currentMajor = $currentMajor[0] . '.' . $currentMajor[1];
1477
				if ($installedMajor === $currentMajor) {
1478
					// Same major, allow downgrade for developers
1479
					return true;
1480
				} else {
1481
					// downgrade attempt, throw exception
1482
					throw new \OC\HintException('Downgrading is not supported and is likely to cause unpredictable issues (from ' . $installedVersion . ' to ' . $currentVersion . ')');
1483
				}
1484
			} else if ($versionDiff < 0) {
1485
				// downgrade attempt, throw exception
1486
				throw new \OC\HintException('Downgrading is not supported and is likely to cause unpredictable issues (from ' . $installedVersion . ' to ' . $currentVersion . ')');
1487
			}
1488
1489
			// also check for upgrades for apps (independently from the user)
1490
			$apps = \OC_App::getEnabledApps(false, true);
1491
			$shouldUpgrade = false;
1492
			foreach ($apps as $app) {
1493
				if (\OC_App::shouldUpgrade($app)) {
1494
					$shouldUpgrade = true;
1495
					break;
1496
				}
1497
			}
1498
			return $shouldUpgrade;
1499
		} else {
1500
			return false;
1501
		}
1502
	}
1503
1504
}
1505