Completed
Push — master ( 6aac1f...83509b )
by Blizzz
21:36 queued 07:41
created

OC_Util::isAnnotationsWorking()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 4
nc 2
nop 0
dl 0
loc 6
rs 9.4285
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 != '' && !OCP\User::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
		$appConfig = \OC::$server->getAppConfig();
304
		$enforcePassword = $appConfig->getValue('core', 'shareapi_enforce_links_password', 'no');
305
		return ($enforcePassword === 'yes') ? true : false;
306
	}
307
308
	/**
309
	 * check if sharing is disabled for the current user
310
	 * @param IConfig $config
311
	 * @param IGroupManager $groupManager
312
	 * @param IUser|null $user
313
	 * @return bool
314
	 */
315
	public static function isSharingDisabledForUser(IConfig $config, IGroupManager $groupManager, $user) {
316
		if ($config->getAppValue('core', 'shareapi_exclude_groups', 'no') === 'yes') {
317
			$groupsList = $config->getAppValue('core', 'shareapi_exclude_groups_list', '');
318
			$excludedGroups = json_decode($groupsList);
319 View Code Duplication
			if (is_null($excludedGroups)) {
320
				$excludedGroups = explode(',', $groupsList);
321
				$newValue = json_encode($excludedGroups);
322
				$config->setAppValue('core', 'shareapi_exclude_groups_list', $newValue);
323
			}
324
			$usersGroups = $groupManager->getUserGroupIds($user);
0 ignored issues
show
Bug introduced by
It seems like $user defined by parameter $user on line 315 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...
325 View Code Duplication
			if (!empty($usersGroups)) {
326
				$remainingGroups = array_diff($usersGroups, $excludedGroups);
327
				// if the user is only in groups which are disabled for sharing then
328
				// sharing is also disabled for the user
329
				if (empty($remainingGroups)) {
330
					return true;
331
				}
332
			}
333
		}
334
		return false;
335
	}
336
337
	/**
338
	 * check if share API enforces a default expire date
339
	 *
340
	 * @return boolean
341
	 * @suppress PhanDeprecatedFunction
342
	 */
343
	public static function isDefaultExpireDateEnforced() {
344
		$isDefaultExpireDateEnabled = \OCP\Config::getAppValue('core', 'shareapi_default_expire_date', 'no');
345
		$enforceDefaultExpireDate = false;
346
		if ($isDefaultExpireDateEnabled === 'yes') {
347
			$value = \OCP\Config::getAppValue('core', 'shareapi_enforce_expire_date', 'no');
348
			$enforceDefaultExpireDate = ($value === 'yes') ? true : false;
349
		}
350
351
		return $enforceDefaultExpireDate;
352
	}
353
354
	/**
355
	 * Get the quota of a user
356
	 *
357
	 * @param string $userId
358
	 * @return float Quota bytes
359
	 */
360
	public static function getUserQuota($userId) {
361
		$user = \OC::$server->getUserManager()->get($userId);
362
		if (is_null($user)) {
363
			return \OCP\Files\FileInfo::SPACE_UNLIMITED;
364
		}
365
		$userQuota = $user->getQuota();
366
		if($userQuota === 'none') {
367
			return \OCP\Files\FileInfo::SPACE_UNLIMITED;
368
		}
369
		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 369 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...
370
	}
371
372
	/**
373
	 * copies the skeleton to the users /files
374
	 *
375
	 * @param String $userId
376
	 * @param \OCP\Files\Folder $userDirectory
377
	 * @throws \RuntimeException
378
	 * @suppress PhanDeprecatedFunction
379
	 */
380
	public static function copySkeleton($userId, \OCP\Files\Folder $userDirectory) {
381
382
		$plainSkeletonDirectory = \OC::$server->getConfig()->getSystemValue('skeletondirectory', \OC::$SERVERROOT . '/core/skeleton');
383
		$userLang = \OC::$server->getL10NFactory()->findLanguage();
384
		$skeletonDirectory = str_replace('{lang}', $userLang, $plainSkeletonDirectory);
385
386
		if (!file_exists($skeletonDirectory)) {
387
			$dialectStart = strpos($userLang, '_');
388
			if ($dialectStart !== false) {
389
				$skeletonDirectory = str_replace('{lang}', substr($userLang, 0, $dialectStart), $plainSkeletonDirectory);
390
			}
391
			if ($dialectStart === false || !file_exists($skeletonDirectory)) {
392
				$skeletonDirectory = str_replace('{lang}', 'default', $plainSkeletonDirectory);
393
			}
394
			if (!file_exists($skeletonDirectory)) {
395
				$skeletonDirectory = '';
396
			}
397
		}
398
399
		$instanceId = \OC::$server->getConfig()->getSystemValue('instanceid', '');
400
401
		if ($instanceId === null) {
402
			throw new \RuntimeException('no instance id!');
403
		}
404
		$appdata = 'appdata_' . $instanceId;
405
		if ($userId === $appdata) {
406
			throw new \RuntimeException('username is reserved name: ' . $appdata);
407
		}
408
409
		if (!empty($skeletonDirectory)) {
410
			\OCP\Util::writeLog(
411
				'files_skeleton',
412
				'copying skeleton for '.$userId.' from '.$skeletonDirectory.' to '.$userDirectory->getFullPath('/'),
413
				\OCP\Util::DEBUG
414
			);
415
			self::copyr($skeletonDirectory, $userDirectory);
416
			// update the file cache
417
			$userDirectory->getStorage()->getScanner()->scan('', \OC\Files\Cache\Scanner::SCAN_RECURSIVE);
418
		}
419
	}
420
421
	/**
422
	 * copies a directory recursively by using streams
423
	 *
424
	 * @param string $source
425
	 * @param \OCP\Files\Folder $target
426
	 * @return void
427
	 */
428
	public static function copyr($source, \OCP\Files\Folder $target) {
429
		$logger = \OC::$server->getLogger();
430
431
		// Verify if folder exists
432
		$dir = opendir($source);
433
		if($dir === false) {
434
			$logger->error(sprintf('Could not opendir "%s"', $source), ['app' => 'core']);
435
			return;
436
		}
437
438
		// Copy the files
439
		while (false !== ($file = readdir($dir))) {
440
			if (!\OC\Files\Filesystem::isIgnoredDir($file)) {
441
				if (is_dir($source . '/' . $file)) {
442
					$child = $target->newFolder($file);
443
					self::copyr($source . '/' . $file, $child);
444
				} else {
445
					$child = $target->newFile($file);
446
					$sourceStream = fopen($source . '/' . $file, 'r');
447
					if($sourceStream === false) {
448
						$logger->error(sprintf('Could not fopen "%s"', $source . '/' . $file), ['app' => 'core']);
449
						closedir($dir);
450
						return;
451
					}
452
					stream_copy_to_stream($sourceStream, $child->fopen('w'));
453
				}
454
			}
455
		}
456
		closedir($dir);
457
	}
458
459
	/**
460
	 * @return void
461
	 * @suppress PhanUndeclaredMethod
462
	 */
463
	public static function tearDownFS() {
464
		\OC\Files\Filesystem::tearDown();
465
		\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...
466
		self::$fsSetup = false;
467
		self::$rootMounted = false;
468
	}
469
470
	/**
471
	 * get the current installed version of ownCloud
472
	 *
473
	 * @return array
474
	 */
475
	public static function getVersion() {
476
		OC_Util::loadVersion();
477
		return self::$versionCache['OC_Version'];
478
	}
479
480
	/**
481
	 * get the current installed version string of ownCloud
482
	 *
483
	 * @return string
484
	 */
485
	public static function getVersionString() {
486
		OC_Util::loadVersion();
487
		return self::$versionCache['OC_VersionString'];
488
	}
489
490
	/**
491
	 * @deprecated the value is of no use anymore
492
	 * @return string
493
	 */
494
	public static function getEditionString() {
495
		return '';
496
	}
497
498
	/**
499
	 * @description get the update channel of the current installed of ownCloud.
500
	 * @return string
501
	 */
502
	public static function getChannel() {
503
		OC_Util::loadVersion();
504
		return \OC::$server->getConfig()->getSystemValue('updater.release.channel', self::$versionCache['OC_Channel']);
505
	}
506
507
	/**
508
	 * @description get the build number of the current installed of ownCloud.
509
	 * @return string
510
	 */
511
	public static function getBuild() {
512
		OC_Util::loadVersion();
513
		return self::$versionCache['OC_Build'];
514
	}
515
516
	/**
517
	 * @description load the version.php into the session as cache
518
	 * @suppress PhanUndeclaredVariable
519
	 */
520
	private static function loadVersion() {
521
		if (self::$versionCache !== null) {
522
			return;
523
		}
524
525
		$timestamp = filemtime(OC::$SERVERROOT . '/version.php');
526
		require OC::$SERVERROOT . '/version.php';
527
		/** @var $timestamp int */
528
		self::$versionCache['OC_Version_Timestamp'] = $timestamp;
529
		/** @var $OC_Version string */
530
		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...
531
		/** @var $OC_VersionString string */
532
		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...
533
		/** @var $OC_Build string */
534
		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...
535
536
		/** @var $OC_Channel string */
537
		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...
538
	}
539
540
	/**
541
	 * generates a path for JS/CSS files. If no application is provided it will create the path for core.
542
	 *
543
	 * @param string $application application to get the files from
544
	 * @param string $directory directory within this application (css, js, vendor, etc)
545
	 * @param string $file the file inside of the above folder
546
	 * @return string the path
547
	 */
548
	private static function generatePath($application, $directory, $file) {
549
		if (is_null($file)) {
550
			$file = $application;
551
			$application = "";
552
		}
553
		if (!empty($application)) {
554
			return "$application/$directory/$file";
555
		} else {
556
			return "$directory/$file";
557
		}
558
	}
559
560
	/**
561
	 * add a javascript file
562
	 *
563
	 * @param string $application application id
564
	 * @param string|null $file filename
565
	 * @param bool $prepend prepend the Script to the beginning of the list
566
	 * @return void
567
	 */
568
	public static function addScript($application, $file = null, $prepend = false) {
569
		$path = OC_Util::generatePath($application, 'js', $file);
570
571
		// core js files need separate handling
572
		if ($application !== 'core' && $file !== null) {
573
			self::addTranslations ( $application );
574
		}
575
		self::addExternalResource($application, $prepend, $path, "script");
576
	}
577
578
	/**
579
	 * add a javascript file from the vendor sub folder
580
	 *
581
	 * @param string $application application id
582
	 * @param string|null $file filename
583
	 * @param bool $prepend prepend the Script to the beginning of the list
584
	 * @return void
585
	 */
586
	public static function addVendorScript($application, $file = null, $prepend = false) {
587
		$path = OC_Util::generatePath($application, 'vendor', $file);
588
		self::addExternalResource($application, $prepend, $path, "script");
589
	}
590
591
	/**
592
	 * add a translation JS file
593
	 *
594
	 * @param string $application application id
595
	 * @param string|null $languageCode language code, defaults to the current language
596
	 * @param bool|null $prepend prepend the Script to the beginning of the list
597
	 */
598
	public static function addTranslations($application, $languageCode = null, $prepend = false) {
599
		if (is_null($languageCode)) {
600
			$languageCode = \OC::$server->getL10NFactory()->findLanguage($application);
601
		}
602
		if (!empty($application)) {
603
			$path = "$application/l10n/$languageCode";
604
		} else {
605
			$path = "l10n/$languageCode";
606
		}
607
		self::addExternalResource($application, $prepend, $path, "script");
0 ignored issues
show
Bug introduced by
It seems like $prepend defined by parameter $prepend on line 598 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...
608
	}
609
610
	/**
611
	 * add a css file
612
	 *
613
	 * @param string $application application id
614
	 * @param string|null $file filename
615
	 * @param bool $prepend prepend the Style to the beginning of the list
616
	 * @return void
617
	 */
618
	public static function addStyle($application, $file = null, $prepend = false) {
619
		$path = OC_Util::generatePath($application, 'css', $file);
620
		self::addExternalResource($application, $prepend, $path, "style");
621
	}
622
623
	/**
624
	 * add a css file from the vendor sub folder
625
	 *
626
	 * @param string $application application id
627
	 * @param string|null $file filename
628
	 * @param bool $prepend prepend the Style to the beginning of the list
629
	 * @return void
630
	 */
631
	public static function addVendorStyle($application, $file = null, $prepend = false) {
632
		$path = OC_Util::generatePath($application, 'vendor', $file);
633
		self::addExternalResource($application, $prepend, $path, "style");
634
	}
635
636
	/**
637
	 * add an external resource css/js file
638
	 *
639
	 * @param string $application application id
640
	 * @param bool $prepend prepend the file to the beginning of the list
641
	 * @param string $path
642
	 * @param string $type (script or style)
643
	 * @return void
644
	 */
645
	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...
646
647
		if ($type === "style") {
648 View Code Duplication
			if (!in_array($path, self::$styles)) {
649
				if ($prepend === true) {
650
					array_unshift ( self::$styles, $path );
651
				} else {
652
					self::$styles[] = $path;
653
				}
654
			}
655 View Code Duplication
		} elseif ($type === "script") {
656
			if (!in_array($path, self::$scripts)) {
657
				if ($prepend === true) {
658
					array_unshift ( self::$scripts, $path );
659
				} else {
660
					self::$scripts [] = $path;
661
				}
662
			}
663
		}
664
	}
665
666
	/**
667
	 * Add a custom element to the header
668
	 * If $text is null then the element will be written as empty element.
669
	 * So use "" to get a closing tag.
670
	 * @param string $tag tag name of the element
671
	 * @param array $attributes array of attributes for the element
672
	 * @param string $text the text content for the element
673
	 */
674 View Code Duplication
	public static function addHeader($tag, $attributes, $text=null) {
675
		self::$headers[] = array(
676
			'tag' => $tag,
677
			'attributes' => $attributes,
678
			'text' => $text
679
		);
680
	}
681
682
	/**
683
	 * formats a timestamp in the "right" way
684
	 *
685
	 * @param int $timestamp
686
	 * @param bool $dateOnly option to omit time from the result
687
	 * @param DateTimeZone|string $timeZone where the given timestamp shall be converted to
688
	 * @return string timestamp
689
	 *
690
	 * @deprecated Use \OC::$server->query('DateTimeFormatter') instead
691
	 */
692
	public static function formatDate($timestamp, $dateOnly = false, $timeZone = null) {
693
		if ($timeZone !== null && !$timeZone instanceof \DateTimeZone) {
694
			$timeZone = new \DateTimeZone($timeZone);
695
		}
696
697
		/** @var \OC\DateTimeFormatter $formatter */
698
		$formatter = \OC::$server->query('DateTimeFormatter');
699
		if ($dateOnly) {
700
			return $formatter->formatDate($timestamp, 'long', $timeZone);
701
		}
702
		return $formatter->formatDateTime($timestamp, 'long', 'long', $timeZone);
703
	}
704
705
	/**
706
	 * check if the current server configuration is suitable for ownCloud
707
	 *
708
	 * @param \OC\SystemConfig $config
709
	 * @return array arrays with error messages and hints
710
	 */
711
	public static function checkServer(\OC\SystemConfig $config) {
712
		$l = \OC::$server->getL10N('lib');
713
		$errors = array();
714
		$CONFIG_DATADIRECTORY = $config->getValue('datadirectory', OC::$SERVERROOT . '/data');
715
716
		if (!self::needUpgrade($config) && $config->getValue('installed', false)) {
717
			// this check needs to be done every time
718
			$errors = self::checkDataDirectoryValidity($CONFIG_DATADIRECTORY);
719
		}
720
721
		// Assume that if checkServer() succeeded before in this session, then all is fine.
722
		if (\OC::$server->getSession()->exists('checkServer_succeeded') && \OC::$server->getSession()->get('checkServer_succeeded')) {
723
			return $errors;
724
		}
725
726
		$webServerRestart = false;
727
		$setup = new \OC\Setup(
728
			$config,
729
			\OC::$server->getIniWrapper(),
730
			\OC::$server->getL10N('lib'),
731
			\OC::$server->query(\OCP\Defaults::class),
732
			\OC::$server->getLogger(),
733
			\OC::$server->getSecureRandom(),
734
			\OC::$server->query(\OC\Installer::class)
735
		);
736
737
		$urlGenerator = \OC::$server->getURLGenerator();
738
739
		$availableDatabases = $setup->getSupportedDatabases();
740
		if (empty($availableDatabases)) {
741
			$errors[] = array(
742
				'error' => $l->t('No database drivers (sqlite, mysql, or postgresql) installed.'),
743
				'hint' => '' //TODO: sane hint
744
			);
745
			$webServerRestart = true;
746
		}
747
748
		// Check if config folder is writable.
749
		if(!OC_Helper::isReadOnlyConfigEnabled()) {
750
			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...
751
				$errors[] = array(
752
					'error' => $l->t('Cannot write into "config" directory'),
753
					'hint' => $l->t('This can usually be fixed by giving the webserver write access to the config directory. See %s',
754
						[$urlGenerator->linkToDocs('admin-dir_permissions')])
755
				);
756
			}
757
		}
758
759
		// Check if there is a writable install folder.
760
		if ($config->getValue('appstoreenabled', true)) {
761
			if (OC_App::getInstallPath() === null
762
				|| !is_writable(OC_App::getInstallPath())
763
				|| !is_readable(OC_App::getInstallPath())
764
			) {
765
				$errors[] = array(
766
					'error' => $l->t('Cannot write into "apps" directory'),
767
					'hint' => $l->t('This can usually be fixed by giving the webserver write access to the apps directory'
768
						. ' or disabling the appstore in the config file. See %s',
769
						[$urlGenerator->linkToDocs('admin-dir_permissions')])
770
				);
771
			}
772
		}
773
		// Create root dir.
774
		if ($config->getValue('installed', false)) {
775
			if (!is_dir($CONFIG_DATADIRECTORY)) {
776
				$success = @mkdir($CONFIG_DATADIRECTORY);
777
				if ($success) {
778
					$errors = array_merge($errors, self::checkDataDirectoryPermissions($CONFIG_DATADIRECTORY));
779
				} else {
780
					$errors[] = [
781
						'error' => $l->t('Cannot create "data" directory'),
782
						'hint' => $l->t('This can usually be fixed by giving the webserver write access to the root directory. See %s',
783
							[$urlGenerator->linkToDocs('admin-dir_permissions')])
784
					];
785
				}
786
			} 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...
787
				//common hint for all file permissions error messages
788
				$permissionsHint = $l->t('Permissions can usually be fixed by giving the webserver write access to the root directory. See %s.',
789
					[$urlGenerator->linkToDocs('admin-dir_permissions')]);
790
				$errors[] = [
791
					'error' => 'Your data directory is not writable',
792
					'hint' => $permissionsHint
793
				];
794
			} else {
795
				$errors = array_merge($errors, self::checkDataDirectoryPermissions($CONFIG_DATADIRECTORY));
796
			}
797
		}
798
799
		if (!OC_Util::isSetLocaleWorking()) {
800
			$errors[] = array(
801
				'error' => $l->t('Setting locale to %s failed',
802
					array('en_US.UTF-8/fr_FR.UTF-8/es_ES.UTF-8/de_DE.UTF-8/ru_RU.UTF-8/'
803
						. 'pt_BR.UTF-8/it_IT.UTF-8/ja_JP.UTF-8/zh_CN.UTF-8')),
804
				'hint' => $l->t('Please install one of these locales on your system and restart your webserver.')
805
			);
806
		}
807
808
		// Contains the dependencies that should be checked against
809
		// classes = class_exists
810
		// functions = function_exists
811
		// defined = defined
812
		// ini = ini_get
813
		// If the dependency is not found the missing module name is shown to the EndUser
814
		// When adding new checks always verify that they pass on Travis as well
815
		// for ini settings, see https://github.com/owncloud/administration/blob/master/travis-ci/custom.ini
816
		$dependencies = array(
817
			'classes' => array(
818
				'ZipArchive' => 'zip',
819
				'DOMDocument' => 'dom',
820
				'XMLWriter' => 'XMLWriter',
821
				'XMLReader' => 'XMLReader',
822
			),
823
			'functions' => [
824
				'xml_parser_create' => 'libxml',
825
				'mb_strcut' => 'mb multibyte',
826
				'ctype_digit' => 'ctype',
827
				'json_encode' => 'JSON',
828
				'gd_info' => 'GD',
829
				'gzencode' => 'zlib',
830
				'iconv' => 'iconv',
831
				'simplexml_load_string' => 'SimpleXML',
832
				'hash' => 'HASH Message Digest Framework',
833
				'curl_init' => 'cURL',
834
				'openssl_verify' => 'OpenSSL',
835
			],
836
			'defined' => array(
837
				'PDO::ATTR_DRIVER_NAME' => 'PDO'
838
			),
839
			'ini' => [
840
				'default_charset' => 'UTF-8',
841
			],
842
		);
843
		$missingDependencies = array();
844
		$invalidIniSettings = [];
845
		$moduleHint = $l->t('Please ask your server administrator to install the module.');
846
847
		/**
848
		 * FIXME: The dependency check does not work properly on HHVM on the moment
849
		 *        and prevents installation. Once HHVM is more compatible with our
850
		 *        approach to check for these values we should re-enable those
851
		 *        checks.
852
		 */
853
		$iniWrapper = \OC::$server->getIniWrapper();
854
		if (!self::runningOnHhvm()) {
855
			foreach ($dependencies['classes'] as $class => $module) {
856
				if (!class_exists($class)) {
857
					$missingDependencies[] = $module;
858
				}
859
			}
860
			foreach ($dependencies['functions'] as $function => $module) {
861
				if (!function_exists($function)) {
862
					$missingDependencies[] = $module;
863
				}
864
			}
865
			foreach ($dependencies['defined'] as $defined => $module) {
866
				if (!defined($defined)) {
867
					$missingDependencies[] = $module;
868
				}
869
			}
870
			foreach ($dependencies['ini'] as $setting => $expected) {
871
				if (is_bool($expected)) {
872
					if ($iniWrapper->getBool($setting) !== $expected) {
873
						$invalidIniSettings[] = [$setting, $expected];
874
					}
875
				}
876
				if (is_int($expected)) {
877
					if ($iniWrapper->getNumeric($setting) !== $expected) {
878
						$invalidIniSettings[] = [$setting, $expected];
879
					}
880
				}
881
				if (is_string($expected)) {
882
					if (strtolower($iniWrapper->getString($setting)) !== strtolower($expected)) {
883
						$invalidIniSettings[] = [$setting, $expected];
884
					}
885
				}
886
			}
887
		}
888
889
		foreach($missingDependencies as $missingDependency) {
890
			$errors[] = array(
891
				'error' => $l->t('PHP module %s not installed.', array($missingDependency)),
892
				'hint' => $moduleHint
893
			);
894
			$webServerRestart = true;
895
		}
896
		foreach($invalidIniSettings as $setting) {
897
			if(is_bool($setting[1])) {
898
				$setting[1] = ($setting[1]) ? 'on' : 'off';
899
			}
900
			$errors[] = [
901
				'error' => $l->t('PHP setting "%s" is not set to "%s".', [$setting[0], var_export($setting[1], true)]),
902
				'hint' =>  $l->t('Adjusting this setting in php.ini will make Nextcloud run again')
903
			];
904
			$webServerRestart = true;
905
		}
906
907
		/**
908
		 * The mbstring.func_overload check can only be performed if the mbstring
909
		 * module is installed as it will return null if the checking setting is
910
		 * not available and thus a check on the boolean value fails.
911
		 *
912
		 * TODO: Should probably be implemented in the above generic dependency
913
		 *       check somehow in the long-term.
914
		 */
915
		if($iniWrapper->getBool('mbstring.func_overload') !== null &&
916
			$iniWrapper->getBool('mbstring.func_overload') === true) {
917
			$errors[] = array(
918
				'error' => $l->t('mbstring.func_overload is set to "%s" instead of the expected value "0"', [$iniWrapper->getString('mbstring.func_overload')]),
919
				'hint' => $l->t('To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini')
920
			);
921
		}
922
923
		if(function_exists('xml_parser_create') &&
924
			LIBXML_LOADED_VERSION < 20700 ) {
925
			$version = LIBXML_LOADED_VERSION;
926
			$major = floor($version/10000);
927
			$version -= ($major * 10000);
928
			$minor = floor($version/100);
929
			$version -= ($minor * 100);
930
			$patch = $version;
931
			$errors[] = array(
932
				'error' => $l->t('libxml2 2.7.0 is at least required. Currently %s is installed.', [$major . '.' . $minor . '.' . $patch]),
933
				'hint' => $l->t('To fix this issue update your libxml2 version and restart your web server.')
934
			);
935
		}
936
937
		if (!self::isAnnotationsWorking()) {
938
			$errors[] = array(
939
				'error' => $l->t('PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible.'),
940
				'hint' => $l->t('This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator.')
941
			);
942
		}
943
944
		if (!\OC::$CLI && $webServerRestart) {
945
			$errors[] = array(
946
				'error' => $l->t('PHP modules have been installed, but they are still listed as missing?'),
947
				'hint' => $l->t('Please ask your server administrator to restart the web server.')
948
			);
949
		}
950
951
		$errors = array_merge($errors, self::checkDatabaseVersion());
952
953
		// Cache the result of this function
954
		\OC::$server->getSession()->set('checkServer_succeeded', count($errors) == 0);
955
956
		return $errors;
957
	}
958
959
	/**
960
	 * Check the database version
961
	 *
962
	 * @return array errors array
963
	 */
964
	public static function checkDatabaseVersion() {
965
		$l = \OC::$server->getL10N('lib');
966
		$errors = array();
967
		$dbType = \OC::$server->getSystemConfig()->getValue('dbtype', 'sqlite');
968
		if ($dbType === 'pgsql') {
969
			// check PostgreSQL version
970
			try {
971
				$result = \OC_DB::executeAudited('SHOW SERVER_VERSION');
972
				$data = $result->fetchRow();
973
				if (isset($data['server_version'])) {
974
					$version = $data['server_version'];
975
					if (version_compare($version, '9.0.0', '<')) {
976
						$errors[] = array(
977
							'error' => $l->t('PostgreSQL >= 9 required'),
978
							'hint' => $l->t('Please upgrade your database version')
979
						);
980
					}
981
				}
982
			} 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...
983
				$logger = \OC::$server->getLogger();
984
				$logger->warning('Error occurred while checking PostgreSQL version, assuming >= 9');
985
				$logger->logException($e);
986
			}
987
		}
988
		return $errors;
989
	}
990
991
	/**
992
	 * Check for correct file permissions of data directory
993
	 *
994
	 * @param string $dataDirectory
995
	 * @return array arrays with error messages and hints
996
	 */
997
	public static function checkDataDirectoryPermissions($dataDirectory) {
998
		if(\OC::$server->getConfig()->getSystemValue('check_data_directory_permissions', true) === false) {
999
			return  [];
1000
		}
1001
		$l = \OC::$server->getL10N('lib');
1002
		$errors = [];
1003
		$permissionsModHint = $l->t('Please change the permissions to 0770 so that the directory'
1004
			. ' cannot be listed by other users.');
1005
		$perms = substr(decoct(@fileperms($dataDirectory)), -3);
1006
		if (substr($perms, -1) !== '0') {
1007
			chmod($dataDirectory, 0770);
1008
			clearstatcache();
1009
			$perms = substr(decoct(@fileperms($dataDirectory)), -3);
1010
			if ($perms[2] !== '0') {
1011
				$errors[] = [
1012
					'error' => $l->t('Your data directory is readable by other users'),
1013
					'hint' => $permissionsModHint
1014
				];
1015
			}
1016
		}
1017
		return $errors;
1018
	}
1019
1020
	/**
1021
	 * Check that the data directory exists and is valid by
1022
	 * checking the existence of the ".ocdata" file.
1023
	 *
1024
	 * @param string $dataDirectory data directory path
1025
	 * @return array errors found
1026
	 */
1027
	public static function checkDataDirectoryValidity($dataDirectory) {
1028
		$l = \OC::$server->getL10N('lib');
1029
		$errors = [];
1030 View Code Duplication
		if ($dataDirectory[0] !== '/') {
1031
			$errors[] = [
1032
				'error' => $l->t('Your data directory must be an absolute path'),
1033
				'hint' => $l->t('Check the value of "datadirectory" in your configuration')
1034
			];
1035
		}
1036 View Code Duplication
		if (!file_exists($dataDirectory . '/.ocdata')) {
1037
			$errors[] = [
1038
				'error' => $l->t('Your data directory is invalid'),
1039
				'hint' => $l->t('Ensure there is a file called ".ocdata"' .
1040
					' in the root of the data directory.')
1041
			];
1042
		}
1043
		return $errors;
1044
	}
1045
1046
	/**
1047
	 * Check if the user is logged in, redirects to home if not. With
1048
	 * redirect URL parameter to the request URI.
1049
	 *
1050
	 * @return void
1051
	 */
1052
	public static function checkLoggedIn() {
1053
		// Check if we are a user
1054
		if (!\OC::$server->getUserSession()->isLoggedIn()) {
1055
			header('Location: ' . \OC::$server->getURLGenerator()->linkToRoute(
1056
						'core.login.showLoginForm',
1057
						[
1058
							'redirect_url' => \OC::$server->getRequest()->getRequestUri(),
1059
						]
1060
					)
1061
			);
1062
			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...
1063
		}
1064
		// Redirect to 2FA challenge selection if 2FA challenge was not solved yet
1065
		if (\OC::$server->getTwoFactorAuthManager()->needsSecondFactor(\OC::$server->getUserSession()->getUser())) {
1066
			header('Location: ' . \OC::$server->getURLGenerator()->linkToRoute('core.TwoFactorChallenge.selectChallenge'));
1067
			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...
1068
		}
1069
	}
1070
1071
	/**
1072
	 * Check if the user is a admin, redirects to home if not
1073
	 *
1074
	 * @return void
1075
	 */
1076
	public static function checkAdminUser() {
1077
		OC_Util::checkLoggedIn();
1078
		if (!OC_User::isAdminUser(OC_User::getUser())) {
1079
			header('Location: ' . \OCP\Util::linkToAbsolute('', 'index.php'));
1080
			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...
1081
		}
1082
	}
1083
1084
	/**
1085
	 * Check if the user is a subadmin, redirects to home if not
1086
	 *
1087
	 * @return null|boolean $groups where the current user is subadmin
1088
	 */
1089
	public static function checkSubAdminUser() {
1090
		OC_Util::checkLoggedIn();
1091
		$userObject = \OC::$server->getUserSession()->getUser();
1092
		$isSubAdmin = false;
1093
		if($userObject !== null) {
1094
			$isSubAdmin = \OC::$server->getGroupManager()->getSubAdmin()->isSubAdmin($userObject);
1095
		}
1096
1097
		if (!$isSubAdmin) {
1098
			header('Location: ' . \OCP\Util::linkToAbsolute('', 'index.php'));
1099
			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...
1100
		}
1101
		return true;
1102
	}
1103
1104
	/**
1105
	 * Returns the URL of the default page
1106
	 * based on the system configuration and
1107
	 * the apps visible for the current user
1108
	 *
1109
	 * @return string URL
1110
	 * @suppress PhanDeprecatedFunction
1111
	 */
1112
	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...
1113
		$urlGenerator = \OC::$server->getURLGenerator();
1114
		// Deny the redirect if the URL contains a @
1115
		// This prevents unvalidated redirects like ?redirect_url=:[email protected]
1116
		if (isset($_REQUEST['redirect_url']) && strpos($_REQUEST['redirect_url'], '@') === false) {
1117
			$location = $urlGenerator->getAbsoluteURL(urldecode($_REQUEST['redirect_url']));
1118
		} else {
1119
			$defaultPage = \OC::$server->getAppConfig()->getValue('core', 'defaultpage');
1120
			if ($defaultPage) {
1121
				$location = $urlGenerator->getAbsoluteURL($defaultPage);
1122
			} else {
1123
				$appId = 'files';
1124
				$config = \OC::$server->getConfig();
1125
				$defaultApps = explode(',', $config->getSystemValue('defaultapp', 'files'));
1126
				// find the first app that is enabled for the current user
1127
				foreach ($defaultApps as $defaultApp) {
1128
					$defaultApp = OC_App::cleanAppId(strip_tags($defaultApp));
1129
					if (static::getAppManager()->isEnabledForUser($defaultApp)) {
1130
						$appId = $defaultApp;
1131
						break;
1132
					}
1133
				}
1134
1135
				if($config->getSystemValue('htaccess.IgnoreFrontController', false) === true || getenv('front_controller_active') === 'true') {
1136
					$location = $urlGenerator->getAbsoluteURL('/apps/' . $appId . '/');
1137
				} else {
1138
					$location = $urlGenerator->getAbsoluteURL('/index.php/apps/' . $appId . '/');
1139
				}
1140
			}
1141
		}
1142
		return $location;
1143
	}
1144
1145
	/**
1146
	 * Redirect to the user default page
1147
	 *
1148
	 * @return void
1149
	 */
1150
	public static function redirectToDefaultPage() {
1151
		$location = self::getDefaultPageUrl();
1152
		header('Location: ' . $location);
1153
		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...
1154
	}
1155
1156
	/**
1157
	 * get an id unique for this instance
1158
	 *
1159
	 * @return string
1160
	 */
1161
	public static function getInstanceId() {
1162
		$id = \OC::$server->getSystemConfig()->getValue('instanceid', null);
1163
		if (is_null($id)) {
1164
			// We need to guarantee at least one letter in instanceid so it can be used as the session_name
1165
			$id = 'oc' . \OC::$server->getSecureRandom()->generate(10, \OCP\Security\ISecureRandom::CHAR_LOWER.\OCP\Security\ISecureRandom::CHAR_DIGITS);
1166
			\OC::$server->getSystemConfig()->setValue('instanceid', $id);
1167
		}
1168
		return $id;
1169
	}
1170
1171
	/**
1172
	 * Public function to sanitize HTML
1173
	 *
1174
	 * This function is used to sanitize HTML and should be applied on any
1175
	 * string or array of strings before displaying it on a web page.
1176
	 *
1177
	 * @param string|array $value
1178
	 * @return string|array an array of sanitized strings or a single sanitized string, depends on the input parameter.
1179
	 */
1180
	public static function sanitizeHTML($value) {
1181
		if (is_array($value)) {
1182
			$value = array_map(function($value) {
1183
				return self::sanitizeHTML($value);
1184
			}, $value);
1185
		} else {
1186
			// Specify encoding for PHP<5.4
1187
			$value = htmlspecialchars((string)$value, ENT_QUOTES, 'UTF-8');
1188
		}
1189
		return $value;
1190
	}
1191
1192
	/**
1193
	 * Public function to encode url parameters
1194
	 *
1195
	 * This function is used to encode path to file before output.
1196
	 * Encoding is done according to RFC 3986 with one exception:
1197
	 * Character '/' is preserved as is.
1198
	 *
1199
	 * @param string $component part of URI to encode
1200
	 * @return string
1201
	 */
1202
	public static function encodePath($component) {
1203
		$encoded = rawurlencode($component);
1204
		$encoded = str_replace('%2F', '/', $encoded);
1205
		return $encoded;
1206
	}
1207
1208
1209
	public function createHtaccessTestFile(\OCP\IConfig $config) {
1210
		// php dev server does not support htaccess
1211
		if (php_sapi_name() === 'cli-server') {
1212
			return false;
1213
		}
1214
1215
		// testdata
1216
		$fileName = '/htaccesstest.txt';
1217
		$testContent = 'This is used for testing whether htaccess is properly enabled to disallow access from the outside. This file can be safely removed.';
1218
1219
		// creating a test file
1220
		$testFile = $config->getSystemValue('datadirectory', OC::$SERVERROOT . '/data') . '/' . $fileName;
1221
1222
		if (file_exists($testFile)) {// already running this test, possible recursive call
1223
			return false;
1224
		}
1225
1226
		$fp = @fopen($testFile, 'w');
1227
		if (!$fp) {
1228
			throw new OC\HintException('Can\'t create test file to check for working .htaccess file.',
1229
				'Make sure it is possible for the webserver to write to ' . $testFile);
1230
		}
1231
		fwrite($fp, $testContent);
1232
		fclose($fp);
1233
1234
		return $testContent;
1235
	}
1236
1237
	/**
1238
	 * Check if the .htaccess file is working
1239
	 * @param \OCP\IConfig $config
1240
	 * @return bool
1241
	 * @throws Exception
1242
	 * @throws \OC\HintException If the test file can't get written.
1243
	 */
1244
	public function isHtaccessWorking(\OCP\IConfig $config) {
1245
1246
		if (\OC::$CLI || !$config->getSystemValue('check_for_working_htaccess', true)) {
1247
			return true;
1248
		}
1249
1250
		$testContent = $this->createHtaccessTestFile($config);
1251
		if ($testContent === false) {
1252
			return false;
1253
		}
1254
1255
		$fileName = '/htaccesstest.txt';
1256
		$testFile = $config->getSystemValue('datadirectory', OC::$SERVERROOT . '/data') . '/' . $fileName;
1257
1258
		// accessing the file via http
1259
		$url = \OC::$server->getURLGenerator()->getAbsoluteURL(OC::$WEBROOT . '/data' . $fileName);
1260
		try {
1261
			$content = \OC::$server->getHTTPClientService()->newClient()->get($url)->getBody();
1262
		} catch (\Exception $e) {
1263
			$content = false;
1264
		}
1265
1266
		// cleanup
1267
		@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...
1268
1269
		/*
1270
		 * If the content is not equal to test content our .htaccess
1271
		 * is working as required
1272
		 */
1273
		return $content !== $testContent;
1274
	}
1275
1276
	/**
1277
	 * Check if the setlocal call does not work. This can happen if the right
1278
	 * local packages are not available on the server.
1279
	 *
1280
	 * @return bool
1281
	 */
1282
	public static function isSetLocaleWorking() {
1283
		\Patchwork\Utf8\Bootup::initLocale();
1284
		if ('' === basename('§')) {
1285
			return false;
1286
		}
1287
		return true;
1288
	}
1289
1290
	/**
1291
	 * Check if it's possible to get the inline annotations
1292
	 *
1293
	 * @return bool
1294
	 */
1295
	public static function isAnnotationsWorking() {
1296
		$reflection = new \ReflectionMethod(__METHOD__);
1297
		$docs = $reflection->getDocComment();
1298
1299
		return (is_string($docs) && strlen($docs) > 50);
1300
	}
1301
1302
	/**
1303
	 * Check if the PHP module fileinfo is loaded.
1304
	 *
1305
	 * @return bool
1306
	 */
1307
	public static function fileInfoLoaded() {
1308
		return function_exists('finfo_open');
1309
	}
1310
1311
	/**
1312
	 * clear all levels of output buffering
1313
	 *
1314
	 * @return void
1315
	 */
1316
	public static function obEnd() {
1317
		while (ob_get_level()) {
1318
			ob_end_clean();
1319
		}
1320
	}
1321
1322
	/**
1323
	 * Checks whether the server is running on Mac OS X
1324
	 *
1325
	 * @return bool true if running on Mac OS X, false otherwise
1326
	 */
1327
	public static function runningOnMac() {
1328
		return (strtoupper(substr(PHP_OS, 0, 6)) === 'DARWIN');
1329
	}
1330
1331
	/**
1332
	 * Checks whether server is running on HHVM
1333
	 *
1334
	 * @return bool True if running on HHVM, false otherwise
1335
	 */
1336
	public static function runningOnHhvm() {
1337
		return defined('HHVM_VERSION');
1338
	}
1339
1340
	/**
1341
	 * Handles the case that there may not be a theme, then check if a "default"
1342
	 * theme exists and take that one
1343
	 *
1344
	 * @return string the theme
1345
	 */
1346
	public static function getTheme() {
1347
		$theme = \OC::$server->getSystemConfig()->getValue("theme", '');
1348
1349
		if ($theme === '') {
1350
			if (is_dir(OC::$SERVERROOT . '/themes/default')) {
1351
				$theme = 'default';
1352
			}
1353
		}
1354
1355
		return $theme;
1356
	}
1357
1358
	/**
1359
	 * Clear a single file from the opcode cache
1360
	 * This is useful for writing to the config file
1361
	 * in case the opcode cache does not re-validate files
1362
	 * Returns true if successful, false if unsuccessful:
1363
	 * caller should fall back on clearing the entire cache
1364
	 * with clearOpcodeCache() if unsuccessful
1365
	 *
1366
	 * @param string $path the path of the file to clear from the cache
1367
	 * @return bool true if underlying function returns true, otherwise false
1368
	 */
1369
	public static function deleteFromOpcodeCache($path) {
1370
		$ret = false;
1371
		if ($path) {
1372
			// APC >= 3.1.1
1373
			if (function_exists('apc_delete_file')) {
1374
				$ret = @apc_delete_file($path);
1375
			}
1376
			// Zend OpCache >= 7.0.0, PHP >= 5.5.0
1377
			if (function_exists('opcache_invalidate')) {
1378
				$ret = opcache_invalidate($path);
1379
			}
1380
		}
1381
		return $ret;
1382
	}
1383
1384
	/**
1385
	 * Clear the opcode cache if one exists
1386
	 * This is necessary for writing to the config file
1387
	 * in case the opcode cache does not re-validate files
1388
	 *
1389
	 * @return void
1390
	 * @suppress PhanDeprecatedFunction
1391
	 * @suppress PhanUndeclaredConstant
1392
	 */
1393
	public static function clearOpcodeCache() {
1394
		// APC
1395
		if (function_exists('apc_clear_cache')) {
1396
			apc_clear_cache();
1397
		}
1398
		// Zend Opcache
1399
		if (function_exists('accelerator_reset')) {
1400
			accelerator_reset();
1401
		}
1402
		// XCache
1403
		if (function_exists('xcache_clear_cache')) {
1404
			if (\OC::$server->getIniWrapper()->getBool('xcache.admin.enable_auth')) {
1405
				\OCP\Util::writeLog('core', 'XCache opcode cache will not be cleared because "xcache.admin.enable_auth" is enabled.', \OCP\Util::WARN);
1406
			} else {
1407
				@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...
1408
			}
1409
		}
1410
		// Opcache (PHP >= 5.5)
1411
		if (function_exists('opcache_reset')) {
1412
			opcache_reset();
1413
		}
1414
	}
1415
1416
	/**
1417
	 * Normalize a unicode string
1418
	 *
1419
	 * @param string $value a not normalized string
1420
	 * @return bool|string
1421
	 */
1422
	public static function normalizeUnicode($value) {
1423
		if(Normalizer::isNormalized($value)) {
1424
			return $value;
1425
		}
1426
1427
		$normalizedValue = Normalizer::normalize($value);
1428
		if ($normalizedValue === null || $normalizedValue === false) {
1429
			\OC::$server->getLogger()->warning('normalizing failed for "' . $value . '"', ['app' => 'core']);
1430
			return $value;
1431
		}
1432
1433
		return $normalizedValue;
1434
	}
1435
1436
	/**
1437
	 * A human readable string is generated based on version and build number
1438
	 *
1439
	 * @return string
1440
	 */
1441
	public static function getHumanVersion() {
1442
		$version = OC_Util::getVersionString();
1443
		$build = OC_Util::getBuild();
1444
		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...
1445
			$version .= ' Build:' . $build;
1446
		}
1447
		return $version;
1448
	}
1449
1450
	/**
1451
	 * Returns whether the given file name is valid
1452
	 *
1453
	 * @param string $file file name to check
1454
	 * @return bool true if the file name is valid, false otherwise
1455
	 * @deprecated use \OC\Files\View::verifyPath()
1456
	 */
1457
	public static function isValidFileName($file) {
1458
		$trimmed = trim($file);
1459
		if ($trimmed === '') {
1460
			return false;
1461
		}
1462
		if (\OC\Files\Filesystem::isIgnoredDir($trimmed)) {
1463
			return false;
1464
		}
1465
1466
		// detect part files
1467
		if (preg_match('/' . \OCP\Files\FileInfo::BLACKLIST_FILES_REGEX . '/', $trimmed) !== 0) {
1468
			return false;
1469
		}
1470
1471
		foreach (str_split($trimmed) as $char) {
1472
			if (strpos(\OCP\Constants::FILENAME_INVALID_CHARS, $char) !== false) {
1473
				return false;
1474
			}
1475
		}
1476
		return true;
1477
	}
1478
1479
	/**
1480
	 * Check whether the instance needs to perform an upgrade,
1481
	 * either when the core version is higher or any app requires
1482
	 * an upgrade.
1483
	 *
1484
	 * @param \OC\SystemConfig $config
1485
	 * @return bool whether the core or any app needs an upgrade
1486
	 * @throws \OC\HintException When the upgrade from the given version is not allowed
1487
	 */
1488
	public static function needUpgrade(\OC\SystemConfig $config) {
1489
		if ($config->getValue('installed', false)) {
1490
			$installedVersion = $config->getValue('version', '0.0.0');
1491
			$currentVersion = implode('.', \OCP\Util::getVersion());
1492
			$versionDiff = version_compare($currentVersion, $installedVersion);
1493
			if ($versionDiff > 0) {
1494
				return true;
1495
			} else if ($config->getValue('debug', false) && $versionDiff < 0) {
1496
				// downgrade with debug
1497
				$installedMajor = explode('.', $installedVersion);
1498
				$installedMajor = $installedMajor[0] . '.' . $installedMajor[1];
1499
				$currentMajor = explode('.', $currentVersion);
1500
				$currentMajor = $currentMajor[0] . '.' . $currentMajor[1];
1501
				if ($installedMajor === $currentMajor) {
1502
					// Same major, allow downgrade for developers
1503
					return true;
1504
				} else {
1505
					// downgrade attempt, throw exception
1506
					throw new \OC\HintException('Downgrading is not supported and is likely to cause unpredictable issues (from ' . $installedVersion . ' to ' . $currentVersion . ')');
1507
				}
1508
			} else if ($versionDiff < 0) {
1509
				// downgrade attempt, throw exception
1510
				throw new \OC\HintException('Downgrading is not supported and is likely to cause unpredictable issues (from ' . $installedVersion . ' to ' . $currentVersion . ')');
1511
			}
1512
1513
			// also check for upgrades for apps (independently from the user)
1514
			$apps = \OC_App::getEnabledApps(false, true);
1515
			$shouldUpgrade = false;
1516
			foreach ($apps as $app) {
1517
				if (\OC_App::shouldUpgrade($app)) {
1518
					$shouldUpgrade = true;
1519
					break;
1520
				}
1521
			}
1522
			return $shouldUpgrade;
1523
		} else {
1524
			return false;
1525
		}
1526
	}
1527
1528
}
1529